// SPDX-License-Identifier: GPL-2.0
/* Copyright (C) B.A.T.M.A.N. contributors:
*
* Linus Lüssing
*/
#include "multicast.h"
#include "main.h"
#include <linux/bug.h>
#include <linux/build_bug.h>
#include <linux/byteorder/generic.h>
#include <linux/compiler.h>
#include <linux/errno.h>
#include <linux/etherdevice.h>
#include <linux/gfp.h>
#include <linux/if_ether.h>
#include <linux/if_vlan.h>
#include <linux/ipv6.h>
#include <linux/limits.h>
#include <linux/netdevice.h>
#include <linux/rculist.h>
#include <linux/rcupdate.h>
#include <linux/skbuff.h>
#include <linux/stddef.h>
#include <linux/string.h>
#include <linux/types.h>
#include <uapi/linux/batadv_packet.h>
#include "bridge_loop_avoidance.h"
#include "originator.h"
#include "send.h"
#include "translation-table.h"
#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)
/**
* 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