2023-09-07 01:09:08 +00: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 01:09:09 +00:00
|
|
|
#include <linux/bug.h>
|
|
|
|
#include <linux/build_bug.h>
|
2023-09-07 01:09:08 +00:00
|
|
|
#include <linux/byteorder/generic.h>
|
2023-09-07 01:09:09 +00:00
|
|
|
#include <linux/compiler.h>
|
2023-09-07 01:09:08 +00:00
|
|
|
#include <linux/errno.h>
|
|
|
|
#include <linux/etherdevice.h>
|
|
|
|
#include <linux/gfp.h>
|
|
|
|
#include <linux/if_ether.h>
|
2023-09-07 01:09:09 +00:00
|
|
|
#include <linux/if_vlan.h>
|
|
|
|
#include <linux/ipv6.h>
|
|
|
|
#include <linux/limits.h>
|
2023-09-07 01:09:08 +00:00
|
|
|
#include <linux/netdevice.h>
|
2023-09-07 01:09:09 +00:00
|
|
|
#include <linux/rculist.h>
|
|
|
|
#include <linux/rcupdate.h>
|
2023-09-07 01:09:08 +00:00
|
|
|
#include <linux/skbuff.h>
|
|
|
|
#include <linux/stddef.h>
|
2023-09-07 01:09:09 +00:00
|
|
|
#include <linux/string.h>
|
2023-09-07 01:09:08 +00:00
|
|
|
#include <linux/types.h>
|
|
|
|
#include <uapi/linux/batadv_packet.h>
|
|
|
|
|
2023-09-07 01:09:09 +00:00
|
|
|
#include "bridge_loop_avoidance.h"
|
2023-09-07 01:09:08 +00:00
|
|
|
#include "originator.h"
|
|
|
|
#include "send.h"
|
2023-09-07 01:09:09 +00:00
|
|
|
#include "translation-table.h"
|
2023-09-07 01:09:08 +00: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 01:09:09 +00: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 01:09:08 +00: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 01:09:10 +00: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 01:09:08 +00: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 01:09:10 +00:00
|
|
|
batadv_mcast_forw_shrink_tracker(nexthop_skb);
|
2023-09-07 01:09:08 +00: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 01:09:09 +00: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;
|
|
|
|
}
|