2023-09-07 04:09:08 +03:00
// SPDX-License-Identifier: GPL-2.0
/* Copyright (C) B.A.T.M.A.N. contributors:
*
* Linus Lüssing
*/
# include "multicast.h"
# include "main.h"
2023-09-07 04:09:09 +03:00
# include <linux/bug.h>
# include <linux/build_bug.h>
2023-09-07 04:09:08 +03:00
# include <linux/byteorder/generic.h>
2023-09-07 04:09:09 +03:00
# include <linux/compiler.h>
2023-09-07 04:09:08 +03:00
# include <linux/errno.h>
# include <linux/etherdevice.h>
# include <linux/gfp.h>
# include <linux/if_ether.h>
2023-09-07 04:09:09 +03:00
# include <linux/if_vlan.h>
# include <linux/ipv6.h>
# include <linux/limits.h>
2023-09-07 04:09:08 +03:00
# include <linux/netdevice.h>
2023-09-07 04:09:09 +03:00
# include <linux/rculist.h>
# include <linux/rcupdate.h>
2023-09-07 04:09:08 +03:00
# include <linux/skbuff.h>
# include <linux/stddef.h>
2023-09-07 04:09:09 +03:00
# include <linux/string.h>
2023-09-07 04:09:08 +03:00
# include <linux/types.h>
# include <uapi/linux/batadv_packet.h>
2023-09-07 04:09:09 +03:00
# include "bridge_loop_avoidance.h"
2023-09-07 04:09:08 +03:00
# include "originator.h"
# include "send.h"
2023-09-07 04:09:09 +03:00
# include "translation-table.h"
2023-09-07 04:09:08 +03:00
# define batadv_mcast_forw_tracker_for_each_dest(dest, num_dests) \
for ( ; num_dests ; num_dests - - , ( dest ) + = ETH_ALEN )
# define batadv_mcast_forw_tracker_for_each_dest2(dest1, dest2, num_dests) \
for ( ; num_dests ; num_dests - - , ( dest1 ) + = ETH_ALEN , ( dest2 ) + = ETH_ALEN )
2023-09-07 04:09:09 +03:00
/**
* batadv_mcast_forw_skb_push ( ) - skb_push and memorize amount of pushed bytes
* @ skb : the skb to push onto
* @ size : the amount of bytes to push
* @ len : stores the total amount of bytes pushed
*
* Performs an skb_push ( ) onto the given skb and adds the amount of pushed bytes
* to the given len pointer .
*
* Return : the return value of the skb_push ( ) call .
*/
static void * batadv_mcast_forw_skb_push ( struct sk_buff * skb , size_t size ,
unsigned short * len )
{
* len + = size ;
return skb_push ( skb , size ) ;
}
/**
* batadv_mcast_forw_push_padding ( ) - push 2 padding bytes to skb ' s front
* @ skb : the skb to push onto
* @ tvlv_len : stores the amount of currently pushed TVLV bytes
*
* Pushes two padding bytes to the front of the given skb .
*
* Return : On success a pointer to the first byte of the two pushed padding
* bytes within the skb . NULL otherwise .
*/
static char *
batadv_mcast_forw_push_padding ( struct sk_buff * skb , unsigned short * tvlv_len )
{
const int pad_len = 2 ;
char * padding ;
if ( skb_headroom ( skb ) < pad_len )
return NULL ;
padding = batadv_mcast_forw_skb_push ( skb , pad_len , tvlv_len ) ;
memset ( padding , 0 , pad_len ) ;
return padding ;
}
/**
* batadv_mcast_forw_push_est_padding ( ) - push padding bytes if necessary
* @ skb : the skb to potentially push the padding onto
* @ count : the ( estimated ) number of originators the multicast packet needs to
* be sent to
* @ tvlv_len : stores the amount of currently pushed TVLV bytes
*
* If the number of destination entries is even then this adds two
* padding bytes to the end of the tracker TVLV .
*
* Return : true on success or if no padding is needed , false otherwise .
*/
static bool
batadv_mcast_forw_push_est_padding ( struct sk_buff * skb , int count ,
unsigned short * tvlv_len )
{
if ( ! ( count % 2 ) & & ! batadv_mcast_forw_push_padding ( skb , tvlv_len ) )
return false ;
return true ;
}
/**
* batadv_mcast_forw_orig_entry ( ) - get orig_node from an hlist node
* @ node : the hlist node to get the orig_node from
* @ entry_offset : the offset of the hlist node within the orig_node struct
*
* Return : The orig_node containing the hlist node on success , NULL on error .
*/
static struct batadv_orig_node *
batadv_mcast_forw_orig_entry ( struct hlist_node * node ,
size_t entry_offset )
{
/* sanity check */
switch ( entry_offset ) {
case offsetof ( struct batadv_orig_node , mcast_want_all_ipv4_node ) :
case offsetof ( struct batadv_orig_node , mcast_want_all_ipv6_node ) :
case offsetof ( struct batadv_orig_node , mcast_want_all_rtr4_node ) :
case offsetof ( struct batadv_orig_node , mcast_want_all_rtr6_node ) :
break ;
default :
WARN_ON ( 1 ) ;
return NULL ;
}
return ( struct batadv_orig_node * ) ( ( void * ) node - entry_offset ) ;
}
/**
* batadv_mcast_forw_push_dest ( ) - push an originator MAC address onto an skb
* @ bat_priv : the bat priv with all the soft interface information
* @ skb : the skb to push the destination address onto
* @ vid : the vlan identifier
* @ orig_node : the originator node to get the MAC address from
* @ num_dests : a pointer to store the number of pushed addresses in
* @ tvlv_len : stores the amount of currently pushed TVLV bytes
*
* If the orig_node is a BLA backbone gateway , if there is not enough skb
* headroom available or if num_dests is already at its maximum ( 65535 ) then
* neither the skb nor num_dests is changed . Otherwise the originator ' s MAC
* address is pushed onto the given skb and num_dests incremented by one .
*
* Return : true if the orig_node is a backbone gateway or if an orig address
* was pushed successfully , false otherwise .
*/
static bool batadv_mcast_forw_push_dest ( struct batadv_priv * bat_priv ,
struct sk_buff * skb , unsigned short vid ,
struct batadv_orig_node * orig_node ,
unsigned short * num_dests ,
unsigned short * tvlv_len )
{
BUILD_BUG_ON ( sizeof_field ( struct batadv_tvlv_mcast_tracker , num_dests )
! = sizeof ( __be16 ) ) ;
/* Avoid sending to other BLA gateways - they already got the frame from
* the LAN side we share with them .
* TODO : Refactor to take BLA into account earlier in mode check .
*/
if ( batadv_bla_is_backbone_gw_orig ( bat_priv , orig_node - > orig , vid ) )
return true ;
if ( skb_headroom ( skb ) < ETH_ALEN | | * num_dests = = U16_MAX )
return false ;
batadv_mcast_forw_skb_push ( skb , ETH_ALEN , tvlv_len ) ;
ether_addr_copy ( skb - > data , orig_node - > orig ) ;
( * num_dests ) + + ;
return true ;
}
/**
* batadv_mcast_forw_push_dests_list ( ) - push originators from list onto an skb
* @ bat_priv : the bat priv with all the soft interface information
* @ skb : the skb to push the destination addresses onto
* @ vid : the vlan identifier
* @ head : the list to gather originators from
* @ entry_offset : offset of an hlist node in an orig_node structure
* @ num_dests : a pointer to store the number of pushed addresses in
* @ tvlv_len : stores the amount of currently pushed TVLV bytes
*
* Push the MAC addresses of all originators in the given list onto the given
* skb .
*
* Return : true on success , false otherwise .
*/
static int batadv_mcast_forw_push_dests_list ( struct batadv_priv * bat_priv ,
struct sk_buff * skb ,
unsigned short vid ,
struct hlist_head * head ,
size_t entry_offset ,
unsigned short * num_dests ,
unsigned short * tvlv_len )
{
struct hlist_node * node ;
struct batadv_orig_node * orig_node ;
rcu_read_lock ( ) ;
__hlist_for_each_rcu ( node , head ) {
orig_node = batadv_mcast_forw_orig_entry ( node , entry_offset ) ;
if ( ! orig_node | |
! batadv_mcast_forw_push_dest ( bat_priv , skb , vid , orig_node ,
num_dests , tvlv_len ) ) {
rcu_read_unlock ( ) ;
return false ;
}
}
rcu_read_unlock ( ) ;
return true ;
}
/**
* batadv_mcast_forw_push_tt ( ) - push originators with interest through TT
* @ bat_priv : the bat priv with all the soft interface information
* @ skb : the skb to push the destination addresses onto
* @ vid : the vlan identifier
* @ num_dests : a pointer to store the number of pushed addresses in
* @ tvlv_len : stores the amount of currently pushed TVLV bytes
*
* Push the MAC addresses of all originators which have indicated interest in
* this multicast packet through the translation table onto the given skb .
*
* Return : true on success , false otherwise .
*/
static bool
batadv_mcast_forw_push_tt ( struct batadv_priv * bat_priv , struct sk_buff * skb ,
unsigned short vid , unsigned short * num_dests ,
unsigned short * tvlv_len )
{
struct batadv_tt_orig_list_entry * orig_entry ;
struct batadv_tt_global_entry * tt_global ;
const u8 * addr = eth_hdr ( skb ) - > h_dest ;
/* ok */
int ret = true ;
tt_global = batadv_tt_global_hash_find ( bat_priv , addr , vid ) ;
if ( ! tt_global )
goto out ;
rcu_read_lock ( ) ;
hlist_for_each_entry_rcu ( orig_entry , & tt_global - > orig_list , list ) {
if ( ! batadv_mcast_forw_push_dest ( bat_priv , skb , vid ,
orig_entry - > orig_node ,
num_dests , tvlv_len ) ) {
ret = false ;
break ;
}
}
rcu_read_unlock ( ) ;
batadv_tt_global_entry_put ( tt_global ) ;
out :
return ret ;
}
/**
* batadv_mcast_forw_push_want_all ( ) - push originators with want - all flag
* @ bat_priv : the bat priv with all the soft interface information
* @ skb : the skb to push the destination addresses onto
* @ vid : the vlan identifier
* @ num_dests : a pointer to store the number of pushed addresses in
* @ tvlv_len : stores the amount of currently pushed TVLV bytes
*
* Push the MAC addresses of all originators which have indicated interest in
* this multicast packet through the want - all flag onto the given skb .
*
* Return : true on success , false otherwise .
*/
static bool batadv_mcast_forw_push_want_all ( struct batadv_priv * bat_priv ,
struct sk_buff * skb ,
unsigned short vid ,
unsigned short * num_dests ,
unsigned short * tvlv_len )
{
struct hlist_head * head = NULL ;
size_t offset ;
int ret ;
switch ( eth_hdr ( skb ) - > h_proto ) {
case htons ( ETH_P_IP ) :
head = & bat_priv - > mcast . want_all_ipv4_list ;
offset = offsetof ( struct batadv_orig_node ,
mcast_want_all_ipv4_node ) ;
break ;
case htons ( ETH_P_IPV6 ) :
head = & bat_priv - > mcast . want_all_ipv6_list ;
offset = offsetof ( struct batadv_orig_node ,
mcast_want_all_ipv6_node ) ;
break ;
default :
return false ;
}
ret = batadv_mcast_forw_push_dests_list ( bat_priv , skb , vid , head ,
offset , num_dests , tvlv_len ) ;
if ( ! ret )
return false ;
return true ;
}
/**
* batadv_mcast_forw_push_want_rtr ( ) - push originators with want - router flag
* @ bat_priv : the bat priv with all the soft interface information
* @ skb : the skb to push the destination addresses onto
* @ vid : the vlan identifier
* @ num_dests : a pointer to store the number of pushed addresses in
* @ tvlv_len : stores the amount of currently pushed TVLV bytes
*
* Push the MAC addresses of all originators which have indicated interest in
* this multicast packet through the want - all - rtr flag onto the given skb .
*
* Return : true on success , false otherwise .
*/
static bool batadv_mcast_forw_push_want_rtr ( struct batadv_priv * bat_priv ,
struct sk_buff * skb ,
unsigned short vid ,
unsigned short * num_dests ,
unsigned short * tvlv_len )
{
struct hlist_head * head = NULL ;
size_t offset ;
int ret ;
switch ( eth_hdr ( skb ) - > h_proto ) {
case htons ( ETH_P_IP ) :
head = & bat_priv - > mcast . want_all_rtr4_list ;
offset = offsetof ( struct batadv_orig_node ,
mcast_want_all_rtr4_node ) ;
break ;
case htons ( ETH_P_IPV6 ) :
head = & bat_priv - > mcast . want_all_rtr6_list ;
offset = offsetof ( struct batadv_orig_node ,
mcast_want_all_rtr6_node ) ;
break ;
default :
return false ;
}
ret = batadv_mcast_forw_push_dests_list ( bat_priv , skb , vid , head ,
offset , num_dests , tvlv_len ) ;
if ( ! ret )
return false ;
return true ;
}
/**
* batadv_mcast_forw_scrape ( ) - remove bytes within skb data
* @ skb : the skb to remove bytes from
* @ offset : the offset from the skb data from which to scrape
* @ len : the amount of bytes to scrape starting from the offset
*
* Scrapes / removes len bytes from the given skb at the given offset from the
* skb data .
*
* Caller needs to ensure that the region from the skb data ' s start up
* to / including the to be removed bytes are linearized .
*/
static void batadv_mcast_forw_scrape ( struct sk_buff * skb ,
unsigned short offset ,
unsigned short len )
{
char * to , * from ;
SKB_LINEAR_ASSERT ( skb ) ;
to = skb_pull ( skb , len ) ;
from = to - len ;
memmove ( to , from , offset ) ;
}
/**
* batadv_mcast_forw_push_scrape_padding ( ) - remove TVLV padding
* @ skb : the skb to potentially adjust the TVLV ' s padding on
* @ tvlv_len : stores the amount of currently pushed TVLV bytes
*
* Remove two padding bytes from the end of the multicast tracker TVLV ,
* from before the payload data .
*
* Caller needs to ensure that the TVLV bytes are linearized .
*/
static void batadv_mcast_forw_push_scrape_padding ( struct sk_buff * skb ,
unsigned short * tvlv_len )
{
const int pad_len = 2 ;
batadv_mcast_forw_scrape ( skb , * tvlv_len - pad_len , pad_len ) ;
* tvlv_len - = pad_len ;
}
/**
* batadv_mcast_forw_push_insert_padding ( ) - insert TVLV padding
* @ skb : the skb to potentially adjust the TVLV ' s padding on
* @ tvlv_len : stores the amount of currently pushed TVLV bytes
*
* Inserts two padding bytes at the end of the multicast tracker TVLV ,
* before the payload data in the given skb .
*
* Return : true on success , false otherwise .
*/
static bool batadv_mcast_forw_push_insert_padding ( struct sk_buff * skb ,
unsigned short * tvlv_len )
{
unsigned short offset = * tvlv_len ;
char * to , * from = skb - > data ;
to = batadv_mcast_forw_push_padding ( skb , tvlv_len ) ;
if ( ! to )
return false ;
memmove ( to , from , offset ) ;
memset ( to + offset , 0 , * tvlv_len - offset ) ;
return true ;
}
/**
* batadv_mcast_forw_push_adjust_padding ( ) - adjust padding if necessary
* @ skb : the skb to potentially adjust the TVLV ' s padding on
* @ count : the estimated number of originators the multicast packet needs to
* be sent to
* @ num_dests_pushed : the number of originators that were actually added to the
* multicast packet ' s tracker TVLV
* @ tvlv_len : stores the amount of currently pushed TVLV bytes
*
* Adjusts the padding in the multicast packet ' s tracker TVLV depending on the
* initially estimated amount of destinations versus the amount of destinations
* that were actually added to the tracker TVLV .
*
* If the initial estimate was correct or at least the oddness was the same then
* no padding adjustment is performed .
* If the initially estimated number was even , so padding was initially added ,
* but it turned out to be odd then padding is removed .
* If the initially estimated number was odd , so no padding was initially added ,
* but it turned out to be even then padding is added .
*
* Return : true if no padding adjustment is needed or the adjustment was
* successful , false otherwise .
*/
static bool
batadv_mcast_forw_push_adjust_padding ( struct sk_buff * skb , int * count ,
unsigned short num_dests_pushed ,
unsigned short * tvlv_len )
{
int ret = true ;
if ( likely ( ( num_dests_pushed % 2 ) = = ( * count % 2 ) ) )
goto out ;
/**
* estimated even number of destinations , but turned out to be odd
* - > remove padding
*/
if ( ! ( * count % 2 ) & & ( num_dests_pushed % 2 ) )
batadv_mcast_forw_push_scrape_padding ( skb , tvlv_len ) ;
/**
* estimated odd number of destinations , but turned out to be even
* - > add padding
*/
else if ( ( * count % 2 ) & & ( ! ( num_dests_pushed % 2 ) ) )
ret = batadv_mcast_forw_push_insert_padding ( skb , tvlv_len ) ;
out :
* count = num_dests_pushed ;
return ret ;
}
/**
* batadv_mcast_forw_push_dests ( ) - push originator addresses onto an skb
* @ bat_priv : the bat priv with all the soft interface information
* @ skb : the skb to push the destination addresses onto
* @ vid : the vlan identifier
* @ is_routable : indicates whether the destination is routable
* @ count : the number of originators the multicast packet needs to be sent to
* @ tvlv_len : stores the amount of currently pushed TVLV bytes
*
* Push the MAC addresses of all originators which have indicated interest in
* this multicast packet onto the given skb .
*
* Return : - ENOMEM if there is not enough skb headroom available . Otherwise , on
* success 0.
*/
static int
batadv_mcast_forw_push_dests ( struct batadv_priv * bat_priv , struct sk_buff * skb ,
unsigned short vid , int is_routable , int * count ,
unsigned short * tvlv_len )
{
unsigned short num_dests = 0 ;
if ( ! batadv_mcast_forw_push_est_padding ( skb , * count , tvlv_len ) )
goto err ;
if ( ! batadv_mcast_forw_push_tt ( bat_priv , skb , vid , & num_dests ,
tvlv_len ) )
goto err ;
if ( ! batadv_mcast_forw_push_want_all ( bat_priv , skb , vid , & num_dests ,
tvlv_len ) )
goto err ;
if ( is_routable & &
! batadv_mcast_forw_push_want_rtr ( bat_priv , skb , vid , & num_dests ,
tvlv_len ) )
goto err ;
if ( ! batadv_mcast_forw_push_adjust_padding ( skb , count , num_dests ,
tvlv_len ) )
goto err ;
return 0 ;
err :
return - ENOMEM ;
}
/**
* batadv_mcast_forw_push_tracker ( ) - push a multicast tracker TVLV header
* @ skb : the skb to push the tracker TVLV onto
* @ num_dests : the number of destination addresses to set in the header
* @ tvlv_len : stores the amount of currently pushed TVLV bytes
*
* Pushes a multicast tracker TVLV header onto the given skb , including the
* generic TVLV header but excluding the destination MAC addresses .
*
* The provided num_dests value is taken into consideration to set the
* num_dests field in the tracker header and to set the appropriate TVLV length
* value fields .
*
* Return : - ENOMEM if there is not enough skb headroom available . Otherwise , on
* success 0.
*/
static int batadv_mcast_forw_push_tracker ( struct sk_buff * skb , int num_dests ,
unsigned short * tvlv_len )
{
struct batadv_tvlv_mcast_tracker * mcast_tracker ;
struct batadv_tvlv_hdr * tvlv_hdr ;
unsigned int tvlv_value_len ;
if ( skb_headroom ( skb ) < sizeof ( * mcast_tracker ) + sizeof ( * tvlv_hdr ) )
return - ENOMEM ;
tvlv_value_len = sizeof ( * mcast_tracker ) + * tvlv_len ;
if ( tvlv_value_len + sizeof ( * tvlv_hdr ) > U16_MAX )
return - ENOMEM ;
batadv_mcast_forw_skb_push ( skb , sizeof ( * mcast_tracker ) , tvlv_len ) ;
mcast_tracker = ( struct batadv_tvlv_mcast_tracker * ) skb - > data ;
mcast_tracker - > num_dests = htons ( num_dests ) ;
skb_reset_network_header ( skb ) ;
batadv_mcast_forw_skb_push ( skb , sizeof ( * tvlv_hdr ) , tvlv_len ) ;
tvlv_hdr = ( struct batadv_tvlv_hdr * ) skb - > data ;
tvlv_hdr - > type = BATADV_TVLV_MCAST_TRACKER ;
tvlv_hdr - > version = 1 ;
tvlv_hdr - > len = htons ( tvlv_value_len ) ;
return 0 ;
}
/**
* batadv_mcast_forw_push_tvlvs ( ) - push a multicast tracker TVLV onto an skb
* @ bat_priv : the bat priv with all the soft interface information
* @ skb : the skb to push the tracker TVLV onto
* @ vid : the vlan identifier
* @ is_routable : indicates whether the destination is routable
* @ count : the number of originators the multicast packet needs to be sent to
* @ tvlv_len : stores the amount of currently pushed TVLV bytes
*
* Pushes a multicast tracker TVLV onto the given skb , including the collected
* destination MAC addresses and the generic TVLV header .
*
* Return : - ENOMEM if there is not enough skb headroom available . Otherwise , on
* success 0.
*/
static int
batadv_mcast_forw_push_tvlvs ( struct batadv_priv * bat_priv , struct sk_buff * skb ,
unsigned short vid , int is_routable , int count ,
unsigned short * tvlv_len )
{
int ret ;
ret = batadv_mcast_forw_push_dests ( bat_priv , skb , vid , is_routable ,
& count , tvlv_len ) ;
if ( ret < 0 )
return ret ;
ret = batadv_mcast_forw_push_tracker ( skb , count , tvlv_len ) ;
if ( ret < 0 )
return ret ;
return 0 ;
}
/**
* batadv_mcast_forw_push_hdr ( ) - push a multicast packet header onto an skb
* @ skb : the skb to push the header onto
* @ tvlv_len : the total TVLV length value to set in the header
*
* Pushes a batman - adv multicast packet header onto the given skb and sets
* the provided total TVLV length value in it .
*
* Caller needs to ensure enough skb headroom is available .
*
* Return : - ENOMEM if there is not enough skb headroom available . Otherwise , on
* success 0.
*/
static int
batadv_mcast_forw_push_hdr ( struct sk_buff * skb , unsigned short tvlv_len )
{
struct batadv_mcast_packet * mcast_packet ;
if ( skb_headroom ( skb ) < sizeof ( * mcast_packet ) )
return - ENOMEM ;
skb_push ( skb , sizeof ( * mcast_packet ) ) ;
mcast_packet = ( struct batadv_mcast_packet * ) skb - > data ;
mcast_packet - > version = BATADV_COMPAT_VERSION ;
mcast_packet - > ttl = BATADV_TTL ;
mcast_packet - > packet_type = BATADV_MCAST ;
mcast_packet - > reserved = 0 ;
mcast_packet - > tvlv_len = htons ( tvlv_len ) ;
return 0 ;
}
2023-09-07 04:09:08 +03:00
/**
* batadv_mcast_forw_scrub_dests ( ) - scrub destinations in a tracker TVLV
* @ bat_priv : the bat priv with all the soft interface information
* @ comp_neigh : next hop neighbor to scrub + collect destinations for
* @ dest : start MAC entry in original skb ' s tracker TVLV
* @ next_dest : start MAC entry in to be sent skb ' s tracker TVLV
* @ num_dests : number of remaining destination MAC entries to iterate over
*
* This sorts destination entries into either the original batman - adv
* multicast packet or the skb ( copy ) that is going to be sent to comp_neigh
* next .
*
* In preparation for the next , to be ( unicast ) transmitted batman - adv multicast
* packet skb to be sent to the given neighbor node , tries to collect all
* originator MAC addresses that have the given neighbor node as their next hop
* in the to be transmitted skb ( copy ) , which next_dest points into . That is we
* zero all destination entries in next_dest which do not have comp_neigh as
* their next hop . And zero all destination entries in the original skb that
* would have comp_neigh as their next hop ( to avoid redundant transmissions and
* duplicated payload later ) .
*/
static void
batadv_mcast_forw_scrub_dests ( struct batadv_priv * bat_priv ,
struct batadv_neigh_node * comp_neigh , u8 * dest ,
u8 * next_dest , u16 num_dests )
{
struct batadv_neigh_node * next_neigh ;
/* skip first entry, this is what we are comparing with */
eth_zero_addr ( dest ) ;
dest + = ETH_ALEN ;
next_dest + = ETH_ALEN ;
num_dests - - ;
batadv_mcast_forw_tracker_for_each_dest2 ( dest , next_dest , num_dests ) {
if ( is_zero_ether_addr ( next_dest ) )
continue ;
/* sanity check, we expect unicast destinations */
if ( is_multicast_ether_addr ( next_dest ) ) {
eth_zero_addr ( dest ) ;
eth_zero_addr ( next_dest ) ;
continue ;
}
next_neigh = batadv_orig_to_router ( bat_priv , next_dest , NULL ) ;
if ( ! next_neigh ) {
eth_zero_addr ( next_dest ) ;
continue ;
}
if ( ! batadv_compare_eth ( next_neigh - > addr , comp_neigh - > addr ) ) {
eth_zero_addr ( next_dest ) ;
batadv_neigh_node_put ( next_neigh ) ;
continue ;
}
/* found an entry for our next packet to transmit, so remove it
* from the original packet
*/
eth_zero_addr ( dest ) ;
batadv_neigh_node_put ( next_neigh ) ;
}
}
2023-09-07 04:09:10 +03:00
/**
* batadv_mcast_forw_shrink_fill ( ) - swap slot with next non - zero destination
* @ slot : the to be filled zero - MAC destination entry in a tracker TVLV
* @ num_dests_slot : remaining entries in tracker TVLV from / including slot
*
* Searches for the next non - zero - MAC destination entry in a tracker TVLV after
* the given slot pointer . And if found , swaps it with the zero - MAC destination
* entry which the slot points to .
*
* Return : true if slot was swapped / filled successfully , false otherwise .
*/
static bool batadv_mcast_forw_shrink_fill ( u8 * slot , u16 num_dests_slot )
{
u16 num_dests_filler ;
u8 * filler ;
/* sanity check, should not happen */
if ( ! num_dests_slot )
return false ;
num_dests_filler = num_dests_slot - 1 ;
filler = slot + ETH_ALEN ;
/* find a candidate to fill the empty slot */
batadv_mcast_forw_tracker_for_each_dest ( filler , num_dests_filler ) {
if ( is_zero_ether_addr ( filler ) )
continue ;
ether_addr_copy ( slot , filler ) ;
eth_zero_addr ( filler ) ;
return true ;
}
return false ;
}
/**
* batadv_mcast_forw_shrink_pack_dests ( ) - pack destinations of a tracker TVLV
* @ skb : the batman - adv multicast packet to compact destinations in
*
* Compacts the originator destination MAC addresses in the multicast tracker
* TVLV of the given multicast packet . This is done by moving all non - zero
* MAC addresses in direction of the skb head and all zero MAC addresses in skb
* tail direction , within the multicast tracker TVLV .
*
* Return : The number of consecutive zero MAC address destinations which are
* now at the end of the multicast tracker TVLV .
*/
static int batadv_mcast_forw_shrink_pack_dests ( struct sk_buff * skb )
{
struct batadv_tvlv_mcast_tracker * mcast_tracker ;
unsigned char * skb_net_hdr ;
u16 num_dests_slot ;
u8 * slot ;
skb_net_hdr = skb_network_header ( skb ) ;
mcast_tracker = ( struct batadv_tvlv_mcast_tracker * ) skb_net_hdr ;
num_dests_slot = ntohs ( mcast_tracker - > num_dests ) ;
slot = ( u8 * ) mcast_tracker + sizeof ( * mcast_tracker ) ;
batadv_mcast_forw_tracker_for_each_dest ( slot , num_dests_slot ) {
/* find an empty slot */
if ( ! is_zero_ether_addr ( slot ) )
continue ;
if ( ! batadv_mcast_forw_shrink_fill ( slot , num_dests_slot ) )
/* could not find a filler, so we successfully packed
* and can stop - and must not reduce num_dests_slot !
*/
break ;
}
/* num_dests_slot is now the amount of reduced, zeroed
* destinations at the end of the tracker TVLV
*/
return num_dests_slot ;
}
/**
* batadv_mcast_forw_shrink_align_offset ( ) - get new alignment offset
* @ num_dests_old : the old , to be updated amount of destination nodes
* @ num_dests_reduce : the number of destinations that were removed
*
* Calculates the amount of potential extra alignment offset that is needed to
* adjust the TVLV padding after the change in destination nodes .
*
* Return :
* 0 : If no change to padding is needed .
* 2 : If padding needs to be removed .
* - 2 : If padding needs to be added .
*/
static short
batadv_mcast_forw_shrink_align_offset ( unsigned int num_dests_old ,
unsigned int num_dests_reduce )
{
/* even amount of removed destinations -> no alignment change */
if ( ! ( num_dests_reduce % 2 ) )
return 0 ;
/* even to odd amount of destinations -> remove padding */
if ( ! ( num_dests_old % 2 ) )
return 2 ;
/* odd to even amount of destinations -> add padding */
return - 2 ;
}
/**
* batadv_mcast_forw_shrink_update_headers ( ) - update shrunk mc packet headers
* @ skb : the batman - adv multicast packet to update headers of
* @ num_dests_reduce : the number of destinations that were removed
*
* This updates any fields of a batman - adv multicast packet that are affected
* by the reduced number of destinations in the multicast tracket TVLV . In
* particular this updates :
*
* The num_dest field of the multicast tracker TVLV .
* The TVLV length field of the according generic TVLV header .
* The batman - adv multicast packet ' s total TVLV length field .
*
* Return : The offset in skb ' s tail direction at which the new batman - adv
* multicast packet header needs to start .
*/
static unsigned int
batadv_mcast_forw_shrink_update_headers ( struct sk_buff * skb ,
unsigned int num_dests_reduce )
{
struct batadv_tvlv_mcast_tracker * mcast_tracker ;
struct batadv_mcast_packet * mcast_packet ;
struct batadv_tvlv_hdr * tvlv_hdr ;
unsigned char * skb_net_hdr ;
unsigned int offset ;
short align_offset ;
u16 num_dests ;
skb_net_hdr = skb_network_header ( skb ) ;
mcast_tracker = ( struct batadv_tvlv_mcast_tracker * ) skb_net_hdr ;
num_dests = ntohs ( mcast_tracker - > num_dests ) ;
align_offset = batadv_mcast_forw_shrink_align_offset ( num_dests ,
num_dests_reduce ) ;
offset = ETH_ALEN * num_dests_reduce + align_offset ;
num_dests - = num_dests_reduce ;
/* update tracker header */
mcast_tracker - > num_dests = htons ( num_dests ) ;
/* update tracker's tvlv header's length field */
tvlv_hdr = ( struct batadv_tvlv_hdr * ) ( skb_network_header ( skb ) -
sizeof ( * tvlv_hdr ) ) ;
tvlv_hdr - > len = htons ( ntohs ( tvlv_hdr - > len ) - offset ) ;
/* update multicast packet header's tvlv length field */
mcast_packet = ( struct batadv_mcast_packet * ) skb - > data ;
mcast_packet - > tvlv_len = htons ( ntohs ( mcast_packet - > tvlv_len ) - offset ) ;
return offset ;
}
/**
* batadv_mcast_forw_shrink_move_headers ( ) - move multicast headers by offset
* @ skb : the batman - adv multicast packet to move headers for
* @ offset : a non - negative offset to move headers by , towards the skb tail
*
* Moves the batman - adv multicast packet header , its multicast tracker TVLV and
* any TVLVs in between by the given offset in direction towards the tail .
*/
static void
batadv_mcast_forw_shrink_move_headers ( struct sk_buff * skb , unsigned int offset )
{
struct batadv_tvlv_mcast_tracker * mcast_tracker ;
unsigned char * skb_net_hdr ;
unsigned int len ;
u16 num_dests ;
skb_net_hdr = skb_network_header ( skb ) ;
mcast_tracker = ( struct batadv_tvlv_mcast_tracker * ) skb_net_hdr ;
num_dests = ntohs ( mcast_tracker - > num_dests ) ;
len = skb_network_offset ( skb ) + sizeof ( * mcast_tracker ) ;
len + = num_dests * ETH_ALEN ;
batadv_mcast_forw_scrape ( skb , len , offset ) ;
}
/**
* batadv_mcast_forw_shrink_tracker ( ) - remove zero addresses in a tracker tvlv
* @ skb : the batman - adv multicast packet to ( potentially ) shrink
*
* Removes all destinations with a zero MAC addresses ( 00 : 00 : 00 : 00 : 00 : 00 ) from
* the given batman - adv multicast packet ' s tracker TVLV and updates headers
* accordingly to maintain a valid batman - adv multicast packet .
*/
static void batadv_mcast_forw_shrink_tracker ( struct sk_buff * skb )
{
unsigned int offset ;
u16 dests_reduced ;
dests_reduced = batadv_mcast_forw_shrink_pack_dests ( skb ) ;
if ( ! dests_reduced )
return ;
offset = batadv_mcast_forw_shrink_update_headers ( skb , dests_reduced ) ;
batadv_mcast_forw_shrink_move_headers ( skb , offset ) ;
}
2023-09-07 04:09:08 +03:00
/**
* batadv_mcast_forw_packet ( ) - forward a batman - adv multicast packet
* @ bat_priv : the bat priv with all the soft interface information
* @ skb : the received or locally generated batman - adv multicast packet
* @ local_xmit : indicates that the packet was locally generated and not received
*
* Parses the tracker TVLV of a batman - adv multicast packet and forwards the
* packet as indicated in this TVLV .
*
* Caller needs to set the skb network header to the start of the multicast
* tracker TVLV ( excluding the generic TVLV header ) and the skb transport header
* to the next byte after this multicast tracker TVLV .
*
* Caller needs to free the skb .
*
* Return : NET_RX_SUCCESS or NET_RX_DROP on success or a negative error
* code on failure . NET_RX_SUCCESS if the received packet is supposed to be
* decapsulated and forwarded to the own soft interface , NET_RX_DROP otherwise .
*/
static int batadv_mcast_forw_packet ( struct batadv_priv * bat_priv ,
struct sk_buff * skb , bool local_xmit )
{
struct batadv_tvlv_mcast_tracker * mcast_tracker ;
struct batadv_neigh_node * neigh_node ;
unsigned long offset , num_dests_off ;
struct sk_buff * nexthop_skb ;
unsigned char * skb_net_hdr ;
bool local_recv = false ;
unsigned int tvlv_len ;
bool xmitted = false ;
u8 * dest , * next_dest ;
u16 num_dests ;
int ret ;
/* (at least) TVLV part needs to be linearized */
SKB_LINEAR_ASSERT ( skb ) ;
/* check if num_dests is within skb length */
num_dests_off = offsetof ( struct batadv_tvlv_mcast_tracker , num_dests ) ;
if ( num_dests_off > skb_network_header_len ( skb ) )
return - EINVAL ;
skb_net_hdr = skb_network_header ( skb ) ;
mcast_tracker = ( struct batadv_tvlv_mcast_tracker * ) skb_net_hdr ;
num_dests = ntohs ( mcast_tracker - > num_dests ) ;
dest = ( u8 * ) mcast_tracker + sizeof ( * mcast_tracker ) ;
/* check if full tracker tvlv is within skb length */
tvlv_len = sizeof ( * mcast_tracker ) + ETH_ALEN * num_dests ;
if ( tvlv_len > skb_network_header_len ( skb ) )
return - EINVAL ;
/* invalidate checksum: */
skb - > ip_summed = CHECKSUM_NONE ;
batadv_mcast_forw_tracker_for_each_dest ( dest , num_dests ) {
if ( is_zero_ether_addr ( dest ) )
continue ;
/* only unicast originator addresses supported */
if ( is_multicast_ether_addr ( dest ) ) {
eth_zero_addr ( dest ) ;
continue ;
}
if ( batadv_is_my_mac ( bat_priv , dest ) ) {
eth_zero_addr ( dest ) ;
local_recv = true ;
continue ;
}
neigh_node = batadv_orig_to_router ( bat_priv , dest , NULL ) ;
if ( ! neigh_node ) {
eth_zero_addr ( dest ) ;
continue ;
}
nexthop_skb = skb_copy ( skb , GFP_ATOMIC ) ;
if ( ! nexthop_skb ) {
batadv_neigh_node_put ( neigh_node ) ;
return - ENOMEM ;
}
offset = dest - skb - > data ;
next_dest = nexthop_skb - > data + offset ;
batadv_mcast_forw_scrub_dests ( bat_priv , neigh_node , dest ,
next_dest , num_dests ) ;
2023-09-07 04:09:10 +03:00
batadv_mcast_forw_shrink_tracker ( nexthop_skb ) ;
2023-09-07 04:09:08 +03:00
batadv_inc_counter ( bat_priv , BATADV_CNT_MCAST_TX ) ;
batadv_add_counter ( bat_priv , BATADV_CNT_MCAST_TX_BYTES ,
nexthop_skb - > len + ETH_HLEN ) ;
xmitted = true ;
ret = batadv_send_unicast_skb ( nexthop_skb , neigh_node ) ;
batadv_neigh_node_put ( neigh_node ) ;
if ( ret < 0 )
return ret ;
}
if ( xmitted ) {
if ( local_xmit ) {
batadv_inc_counter ( bat_priv , BATADV_CNT_MCAST_TX_LOCAL ) ;
batadv_add_counter ( bat_priv ,
BATADV_CNT_MCAST_TX_LOCAL_BYTES ,
skb - > len -
skb_transport_offset ( skb ) ) ;
} else {
batadv_inc_counter ( bat_priv , BATADV_CNT_MCAST_FWD ) ;
batadv_add_counter ( bat_priv , BATADV_CNT_MCAST_FWD_BYTES ,
skb - > len + ETH_HLEN ) ;
}
}
if ( local_recv )
return NET_RX_SUCCESS ;
else
return NET_RX_DROP ;
}
/**
* batadv_mcast_forw_tracker_tvlv_handler ( ) - handle an mcast tracker tvlv
* @ bat_priv : the bat priv with all the soft interface information
* @ skb : the received batman - adv multicast packet
*
* Parses the tracker TVLV of an incoming batman - adv multicast packet and
* forwards the packet as indicated in this TVLV .
*
* Caller needs to set the skb network header to the start of the multicast
* tracker TVLV ( excluding the generic TVLV header ) and the skb transport header
* to the next byte after this multicast tracker TVLV .
*
* Caller needs to free the skb .
*
* Return : NET_RX_SUCCESS or NET_RX_DROP on success or a negative error
* code on failure . NET_RX_SUCCESS if the received packet is supposed to be
* decapsulated and forwarded to the own soft interface , NET_RX_DROP otherwise .
*/
int batadv_mcast_forw_tracker_tvlv_handler ( struct batadv_priv * bat_priv ,
struct sk_buff * skb )
{
return batadv_mcast_forw_packet ( bat_priv , skb , false ) ;
}
2023-09-07 04:09:09 +03:00
/**
* batadv_mcast_forw_packet_hdrlen ( ) - multicast packet header length
* @ num_dests : number of destination nodes
*
* Calculates the total batman - adv multicast packet header length for a given
* number of destination nodes ( excluding the outer ethernet frame ) .
*
* Return : The calculated total batman - adv multicast packet header length .
*/
unsigned int batadv_mcast_forw_packet_hdrlen ( unsigned int num_dests )
{
/**
* If the number of destination entries is even then we need to add
* two byte padding to the tracker TVLV .
*/
int padding = ( ! ( num_dests % 2 ) ) ? 2 : 0 ;
return padding + num_dests * ETH_ALEN +
sizeof ( struct batadv_tvlv_mcast_tracker ) +
sizeof ( struct batadv_tvlv_hdr ) +
sizeof ( struct batadv_mcast_packet ) ;
}
/**
* batadv_mcast_forw_expand_head ( ) - expand headroom for an mcast packet
* @ bat_priv : the bat priv with all the soft interface information
* @ skb : the multicast packet to send
*
* Tries to expand an skb ' s headroom so that its head to tail is 1298
* bytes ( minimum IPv6 MTU + vlan ethernet header size ) large .
*
* Return : - EINVAL if the given skb ' s length is too large or - ENOMEM on memory
* allocation failure . Otherwise , on success , zero is returned .
*/
static int batadv_mcast_forw_expand_head ( struct batadv_priv * bat_priv ,
struct sk_buff * skb )
{
int hdr_size = VLAN_ETH_HLEN + IPV6_MIN_MTU - skb - > len ;
/* TODO: Could be tightened to actual number of destination nodes?
* But it ' s tricky , number of destinations might have increased since
* we last checked .
*/
if ( hdr_size < 0 ) {
/* batadv_mcast_forw_mode_check_count() should ensure we do not
* end up here
*/
WARN_ON ( 1 ) ;
return - EINVAL ;
}
if ( skb_headroom ( skb ) < hdr_size & &
pskb_expand_head ( skb , hdr_size , 0 , GFP_ATOMIC ) < 0 )
return - ENOMEM ;
return 0 ;
}
/**
* batadv_mcast_forw_push ( ) - encapsulate skb in a batman - adv multicast packet
* @ bat_priv : the bat priv with all the soft interface information
* @ skb : the multicast packet to encapsulate and send
* @ vid : the vlan identifier
* @ is_routable : indicates whether the destination is routable
* @ count : the number of originators the multicast packet needs to be sent to
*
* Encapsulates the given multicast packet in a batman - adv multicast packet .
* A multicast tracker TVLV with destination originator addresses for any node
* that signaled interest in it , that is either via the translation table or the
* according want - all flags , is attached accordingly .
*
* Return : true on success , false otherwise .
*/
bool batadv_mcast_forw_push ( struct batadv_priv * bat_priv , struct sk_buff * skb ,
unsigned short vid , int is_routable , int count )
{
unsigned short tvlv_len = 0 ;
int ret ;
if ( batadv_mcast_forw_expand_head ( bat_priv , skb ) < 0 )
goto err ;
skb_reset_transport_header ( skb ) ;
ret = batadv_mcast_forw_push_tvlvs ( bat_priv , skb , vid , is_routable ,
count , & tvlv_len ) ;
if ( ret < 0 )
goto err ;
ret = batadv_mcast_forw_push_hdr ( skb , tvlv_len ) ;
if ( ret < 0 )
goto err ;
return true ;
err :
if ( tvlv_len )
skb_pull ( skb , tvlv_len ) ;
return false ;
}
/**
* batadv_mcast_forw_mcsend ( ) - send a self prepared batman - adv multicast packet
* @ bat_priv : the bat priv with all the soft interface information
* @ skb : the multicast packet to encapsulate and send
*
* Transmits a batman - adv multicast packet that was locally prepared and
* consumes / frees it .
*
* Return : NET_XMIT_DROP on memory allocation failure . NET_XMIT_SUCCESS
* otherwise .
*/
int batadv_mcast_forw_mcsend ( struct batadv_priv * bat_priv ,
struct sk_buff * skb )
{
int ret = batadv_mcast_forw_packet ( bat_priv , skb , true ) ;
if ( ret < 0 ) {
kfree_skb ( skb ) ;
return NET_XMIT_DROP ;
}
consume_skb ( skb ) ;
return NET_XMIT_SUCCESS ;
}