2017-11-19 17:05:11 +03:00
// SPDX-License-Identifier: GPL-2.0
2021-01-01 02:00:01 +03:00
/* Copyright (C) B.A.T.M.A.N. contributors:
2016-01-16 11:40:09 +03:00
*
* Linus Lüssing , Marek Lindner
*/
# include "bat_v_elp.h"
# include "main.h"
# include <linux/atomic.h>
2017-06-09 18:06:51 +03:00
# include <linux/bitops.h>
2016-01-16 11:40:09 +03:00
# include <linux/byteorder/generic.h>
2022-01-21 19:14:44 +03:00
# include <linux/container_of.h>
2016-01-16 11:40:09 +03:00
# include <linux/errno.h>
# include <linux/etherdevice.h>
2015-11-10 20:50:51 +03:00
# include <linux/ethtool.h>
2017-11-19 19:12:02 +03:00
# include <linux/gfp.h>
2016-01-16 11:40:09 +03:00
# include <linux/if_ether.h>
# include <linux/jiffies.h>
2015-11-10 20:50:51 +03:00
# include <linux/kref.h>
2020-10-26 23:01:59 +03:00
# include <linux/minmax.h>
2016-01-16 11:40:09 +03:00
# include <linux/netdevice.h>
2017-06-09 18:06:51 +03:00
# include <linux/nl80211.h>
2016-01-16 11:40:09 +03:00
# include <linux/random.h>
# include <linux/rculist.h>
# include <linux/rcupdate.h>
2015-11-10 20:50:51 +03:00
# include <linux/rtnetlink.h>
2016-01-16 11:40:09 +03:00
# include <linux/skbuff.h>
# include <linux/stddef.h>
# include <linux/string.h>
# include <linux/types.h>
# include <linux/workqueue.h>
2015-11-10 20:50:51 +03:00
# include <net/cfg80211.h>
2017-12-21 12:17:41 +03:00
# include <uapi/linux/batadv_packet.h>
2016-01-16 11:40:09 +03:00
# include "bat_algo.h"
2016-01-16 11:40:13 +03:00
# include "bat_v_ogm.h"
2016-01-16 11:40:09 +03:00
# include "hard-interface.h"
2016-05-16 00:48:31 +03:00
# include "log.h"
2016-01-16 11:40:10 +03:00
# include "originator.h"
# include "routing.h"
2016-01-16 11:40:09 +03:00
# include "send.h"
/**
2017-12-02 21:51:47 +03:00
* batadv_v_elp_start_timer ( ) - restart timer for ELP periodic work
2016-01-16 11:40:09 +03:00
* @ hard_iface : the interface for which the timer has to be reset
*/
static void batadv_v_elp_start_timer ( struct batadv_hard_iface * hard_iface )
{
unsigned int msecs ;
msecs = atomic_read ( & hard_iface - > bat_v . elp_interval ) - BATADV_JITTER ;
2022-10-10 05:44:02 +03:00
msecs + = get_random_u32_below ( 2 * BATADV_JITTER ) ;
2016-01-16 11:40:09 +03:00
queue_delayed_work ( batadv_event_workqueue , & hard_iface - > bat_v . elp_wq ,
msecs_to_jiffies ( msecs ) ) ;
}
2015-11-10 20:50:51 +03:00
/**
2017-12-02 21:51:47 +03:00
* batadv_v_elp_get_throughput ( ) - get the throughput towards a neighbour
2015-11-10 20:50:51 +03:00
* @ neigh : the neighbour for which the throughput has to be obtained
*
* Return : The throughput towards the given neighbour in multiples of 100 kpbs
2020-06-01 21:13:21 +03:00
* ( a value of ' 1 ' equals 0.1 Mbps , ' 10 ' equals 1 Mbps , etc ) .
2015-11-10 20:50:51 +03:00
*/
static u32 batadv_v_elp_get_throughput ( struct batadv_hardif_neigh_node * neigh )
{
struct batadv_hard_iface * hard_iface = neigh - > if_incoming ;
struct ethtool_link_ksettings link_settings ;
2016-09-30 16:21:05 +03:00
struct net_device * real_netdev ;
2015-11-10 20:50:51 +03:00
struct station_info sinfo ;
u32 throughput ;
int ret ;
/* if the user specified a customised value for this interface, then
* return it directly
*/
throughput = atomic_read ( & hard_iface - > bat_v . throughput_override ) ;
if ( throughput ! = 0 )
return throughput ;
/* if this is a wireless device, then ask its throughput through
* cfg80211 API
*/
2016-09-30 16:21:03 +03:00
if ( batadv_is_wifi_hardif ( hard_iface ) ) {
if ( ! batadv_is_cfg80211_hardif ( hard_iface ) )
2016-09-30 16:21:02 +03:00
/* unsupported WiFi driver version */
goto default_throughput ;
2015-11-10 20:50:51 +03:00
2016-09-30 16:21:05 +03:00
real_netdev = batadv_get_real_netdev ( hard_iface - > net_dev ) ;
if ( ! real_netdev )
goto default_throughput ;
ret = cfg80211_get_station ( real_netdev , neigh - > addr , & sinfo ) ;
2019-02-22 18:25:54 +03:00
if ( ! ret ) {
/* free the TID stats immediately */
cfg80211_sinfo_release_content ( & sinfo ) ;
}
2019-01-25 10:21:26 +03:00
2016-09-30 16:21:05 +03:00
dev_put ( real_netdev ) ;
2016-09-30 16:21:02 +03:00
if ( ret = = - ENOENT ) {
/* Node is not associated anymore! It would be
* possible to delete this neighbor . For now set
* the throughput metric to 0.
*/
return 0 ;
}
2017-06-09 18:06:50 +03:00
if ( ret )
goto default_throughput ;
2019-11-26 05:27:38 +03:00
if ( sinfo . filled & BIT ( NL80211_STA_INFO_EXPECTED_THROUGHPUT ) )
return sinfo . expected_throughput / 100 ;
/* try to estimate the expected throughput based on reported tx
* rates
*/
if ( sinfo . filled & BIT ( NL80211_STA_INFO_TX_BITRATE ) )
return cfg80211_calculate_bitrate ( & sinfo . txrate ) / 3 ;
goto default_throughput ;
2015-11-10 20:50:51 +03:00
}
/* if not a wifi interface, check if this device provides data via
* ethtool ( e . g . an Ethernet adapter )
*/
rtnl_lock ( ) ;
ret = __ethtool_get_link_ksettings ( hard_iface - > net_dev , & link_settings ) ;
rtnl_unlock ( ) ;
2019-11-25 12:46:50 +03:00
if ( ret = = 0 ) {
2015-11-10 20:50:51 +03:00
/* link characteristics might change over time */
if ( link_settings . base . duplex = = DUPLEX_FULL )
hard_iface - > bat_v . flags | = BATADV_FULL_DUPLEX ;
else
hard_iface - > bat_v . flags & = ~ BATADV_FULL_DUPLEX ;
throughput = link_settings . base . speed ;
2017-08-23 22:52:13 +03:00
if ( throughput & & throughput ! = SPEED_UNKNOWN )
2015-11-10 20:50:51 +03:00
return throughput * 10 ;
}
default_throughput :
if ( ! ( hard_iface - > bat_v . flags & BATADV_WARNING_DEFAULT ) ) {
batadv_info ( hard_iface - > soft_iface ,
" WiFi driver or ethtool info does not provide information about link speeds on interface %s, therefore defaulting to hardcoded throughput values of %u.%1u Mbps. Consider overriding the throughput manually or checking your driver. \n " ,
hard_iface - > net_dev - > name ,
BATADV_THROUGHPUT_DEFAULT_VALUE / 10 ,
BATADV_THROUGHPUT_DEFAULT_VALUE % 10 ) ;
hard_iface - > bat_v . flags | = BATADV_WARNING_DEFAULT ;
}
/* if none of the above cases apply, return the base_throughput */
return BATADV_THROUGHPUT_DEFAULT_VALUE ;
}
/**
2017-12-02 21:51:47 +03:00
* batadv_v_elp_throughput_metric_update ( ) - worker updating the throughput
* metric of a single hop neighbour
2015-11-10 20:50:51 +03:00
* @ work : the work queue item
*/
void batadv_v_elp_throughput_metric_update ( struct work_struct * work )
{
struct batadv_hardif_neigh_node_bat_v * neigh_bat_v ;
struct batadv_hardif_neigh_node * neigh ;
neigh_bat_v = container_of ( work , struct batadv_hardif_neigh_node_bat_v ,
metric_work ) ;
neigh = container_of ( neigh_bat_v , struct batadv_hardif_neigh_node ,
bat_v ) ;
ewma_throughput_add ( & neigh - > bat_v . throughput ,
batadv_v_elp_get_throughput ( neigh ) ) ;
/* decrement refcounter to balance increment performed before scheduling
* this task
*/
batadv_hardif_neigh_put ( neigh ) ;
}
2015-11-10 20:51:22 +03:00
/**
2017-12-02 21:51:47 +03:00
* batadv_v_elp_wifi_neigh_probe ( ) - send link probing packets to a neighbour
2015-11-10 20:51:22 +03:00
* @ neigh : the neighbour to probe
*
* Sends a predefined number of unicast wifi packets to a given neighbour in
* order to trigger the throughput estimation on this link by the RC algorithm .
2020-06-01 21:13:21 +03:00
* Packets are sent only if there is not enough payload unicast traffic towards
* this neighbour . .
2015-11-10 20:51:22 +03:00
*
* Return : True on success and false in case of error during skb preparation .
*/
static bool
batadv_v_elp_wifi_neigh_probe ( struct batadv_hardif_neigh_node * neigh )
{
struct batadv_hard_iface * hard_iface = neigh - > if_incoming ;
struct batadv_priv * bat_priv = netdev_priv ( hard_iface - > soft_iface ) ;
unsigned long last_tx_diff ;
struct sk_buff * skb ;
int probe_len , i ;
int elp_skb_len ;
/* this probing routine is for Wifi neighbours only */
2016-09-30 16:21:03 +03:00
if ( ! batadv_is_wifi_hardif ( hard_iface ) )
2015-11-10 20:51:22 +03:00
return true ;
/* probe the neighbor only if no unicast packets have been sent
* to it in the last 100 milliseconds : this is the rate control
* algorithm sampling interval ( minstrel ) . In this way , if not
* enough traffic has been sent to the neighbor , batman - adv can
* generate 2 probe packets and push the RC algorithm to perform
* the sampling
*/
last_tx_diff = jiffies_to_msecs ( jiffies - neigh - > bat_v . last_unicast_tx ) ;
if ( last_tx_diff < = BATADV_ELP_PROBE_MAX_TX_DIFF )
return true ;
probe_len = max_t ( int , sizeof ( struct batadv_elp_packet ) ,
BATADV_ELP_MIN_PROBE_SIZE ) ;
for ( i = 0 ; i < BATADV_ELP_PROBES_PER_NODE ; i + + ) {
elp_skb_len = hard_iface - > bat_v . elp_skb - > len ;
skb = skb_copy_expand ( hard_iface - > bat_v . elp_skb , 0 ,
probe_len - elp_skb_len ,
GFP_ATOMIC ) ;
if ( ! skb )
return false ;
/* Tell the skb to get as big as the allocated space (we want
* the packet to be exactly of that size to make the link
* throughput estimation effective .
*/
2018-08-31 16:08:44 +03:00
skb_put_zero ( skb , probe_len - hard_iface - > bat_v . elp_skb - > len ) ;
2015-11-10 20:51:22 +03:00
batadv_dbg ( BATADV_DBG_BATMAN , bat_priv ,
" Sending unicast (probe) ELP packet on interface %s to %pM \n " ,
hard_iface - > net_dev - > name , neigh - > addr ) ;
batadv_send_skb_packet ( skb , hard_iface , neigh - > addr ) ;
}
return true ;
}
2016-01-16 11:40:09 +03:00
/**
2017-12-02 21:51:47 +03:00
* batadv_v_elp_periodic_work ( ) - ELP periodic task per interface
2016-01-16 11:40:09 +03:00
* @ work : work queue item
*
2020-06-01 21:13:21 +03:00
* Emits broadcast ELP messages in regular intervals .
2016-01-16 11:40:09 +03:00
*/
static void batadv_v_elp_periodic_work ( struct work_struct * work )
{
2015-11-10 20:50:51 +03:00
struct batadv_hardif_neigh_node * hardif_neigh ;
2016-01-16 11:40:09 +03:00
struct batadv_hard_iface * hard_iface ;
struct batadv_hard_iface_bat_v * bat_v ;
struct batadv_elp_packet * elp_packet ;
struct batadv_priv * bat_priv ;
struct sk_buff * skb ;
u32 elp_interval ;
2018-09-07 00:45:55 +03:00
bool ret ;
2016-01-16 11:40:09 +03:00
bat_v = container_of ( work , struct batadv_hard_iface_bat_v , elp_wq . work ) ;
hard_iface = container_of ( bat_v , struct batadv_hard_iface , bat_v ) ;
bat_priv = netdev_priv ( hard_iface - > soft_iface ) ;
if ( atomic_read ( & bat_priv - > mesh_state ) = = BATADV_MESH_DEACTIVATING )
goto out ;
/* we are in the process of shutting this interface down */
2017-08-23 22:52:13 +03:00
if ( hard_iface - > if_status = = BATADV_IF_NOT_IN_USE | |
hard_iface - > if_status = = BATADV_IF_TO_BE_REMOVED )
2016-01-16 11:40:09 +03:00
goto out ;
/* the interface was enabled but may not be ready yet */
if ( hard_iface - > if_status ! = BATADV_IF_ACTIVE )
goto restart_timer ;
skb = skb_copy ( hard_iface - > bat_v . elp_skb , GFP_ATOMIC ) ;
if ( ! skb )
goto restart_timer ;
elp_packet = ( struct batadv_elp_packet * ) skb - > data ;
elp_packet - > seqno = htonl ( atomic_read ( & hard_iface - > bat_v . elp_seqno ) ) ;
elp_interval = atomic_read ( & hard_iface - > bat_v . elp_interval ) ;
elp_packet - > elp_interval = htonl ( elp_interval ) ;
batadv_dbg ( BATADV_DBG_BATMAN , bat_priv ,
" Sending broadcast ELP packet on interface %s, seqno %u \n " ,
hard_iface - > net_dev - > name ,
atomic_read ( & hard_iface - > bat_v . elp_seqno ) ) ;
2016-01-16 11:40:15 +03:00
batadv_send_broadcast_skb ( skb , hard_iface ) ;
2016-01-16 11:40:09 +03:00
atomic_inc ( & hard_iface - > bat_v . elp_seqno ) ;
2015-11-10 20:50:51 +03:00
/* The throughput metric is updated on each sent packet. This way, if a
* node is dead and no longer sends packets , batman - adv is still able to
* react timely to its death .
*
* The throughput metric is updated by following these steps :
* 1 ) if the hard_iface is wifi = > send a number of unicast ELPs for
* probing / sampling to each neighbor
* 2 ) update the throughput metric value of each neighbor ( note that the
* value retrieved in this step might be 100 ms old because the
* probing packets at point 1 ) could still be in the HW queue )
*/
rcu_read_lock ( ) ;
hlist_for_each_entry_rcu ( hardif_neigh , & hard_iface - > neigh_list , list ) {
2015-11-10 20:51:22 +03:00
if ( ! batadv_v_elp_wifi_neigh_probe ( hardif_neigh ) )
/* if something goes wrong while probing, better to stop
* sending packets immediately and reschedule the task
*/
break ;
2015-11-10 20:50:51 +03:00
if ( ! kref_get_unless_zero ( & hardif_neigh - > refcount ) )
continue ;
/* Reading the estimated throughput from cfg80211 is a task that
* may sleep and that is not allowed in an rcu protected
* context . Therefore schedule a task for that .
*/
2018-09-07 00:45:55 +03:00
ret = queue_work ( batadv_event_workqueue ,
& hardif_neigh - > bat_v . metric_work ) ;
if ( ! ret )
batadv_hardif_neigh_put ( hardif_neigh ) ;
2015-11-10 20:50:51 +03:00
}
rcu_read_unlock ( ) ;
2016-01-16 11:40:09 +03:00
restart_timer :
batadv_v_elp_start_timer ( hard_iface ) ;
out :
return ;
}
/**
2017-12-02 21:51:47 +03:00
* batadv_v_elp_iface_enable ( ) - setup the ELP interface private resources
2016-01-16 11:40:09 +03:00
* @ hard_iface : interface for which the data has to be prepared
*
* Return : 0 on success or a - ENOMEM in case of failure .
*/
int batadv_v_elp_iface_enable ( struct batadv_hard_iface * hard_iface )
{
2018-10-30 14:17:10 +03:00
static const size_t tvlv_padding = sizeof ( __be32 ) ;
2016-01-16 11:40:09 +03:00
struct batadv_elp_packet * elp_packet ;
unsigned char * elp_buff ;
u32 random_seqno ;
size_t size ;
int res = - ENOMEM ;
2018-10-30 14:17:10 +03:00
size = ETH_HLEN + NET_IP_ALIGN + BATADV_ELP_HLEN + tvlv_padding ;
2016-01-16 11:40:09 +03:00
hard_iface - > bat_v . elp_skb = dev_alloc_skb ( size ) ;
if ( ! hard_iface - > bat_v . elp_skb )
goto out ;
skb_reserve ( hard_iface - > bat_v . elp_skb , ETH_HLEN + NET_IP_ALIGN ) ;
2018-10-30 14:17:10 +03:00
elp_buff = skb_put_zero ( hard_iface - > bat_v . elp_skb ,
BATADV_ELP_HLEN + tvlv_padding ) ;
2016-01-16 11:40:09 +03:00
elp_packet = ( struct batadv_elp_packet * ) elp_buff ;
elp_packet - > packet_type = BATADV_ELP ;
elp_packet - > version = BATADV_COMPAT_VERSION ;
/* randomize initial seqno to avoid collision */
get_random_bytes ( & random_seqno , sizeof ( random_seqno ) ) ;
atomic_set ( & hard_iface - > bat_v . elp_seqno , random_seqno ) ;
2015-11-10 20:50:51 +03:00
/* assume full-duplex by default */
hard_iface - > bat_v . flags | = BATADV_FULL_DUPLEX ;
/* warn the user (again) if there is no throughput data is available */
hard_iface - > bat_v . flags & = ~ BATADV_WARNING_DEFAULT ;
2016-09-30 16:21:03 +03:00
if ( batadv_is_wifi_hardif ( hard_iface ) )
2015-11-10 20:50:51 +03:00
hard_iface - > bat_v . flags & = ~ BATADV_FULL_DUPLEX ;
2016-01-16 11:40:09 +03:00
INIT_DELAYED_WORK ( & hard_iface - > bat_v . elp_wq ,
batadv_v_elp_periodic_work ) ;
batadv_v_elp_start_timer ( hard_iface ) ;
res = 0 ;
out :
return res ;
}
/**
2017-12-02 21:51:47 +03:00
* batadv_v_elp_iface_disable ( ) - release ELP interface private resources
2016-01-16 11:40:09 +03:00
* @ hard_iface : interface for which the resources have to be released
*/
void batadv_v_elp_iface_disable ( struct batadv_hard_iface * hard_iface )
{
cancel_delayed_work_sync ( & hard_iface - > bat_v . elp_wq ) ;
dev_kfree_skb ( hard_iface - > bat_v . elp_skb ) ;
hard_iface - > bat_v . elp_skb = NULL ;
}
2016-05-07 14:54:17 +03:00
/**
2017-12-02 21:51:47 +03:00
* batadv_v_elp_iface_activate ( ) - update the ELP buffer belonging to the given
2016-05-07 14:54:17 +03:00
* hard - interface
* @ primary_iface : the new primary interface
* @ hard_iface : interface holding the to - be - updated buffer
*/
void batadv_v_elp_iface_activate ( struct batadv_hard_iface * primary_iface ,
struct batadv_hard_iface * hard_iface )
{
struct batadv_elp_packet * elp_packet ;
struct sk_buff * skb ;
if ( ! hard_iface - > bat_v . elp_skb )
return ;
skb = hard_iface - > bat_v . elp_skb ;
elp_packet = ( struct batadv_elp_packet * ) skb - > data ;
ether_addr_copy ( elp_packet - > orig ,
primary_iface - > net_dev - > dev_addr ) ;
}
2016-01-16 11:40:09 +03:00
/**
2017-12-02 21:51:47 +03:00
* batadv_v_elp_primary_iface_set ( ) - change internal data to reflect the new
2016-01-16 11:40:09 +03:00
* primary interface
* @ primary_iface : the new primary interface
*/
void batadv_v_elp_primary_iface_set ( struct batadv_hard_iface * primary_iface )
{
struct batadv_hard_iface * hard_iface ;
/* update orig field of every elp iface belonging to this mesh */
rcu_read_lock ( ) ;
list_for_each_entry_rcu ( hard_iface , & batadv_hardif_list , list ) {
if ( primary_iface - > soft_iface ! = hard_iface - > soft_iface )
continue ;
2016-05-07 14:54:17 +03:00
batadv_v_elp_iface_activate ( primary_iface , hard_iface ) ;
2016-01-16 11:40:09 +03:00
}
rcu_read_unlock ( ) ;
}
2016-01-16 11:40:10 +03:00
/**
2017-12-02 21:51:47 +03:00
* batadv_v_elp_neigh_update ( ) - update an ELP neighbour node
2016-01-16 11:40:10 +03:00
* @ bat_priv : the bat priv with all the soft interface information
* @ neigh_addr : the neighbour interface address
* @ if_incoming : the interface the packet was received through
* @ elp_packet : the received ELP packet
*
* Updates the ELP neighbour node state with the data received within the new
* ELP packet .
*/
static void batadv_v_elp_neigh_update ( struct batadv_priv * bat_priv ,
u8 * neigh_addr ,
struct batadv_hard_iface * if_incoming ,
struct batadv_elp_packet * elp_packet )
{
struct batadv_neigh_node * neigh ;
struct batadv_orig_node * orig_neigh ;
struct batadv_hardif_neigh_node * hardif_neigh ;
s32 seqno_diff ;
s32 elp_latest_seqno ;
orig_neigh = batadv_v_ogm_orig_get ( bat_priv , elp_packet - > orig ) ;
if ( ! orig_neigh )
return ;
2016-05-02 20:52:08 +03:00
neigh = batadv_neigh_node_get_or_create ( orig_neigh ,
if_incoming , neigh_addr ) ;
2016-01-16 11:40:10 +03:00
if ( ! neigh )
goto orig_free ;
hardif_neigh = batadv_hardif_neigh_get ( if_incoming , neigh_addr ) ;
if ( ! hardif_neigh )
goto neigh_free ;
elp_latest_seqno = hardif_neigh - > bat_v . elp_latest_seqno ;
seqno_diff = ntohl ( elp_packet - > seqno ) - elp_latest_seqno ;
/* known or older sequence numbers are ignored. However always adopt
* if the router seems to have been restarted .
*/
if ( seqno_diff < 1 & & seqno_diff > - BATADV_ELP_MAX_AGE )
goto hardif_free ;
neigh - > last_seen = jiffies ;
hardif_neigh - > last_seen = jiffies ;
hardif_neigh - > bat_v . elp_latest_seqno = ntohl ( elp_packet - > seqno ) ;
hardif_neigh - > bat_v . elp_interval = ntohl ( elp_packet - > elp_interval ) ;
hardif_free :
2021-08-08 20:11:08 +03:00
batadv_hardif_neigh_put ( hardif_neigh ) ;
2016-01-16 11:40:10 +03:00
neigh_free :
2021-08-08 20:11:08 +03:00
batadv_neigh_node_put ( neigh ) ;
2016-01-16 11:40:10 +03:00
orig_free :
2021-08-08 20:11:08 +03:00
batadv_orig_node_put ( orig_neigh ) ;
2016-01-16 11:40:10 +03:00
}
/**
2017-12-02 21:51:47 +03:00
* batadv_v_elp_packet_recv ( ) - main ELP packet handler
2016-01-16 11:40:10 +03:00
* @ skb : the received packet
* @ if_incoming : the interface this packet was received through
*
2020-06-01 21:13:21 +03:00
* Return : NET_RX_SUCCESS and consumes the skb if the packet was properly
2016-01-16 11:40:10 +03:00
* processed or NET_RX_DROP in case of failure .
*/
int batadv_v_elp_packet_recv ( struct sk_buff * skb ,
struct batadv_hard_iface * if_incoming )
{
struct batadv_priv * bat_priv = netdev_priv ( if_incoming - > soft_iface ) ;
struct batadv_elp_packet * elp_packet ;
struct batadv_hard_iface * primary_if ;
batman-adv: Do not get eth header before batadv_check_management_packet
If received skb in batadv_v_elp_packet_recv or batadv_v_ogm_packet_recv
is either cloned or non linearized then its data buffer will be
reallocated by batadv_check_management_packet when skb_cow or
skb_linearize get called. Thus geting ethernet header address inside
skb data buffer before batadv_check_management_packet had any chance to
reallocate it could lead to the following kernel panic:
Unable to handle kernel paging request at virtual address ffffff8020ab069a
Mem abort info:
ESR = 0x96000007
EC = 0x25: DABT (current EL), IL = 32 bits
SET = 0, FnV = 0
EA = 0, S1PTW = 0
FSC = 0x07: level 3 translation fault
Data abort info:
ISV = 0, ISS = 0x00000007
CM = 0, WnR = 0
swapper pgtable: 4k pages, 39-bit VAs, pgdp=0000000040f45000
[ffffff8020ab069a] pgd=180000007fffa003, p4d=180000007fffa003, pud=180000007fffa003, pmd=180000007fefe003, pte=0068000020ab0706
Internal error: Oops: 96000007 [#1] SMP
Modules linked in: ahci_mvebu libahci_platform libahci dvb_usb_af9035 dvb_usb_dib0700 dib0070 dib7000m dibx000_common ath11k_pci ath10k_pci ath10k_core mwl8k_new nf_nat_sip nf_conntrack_sip xhci_plat_hcd xhci_hcd nf_nat_pptp nf_conntrack_pptp at24 sbsa_gwdt
CPU: 1 PID: 16 Comm: ksoftirqd/1 Not tainted 5.15.42-00066-g3242268d425c-dirty #550
Hardware name: A8k (DT)
pstate: 60000005 (nZCv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--)
pc : batadv_is_my_mac+0x60/0xc0
lr : batadv_v_ogm_packet_recv+0x98/0x5d0
sp : ffffff8000183820
x29: ffffff8000183820 x28: 0000000000000001 x27: ffffff8014f9af00
x26: 0000000000000000 x25: 0000000000000543 x24: 0000000000000003
x23: ffffff8020ab0580 x22: 0000000000000110 x21: ffffff80168ae880
x20: 0000000000000000 x19: ffffff800b561000 x18: 0000000000000000
x17: 0000000000000000 x16: 0000000000000000 x15: 00dc098924ae0032
x14: 0f0405433e0054b0 x13: ffffffff00000080 x12: 0000004000000001
x11: 0000000000000000 x10: 0000000000000000 x9 : 0000000000000000
x8 : 0000000000000000 x7 : ffffffc076dae000 x6 : ffffff8000183700
x5 : ffffffc00955e698 x4 : ffffff80168ae000 x3 : ffffff80059cf000
x2 : ffffff800b561000 x1 : ffffff8020ab0696 x0 : ffffff80168ae880
Call trace:
batadv_is_my_mac+0x60/0xc0
batadv_v_ogm_packet_recv+0x98/0x5d0
batadv_batman_skb_recv+0x1b8/0x244
__netif_receive_skb_core.isra.0+0x440/0xc74
__netif_receive_skb_one_core+0x14/0x20
netif_receive_skb+0x68/0x140
br_pass_frame_up+0x70/0x80
br_handle_frame_finish+0x108/0x284
br_handle_frame+0x190/0x250
__netif_receive_skb_core.isra.0+0x240/0xc74
__netif_receive_skb_list_core+0x6c/0x90
netif_receive_skb_list_internal+0x1f4/0x310
napi_complete_done+0x64/0x1d0
gro_cell_poll+0x7c/0xa0
__napi_poll+0x34/0x174
net_rx_action+0xf8/0x2a0
_stext+0x12c/0x2ac
run_ksoftirqd+0x4c/0x7c
smpboot_thread_fn+0x120/0x210
kthread+0x140/0x150
ret_from_fork+0x10/0x20
Code: f9403844 eb03009f 54fffee1 f94
Thus ethernet header address should only be fetched after
batadv_check_management_packet has been called.
Fixes: 0da0035942d4 ("batman-adv: OGMv2 - add basic infrastructure")
Cc: stable@vger.kernel.org
Signed-off-by: Remi Pommarel <repk@triplefau.lt>
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Signed-off-by: Simon Wunderlich <sw@simonwunderlich.de>
2023-07-28 16:38:50 +03:00
struct ethhdr * ethhdr ;
2016-07-17 22:04:04 +03:00
bool res ;
int ret = NET_RX_DROP ;
2016-01-16 11:40:10 +03:00
2016-07-17 22:04:04 +03:00
res = batadv_check_management_packet ( skb , if_incoming , BATADV_ELP_HLEN ) ;
if ( ! res )
goto free_skb ;
2016-01-16 11:40:10 +03:00
batman-adv: Do not get eth header before batadv_check_management_packet
If received skb in batadv_v_elp_packet_recv or batadv_v_ogm_packet_recv
is either cloned or non linearized then its data buffer will be
reallocated by batadv_check_management_packet when skb_cow or
skb_linearize get called. Thus geting ethernet header address inside
skb data buffer before batadv_check_management_packet had any chance to
reallocate it could lead to the following kernel panic:
Unable to handle kernel paging request at virtual address ffffff8020ab069a
Mem abort info:
ESR = 0x96000007
EC = 0x25: DABT (current EL), IL = 32 bits
SET = 0, FnV = 0
EA = 0, S1PTW = 0
FSC = 0x07: level 3 translation fault
Data abort info:
ISV = 0, ISS = 0x00000007
CM = 0, WnR = 0
swapper pgtable: 4k pages, 39-bit VAs, pgdp=0000000040f45000
[ffffff8020ab069a] pgd=180000007fffa003, p4d=180000007fffa003, pud=180000007fffa003, pmd=180000007fefe003, pte=0068000020ab0706
Internal error: Oops: 96000007 [#1] SMP
Modules linked in: ahci_mvebu libahci_platform libahci dvb_usb_af9035 dvb_usb_dib0700 dib0070 dib7000m dibx000_common ath11k_pci ath10k_pci ath10k_core mwl8k_new nf_nat_sip nf_conntrack_sip xhci_plat_hcd xhci_hcd nf_nat_pptp nf_conntrack_pptp at24 sbsa_gwdt
CPU: 1 PID: 16 Comm: ksoftirqd/1 Not tainted 5.15.42-00066-g3242268d425c-dirty #550
Hardware name: A8k (DT)
pstate: 60000005 (nZCv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--)
pc : batadv_is_my_mac+0x60/0xc0
lr : batadv_v_ogm_packet_recv+0x98/0x5d0
sp : ffffff8000183820
x29: ffffff8000183820 x28: 0000000000000001 x27: ffffff8014f9af00
x26: 0000000000000000 x25: 0000000000000543 x24: 0000000000000003
x23: ffffff8020ab0580 x22: 0000000000000110 x21: ffffff80168ae880
x20: 0000000000000000 x19: ffffff800b561000 x18: 0000000000000000
x17: 0000000000000000 x16: 0000000000000000 x15: 00dc098924ae0032
x14: 0f0405433e0054b0 x13: ffffffff00000080 x12: 0000004000000001
x11: 0000000000000000 x10: 0000000000000000 x9 : 0000000000000000
x8 : 0000000000000000 x7 : ffffffc076dae000 x6 : ffffff8000183700
x5 : ffffffc00955e698 x4 : ffffff80168ae000 x3 : ffffff80059cf000
x2 : ffffff800b561000 x1 : ffffff8020ab0696 x0 : ffffff80168ae880
Call trace:
batadv_is_my_mac+0x60/0xc0
batadv_v_ogm_packet_recv+0x98/0x5d0
batadv_batman_skb_recv+0x1b8/0x244
__netif_receive_skb_core.isra.0+0x440/0xc74
__netif_receive_skb_one_core+0x14/0x20
netif_receive_skb+0x68/0x140
br_pass_frame_up+0x70/0x80
br_handle_frame_finish+0x108/0x284
br_handle_frame+0x190/0x250
__netif_receive_skb_core.isra.0+0x240/0xc74
__netif_receive_skb_list_core+0x6c/0x90
netif_receive_skb_list_internal+0x1f4/0x310
napi_complete_done+0x64/0x1d0
gro_cell_poll+0x7c/0xa0
__napi_poll+0x34/0x174
net_rx_action+0xf8/0x2a0
_stext+0x12c/0x2ac
run_ksoftirqd+0x4c/0x7c
smpboot_thread_fn+0x120/0x210
kthread+0x140/0x150
ret_from_fork+0x10/0x20
Code: f9403844 eb03009f 54fffee1 f94
Thus ethernet header address should only be fetched after
batadv_check_management_packet has been called.
Fixes: 0da0035942d4 ("batman-adv: OGMv2 - add basic infrastructure")
Cc: stable@vger.kernel.org
Signed-off-by: Remi Pommarel <repk@triplefau.lt>
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Signed-off-by: Simon Wunderlich <sw@simonwunderlich.de>
2023-07-28 16:38:50 +03:00
ethhdr = eth_hdr ( skb ) ;
2016-01-16 11:40:10 +03:00
if ( batadv_is_my_mac ( bat_priv , ethhdr - > h_source ) )
2016-07-17 22:04:04 +03:00
goto free_skb ;
2016-01-16 11:40:10 +03:00
/* did we receive a B.A.T.M.A.N. V ELP packet on an interface
* that does not have B . A . T . M . A . N . V ELP enabled ?
*/
2016-05-25 18:27:31 +03:00
if ( strcmp ( bat_priv - > algo_ops - > name , " BATMAN_V " ) ! = 0 )
2016-07-17 22:04:04 +03:00
goto free_skb ;
2016-01-16 11:40:10 +03:00
elp_packet = ( struct batadv_elp_packet * ) skb - > data ;
batadv_dbg ( BATADV_DBG_BATMAN , bat_priv ,
" Received ELP packet from %pM seqno %u ORIG: %pM \n " ,
ethhdr - > h_source , ntohl ( elp_packet - > seqno ) ,
elp_packet - > orig ) ;
primary_if = batadv_primary_if_get_selected ( bat_priv ) ;
if ( ! primary_if )
2016-07-17 22:04:04 +03:00
goto free_skb ;
2016-01-16 11:40:10 +03:00
batadv_v_elp_neigh_update ( bat_priv , ethhdr - > h_source , if_incoming ,
elp_packet ) ;
2016-07-17 22:04:04 +03:00
ret = NET_RX_SUCCESS ;
batadv_hardif_put ( primary_if ) ;
free_skb :
if ( ret = = NET_RX_SUCCESS )
consume_skb ( skb ) ;
else
kfree_skb ( skb ) ;
return ret ;
2016-01-16 11:40:10 +03:00
}