batman-adv: Modified forwarding behaviour for multicast packets

With this patch a multicast packet is not always simply flooded anymore,
the behaviour for the following cases is changed to reduce
unnecessary overhead:

If all nodes within the horizon of a certain node have signalized
multicast listener announcement capability then an IPv6 multicast packet
with a destination of IPv6 link-local scope (excluding ff02::1) coming
from the upstream of this node...

* ...is dropped if there is no according multicast listener in the
  translation table,
* ...is forwarded via unicast if there is a single node with interested
  multicast listeners
* ...and otherwise still gets flooded.

Signed-off-by: Linus Lüssing <linus.luessing@web.de>
Signed-off-by: Marek Lindner <mareklindner@neomailbox.ch>
Signed-off-by: Antonio Quartulli <antonio@meshcoding.com>
This commit is contained in:
Linus Lüssing 2014-02-15 17:47:52 +01:00 committed by Antonio Quartulli
parent 60432d756c
commit 1d8ab8d3c1
10 changed files with 275 additions and 26 deletions

View File

@ -76,6 +76,15 @@ Description:
is used to classify clients as "isolated" by the
Extended Isolation feature.
What: /sys/class/net/<mesh_iface>/mesh/multicast_mode
Date: Feb 2014
Contact: Linus Lüssing <linus.luessing@web.de>
Description:
Indicates whether multicast optimizations are enabled
or disabled. If set to zero then all nodes in the
mesh are going to use classic flooding for any
multicast packet with no optimizations.
What: /sys/class/net/<mesh_iface>/mesh/network_coding
Date: Nov 2012
Contact: Martin Hundeboll <martin@hundeboll.net>

View File

@ -20,6 +20,7 @@
#include "originator.h"
#include "hard-interface.h"
#include "translation-table.h"
#include "multicast.h"
/**
* batadv_mcast_mla_softif_get - get softif multicast listeners
@ -246,6 +247,131 @@ void batadv_mcast_mla_update(struct batadv_priv *bat_priv)
batadv_mcast_mla_list_free(&mcast_list);
}
/**
* batadv_mcast_forw_mode_check_ipv6 - check for optimized forwarding potential
* @bat_priv: the bat priv with all the soft interface information
* @skb: the IPv6 packet to check
*
* Checks whether the given IPv6 packet has the potential to be forwarded with a
* mode more optimal than classic flooding.
*
* If so then returns 0. Otherwise -EINVAL is returned or -ENOMEM if we are out
* of memory.
*/
static int batadv_mcast_forw_mode_check_ipv6(struct batadv_priv *bat_priv,
struct sk_buff *skb)
{
struct ipv6hdr *ip6hdr;
/* We might fail due to out-of-memory -> drop it */
if (!pskb_may_pull(skb, sizeof(struct ethhdr) + sizeof(*ip6hdr)))
return -ENOMEM;
ip6hdr = ipv6_hdr(skb);
/* TODO: Implement Multicast Router Discovery (RFC4286),
* then allow scope > link local, too
*/
if (IPV6_ADDR_MC_SCOPE(&ip6hdr->daddr) != IPV6_ADDR_SCOPE_LINKLOCAL)
return -EINVAL;
/* link-local-all-nodes multicast listeners behind a bridge are
* not snoopable (see RFC4541, section 3, paragraph 3)
*/
if (ipv6_addr_is_ll_all_nodes(&ip6hdr->daddr))
return -EINVAL;
return 0;
}
/**
* batadv_mcast_forw_mode_check - check for optimized forwarding potential
* @bat_priv: the bat priv with all the soft interface information
* @skb: the multicast frame to check
*
* Checks whether the given multicast ethernet frame has the potential to be
* forwarded with a mode more optimal than classic flooding.
*
* If so then returns 0. Otherwise -EINVAL is returned or -ENOMEM if we are out
* of memory.
*/
static int batadv_mcast_forw_mode_check(struct batadv_priv *bat_priv,
struct sk_buff *skb)
{
struct ethhdr *ethhdr = eth_hdr(skb);
if (!atomic_read(&bat_priv->multicast_mode))
return -EINVAL;
if (atomic_read(&bat_priv->mcast.num_disabled))
return -EINVAL;
switch (ntohs(ethhdr->h_proto)) {
case ETH_P_IPV6:
return batadv_mcast_forw_mode_check_ipv6(bat_priv, skb);
default:
return -EINVAL;
}
}
/**
* batadv_mcast_forw_tt_node_get - get a multicast tt node
* @bat_priv: the bat priv with all the soft interface information
* @ethhdr: the ether header containing the multicast destination
*
* Returns an orig_node matching the multicast address provided by ethhdr
* via a translation table lookup. This increases the returned nodes refcount.
*/
static struct batadv_orig_node *
batadv_mcast_forw_tt_node_get(struct batadv_priv *bat_priv,
struct ethhdr *ethhdr)
{
return batadv_transtable_search(bat_priv, ethhdr->h_source,
ethhdr->h_dest, BATADV_NO_FLAGS);
}
/**
* 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
* @orig: an originator to be set to forward the skb to
*
* Returns the forwarding mode as enum batadv_forw_mode and in case of
* BATADV_FORW_SINGLE set the orig to the single originator the skb
* should be forwarded to.
*/
enum batadv_forw_mode
batadv_mcast_forw_mode(struct batadv_priv *bat_priv, struct sk_buff *skb,
struct batadv_orig_node **orig)
{
struct ethhdr *ethhdr;
int ret, tt_count;
ret = batadv_mcast_forw_mode_check(bat_priv, skb);
if (ret == -ENOMEM)
return BATADV_FORW_NONE;
else if (ret < 0)
return BATADV_FORW_ALL;
ethhdr = eth_hdr(skb);
tt_count = batadv_tt_global_hash_count(bat_priv, ethhdr->h_dest,
BATADV_NO_FLAGS);
switch (tt_count) {
case 1:
*orig = batadv_mcast_forw_tt_node_get(bat_priv, ethhdr);
if (*orig)
return BATADV_FORW_SINGLE;
/* fall through */
case 0:
return BATADV_FORW_NONE;
default:
return BATADV_FORW_ALL;
}
}
/**
* batadv_mcast_tvlv_ogm_handler_v1 - process incoming multicast tvlv container
* @bat_priv: the bat priv with all the soft interface information

View File

@ -18,10 +18,28 @@
#ifndef _NET_BATMAN_ADV_MULTICAST_H_
#define _NET_BATMAN_ADV_MULTICAST_H_
/**
* batadv_forw_mode - the way a packet should be forwarded as
* @BATADV_FORW_ALL: forward the packet to all nodes (currently via classic
* flooding)
* @BATADV_FORW_SINGLE: forward the packet to a single node (currently via the
* BATMAN unicast routing protocol)
* @BATADV_FORW_NONE: don't forward, drop it
*/
enum batadv_forw_mode {
BATADV_FORW_ALL,
BATADV_FORW_SINGLE,
BATADV_FORW_NONE,
};
#ifdef CONFIG_BATMAN_ADV_MCAST
void batadv_mcast_mla_update(struct batadv_priv *bat_priv);
enum batadv_forw_mode
batadv_mcast_forw_mode(struct batadv_priv *bat_priv, struct sk_buff *skb,
struct batadv_orig_node **mcast_single_orig);
void batadv_mcast_init(struct batadv_priv *bat_priv);
void batadv_mcast_free(struct batadv_priv *bat_priv);
@ -35,6 +53,13 @@ static inline void batadv_mcast_mla_update(struct batadv_priv *bat_priv)
return;
}
static inline enum batadv_forw_mode
batadv_mcast_forw_mode(struct batadv_priv *bat_priv, struct sk_buff *skb,
struct batadv_orig_node **mcast_single_orig)
{
return BATADV_FORW_ALL;
}
static inline int batadv_mcast_init(struct batadv_priv *bat_priv)
{
return 0;

View File

@ -248,7 +248,7 @@ bool batadv_send_skb_prepare_unicast_4addr(struct batadv_priv *bat_priv,
*
* Returns NET_XMIT_DROP in case of error or NET_XMIT_SUCCESS otherwise.
*/
static int batadv_send_skb_unicast(struct batadv_priv *bat_priv,
int batadv_send_skb_unicast(struct batadv_priv *bat_priv,
struct sk_buff *skb, int packet_type,
int packet_subtype,
struct batadv_orig_node *orig_node,

View File

@ -36,6 +36,11 @@ bool batadv_send_skb_prepare_unicast_4addr(struct batadv_priv *bat_priv,
struct sk_buff *skb,
struct batadv_orig_node *orig_node,
int packet_subtype);
int batadv_send_skb_unicast(struct batadv_priv *bat_priv,
struct sk_buff *skb, int packet_type,
int packet_subtype,
struct batadv_orig_node *orig_node,
unsigned short vid);
int batadv_send_skb_via_tt_generic(struct batadv_priv *bat_priv,
struct sk_buff *skb, int packet_type,
int packet_subtype, uint8_t *dst_hint,

View File

@ -32,6 +32,7 @@
#include <linux/ethtool.h>
#include <linux/etherdevice.h>
#include <linux/if_vlan.h>
#include "multicast.h"
#include "bridge_loop_avoidance.h"
#include "network-coding.h"
@ -170,6 +171,8 @@ static int batadv_interface_tx(struct sk_buff *skb,
unsigned short vid;
uint32_t seqno;
int gw_mode;
enum batadv_forw_mode forw_mode;
struct batadv_orig_node *mcast_single_orig = NULL;
if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE)
goto dropped;
@ -247,9 +250,19 @@ static int batadv_interface_tx(struct sk_buff *skb,
* directed to a DHCP server
*/
goto dropped;
}
send:
if (do_bcast && !is_broadcast_ether_addr(ethhdr->h_dest)) {
forw_mode = batadv_mcast_forw_mode(bat_priv, skb,
&mcast_single_orig);
if (forw_mode == BATADV_FORW_NONE)
goto dropped;
if (forw_mode == BATADV_FORW_SINGLE)
do_bcast = false;
}
}
batadv_skb_set_priority(skb, 0);
/* ethernet packet should be broadcasted */
@ -301,6 +314,10 @@ static int batadv_interface_tx(struct sk_buff *skb,
if (ret)
goto dropped;
ret = batadv_send_skb_via_gw(bat_priv, skb, vid);
} else if (mcast_single_orig) {
ret = batadv_send_skb_unicast(bat_priv, skb,
BATADV_UNICAST, 0,
mcast_single_orig, vid);
} else {
if (batadv_dat_snoop_outgoing_arp_request(bat_priv,
skb))
@ -691,6 +708,7 @@ static int batadv_softif_init_late(struct net_device *dev)
#endif
#ifdef CONFIG_BATMAN_ADV_MCAST
bat_priv->mcast.flags = BATADV_NO_FLAGS;
atomic_set(&bat_priv->multicast_mode, 1);
atomic_set(&bat_priv->mcast.num_disabled, 0);
#endif
atomic_set(&bat_priv->gw_mode, BATADV_GW_MODE_OFF);

View File

@ -539,6 +539,9 @@ BATADV_ATTR_SIF_UINT(gw_sel_class, S_IRUGO | S_IWUSR, 1, BATADV_TQ_MAX_VALUE,
batadv_post_gw_reselect);
static BATADV_ATTR(gw_bandwidth, S_IRUGO | S_IWUSR, batadv_show_gw_bwidth,
batadv_store_gw_bwidth);
#ifdef CONFIG_BATMAN_ADV_MCAST
BATADV_ATTR_SIF_BOOL(multicast_mode, S_IRUGO | S_IWUSR, NULL);
#endif
#ifdef CONFIG_BATMAN_ADV_DEBUG
BATADV_ATTR_SIF_UINT(log_level, S_IRUGO | S_IWUSR, 0, BATADV_DBG_ALL, NULL);
#endif
@ -557,6 +560,9 @@ static struct batadv_attribute *batadv_mesh_attrs[] = {
#endif
#ifdef CONFIG_BATMAN_ADV_DAT
&batadv_attr_distributed_arp_table,
#endif
#ifdef CONFIG_BATMAN_ADV_MCAST
&batadv_attr_multicast_mode,
#endif
&batadv_attr_fragmentation,
&batadv_attr_routing_algo,

View File

@ -193,6 +193,31 @@ batadv_tt_global_entry_free_ref(struct batadv_tt_global_entry *tt_global_entry)
}
}
/**
* batadv_tt_global_hash_count - count the number of orig entries
* @hash: hash table containing the tt entries
* @addr: the mac address of the client to count entries for
* @vid: VLAN identifier
*
* Return the number of originators advertising the given address/data
* (excluding ourself).
*/
int batadv_tt_global_hash_count(struct batadv_priv *bat_priv,
const uint8_t *addr, unsigned short vid)
{
struct batadv_tt_global_entry *tt_global_entry;
int count;
tt_global_entry = batadv_tt_global_hash_find(bat_priv, addr, vid);
if (!tt_global_entry)
return 0;
count = atomic_read(&tt_global_entry->orig_list_count);
batadv_tt_global_entry_free_ref(tt_global_entry);
return count;
}
static void batadv_tt_orig_list_entry_free_rcu(struct rcu_head *rcu)
{
struct batadv_tt_orig_list_entry *orig_entry;
@ -1225,6 +1250,8 @@ batadv_tt_global_orig_entry_add(struct batadv_tt_global_entry *tt_global,
hlist_add_head_rcu(&orig_entry->list,
&tt_global->orig_list);
spin_unlock_bh(&tt_global->list_lock);
atomic_inc(&tt_global->orig_list_count);
out:
if (orig_entry)
batadv_tt_orig_list_entry_free_ref(orig_entry);
@ -1298,6 +1325,7 @@ static bool batadv_tt_global_add(struct batadv_priv *bat_priv,
common->added_at = jiffies;
INIT_HLIST_HEAD(&tt_global_entry->orig_list);
atomic_set(&tt_global_entry->orig_list_count, 0);
spin_lock_init(&tt_global_entry->list_lock);
hash_added = batadv_hash_add(bat_priv->tt.global_hash,
@ -1563,6 +1591,25 @@ int batadv_tt_global_seq_print_text(struct seq_file *seq, void *offset)
return 0;
}
/**
* batadv_tt_global_del_orig_entry - remove and free an orig_entry
* @tt_global_entry: the global entry to remove the orig_entry from
* @orig_entry: the orig entry to remove and free
*
* Remove an orig_entry from its list in the given tt_global_entry and
* free this orig_entry afterwards.
*/
static void
batadv_tt_global_del_orig_entry(struct batadv_tt_global_entry *tt_global_entry,
struct batadv_tt_orig_list_entry *orig_entry)
{
batadv_tt_global_size_dec(orig_entry->orig_node,
tt_global_entry->common.vid);
atomic_dec(&tt_global_entry->orig_list_count);
hlist_del_rcu(&orig_entry->list);
batadv_tt_orig_list_entry_free_ref(orig_entry);
}
/* deletes the orig list of a tt_global_entry */
static void
batadv_tt_global_del_orig_list(struct batadv_tt_global_entry *tt_global_entry)
@ -1573,17 +1620,23 @@ batadv_tt_global_del_orig_list(struct batadv_tt_global_entry *tt_global_entry)
spin_lock_bh(&tt_global_entry->list_lock);
head = &tt_global_entry->orig_list;
hlist_for_each_entry_safe(orig_entry, safe, head, list) {
hlist_del_rcu(&orig_entry->list);
batadv_tt_global_size_dec(orig_entry->orig_node,
tt_global_entry->common.vid);
batadv_tt_orig_list_entry_free_ref(orig_entry);
}
hlist_for_each_entry_safe(orig_entry, safe, head, list)
batadv_tt_global_del_orig_entry(tt_global_entry, orig_entry);
spin_unlock_bh(&tt_global_entry->list_lock);
}
/**
* batadv_tt_global_del_orig_node - remove orig_node from a global tt entry
* @bat_priv: the bat priv with all the soft interface information
* @tt_global_entry: the global entry to remove the orig_node from
* @orig_node: the originator announcing the client
* @message: message to append to the log on deletion
*
* Remove the given orig_node and its according orig_entry from the given
* global tt entry.
*/
static void
batadv_tt_global_del_orig_entry(struct batadv_priv *bat_priv,
batadv_tt_global_del_orig_node(struct batadv_priv *bat_priv,
struct batadv_tt_global_entry *tt_global_entry,
struct batadv_orig_node *orig_node,
const char *message)
@ -1603,10 +1656,8 @@ batadv_tt_global_del_orig_entry(struct batadv_priv *bat_priv,
orig_node->orig,
tt_global_entry->common.addr,
BATADV_PRINT_VID(vid), message);
hlist_del_rcu(&orig_entry->list);
batadv_tt_global_size_dec(orig_node,
tt_global_entry->common.vid);
batadv_tt_orig_list_entry_free_ref(orig_entry);
batadv_tt_global_del_orig_entry(tt_global_entry,
orig_entry);
}
}
spin_unlock_bh(&tt_global_entry->list_lock);
@ -1648,7 +1699,7 @@ batadv_tt_global_del_roaming(struct batadv_priv *bat_priv,
/* there is another entry, we can simply delete this
* one and can still use the other one.
*/
batadv_tt_global_del_orig_entry(bat_priv, tt_global_entry,
batadv_tt_global_del_orig_node(bat_priv, tt_global_entry,
orig_node, message);
}
@ -1675,7 +1726,7 @@ static void batadv_tt_global_del(struct batadv_priv *bat_priv,
goto out;
if (!roaming) {
batadv_tt_global_del_orig_entry(bat_priv, tt_global_entry,
batadv_tt_global_del_orig_node(bat_priv, tt_global_entry,
orig_node, message);
if (hlist_empty(&tt_global_entry->orig_list))
@ -1759,7 +1810,7 @@ void batadv_tt_global_del_orig(struct batadv_priv *bat_priv,
struct batadv_tt_global_entry,
common);
batadv_tt_global_del_orig_entry(bat_priv, tt_global,
batadv_tt_global_del_orig_node(bat_priv, tt_global,
orig_node, message);
if (hlist_empty(&tt_global->orig_list)) {

View File

@ -29,6 +29,8 @@ int batadv_tt_global_seq_print_text(struct seq_file *seq, void *offset);
void batadv_tt_global_del_orig(struct batadv_priv *bat_priv,
struct batadv_orig_node *orig_node,
int32_t match_vid, const char *message);
int batadv_tt_global_hash_count(struct batadv_priv *bat_priv,
const uint8_t *addr, unsigned short vid);
struct batadv_orig_node *batadv_transtable_search(struct batadv_priv *bat_priv,
const uint8_t *src,
const uint8_t *addr,

View File

@ -696,6 +696,8 @@ struct batadv_softif_vlan {
* enabled
* @distributed_arp_table: bool indicating whether distributed ARP table is
* enabled
* @multicast_mode: Enable or disable multicast optimizations on this node's
* sender/originating side
* @gw_mode: gateway operation: off, client or server (see batadv_gw_modes)
* @gw_sel_class: gateway selection class (applies if gw_mode client)
* @orig_interval: OGM broadcast interval in milliseconds
@ -745,6 +747,9 @@ struct batadv_priv {
#endif
#ifdef CONFIG_BATMAN_ADV_DAT
atomic_t distributed_arp_table;
#endif
#ifdef CONFIG_BATMAN_ADV_MCAST
atomic_t multicast_mode;
#endif
atomic_t gw_mode;
atomic_t gw_sel_class;
@ -909,12 +914,14 @@ struct batadv_tt_local_entry {
* struct batadv_tt_global_entry - translation table global entry data
* @common: general translation table data
* @orig_list: list of orig nodes announcing this non-mesh client
* @orig_list_count: number of items in the orig_list
* @list_lock: lock protecting orig_list
* @roam_at: time at which TT_GLOBAL_ROAM was set
*/
struct batadv_tt_global_entry {
struct batadv_tt_common_entry common;
struct hlist_head orig_list;
atomic_t orig_list_count;
spinlock_t list_lock; /* protects orig_list */
unsigned long roam_at;
};