Commit 39620a35 authored by David S. Miller's avatar David S. Miller

Merge tag 'batadv-next-pullrequest-20231115' of git://git.open-mesh.org/linux-merge

This feature/cleanup patchset includes the following patches:

 - bump version strings, by Simon Wunderlich

 - Implement new multicast packet type, including its transmission,
   forwarding and parsing, by Linus Lüssing (3 patches)

 - Switch to new headers for sprintf and array size,
   by Sven Eckelmann (2 patches)
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 72a813a4 c3ed16a6
......@@ -116,6 +116,9 @@ enum batadv_icmp_packettype {
* only need routable IPv4 multicast packets we signed up for explicitly
* @BATADV_MCAST_WANT_NO_RTR6: we have no IPv6 multicast router and therefore
* only need routable IPv6 multicast packets we signed up for explicitly
* @BATADV_MCAST_HAVE_MC_PTYPE_CAPA: we can parse, receive and forward
* batman-adv multicast packets with a multicast tracker TVLV. And all our
* hard interfaces have an MTU of at least 1280 bytes.
*/
enum batadv_mcast_flags {
BATADV_MCAST_WANT_ALL_UNSNOOPABLES = 1UL << 0,
......@@ -123,6 +126,7 @@ enum batadv_mcast_flags {
BATADV_MCAST_WANT_ALL_IPV6 = 1UL << 2,
BATADV_MCAST_WANT_NO_RTR4 = 1UL << 3,
BATADV_MCAST_WANT_NO_RTR6 = 1UL << 4,
BATADV_MCAST_HAVE_MC_PTYPE_CAPA = 1UL << 5,
};
/* tt data subtypes */
......@@ -174,14 +178,16 @@ enum batadv_bla_claimframe {
* @BATADV_TVLV_TT: translation table tvlv
* @BATADV_TVLV_ROAM: roaming advertisement tvlv
* @BATADV_TVLV_MCAST: multicast capability tvlv
* @BATADV_TVLV_MCAST_TRACKER: multicast tracker tvlv
*/
enum batadv_tvlv_type {
BATADV_TVLV_GW = 0x01,
BATADV_TVLV_DAT = 0x02,
BATADV_TVLV_NC = 0x03,
BATADV_TVLV_TT = 0x04,
BATADV_TVLV_ROAM = 0x05,
BATADV_TVLV_MCAST = 0x06,
BATADV_TVLV_GW = 0x01,
BATADV_TVLV_DAT = 0x02,
BATADV_TVLV_NC = 0x03,
BATADV_TVLV_TT = 0x04,
BATADV_TVLV_ROAM = 0x05,
BATADV_TVLV_MCAST = 0x06,
BATADV_TVLV_MCAST_TRACKER = 0x07,
};
#pragma pack(2)
......@@ -487,6 +493,25 @@ struct batadv_bcast_packet {
*/
};
/**
* struct batadv_mcast_packet - multicast packet for network payload
* @packet_type: batman-adv packet type, part of the general header
* @version: batman-adv protocol version, part of the general header
* @ttl: time to live for this packet, part of the general header
* @reserved: reserved byte for alignment
* @tvlv_len: length of the appended tvlv buffer (in bytes)
*/
struct batadv_mcast_packet {
__u8 packet_type;
__u8 version;
__u8 ttl;
__u8 reserved;
__be16 tvlv_len;
/* "4 bytes boundary + 2 bytes" long to make the payload after the
* following ethernet header again 4 bytes boundary aligned
*/
};
/**
* struct batadv_coded_packet - network coded packet
* @packet_type: batman-adv packet type, part of the general header
......@@ -628,6 +653,14 @@ struct batadv_tvlv_mcast_data {
__u8 reserved[3];
};
/**
* struct batadv_tvlv_mcast_tracker - payload of a multicast tracker tvlv
* @num_dests: number of subsequent destination originator MAC addresses
*/
struct batadv_tvlv_mcast_tracker {
__be16 num_dests;
};
#pragma pack()
#endif /* _UAPI_LINUX_BATADV_PACKET_H_ */
......@@ -20,6 +20,7 @@ batman-adv-y += hash.o
batman-adv-$(CONFIG_BATMAN_ADV_DEBUG) += log.o
batman-adv-y += main.o
batman-adv-$(CONFIG_BATMAN_ADV_MCAST) += multicast.o
batman-adv-$(CONFIG_BATMAN_ADV_MCAST) += multicast_forw.o
batman-adv-y += netlink.o
batman-adv-$(CONFIG_BATMAN_ADV_NC) += network-coding.o
batman-adv-y += originator.o
......
......@@ -20,7 +20,6 @@
#include <linux/if_vlan.h>
#include <linux/jhash.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/kref.h>
#include <linux/list.h>
#include <linux/lockdep.h>
......@@ -31,6 +30,7 @@
#include <linux/skbuff.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/sprintf.h>
#include <linux/stddef.h>
#include <linux/string.h>
#include <linux/workqueue.h>
......
......@@ -25,7 +25,6 @@
#include "hard-interface.h"
#include "originator.h"
#include "routing.h"
#include "send.h"
/**
......@@ -351,18 +350,14 @@ bool batadv_frag_skb_fwd(struct sk_buff *skb,
struct batadv_orig_node *orig_node_src)
{
struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface);
struct batadv_orig_node *orig_node_dst;
struct batadv_neigh_node *neigh_node = NULL;
struct batadv_frag_packet *packet;
u16 total_size;
bool ret = false;
packet = (struct batadv_frag_packet *)skb->data;
orig_node_dst = batadv_orig_hash_find(bat_priv, packet->dest);
if (!orig_node_dst)
goto out;
neigh_node = batadv_find_router(bat_priv, orig_node_dst, recv_if);
neigh_node = batadv_orig_to_router(bat_priv, packet->dest, recv_if);
if (!neigh_node)
goto out;
......@@ -381,7 +376,6 @@ bool batadv_frag_skb_fwd(struct sk_buff *skb,
}
out:
batadv_orig_node_put(orig_node_dst);
batadv_neigh_node_put(neigh_node);
return ret;
}
......
......@@ -18,7 +18,6 @@
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/kernel.h>
#include <linux/kref.h>
#include <linux/list.h>
#include <linux/lockdep.h>
......@@ -29,6 +28,7 @@
#include <linux/skbuff.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/sprintf.h>
#include <linux/stddef.h>
#include <linux/udp.h>
#include <net/sock.h>
......
......@@ -6,6 +6,7 @@
#include "main.h"
#include <linux/array_size.h>
#include <linux/atomic.h>
#include <linux/build_bug.h>
#include <linux/byteorder/generic.h>
......@@ -20,7 +21,6 @@
#include <linux/init.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/kref.h>
#include <linux/list.h>
......@@ -33,6 +33,7 @@
#include <linux/skbuff.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/sprintf.h>
#include <linux/stddef.h>
#include <linux/string.h>
#include <linux/workqueue.h>
......@@ -532,6 +533,8 @@ static void batadv_recv_handler_init(void)
/* broadcast packet */
batadv_rx_handler[BATADV_BCAST] = batadv_recv_bcast_packet;
/* multicast packet */
batadv_rx_handler[BATADV_MCAST] = batadv_recv_mcast_packet;
/* unicast packets ... */
/* unicast with 4 addresses packet */
......
......@@ -13,7 +13,7 @@
#define BATADV_DRIVER_DEVICE "batman-adv"
#ifndef BATADV_SOURCE_VERSION
#define BATADV_SOURCE_VERSION "2023.3"
#define BATADV_SOURCE_VERSION "2024.0"
#endif
/* B.A.T.M.A.N. parameters */
......
......@@ -25,7 +25,6 @@
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/lockdep.h>
#include <linux/netdevice.h>
......@@ -36,6 +35,7 @@
#include <linux/skbuff.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/sprintf.h>
#include <linux/stddef.h>
#include <linux/string.h>
#include <linux/types.h>
......@@ -235,6 +235,37 @@ static u8 batadv_mcast_mla_rtr_flags_get(struct batadv_priv *bat_priv,
return flags;
}
/**
* batadv_mcast_mla_forw_flags_get() - get multicast forwarding flags
* @bat_priv: the bat priv with all the soft interface information
*
* Checks if all active hard interfaces have an MTU larger or equal to 1280
* bytes (IPv6 minimum MTU).
*
* Return: BATADV_MCAST_HAVE_MC_PTYPE_CAPA if yes, BATADV_NO_FLAGS otherwise.
*/
static u8 batadv_mcast_mla_forw_flags_get(struct batadv_priv *bat_priv)
{
const struct batadv_hard_iface *hard_iface;
rcu_read_lock();
list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
if (hard_iface->if_status != BATADV_IF_ACTIVE)
continue;
if (hard_iface->soft_iface != bat_priv->soft_iface)
continue;
if (hard_iface->net_dev->mtu < IPV6_MIN_MTU) {
rcu_read_unlock();
return BATADV_NO_FLAGS;
}
}
rcu_read_unlock();
return BATADV_MCAST_HAVE_MC_PTYPE_CAPA;
}
/**
* batadv_mcast_mla_flags_get() - get the new multicast flags
* @bat_priv: the bat priv with all the soft interface information
......@@ -256,6 +287,7 @@ batadv_mcast_mla_flags_get(struct batadv_priv *bat_priv)
mla_flags.enabled = 1;
mla_flags.tvlv_flags |= batadv_mcast_mla_rtr_flags_get(bat_priv,
bridge);
mla_flags.tvlv_flags |= batadv_mcast_mla_forw_flags_get(bat_priv);
if (!bridge)
return mla_flags;
......@@ -806,23 +838,25 @@ static void batadv_mcast_flags_log(struct batadv_priv *bat_priv, u8 flags)
{
bool old_enabled = bat_priv->mcast.mla_flags.enabled;
u8 old_flags = bat_priv->mcast.mla_flags.tvlv_flags;
char str_old_flags[] = "[.... . ]";
char str_old_flags[] = "[.... . .]";
sprintf(str_old_flags, "[%c%c%c%s%s]",
sprintf(str_old_flags, "[%c%c%c%s%s%c]",
(old_flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES) ? 'U' : '.',
(old_flags & BATADV_MCAST_WANT_ALL_IPV4) ? '4' : '.',
(old_flags & BATADV_MCAST_WANT_ALL_IPV6) ? '6' : '.',
!(old_flags & BATADV_MCAST_WANT_NO_RTR4) ? "R4" : ". ",
!(old_flags & BATADV_MCAST_WANT_NO_RTR6) ? "R6" : ". ");
!(old_flags & BATADV_MCAST_WANT_NO_RTR6) ? "R6" : ". ",
!(old_flags & BATADV_MCAST_HAVE_MC_PTYPE_CAPA) ? 'P' : '.');
batadv_dbg(BATADV_DBG_MCAST, bat_priv,
"Changing multicast flags from '%s' to '[%c%c%c%s%s]'\n",
"Changing multicast flags from '%s' to '[%c%c%c%s%s%c]'\n",
old_enabled ? str_old_flags : "<undefined>",
(flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES) ? 'U' : '.',
(flags & BATADV_MCAST_WANT_ALL_IPV4) ? '4' : '.',
(flags & BATADV_MCAST_WANT_ALL_IPV6) ? '6' : '.',
!(flags & BATADV_MCAST_WANT_NO_RTR4) ? "R4" : ". ",
!(flags & BATADV_MCAST_WANT_NO_RTR6) ? "R6" : ". ");
!(flags & BATADV_MCAST_WANT_NO_RTR6) ? "R6" : ". ",
!(flags & BATADV_MCAST_HAVE_MC_PTYPE_CAPA) ? 'P' : '.');
}
/**
......@@ -1135,17 +1169,62 @@ static int batadv_mcast_forw_rtr_count(struct batadv_priv *bat_priv,
}
}
/**
* batadv_mcast_forw_mode_by_count() - get forwarding mode by count
* @bat_priv: the bat priv with all the soft interface information
* @skb: the multicast packet to check
* @vid: the vlan identifier
* @is_routable: stores whether the destination is routable
* @count: the number of originators the multicast packet need to be sent to
*
* For a multicast packet with multiple destination originators, checks which
* mode to use. For BATADV_FORW_MCAST it also encapsulates the packet with a
* complete batman-adv multicast header.
*
* Return:
* BATADV_FORW_MCAST: If all nodes have multicast packet routing
* capabilities and an MTU >= 1280 on all hard interfaces (including us)
* and the encapsulated multicast packet with all destination addresses
* would still fit into an 1280 bytes batman-adv multicast packet
* (excluding the outer ethernet frame) and we could successfully push
* the full batman-adv multicast packet header.
* BATADV_FORW_UCASTS: If the packet cannot be sent in a batman-adv
* multicast packet and the amount of batman-adv unicast packets needed
* is smaller or equal to the configured multicast fanout.
* BATADV_FORW_BCAST: Otherwise.
*/
static enum batadv_forw_mode
batadv_mcast_forw_mode_by_count(struct batadv_priv *bat_priv,
struct sk_buff *skb, unsigned short vid,
int is_routable, int count)
{
unsigned int mcast_hdrlen = batadv_mcast_forw_packet_hdrlen(count);
u8 own_tvlv_flags = bat_priv->mcast.mla_flags.tvlv_flags;
if (!atomic_read(&bat_priv->mcast.num_no_mc_ptype_capa) &&
own_tvlv_flags & BATADV_MCAST_HAVE_MC_PTYPE_CAPA &&
skb->len + mcast_hdrlen <= IPV6_MIN_MTU &&
batadv_mcast_forw_push(bat_priv, skb, vid, is_routable, count))
return BATADV_FORW_MCAST;
if (count <= atomic_read(&bat_priv->multicast_fanout))
return BATADV_FORW_UCASTS;
return BATADV_FORW_BCAST;
}
/**
* batadv_mcast_forw_mode() - check on how to forward a multicast packet
* @bat_priv: the bat priv with all the soft interface information
* @skb: the multicast packet to check
* @vid: the vlan identifier
* @is_routable: stores whether the destination is routable
*
* Return: The forwarding mode as enum batadv_forw_mode.
*/
enum batadv_forw_mode
batadv_mcast_forw_mode(struct batadv_priv *bat_priv, struct sk_buff *skb,
int *is_routable)
unsigned short vid, int *is_routable)
{
int ret, tt_count, ip_count, unsnoop_count, total_count;
bool is_unsnoopable = false;
......@@ -1175,10 +1254,8 @@ batadv_mcast_forw_mode(struct batadv_priv *bat_priv, struct sk_buff *skb,
else if (unsnoop_count)
return BATADV_FORW_BCAST;
if (total_count <= atomic_read(&bat_priv->multicast_fanout))
return BATADV_FORW_UCASTS;
return BATADV_FORW_BCAST;
return batadv_mcast_forw_mode_by_count(bat_priv, skb, vid, *is_routable,
total_count);
}
/**
......@@ -1738,6 +1815,31 @@ static void batadv_mcast_want_rtr6_update(struct batadv_priv *bat_priv,
}
}
/**
* batadv_mcast_have_mc_ptype_update() - update multicast packet type counter
* @bat_priv: the bat priv with all the soft interface information
* @orig: the orig_node which multicast state might have changed of
* @mcast_flags: flags indicating the new multicast state
*
* If the BATADV_MCAST_HAVE_MC_PTYPE_CAPA flag of this originator, orig, has
* toggled then this method updates the counter accordingly.
*/
static void batadv_mcast_have_mc_ptype_update(struct batadv_priv *bat_priv,
struct batadv_orig_node *orig,
u8 mcast_flags)
{
lockdep_assert_held(&orig->mcast_handler_lock);
/* switched from flag set to unset */
if (!(mcast_flags & BATADV_MCAST_HAVE_MC_PTYPE_CAPA) &&
orig->mcast_flags & BATADV_MCAST_HAVE_MC_PTYPE_CAPA)
atomic_inc(&bat_priv->mcast.num_no_mc_ptype_capa);
/* switched from flag unset to set */
else if (mcast_flags & BATADV_MCAST_HAVE_MC_PTYPE_CAPA &&
!(orig->mcast_flags & BATADV_MCAST_HAVE_MC_PTYPE_CAPA))
atomic_dec(&bat_priv->mcast.num_no_mc_ptype_capa);
}
/**
* batadv_mcast_tvlv_flags_get() - get multicast flags from an OGM TVLV
* @enabled: whether the originator has multicast TVLV support enabled
......@@ -1806,6 +1908,7 @@ static void batadv_mcast_tvlv_ogm_handler(struct batadv_priv *bat_priv,
batadv_mcast_want_ipv6_update(bat_priv, orig, mcast_flags);
batadv_mcast_want_rtr4_update(bat_priv, orig, mcast_flags);
batadv_mcast_want_rtr6_update(bat_priv, orig, mcast_flags);
batadv_mcast_have_mc_ptype_update(bat_priv, orig, mcast_flags);
orig->mcast_flags = mcast_flags;
spin_unlock_bh(&orig->mcast_handler_lock);
......@@ -1820,6 +1923,10 @@ void batadv_mcast_init(struct batadv_priv *bat_priv)
batadv_tvlv_handler_register(bat_priv, batadv_mcast_tvlv_ogm_handler,
NULL, NULL, BATADV_TVLV_MCAST, 2,
BATADV_TVLV_HANDLER_OGM_CIFNOTFND);
batadv_tvlv_handler_register(bat_priv, NULL, NULL,
batadv_mcast_forw_tracker_tvlv_handler,
BATADV_TVLV_MCAST_TRACKER, 1,
BATADV_TVLV_HANDLER_OGM_CIFNOTFND);
INIT_DELAYED_WORK(&bat_priv->mcast.work, batadv_mcast_mla_update);
batadv_mcast_start_timer(bat_priv);
......
......@@ -11,6 +11,7 @@
#include <linux/netlink.h>
#include <linux/skbuff.h>
#include <linux/types.h>
/**
* enum batadv_forw_mode - the way a packet should be forwarded as
......@@ -28,6 +29,12 @@ enum batadv_forw_mode {
*/
BATADV_FORW_UCASTS,
/**
* @BATADV_FORW_MCAST: forward the packet to some nodes via a
* batman-adv multicast packet
*/
BATADV_FORW_MCAST,
/** @BATADV_FORW_NONE: don't forward, drop it */
BATADV_FORW_NONE,
};
......@@ -36,7 +43,7 @@ enum batadv_forw_mode {
enum batadv_forw_mode
batadv_mcast_forw_mode(struct batadv_priv *bat_priv, struct sk_buff *skb,
int *is_routable);
unsigned short vid, int *is_routable);
int batadv_mcast_forw_send(struct batadv_priv *bat_priv, struct sk_buff *skb,
unsigned short vid, int is_routable);
......@@ -52,11 +59,23 @@ void batadv_mcast_free(struct batadv_priv *bat_priv);
void batadv_mcast_purge_orig(struct batadv_orig_node *orig_node);
/* multicast_forw.c */
int batadv_mcast_forw_tracker_tvlv_handler(struct batadv_priv *bat_priv,
struct sk_buff *skb);
unsigned int batadv_mcast_forw_packet_hdrlen(unsigned int num_dests);
bool batadv_mcast_forw_push(struct batadv_priv *bat_priv, struct sk_buff *skb,
unsigned short vid, int is_routable, int count);
int batadv_mcast_forw_mcsend(struct batadv_priv *bat_priv, struct sk_buff *skb);
#else
static inline enum batadv_forw_mode
batadv_mcast_forw_mode(struct batadv_priv *bat_priv, struct sk_buff *skb,
int *is_routable)
unsigned short vid, int *is_routable)
{
return BATADV_FORW_BCAST;
}
......@@ -94,6 +113,13 @@ static inline void batadv_mcast_purge_orig(struct batadv_orig_node *orig_node)
{
}
static inline int batadv_mcast_forw_mcsend(struct batadv_priv *bat_priv,
struct sk_buff *skb)
{
kfree_skb(skb);
return NET_XMIT_DROP;
}
#endif /* CONFIG_BATMAN_ADV_MCAST */
#endif /* _NET_BATMAN_ADV_MULTICAST_H_ */
// 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
* @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;
}
/**
* 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);
}
}
/**
* 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);
}
/**
* 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);
batadv_mcast_forw_shrink_tracker(nexthop_skb);
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);
}
/**
* 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;
}
......@@ -7,6 +7,7 @@
#include "netlink.h"
#include "main.h"
#include <linux/array_size.h>
#include <linux/atomic.h>
#include <linux/bitops.h>
#include <linux/bug.h>
......@@ -20,7 +21,6 @@
#include <linux/if_ether.h>
#include <linux/if_vlan.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/limits.h>
#include <linux/list.h>
#include <linux/minmax.h>
......
......@@ -311,6 +311,33 @@ batadv_orig_router_get(struct batadv_orig_node *orig_node,
return router;
}
/**
* batadv_orig_to_router() - get next hop neighbor to an orig address
* @bat_priv: the bat priv with all the soft interface information
* @orig_addr: the originator MAC address to search the best next hop router for
* @if_outgoing: the interface where the payload packet has been received or
* the OGM should be sent to
*
* Return: A neighbor node which is the best router towards the given originator
* address.
*/
struct batadv_neigh_node *
batadv_orig_to_router(struct batadv_priv *bat_priv, u8 *orig_addr,
struct batadv_hard_iface *if_outgoing)
{
struct batadv_neigh_node *neigh_node;
struct batadv_orig_node *orig_node;
orig_node = batadv_orig_hash_find(bat_priv, orig_addr);
if (!orig_node)
return NULL;
neigh_node = batadv_find_router(bat_priv, orig_node, if_outgoing);
batadv_orig_node_put(orig_node);
return neigh_node;
}
/**
* batadv_orig_ifinfo_get() - find the ifinfo from an orig_node
* @orig_node: the orig node to be queried
......@@ -942,6 +969,7 @@ struct batadv_orig_node *batadv_orig_node_new(struct batadv_priv *bat_priv,
#ifdef CONFIG_BATMAN_ADV_MCAST
orig_node->mcast_flags = BATADV_MCAST_WANT_NO_RTR4;
orig_node->mcast_flags |= BATADV_MCAST_WANT_NO_RTR6;
orig_node->mcast_flags |= BATADV_MCAST_HAVE_MC_PTYPE_CAPA;
INIT_HLIST_NODE(&orig_node->mcast_want_all_unsnoopables_node);
INIT_HLIST_NODE(&orig_node->mcast_want_all_ipv4_node);
INIT_HLIST_NODE(&orig_node->mcast_want_all_ipv6_node);
......
......@@ -36,6 +36,9 @@ void batadv_neigh_node_release(struct kref *ref);
struct batadv_neigh_node *
batadv_orig_router_get(struct batadv_orig_node *orig_node,
const struct batadv_hard_iface *if_outgoing);
struct batadv_neigh_node *
batadv_orig_to_router(struct batadv_priv *bat_priv, u8 *orig_addr,
struct batadv_hard_iface *if_outgoing);
struct batadv_neigh_ifinfo *
batadv_neigh_ifinfo_new(struct batadv_neigh_node *neigh,
struct batadv_hard_iface *if_outgoing);
......
......@@ -1270,3 +1270,73 @@ int batadv_recv_bcast_packet(struct sk_buff *skb,
batadv_orig_node_put(orig_node);
return ret;
}
#ifdef CONFIG_BATMAN_ADV_MCAST
/**
* batadv_recv_mcast_packet() - process received batman-adv multicast packet
* @skb: the received batman-adv multicast packet
* @recv_if: interface that the skb is received on
*
* Parses the given, received batman-adv multicast packet. Depending on the
* contents of its TVLV forwards it and/or decapsulates it to hand it to the
* soft interface.
*
* Return: NET_RX_DROP if the skb is not consumed, NET_RX_SUCCESS otherwise.
*/
int batadv_recv_mcast_packet(struct sk_buff *skb,
struct batadv_hard_iface *recv_if)
{
struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface);
struct batadv_mcast_packet *mcast_packet;
int hdr_size = sizeof(*mcast_packet);
unsigned char *tvlv_buff;
int ret = NET_RX_DROP;
u16 tvlv_buff_len;
if (batadv_check_unicast_packet(bat_priv, skb, hdr_size) < 0)
goto free_skb;
/* create a copy of the skb, if needed, to modify it. */
if (skb_cow(skb, ETH_HLEN) < 0)
goto free_skb;
/* packet needs to be linearized to access the tvlv content */
if (skb_linearize(skb) < 0)
goto free_skb;
mcast_packet = (struct batadv_mcast_packet *)skb->data;
if (mcast_packet->ttl-- < 2)
goto free_skb;
tvlv_buff = (unsigned char *)(skb->data + hdr_size);
tvlv_buff_len = ntohs(mcast_packet->tvlv_len);
if (tvlv_buff_len > skb->len - hdr_size)
goto free_skb;
ret = batadv_tvlv_containers_process(bat_priv, BATADV_MCAST, NULL, skb,
tvlv_buff, tvlv_buff_len);
if (ret >= 0) {
batadv_inc_counter(bat_priv, BATADV_CNT_MCAST_RX);
batadv_add_counter(bat_priv, BATADV_CNT_MCAST_RX_BYTES,
skb->len + ETH_HLEN);
}
hdr_size += tvlv_buff_len;
if (ret == NET_RX_SUCCESS && (skb->len - hdr_size >= ETH_HLEN)) {
batadv_inc_counter(bat_priv, BATADV_CNT_MCAST_RX_LOCAL);
batadv_add_counter(bat_priv, BATADV_CNT_MCAST_RX_LOCAL_BYTES,
skb->len - hdr_size);
batadv_interface_rx(bat_priv->soft_iface, skb, hdr_size, NULL);
/* skb was consumed */
skb = NULL;
}
free_skb:
kfree_skb(skb);
return ret;
}
#endif /* CONFIG_BATMAN_ADV_MCAST */
......@@ -27,6 +27,17 @@ int batadv_recv_frag_packet(struct sk_buff *skb,
struct batadv_hard_iface *iface);
int batadv_recv_bcast_packet(struct sk_buff *skb,
struct batadv_hard_iface *recv_if);
#ifdef CONFIG_BATMAN_ADV_MCAST
int batadv_recv_mcast_packet(struct sk_buff *skb,
struct batadv_hard_iface *recv_if);
#else
static inline int batadv_recv_mcast_packet(struct sk_buff *skb,
struct batadv_hard_iface *recv_if)
{
kfree_skb(skb);
return NET_RX_DROP;
}
#endif
int batadv_recv_unicast_tvlv(struct sk_buff *skb,
struct batadv_hard_iface *recv_if);
int batadv_recv_unhandled_unicast_packet(struct sk_buff *skb,
......
......@@ -301,12 +301,13 @@ static netdev_tx_t batadv_interface_tx(struct sk_buff *skb,
send:
if (do_bcast && !is_broadcast_ether_addr(ethhdr->h_dest)) {
forw_mode = batadv_mcast_forw_mode(bat_priv, skb,
forw_mode = batadv_mcast_forw_mode(bat_priv, skb, vid,
&mcast_is_routable);
switch (forw_mode) {
case BATADV_FORW_BCAST:
break;
case BATADV_FORW_UCASTS:
case BATADV_FORW_MCAST:
do_bcast = false;
break;
case BATADV_FORW_NONE:
......@@ -365,6 +366,8 @@ static netdev_tx_t batadv_interface_tx(struct sk_buff *skb,
} else if (forw_mode == BATADV_FORW_UCASTS) {
ret = batadv_mcast_forw_send(bat_priv, skb, vid,
mcast_is_routable);
} else if (forw_mode == BATADV_FORW_MCAST) {
ret = batadv_mcast_forw_mcsend(bat_priv, skb);
} else {
if (batadv_dat_snoop_outgoing_arp_request(bat_priv,
skb))
......@@ -762,6 +765,7 @@ static int batadv_softif_init_late(struct net_device *dev)
atomic_set(&bat_priv->mcast.num_want_all_unsnoopables, 0);
atomic_set(&bat_priv->mcast.num_want_all_ipv4, 0);
atomic_set(&bat_priv->mcast.num_want_all_ipv6, 0);
atomic_set(&bat_priv->mcast.num_no_mc_ptype_capa, 0);
#endif
atomic_set(&bat_priv->gw.mode, BATADV_GW_MODE_OFF);
atomic_set(&bat_priv->gw.bandwidth_down, 100);
......@@ -925,6 +929,18 @@ static const struct {
{ "tt_response_rx" },
{ "tt_roam_adv_tx" },
{ "tt_roam_adv_rx" },
#ifdef CONFIG_BATMAN_ADV_MCAST
{ "mcast_tx" },
{ "mcast_tx_bytes" },
{ "mcast_tx_local" },
{ "mcast_tx_local_bytes" },
{ "mcast_rx" },
{ "mcast_rx_bytes" },
{ "mcast_rx_local" },
{ "mcast_rx_local_bytes" },
{ "mcast_fwd" },
{ "mcast_fwd_bytes" },
#endif
#ifdef CONFIG_BATMAN_ADV_DAT
{ "dat_get_tx" },
{ "dat_get_rx" },
......
......@@ -862,6 +862,70 @@ enum batadv_counters {
*/
BATADV_CNT_TT_ROAM_ADV_RX,
#ifdef CONFIG_BATMAN_ADV_MCAST
/**
* @BATADV_CNT_MCAST_TX: transmitted batman-adv multicast packets
* counter
*/
BATADV_CNT_MCAST_TX,
/**
* @BATADV_CNT_MCAST_TX_BYTES: transmitted batman-adv multicast packets
* bytes counter
*/
BATADV_CNT_MCAST_TX_BYTES,
/**
* @BATADV_CNT_MCAST_TX_LOCAL: counter for multicast packets which
* were locally encapsulated and transmitted as batman-adv multicast
* packets
*/
BATADV_CNT_MCAST_TX_LOCAL,
/**
* @BATADV_CNT_MCAST_TX_LOCAL_BYTES: bytes counter for multicast packets
* which were locally encapsulated and transmitted as batman-adv
* multicast packets
*/
BATADV_CNT_MCAST_TX_LOCAL_BYTES,
/**
* @BATADV_CNT_MCAST_RX: received batman-adv multicast packet counter
*/
BATADV_CNT_MCAST_RX,
/**
* @BATADV_CNT_MCAST_RX_BYTES: received batman-adv multicast packet
* bytes counter
*/
BATADV_CNT_MCAST_RX_BYTES,
/**
* @BATADV_CNT_MCAST_RX_LOCAL: counter for received batman-adv multicast
* packets which were forwarded to the local soft interface
*/
BATADV_CNT_MCAST_RX_LOCAL,
/**
* @BATADV_CNT_MCAST_RX_LOCAL_BYTES: bytes counter for received
* batman-adv multicast packets which were forwarded to the local soft
* interface
*/
BATADV_CNT_MCAST_RX_LOCAL_BYTES,
/**
* @BATADV_CNT_MCAST_FWD: counter for received batman-adv multicast
* packets which were forwarded to other, neighboring nodes
*/
BATADV_CNT_MCAST_FWD,
/**
* @BATADV_CNT_MCAST_FWD_BYTES: bytes counter for received batman-adv
* multicast packets which were forwarded to other, neighboring nodes
*/
BATADV_CNT_MCAST_FWD_BYTES,
#endif
#ifdef CONFIG_BATMAN_ADV_DAT
/**
* @BATADV_CNT_DAT_GET_TX: transmitted dht GET traffic packet counter
......@@ -1278,6 +1342,12 @@ struct batadv_priv_mcast {
/** @num_want_all_rtr6: counter for items in want_all_rtr6_list */
atomic_t num_want_all_rtr6;
/**
* @num_no_mc_ptype_capa: counter for number of nodes without the
* BATADV_MCAST_HAVE_MC_PTYPE_CAPA flag
*/
atomic_t num_no_mc_ptype_capa;
/**
* @want_lists_lock: lock for protecting modifications to mcasts
* want_all_{unsnoopables,ipv4,ipv6}_list (traversals are rcu-locked)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment