This is the twelfth revision of the basic multicast optimization patches.
Changes in v12 include (thanks for the feedback again, Marek and Simon):
* added three reserved bytes to the mcast TVLV container to have a four bytes alignment (thanks Simon) * Return value description in kernel docs for batadv_mcast_mla_tvlv_update added (thanks Marek) * Using eth_hdr() instead of skb->data in various places (thanks Marek) * Switched order of function parameters bat_priv and skb in various places for style consistency reasons (thanks Marek) * renamed one occurence of "BATMAN_IV" to "BATMAN" (thanks Marek) * kerneldoc for multicast_mode added (thanks Marek) * rebased on top of master * now that batadv_tt_local_add() has a potential failure case, a check for its return value was added
For Marek's question on gateway code interoperability, I reread that part and couldn't find any issues with it either. Moreover, any additional dhcp_rcp variable check seemed redundant to me, so I left that part as is.
Cheers, Linus
With this patch a node which has no bridge interface on top of its soft interface announces its local multicast listeners via the translation table.
Signed-off-by: Linus Lüssing linus.luessing@web.de --- Makefile | 2 + Makefile.kbuild | 1 + compat.h | 37 ++++++++ gen-compat-autoconf.sh | 1 + main.c | 6 ++ main.h | 1 + multicast.c | 242 ++++++++++++++++++++++++++++++++++++++++++++++++ multicast.h | 38 ++++++++ translation-table.c | 22 ++++- types.h | 24 +++++ 10 files changed, 370 insertions(+), 4 deletions(-) create mode 100644 multicast.c create mode 100644 multicast.h
diff --git a/Makefile b/Makefile index 9a7cbae..4f6c30a 100644 --- a/Makefile +++ b/Makefile @@ -25,6 +25,8 @@ export CONFIG_BATMAN_ADV_BLA=y export CONFIG_BATMAN_ADV_DAT=y # B.A.T.M.A.N network coding (catwoman): export CONFIG_BATMAN_ADV_NC=n +# B.A.T.M.A.N. multicast optimizations: +export CONFIG_BATMAN_ADV_MCAST=y
PWD:=$(shell pwd) KERNELPATH ?= /lib/modules/$(shell uname -r)/build diff --git a/Makefile.kbuild b/Makefile.kbuild index 42df18f..eb7d8c0 100644 --- a/Makefile.kbuild +++ b/Makefile.kbuild @@ -36,3 +36,4 @@ batman-adv-y += send.o batman-adv-y += soft-interface.o batman-adv-y += sysfs.o batman-adv-y += translation-table.o +batman-adv-$(CONFIG_BATMAN_ADV_MCAST) += multicast.o diff --git a/compat.h b/compat.h index 12bc8d8..7a3d235 100644 --- a/compat.h +++ b/compat.h @@ -103,6 +103,24 @@ static inline struct sk_buff *netdev_alloc_skb_ip_align(struct net_device *dev,
#define pr_warn pr_warning
+#undef netdev_for_each_mc_addr +#define netdev_for_each_mc_addr(mclist, dev) \ + for (mclist = (struct batadv_dev_addr_list *)dev->mc_list; mclist; \ + mclist = (struct batadv_dev_addr_list *)mclist->next) + +/* Note, that this breaks the usage of the normal 'struct netdev_hw_addr' + * for kernels < 2.6.35 in batman-adv! + */ +#define netdev_hw_addr batadv_dev_addr_list +struct batadv_dev_addr_list { + struct dev_addr_list *next; + u8 addr[MAX_ADDR_LEN]; + u8 da_addrlen; + u8 da_synced; + int da_users; + int da_gusers; +}; + #endif /* < KERNEL_VERSION(2, 6, 35) */
@@ -143,6 +161,14 @@ static inline int batadv_param_set_copystring(const char *val, #define addr_assign_type ifindex #define NET_ADDR_RANDOM 0
+#define netdev_master_upper_dev_get_rcu(dev) \ + NULL; \ + if (dev->br_port ? 1 : 0) { \ + rcu_read_unlock(); \ + dev_hold(dev); \ + return dev; \ + } + #endif /* < KERNEL_VERSION(2, 6, 36) */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37) @@ -343,6 +369,17 @@ static int __batadv_interface_tx(struct sk_buff *skb, \ pos && ({ n = pos->member.next; 1; }); \ pos = hlist_entry_safe(n, typeof(*pos), member))
+#ifndef netdev_master_upper_dev_get_rcu +#define netdev_master_upper_dev_get_rcu(dev) \ + NULL; \ + if (dev->priv_flags & IFF_BRIDGE_PORT) { \ + rcu_read_unlock(); \ + dev_hold(dev); \ + return dev; \ + } + +#endif /* netdev_master_upper_dev_get_rcu */ + #endif /* < KERNEL_VERSION(3, 9, 0) */
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0) diff --git a/gen-compat-autoconf.sh b/gen-compat-autoconf.sh index 78573e4..f625b6f 100755 --- a/gen-compat-autoconf.sh +++ b/gen-compat-autoconf.sh @@ -39,6 +39,7 @@ gen_config() { gen_config 'CONFIG_BATMAN_ADV_DEBUG' ${CONFIG_BATMAN_ADV_DEBUG:="n"} >> "${TMP}" gen_config 'CONFIG_BATMAN_ADV_BLA' ${CONFIG_BATMAN_ADV_BLA:="y"} >> "${TMP}" gen_config 'CONFIG_BATMAN_ADV_DAT' ${CONFIG_BATMAN_ADV_DAT:="y"} >> "${TMP}" +gen_config 'CONFIG_BATMAN_ADV_MCAST' ${CONFIG_BATMAN_ADV_MCAST:="y"} >> "${TMP}" gen_config 'CONFIG_BATMAN_ADV_NC' ${CONFIG_BATMAN_ADV_NC:="n"} >> "${TMP}"
# only regenerate compat-autoconf.h when config was changed diff --git a/main.c b/main.c index fbeaebd..58e98c8 100644 --- a/main.c +++ b/main.c @@ -34,6 +34,7 @@ #include "gateway_client.h" #include "bridge_loop_avoidance.h" #include "distributed-arp-table.h" +#include "multicast.h" #include "gateway_common.h" #include "hash.h" #include "bat_algo.h" @@ -120,6 +121,9 @@ int batadv_mesh_init(struct net_device *soft_iface) INIT_LIST_HEAD(&bat_priv->tt.changes_list); INIT_LIST_HEAD(&bat_priv->tt.req_list); INIT_LIST_HEAD(&bat_priv->tt.roam_list); +#ifdef CONFIG_BATMAN_ADV_MCAST + INIT_HLIST_HEAD(&bat_priv->mcast.mla_list); +#endif INIT_HLIST_HEAD(&bat_priv->tvlv.container_list); INIT_HLIST_HEAD(&bat_priv->tvlv.handler_list); INIT_HLIST_HEAD(&bat_priv->softif_vlan_list); @@ -169,6 +173,8 @@ void batadv_mesh_free(struct net_device *soft_iface) batadv_dat_free(bat_priv); batadv_bla_free(bat_priv);
+ batadv_mcast_free(bat_priv); + /* Free the TT and the originator tables only after having terminated * all the other depending components which may use these structures for * their purposes. diff --git a/main.h b/main.h index 34e85a2..dc6b4a2 100644 --- a/main.h +++ b/main.h @@ -176,6 +176,7 @@ enum batadv_uev_type { #include <linux/percpu.h> #include <linux/slab.h> #include <net/sock.h> /* struct sock */ +#include <net/addrconf.h> /* ipv6 address stuff */ #include <net/rtnetlink.h> #include <linux/jiffies.h> #include <linux/seq_file.h> diff --git a/multicast.c b/multicast.c new file mode 100644 index 0000000..ea04c46 --- /dev/null +++ b/multicast.c @@ -0,0 +1,242 @@ +/* Copyright (C) 2013 B.A.T.M.A.N. contributors: + * + * Linus Lüssing + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include "main.h" +#include "multicast.h" +#include "originator.h" +#include "hard-interface.h" +#include "translation-table.h" + +/** + * batadv_mcast_mla_softif_get - get softif multicast listeners + * @dev: the device to collect multicast addresses from + * @mcast_list: a list to put found addresses into + * + * Collect multicast addresses of the local multicast listeners + * on the given soft interface, dev, in the given mcast_list. + * + * Return -ENOMEM on memory allocation error or the number of + * items added to the mcast_list otherwise. + */ +static int batadv_mcast_mla_softif_get(struct net_device *dev, + struct hlist_head *mcast_list) +{ + struct netdev_hw_addr *mc_list_entry; + struct batadv_hw_addr *new; + int ret = 0; + + netif_addr_lock_bh(dev); + netdev_for_each_mc_addr(mc_list_entry, dev) { + new = kmalloc(sizeof(*new), GFP_ATOMIC); + if (!new) { + ret = -ENOMEM; + break; + } + + memcpy(&new->addr, &mc_list_entry->addr, ETH_ALEN); + hlist_add_head(&new->list, mcast_list); + ret++; + } + netif_addr_unlock_bh(dev); + + return ret; +} + +/** + * batadv_mcast_mla_is_duplicate - check whether an address is in a list + * @mcast_addr: the multicast address to check + * @mcast_list: the list with multicast addresses to search in + * + * Return true if the given address is already in the given list. + * Otherwise returns false. + */ +static bool batadv_mcast_mla_is_duplicate(uint8_t *mcast_addr, + struct hlist_head *mcast_list) +{ + struct batadv_hw_addr *mcast_entry; + + hlist_for_each_entry(mcast_entry, mcast_list, list) + if (batadv_compare_eth(mcast_entry->addr, mcast_addr)) + return true; + + return false; +} + +/** + * batadv_mcast_mla_list_free - free a list of multicast addresses + * @mcast_list: the list to free + * + * Remove and free all items in the given mcast_list. + */ +static void batadv_mcast_mla_list_free(struct hlist_head *mcast_list) +{ + struct batadv_hw_addr *mcast_entry; + struct hlist_node *tmp; + + hlist_for_each_entry_safe(mcast_entry, tmp, mcast_list, list) { + hlist_del(&mcast_entry->list); + kfree(mcast_entry); + } +} + +/** + * batadv_mcast_mla_tt_retract - clean up multicast listener announcements + * @bat_priv: the bat priv with all the soft interface information + * @mcast_list: a list of addresses which should _not_ be removed + * + * Retract the announcement of any multicast listener from the + * translation table except the ones listed in the given mcast_list. + * + * If mcast_list is NULL then all are retracted. + */ +static void batadv_mcast_mla_tt_retract(struct batadv_priv *bat_priv, + struct hlist_head *mcast_list) +{ + struct batadv_hw_addr *mcast_entry; + struct hlist_node *tmp; + + hlist_for_each_entry_safe(mcast_entry, tmp, &bat_priv->mcast.mla_list, + list) { + if (mcast_list && + batadv_mcast_mla_is_duplicate(mcast_entry->addr, + mcast_list)) + continue; + + batadv_tt_local_remove(bat_priv, mcast_entry->addr, + BATADV_NO_FLAGS, + "mcast TT outdated", false); + + hlist_del(&mcast_entry->list); + kfree(mcast_entry); + } +} + +/** + * batadv_mcast_mla_tt_add - add multicast listener announcements + * @bat_priv: the bat priv with all the soft interface information + * @mcast_list: a list of addresses which are going to get added + * + * Add multicast listener announcements from the given mcast_list to the + * translation table if they have not been added yet. + */ +static void batadv_mcast_mla_tt_add(struct batadv_priv *bat_priv, + struct hlist_head *mcast_list) +{ + struct batadv_hw_addr *mcast_entry; + struct hlist_node *tmp; + + if (!mcast_list) + return; + + hlist_for_each_entry_safe(mcast_entry, tmp, mcast_list, list) { + if (batadv_mcast_mla_is_duplicate(mcast_entry->addr, + &bat_priv->mcast.mla_list)) + continue; + + if (!batadv_tt_local_add(bat_priv->soft_iface, + mcast_entry->addr, BATADV_NO_FLAGS, + BATADV_NULL_IFINDEX, BATADV_NO_MARK)) + continue; + + hlist_del(&mcast_entry->list); + hlist_add_head(&mcast_entry->list, &bat_priv->mcast.mla_list); + } +} + +/** + * batadv_mcast_get_bridge - get the bridge interface on our soft interface + * @bat_priv: the bat priv with all the soft interface information + * + * Return the next bridge interface on top of our soft interface and increase + * its refcount. If no such bridge interface exists, then return NULL. + */ +static struct net_device * +batadv_mcast_get_bridge(struct batadv_priv *bat_priv) +{ + struct net_device *upper = bat_priv->soft_iface; + + rcu_read_lock(); + + do { + upper = netdev_master_upper_dev_get_rcu(upper); + } while (upper && !(upper->priv_flags & IFF_EBRIDGE)); + + if (upper) + dev_hold(upper); + + rcu_read_unlock(); + + return upper; +} + +/** + * batadv_mcast_has_bridge - check whether the soft-iface is bridged + * @bat_priv: the bat priv with all the soft interface information + * + * Check whether there is a bridge on top of our soft interface. Return + * true if so, false otherwise. + */ +static bool batadv_mcast_has_bridge(struct batadv_priv *bat_priv) +{ + struct net_device *bridge; + + bridge = batadv_mcast_get_bridge(bat_priv); + if (!bridge) + goto out; + + dev_put(bridge); + return true; +out: + return false; +} + +/** + * batadv_mcast_mla_update - update the own MLAs + * @bat_priv: the bat priv with all the soft interface information + * + * Update the own multicast listener announcements in the translation + * table. + */ +void batadv_mcast_mla_update(struct batadv_priv *bat_priv) +{ + struct net_device *soft_iface = bat_priv->soft_iface; + struct hlist_head mcast_list = HLIST_HEAD_INIT; + int ret; + + /* Avoid attaching MLAs, if there is a bridge on top of our soft + * interface, we don't support that yet (TODO) + */ + if (batadv_mcast_has_bridge(bat_priv)) + goto update; + + ret = batadv_mcast_mla_softif_get(soft_iface, &mcast_list); + if (ret < 0) + goto out; + +update: + batadv_mcast_mla_tt_retract(bat_priv, &mcast_list); + batadv_mcast_mla_tt_add(bat_priv, &mcast_list); + +out: + batadv_mcast_mla_list_free(&mcast_list); +} + +/** + * batadv_mcast_free - free the multicast optimizations structures + * @bat_priv: the bat priv with all the soft interface information + */ +void batadv_mcast_free(struct batadv_priv *bat_priv) +{ + batadv_mcast_mla_tt_retract(bat_priv, NULL); +} diff --git a/multicast.h b/multicast.h new file mode 100644 index 0000000..b5c50ed --- /dev/null +++ b/multicast.h @@ -0,0 +1,38 @@ +/* Copyright (C) 2013 B.A.T.M.A.N. contributors: + * + * Linus Lüssing + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef _NET_BATMAN_ADV_MULTICAST_H_ +#define _NET_BATMAN_ADV_MULTICAST_H_ + +#ifdef CONFIG_BATMAN_ADV_MCAST + +void batadv_mcast_mla_update(struct batadv_priv *bat_priv); + +void batadv_mcast_free(struct batadv_priv *bat_priv); + +#else + +static inline void batadv_mcast_mla_update(struct batadv_priv *bat_priv) +{ + return; +} + +static inline void batadv_mcast_free(struct batadv_priv *bat_priv) +{ + return; +} + +#endif /* CONFIG_BATMAN_ADV_MCAST */ + +#endif /* _NET_BATMAN_ADV_MULTICAST_H_ */ diff --git a/translation-table.c b/translation-table.c index 138cffa..fb85c81 100644 --- a/translation-table.c +++ b/translation-table.c @@ -24,6 +24,7 @@ #include "originator.h" #include "routing.h" #include "bridge_loop_avoidance.h" +#include "multicast.h"
#include <linux/crc32c.h>
@@ -484,7 +485,7 @@ bool batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr, { struct batadv_priv *bat_priv = netdev_priv(soft_iface); struct batadv_tt_local_entry *tt_local; - struct batadv_tt_global_entry *tt_global; + struct batadv_tt_global_entry *tt_global = NULL; struct net_device *in_dev = NULL; struct hlist_head *head; struct batadv_tt_orig_list_entry *orig_entry; @@ -497,7 +498,9 @@ bool batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr, in_dev = dev_get_by_index(&init_net, ifindex);
tt_local = batadv_tt_local_hash_find(bat_priv, addr, vid); - tt_global = batadv_tt_global_hash_find(bat_priv, addr, vid); + + if (!is_multicast_ether_addr(addr)) + tt_global = batadv_tt_global_hash_find(bat_priv, addr, vid);
if (tt_local) { tt_local->last_seen = jiffies; @@ -562,8 +565,11 @@ bool batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr, tt_local->last_seen = jiffies; tt_local->common.added_at = tt_local->last_seen;
- /* the batman interface mac address should never be purged */ - if (batadv_compare_eth(addr, soft_iface->dev_addr)) + /* the batman interface mac and multicast addresses should never be + * purged + */ + if (batadv_compare_eth(addr, soft_iface->dev_addr) || + is_multicast_ether_addr(addr)) tt_local->common.flags |= BATADV_TT_CLIENT_NOPURGE;
hash_added = batadv_hash_add(bat_priv->tt.local_hash, batadv_compare_tt, @@ -1361,6 +1367,11 @@ add_orig_entry: ret = true;
out_remove: + /* Do not remove multicast addresses from the local hash on + * global additions + */ + if (is_multicast_ether_addr(tt_addr)) + goto out;
/* remove address from local hash if present */ local_flags = batadv_tt_local_remove(bat_priv, tt_addr, vid, @@ -3108,6 +3119,9 @@ static void batadv_tt_local_purge_pending_clients(struct batadv_priv *bat_priv) */ static void batadv_tt_local_commit_changes_nolock(struct batadv_priv *bat_priv) { + /* Update multicast addresses in local translation table */ + batadv_mcast_mla_update(bat_priv); + if (atomic_read(&bat_priv->tt.local_changes) < 1) { if (!batadv_atomic_dec_not_zero(&bat_priv->tt.ogm_append_cnt)) batadv_tt_tvlv_container_update(bat_priv); diff --git a/types.h b/types.h index 9f52517..d553264 100644 --- a/types.h +++ b/types.h @@ -607,6 +607,16 @@ struct batadv_priv_dat { }; #endif
+#ifdef CONFIG_BATMAN_ADV_MCAST +/** + * struct batadv_priv_mcast - per mesh interface mcast data + * @mla_list: list of multicast addresses we are currently announcing via TT + */ +struct batadv_priv_mcast { + struct hlist_head mla_list; +}; +#endif + /** * struct batadv_priv_nc - per mesh interface network coding private data * @work: work queue callback item for cleanup @@ -702,6 +712,7 @@ struct batadv_softif_vlan { * @tt: translation table data * @tvlv: type-version-length-value data * @dat: distributed arp table data + * @mcast: multicast data * @network_coding: bool indicating whether network coding is enabled * @batadv_priv_nc: network coding data */ @@ -759,6 +770,9 @@ struct batadv_priv { #ifdef CONFIG_BATMAN_ADV_DAT struct batadv_priv_dat dat; #endif +#ifdef CONFIG_BATMAN_ADV_MCAST + struct batadv_priv_mcast mcast; +#endif #ifdef CONFIG_BATMAN_ADV_NC atomic_t network_coding; struct batadv_priv_nc nc; @@ -1116,6 +1130,16 @@ struct batadv_dat_entry { };
/** + * struct batadv_hw_addr - a list entry for a MAC address + * @list: list node for the linking of entries + * @addr: the MAC address of this list entry + */ +struct batadv_hw_addr { + struct hlist_node list; + unsigned char addr[ETH_ALEN]; +}; + +/** * struct batadv_dat_candidate - candidate destination for DAT operations * @type: the type of the selected candidate. It can one of the following: * - BATADV_DAT_CANDIDATE_NOT_FOUND
The new bitfield allows us to keep track whether capability subsets of an originator have gone through their initialization phase yet.
The translation table is the only user right now, but a new one will be added soon.
Signed-off-by: Linus Lüssing linus.luessing@web.de --- originator.c | 1 - translation-table.c | 12 +++++++----- types.h | 7 ++++--- 3 files changed, 11 insertions(+), 9 deletions(-)
diff --git a/originator.c b/originator.c index c4d16e5..ab22450 100644 --- a/originator.c +++ b/originator.c @@ -628,7 +628,6 @@ struct batadv_orig_node *batadv_orig_node_new(struct batadv_priv *bat_priv, /* extra reference for return */ atomic_set(&orig_node->refcount, 2);
- orig_node->tt_initialised = false; orig_node->bat_priv = bat_priv; ether_addr_copy(orig_node->orig, addr); batadv_dat_init_orig_node_addr(orig_node); diff --git a/translation-table.c b/translation-table.c index fb85c81..d1d4e7d 100644 --- a/translation-table.c +++ b/translation-table.c @@ -1774,7 +1774,7 @@ void batadv_tt_global_del_orig(struct batadv_priv *bat_priv, } spin_unlock_bh(list_lock); } - orig_node->tt_initialised = false; + orig_node->capa_initialized &= ~BATADV_ORIG_CAPA_HAS_TT; }
static bool batadv_tt_global_to_purge(struct batadv_tt_global_entry *tt_global, @@ -2722,7 +2722,7 @@ static void _batadv_tt_update_changes(struct batadv_priv *bat_priv, return; } } - orig_node->tt_initialised = true; + orig_node->capa_initialized |= BATADV_ORIG_CAPA_HAS_TT; }
static void batadv_tt_fill_gtable(struct batadv_priv *bat_priv, @@ -3212,13 +3212,15 @@ static void batadv_tt_update_orig(struct batadv_priv *bat_priv, uint8_t orig_ttvn = (uint8_t)atomic_read(&orig_node->last_ttvn); struct batadv_tvlv_tt_vlan_data *tt_vlan; bool full_table = true; + bool has_tt_init;
tt_vlan = (struct batadv_tvlv_tt_vlan_data *)tt_buff; + has_tt_init = orig_node->capa_initialized & BATADV_ORIG_CAPA_HAS_TT; + /* orig table not initialised AND first diff is in the OGM OR the ttvn * increased by one -> we can apply the attached changes */ - if ((!orig_node->tt_initialised && ttvn == 1) || - ttvn - orig_ttvn == 1) { + if ((!has_tt_init && ttvn == 1) || ttvn - orig_ttvn == 1) { /* the OGM could not contain the changes due to their size or * because they have already been sent BATADV_TT_OGM_APPEND_MAX * times. @@ -3259,7 +3261,7 @@ static void batadv_tt_update_orig(struct batadv_priv *bat_priv, /* if we missed more than one change or our tables are not * in sync anymore -> request fresh tt data */ - if (!orig_node->tt_initialised || ttvn != orig_ttvn || + if (!has_tt_init || ttvn != orig_ttvn || !batadv_tt_global_check_crc(orig_node, tt_vlan, tt_num_vlan)) { request_table: diff --git a/types.h b/types.h index d553264..b46117c 100644 --- a/types.h +++ b/types.h @@ -205,13 +205,12 @@ struct batadv_orig_bat_iv { * @last_seen: time when last packet from this node was received * @bcast_seqno_reset: time when the broadcast seqno window was reset * @capabilities: announced capabilities of this originator + * @capa_initialized: bitfield to remember whether a capability was initialized * @last_ttvn: last seen translation table version number * @tt_buff: last tt changeset this node received from the orig node * @tt_buff_len: length of the last tt changeset this node received from the * orig node * @tt_buff_lock: lock that protects tt_buff and tt_buff_len - * @tt_initialised: bool keeping track of whether or not this node have received - * any translation table information from the orig node yet * @tt_lock: prevents from updating the table while reading it. Table update is * made up by two operations (data structure update and metdata -CRC/TTVN- * recalculation) and they have to be executed atomically in order to avoid @@ -248,11 +247,11 @@ struct batadv_orig_node { unsigned long last_seen; unsigned long bcast_seqno_reset; uint8_t capabilities; + uint8_t capa_initialized; atomic_t last_ttvn; unsigned char *tt_buff; int16_t tt_buff_len; spinlock_t tt_buff_lock; /* protects tt_buff & tt_buff_len */ - bool tt_initialised; /* prevents from changing the table while reading it */ spinlock_t tt_lock; DECLARE_BITMAP(bcast_bits, BATADV_TQ_LOCAL_WINDOW_SIZE); @@ -282,10 +281,12 @@ struct batadv_orig_node { * enum batadv_orig_capabilities - orig node capabilities * @BATADV_ORIG_CAPA_HAS_DAT: orig node has distributed arp table enabled * @BATADV_ORIG_CAPA_HAS_NC: orig node has network coding enabled + * @BATADV_ORIG_CAPA_HAS_TT: orig node has tt capability */ enum batadv_orig_capabilities { BATADV_ORIG_CAPA_HAS_DAT = BIT(0), BATADV_ORIG_CAPA_HAS_NC = BIT(1), + BATADV_ORIG_CAPA_HAS_TT = BIT(2), };
/**
If the soft interface of a node is not part of a bridge then a node announces a new multicast TVLV: The existence of this TVLV signalizes that this node is announcing all of its multicast listeners via the translation table infrastructure.
Signed-off-by: Linus Lüssing linus.luessing@web.de --- main.c | 1 + multicast.c | 121 +++++++++++++++++++++++++++++++++++++++++++++++++++--- multicast.h | 14 +++++++ originator.c | 6 +++ packet.h | 12 ++++++ soft-interface.c | 4 ++ types.h | 13 ++++++ 7 files changed, 166 insertions(+), 5 deletions(-)
diff --git a/main.c b/main.c index 58e98c8..8f11b67 100644 --- a/main.c +++ b/main.c @@ -149,6 +149,7 @@ int batadv_mesh_init(struct net_device *soft_iface) goto err;
batadv_gw_init(bat_priv); + batadv_mcast_init(bat_priv);
atomic_set(&bat_priv->gw.reselect, 0); atomic_set(&bat_priv->mesh_state, BATADV_MESH_ACTIVE); diff --git a/multicast.c b/multicast.c index ea04c46..bc66c07 100644 --- a/multicast.c +++ b/multicast.c @@ -202,11 +202,52 @@ out: }
/** + * batadv_mcast_mla_tvlv_update - update multicast tvlv + * @bat_priv: the bat priv with all the soft interface information + * + * Update the own multicast tvlv with our current multicast related settings, + * capabilities and inabilities. + * + * Return true if the tvlv container is registered afterwards. Otherwise return + * false. + */ +static bool batadv_mcast_mla_tvlv_update(struct batadv_priv *bat_priv) +{ + struct batadv_tvlv_mcast_data mcast_data; + + mcast_data.flags = BATADV_NO_FLAGS; + memset(mcast_data.reserved, 0, sizeof(mcast_data.reserved)); + + /* Avoid attaching MLAs, if there is a bridge on top of our soft + * interface, we don't support that yet (TODO) + */ + if (batadv_mcast_has_bridge(bat_priv)) { + if (bat_priv->mcast.enabled) { + batadv_tvlv_container_unregister(bat_priv, + BATADV_TVLV_MCAST, 1); + bat_priv->mcast.enabled = false; + } + + return false; + } + + if (!bat_priv->mcast.enabled || + mcast_data.flags != bat_priv->mcast.flags) { + batadv_tvlv_container_register(bat_priv, BATADV_TVLV_MCAST, 1, + &mcast_data, sizeof(mcast_data)); + bat_priv->mcast.flags = mcast_data.flags; + bat_priv->mcast.enabled = true; + } + + return true; +} + +/** * batadv_mcast_mla_update - update the own MLAs * @bat_priv: the bat priv with all the soft interface information * * Update the own multicast listener announcements in the translation - * table. + * table as well as the own, announced multicast tvlv container. */ void batadv_mcast_mla_update(struct batadv_priv *bat_priv) { @@ -214,10 +255,7 @@ void batadv_mcast_mla_update(struct batadv_priv *bat_priv) struct hlist_head mcast_list = HLIST_HEAD_INIT; int ret;
- /* Avoid attaching MLAs, if there is a bridge on top of our soft - * interface, we don't support that yet (TODO) - */ - if (batadv_mcast_has_bridge(bat_priv)) + if (!batadv_mcast_mla_tvlv_update(bat_priv)) goto update;
ret = batadv_mcast_mla_softif_get(soft_iface, &mcast_list); @@ -233,10 +271,83 @@ out: }
/** + * batadv_mcast_tvlv_ogm_handler_v1 - process incoming multicast tvlv container + * @bat_priv: the bat priv with all the soft interface information + * @orig: the orig_node of the ogm + * @flags: flags indicating the tvlv state (see batadv_tvlv_handler_flags) + * @tvlv_value: tvlv buffer containing the multicast data + * @tvlv_value_len: tvlv buffer length + */ +static void batadv_mcast_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv, + struct batadv_orig_node *orig, + uint8_t flags, + void *tvlv_value, + uint16_t tvlv_value_len) +{ + bool orig_mcast_enabled = !(flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND); + uint8_t mcast_flags = BATADV_NO_FLAGS; + bool orig_initialized; + + orig_initialized = orig->capa_initialized & BATADV_ORIG_CAPA_HAS_MCAST; + + /* If mcast support is turned on decrease the disabled mcast node + * counter only if we had increased it for this node before. If this + * is a completely new orig_node no need to decrease the counter. + */ + if (orig_mcast_enabled && + !(orig->capabilities & BATADV_ORIG_CAPA_HAS_MCAST)) { + if (orig_initialized) + atomic_dec(&bat_priv->mcast.num_disabled); + orig->capabilities |= BATADV_ORIG_CAPA_HAS_MCAST; + /* If mcast support is being switched off increase the disabled + * mcast node counter. + */ + } else if (!orig_mcast_enabled && + orig->capabilities & BATADV_ORIG_CAPA_HAS_MCAST) { + atomic_inc(&bat_priv->mcast.num_disabled); + orig->capabilities &= ~BATADV_ORIG_CAPA_HAS_MCAST; + } + + orig->capa_initialized |= BATADV_ORIG_CAPA_HAS_MCAST; + + if (orig_mcast_enabled && tvlv_value && + (tvlv_value_len >= sizeof(mcast_flags))) + mcast_flags = *(uint8_t *)tvlv_value; + + orig->mcast_flags = mcast_flags; +} + +/** + * batadv_mcast_init - initialize the multicast optimizations structures + * @bat_priv: the bat priv with all the soft interface information + */ +void batadv_mcast_init(struct batadv_priv *bat_priv) +{ + batadv_tvlv_handler_register(bat_priv, batadv_mcast_tvlv_ogm_handler_v1, + NULL, BATADV_TVLV_MCAST, 1, + BATADV_TVLV_HANDLER_OGM_CIFNOTFND); +} + +/** * batadv_mcast_free - free the multicast optimizations structures * @bat_priv: the bat priv with all the soft interface information */ void batadv_mcast_free(struct batadv_priv *bat_priv) { + batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_MCAST, 1); + batadv_tvlv_handler_unregister(bat_priv, BATADV_TVLV_MCAST, 1); + batadv_mcast_mla_tt_retract(bat_priv, NULL); } + +/** + * batadv_mcast_purge_orig - reset originator global mcast state modifications + * @orig: the originator which is going to get purged + */ +void batadv_mcast_purge_orig(struct batadv_orig_node *orig) +{ + struct batadv_priv *bat_priv = orig->bat_priv; + + if (!(orig->capabilities & BATADV_ORIG_CAPA_HAS_MCAST)) + atomic_dec(&bat_priv->mcast.num_disabled); +} diff --git a/multicast.h b/multicast.h index b5c50ed..ad83fbe 100644 --- a/multicast.h +++ b/multicast.h @@ -19,8 +19,12 @@
void batadv_mcast_mla_update(struct batadv_priv *bat_priv);
+void batadv_mcast_init(struct batadv_priv *bat_priv); + void batadv_mcast_free(struct batadv_priv *bat_priv);
+void batadv_mcast_purge_orig(struct batadv_orig_node *orig_node); + #else
static inline void batadv_mcast_mla_update(struct batadv_priv *bat_priv) @@ -28,11 +32,21 @@ static inline void batadv_mcast_mla_update(struct batadv_priv *bat_priv) return; }
+static inline int batadv_mcast_init(struct batadv_priv *bat_priv) +{ + return 0; +} + static inline void batadv_mcast_free(struct batadv_priv *bat_priv) { return; }
+static inline void batadv_mcast_purge_orig(struct batadv_orig_node *orig_node) +{ + return; +} + #endif /* CONFIG_BATMAN_ADV_MCAST */
#endif /* _NET_BATMAN_ADV_MULTICAST_H_ */ diff --git a/originator.c b/originator.c index ab22450..fb1aa8e 100644 --- a/originator.c +++ b/originator.c @@ -27,6 +27,7 @@ #include "bridge_loop_avoidance.h" #include "network-coding.h" #include "fragmentation.h" +#include "multicast.h"
/* hash class keys */ static struct lock_class_key batadv_orig_hash_lock_class_key; @@ -521,6 +522,8 @@ static void batadv_orig_node_free_rcu(struct rcu_head *rcu) } spin_unlock_bh(&orig_node->neigh_list_lock);
+ batadv_mcast_purge_orig(orig_node); + /* Free nc_nodes */ batadv_nc_purge_orig(orig_node->bat_priv, orig_node, NULL);
@@ -636,6 +639,9 @@ struct batadv_orig_node *batadv_orig_node_new(struct batadv_priv *bat_priv, orig_node->tt_buff_len = 0; reset_time = jiffies - 1 - msecs_to_jiffies(BATADV_RESET_PROTECTION_MS); orig_node->bcast_seqno_reset = reset_time; +#ifdef CONFIG_BATMAN_ADV_MCAST + orig_node->mcast_flags = BATADV_NO_FLAGS; +#endif
/* create a vlan object for the "untagged" LAN */ vlan = batadv_orig_node_vlan_new(orig_node, BATADV_NO_FLAGS); diff --git a/packet.h b/packet.h index 0a381d1..e8c483d 100644 --- a/packet.h +++ b/packet.h @@ -145,6 +145,7 @@ enum batadv_bla_claimframe { * @BATADV_TVLV_NC: network coding tvlv * @BATADV_TVLV_TT: translation table tvlv * @BATADV_TVLV_ROAM: roaming advertisement tvlv + * @BATADV_TVLV_MCAST: multicast capability tvlv */ enum batadv_tvlv_type { BATADV_TVLV_GW = 0x01, @@ -152,6 +153,7 @@ enum batadv_tvlv_type { BATADV_TVLV_NC = 0x03, BATADV_TVLV_TT = 0x04, BATADV_TVLV_ROAM = 0x05, + BATADV_TVLV_MCAST = 0x06, };
#pragma pack(2) @@ -504,4 +506,14 @@ struct batadv_tvlv_roam_adv { __be16 vid; };
+/** + * struct batadv_tvlv_mcast_data - payload of a multicast tvlv + * @flags: multicast flags announced by the orig node + * @reserved: reserved field + */ +struct batadv_tvlv_mcast_data { + uint8_t flags; + uint8_t reserved[3]; +}; + #endif /* _NET_BATMAN_ADV_PACKET_H_ */ diff --git a/soft-interface.c b/soft-interface.c index db3e467..ef9d0aa 100644 --- a/soft-interface.c +++ b/soft-interface.c @@ -692,6 +692,10 @@ static int batadv_softif_init_late(struct net_device *dev) #ifdef CONFIG_BATMAN_ADV_DAT atomic_set(&bat_priv->distributed_arp_table, 1); #endif +#ifdef CONFIG_BATMAN_ADV_MCAST + bat_priv->mcast.flags = BATADV_NO_FLAGS; + atomic_set(&bat_priv->mcast.num_disabled, 0); +#endif atomic_set(&bat_priv->gw_mode, BATADV_GW_MODE_OFF); atomic_set(&bat_priv->gw_sel_class, 20); atomic_set(&bat_priv->gw.bandwidth_down, 100); diff --git a/types.h b/types.h index b46117c..96ee0d2 100644 --- a/types.h +++ b/types.h @@ -204,6 +204,7 @@ struct batadv_orig_bat_iv { * @batadv_dat_addr_t: address of the orig node in the distributed hash * @last_seen: time when last packet from this node was received * @bcast_seqno_reset: time when the broadcast seqno window was reset + * @mcast_flags: multicast flags announced by the orig node * @capabilities: announced capabilities of this originator * @capa_initialized: bitfield to remember whether a capability was initialized * @last_ttvn: last seen translation table version number @@ -246,6 +247,9 @@ struct batadv_orig_node { #endif unsigned long last_seen; unsigned long bcast_seqno_reset; +#ifdef CONFIG_BATMAN_ADV_MCAST + uint8_t mcast_flags; +#endif uint8_t capabilities; uint8_t capa_initialized; atomic_t last_ttvn; @@ -282,11 +286,14 @@ struct batadv_orig_node { * @BATADV_ORIG_CAPA_HAS_DAT: orig node has distributed arp table enabled * @BATADV_ORIG_CAPA_HAS_NC: orig node has network coding enabled * @BATADV_ORIG_CAPA_HAS_TT: orig node has tt capability + * @BATADV_ORIG_CAPA_HAS_MCAST: orig node has some multicast capability + * (= orig node announces a tvlv of type BATADV_TVLV_MCAST) */ enum batadv_orig_capabilities { BATADV_ORIG_CAPA_HAS_DAT = BIT(0), BATADV_ORIG_CAPA_HAS_NC = BIT(1), BATADV_ORIG_CAPA_HAS_TT = BIT(2), + BATADV_ORIG_CAPA_HAS_MCAST = BIT(3), };
/** @@ -612,9 +619,15 @@ struct batadv_priv_dat { /** * struct batadv_priv_mcast - per mesh interface mcast data * @mla_list: list of multicast addresses we are currently announcing via TT + * @flags: the flags we have last sent in our mcast tvlv + * @enabled: whether the multicast tvlv is currently enabled + * @num_disabled: number of nodes that have no mcast tvlv */ struct batadv_priv_mcast { struct hlist_head mla_list; + uint8_t flags; + bool enabled; + atomic_t num_disabled; }; #endif
With this patch a multicast packet is not always simply flooded anymore, the bevahiour 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 --- multicast.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++ multicast.h | 23 ++++++++++++ soft-interface.c | 14 ++++++- sysfs-class-net-mesh | 9 +++++ sysfs.c | 6 +++ translation-table.c | 92 +++++++++++++++++++++++++++++++++++---------- translation-table.h | 2 + types.h | 7 ++++ 8 files changed, 234 insertions(+), 21 deletions(-)
diff --git a/multicast.c b/multicast.c index bc66c07..698a654 100644 --- a/multicast.c +++ b/multicast.c @@ -17,6 +17,7 @@ #include "originator.h" #include "hard-interface.h" #include "translation-table.h" +#include "multicast.h"
/** * batadv_mcast_mla_softif_get - get softif multicast listeners @@ -271,6 +272,107 @@ out: }
/** + * 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 + * + * Check whether the given IPv6 packet has the potential to + * be forwarded with a mode more optimal than classic flooding. + * + * If so then return 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; + + /* With one bridge involved, we cannot be certain about + * link-local-all-nodes multicast listener announcements anymore + * (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 + * + * Check whether the given multicast ethernet frame has the potential to + * be forwarded with a mode more optimal than classic flooding. + * + * If so then return 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_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 + * + * 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) +{ + struct ethhdr *ethhdr = eth_hdr(skb); + int ret; + + ret = batadv_mcast_forw_mode_check(bat_priv, skb); + if (ret == -ENOMEM) + return BATADV_FORW_NONE; + else if (ret < 0) + return BATADV_FORW_ALL; + + ret = batadv_tt_global_hash_count(bat_priv, ethhdr->h_dest, + BATADV_NO_FLAGS); + + switch (ret) { + case 0: + return BATADV_FORW_NONE; + case 1: + return BATADV_FORW_SINGLE; + 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 * @orig: the orig_node of the ogm diff --git a/multicast.h b/multicast.h index ad83fbe..d92f689 100644 --- a/multicast.h +++ b/multicast.h @@ -15,10 +15,27 @@ #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); + void batadv_mcast_init(struct batadv_priv *bat_priv);
void batadv_mcast_free(struct batadv_priv *bat_priv); @@ -32,6 +49,12 @@ 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) +{ + return BATADV_FORW_ALL; +} + static inline int batadv_mcast_init(struct batadv_priv *bat_priv) { return 0; diff --git a/soft-interface.c b/soft-interface.c index ef9d0aa..af68af5 100644 --- a/soft-interface.c +++ b/soft-interface.c @@ -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,7 @@ static int batadv_interface_tx(struct sk_buff *skb, unsigned short vid; uint32_t seqno; int gw_mode; + enum batadv_forw_mode forw_mode;
if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE) goto dropped; @@ -247,9 +249,18 @@ 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); + 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 */ @@ -694,6 +705,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); diff --git a/sysfs-class-net-mesh b/sysfs-class-net-mesh index 4793d3d..a8e918c 100644 --- a/sysfs-class-net-mesh +++ b/sysfs-class-net-mesh @@ -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: June 2013 +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 diff --git a/sysfs.c b/sysfs.c index e456bf6..1ebb0d9 100644 --- a/sysfs.c +++ b/sysfs.c @@ -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 @@ -558,6 +561,9 @@ static struct batadv_attribute *batadv_mesh_attrs[] = { #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, &batadv_attr_gw_mode, diff --git a/translation-table.c b/translation-table.c index d1d4e7d..20602b7 100644 --- a/translation-table.c +++ b/translation-table.c @@ -193,6 +193,32 @@ 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 = 0; + + tt_global_entry = batadv_tt_global_hash_find(bat_priv, addr, vid); + if (!tt_global_entry) + goto out; + + count = atomic_read(&tt_global_entry->orig_list_count); + batadv_tt_global_entry_free_ref(tt_global_entry); + +out: + 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 +1251,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 +1326,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 +1592,25 @@ out: 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,20 +1621,26 @@ 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, - struct batadv_tt_global_entry *tt_global_entry, - struct batadv_orig_node *orig_node, - const char *message) +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) { struct hlist_head *head; struct hlist_node *safe; @@ -1603,10 +1657,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,8 +1700,8 @@ 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, - orig_node, message); + batadv_tt_global_del_orig_node(bat_priv, tt_global_entry, + orig_node, message); }
/** @@ -1675,8 +1727,8 @@ 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, - orig_node, message); + batadv_tt_global_del_orig_node(bat_priv, tt_global_entry, + orig_node, message);
if (hlist_empty(&tt_global_entry->orig_list)) batadv_tt_global_free(bat_priv, tt_global_entry, @@ -1759,8 +1811,8 @@ 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, - orig_node, message); + batadv_tt_global_del_orig_node(bat_priv, tt_global, + orig_node, message);
if (hlist_empty(&tt_global->orig_list)) { vid = tt_global->common.vid; diff --git a/translation-table.h b/translation-table.h index 20a1d78..ad84d7b 100644 --- a/translation-table.h +++ b/translation-table.h @@ -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, diff --git a/types.h b/types.h index 96ee0d2..c28fc4a 100644 --- a/types.h +++ b/types.h @@ -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 @@ -746,6 +748,9 @@ struct batadv_priv { #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; atomic_t orig_interval; @@ -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; };
With this patch a node may additionally perform the dropping or unicasting behaviour for a link-local IPv4 and link-local-all-nodes IPv6 multicast packet, too.
The extra counter and BATADV_MCAST_WANT_ALL_UNSNOOPABLES flag is needed because with a future bridge snooping support integration a node with a bridge on top of its soft interface is not able to reliably detect its multicast listeners for IPv4 link-local and the IPv6 link-local-all-nodes addresses anymore (see RFC4541, section 2.1.2.2 and section 3).
Even though this new flag does make "no difference" now, it'll ensure a seamless integration of multicast bridge support without needing to break compatibility later.
Also note, that even with multicast bridge support it will need only one node with a bridge to disable optimizations for link-local IPv4 and IPv6-link-local-all-nodes multicast, resulting in flooding all these packets again. So the 224.0.0.x address range and the ff02::1 address will never be a safe choice for multicast streaming etc. if you do not control every node.
Signed-off-by: Linus Lüssing linus.luessing@web.de --- main.h | 1 + multicast.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++- packet.h | 9 +++++++++ soft-interface.c | 1 + types.h | 2 ++ 5 files changed, 64 insertions(+), 1 deletion(-)
diff --git a/main.h b/main.h index dc6b4a2..a31da5a 100644 --- a/main.h +++ b/main.h @@ -177,6 +177,7 @@ enum batadv_uev_type { #include <linux/slab.h> #include <net/sock.h> /* struct sock */ #include <net/addrconf.h> /* ipv6 address stuff */ +#include <linux/ip.h> #include <net/rtnetlink.h> #include <linux/jiffies.h> #include <linux/seq_file.h> diff --git a/multicast.c b/multicast.c index 698a654..cd85378 100644 --- a/multicast.c +++ b/multicast.c @@ -272,6 +272,44 @@ out: }
/** + * batadv_mcast_forw_mode_check_ipv4 - check for optimized forwarding potential + * @bat_priv: the bat priv with all the soft interface information + * @skb: the IPv4 packet to check + * + * Check whether the given IPv4 packet has the potential to + * be forwarded with a mode more optimal than classic flooding. + * + * If so then return 0. Otherwise -EINVAL is returned or -ENOMEM if we are + * out of memory. + */ +static int batadv_mcast_forw_mode_check_ipv4(struct batadv_priv *bat_priv, + struct sk_buff *skb) +{ + struct iphdr *iphdr; + + /* We might fail due to out-of-memory -> drop it */ + if (!pskb_may_pull(skb, sizeof(struct ethhdr) + sizeof(*iphdr))) + return -ENOMEM; + + iphdr = ip_hdr(skb); + + /* TODO: Implement Multicast Router Discovery (RFC4286), + * then allow scope > link local, too + */ + if (!ipv4_is_local_multicast(iphdr->daddr)) + return -EINVAL; + + /* With one bridge involved, we cannot be certain about + * link-local multicast listener announcements anymore + * (see RFC4541, section 2.1.2.2) + */ + if (atomic_read(&bat_priv->mcast.num_want_all_unsnoopables)) + return -EINVAL; + + return 0; +} + +/** * 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 @@ -304,7 +342,8 @@ static int batadv_mcast_forw_mode_check_ipv6(struct batadv_priv *bat_priv, * link-local-all-nodes multicast listener announcements anymore * (see RFC4541, section 3, paragraph 3) */ - if (ipv6_addr_is_ll_all_nodes(&ip6hdr->daddr)) + if (ipv6_addr_is_ll_all_nodes(&ip6hdr->daddr) && + atomic_read(&bat_priv->mcast.num_want_all_unsnoopables)) return -EINVAL;
return 0; @@ -333,6 +372,8 @@ static int batadv_mcast_forw_mode_check(struct batadv_priv *bat_priv, return -EINVAL;
switch (ntohs(ethhdr->h_proto)) { + case ETH_P_IP: + return batadv_mcast_forw_mode_check_ipv4(bat_priv, skb); case ETH_P_IPV6: return batadv_mcast_forw_mode_check_ipv6(bat_priv, skb); default: @@ -416,6 +457,13 @@ static void batadv_mcast_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv, (tvlv_value_len >= sizeof(mcast_flags))) mcast_flags = *(uint8_t *)tvlv_value;
+ if (mcast_flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES && + !(orig->mcast_flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES)) + atomic_inc(&bat_priv->mcast.num_want_all_unsnoopables); + else if (!(mcast_flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES) && + orig->mcast_flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES) + atomic_dec(&bat_priv->mcast.num_want_all_unsnoopables); + orig->mcast_flags = mcast_flags; }
@@ -452,4 +500,6 @@ void batadv_mcast_purge_orig(struct batadv_orig_node *orig)
if (!(orig->capabilities & BATADV_ORIG_CAPA_HAS_MCAST)) atomic_dec(&bat_priv->mcast.num_disabled); + if (orig->mcast_flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES) + atomic_dec(&bat_priv->mcast.num_want_all_unsnoopables); } diff --git a/packet.h b/packet.h index e8c483d..0aada24 100644 --- a/packet.h +++ b/packet.h @@ -89,6 +89,15 @@ enum batadv_icmp_packettype { BATADV_PARAMETER_PROBLEM = 12, };
+/** + * enum batadv_mcast_flags - flags for multicast capabilities and settings + * @BATADV_MCAST_WANT_ALL_UNSNOOPABLES: we want all packets destined for + * 224.0.0.0/24 or ff02::1 + */ +enum batadv_mcast_flags { + BATADV_MCAST_WANT_ALL_UNSNOOPABLES = BIT(0), +}; + /* tt data subtypes */ #define BATADV_TT_DATA_TYPE_MASK 0x0F
diff --git a/soft-interface.c b/soft-interface.c index af68af5..706825a 100644 --- a/soft-interface.c +++ b/soft-interface.c @@ -707,6 +707,7 @@ static int batadv_softif_init_late(struct net_device *dev) bat_priv->mcast.flags = BATADV_NO_FLAGS; atomic_set(&bat_priv->multicast_mode, 1); atomic_set(&bat_priv->mcast.num_disabled, 0); + atomic_set(&bat_priv->mcast.num_want_all_unsnoopables, 0); #endif atomic_set(&bat_priv->gw_mode, BATADV_GW_MODE_OFF); atomic_set(&bat_priv->gw_sel_class, 20); diff --git a/types.h b/types.h index c28fc4a..a9ac674 100644 --- a/types.h +++ b/types.h @@ -622,12 +622,14 @@ struct batadv_priv_dat { * @flags: the flags we have last sent in our mcast tvlv * @enabled: whether the multicast tvlv is currently enabled * @num_disabled: number of nodes that have no mcast tvlv + * @num_want_all_unsnoopables: number of nodes wanting unsnoopable IP traffic */ struct batadv_priv_mcast { struct hlist_head mla_list; uint8_t flags; bool enabled; atomic_t num_disabled; + atomic_t num_want_all_unsnoopables; }; #endif
With this patch a node sends IPv4 multicast packets to nodes which have a BATADV_MCAST_WANT_ALL_IPV4 flag set and IPv6 multicast packets to nodes which have a BATADV_MCAST_WANT_ALL_IPV6 flag set, too.
Why is this needed? There are scenarios involving bridges where multicast report snooping and multicast TT announcements are not sufficient, which would lead to packet loss for some nodes otherwise:
MLDv1 and IGMPv1/IGMPv2 have a suppression mechanism for multicast listener reports. When we have an MLDv1/IGMPv1/IGMPv2 querier behind a bridge then our snooping bridge is potentially not going to see any reports even though listeners exist because according to RFC4541 such reports are only forwarded to multicast routers:
----------------------------------------------------------- --------------- {Querier}---|Snoop. Switch|----{Listener} --------------- \ ^ ------- | br0 | < ??? ------- \ _-~---~_ _-~/ ~-_ ~ batman-adv -----{Sender} ~_ cloud ~/ -~~__-__-~_/
I) MLDv1 Query: {Querier} -> flooded II) MLDv1 Report: {Listener} -> {Querier}
-> br0 cannot detect the {Listener} => Packets from {Sender} need to be forwarded to all detected listeners and MLDv1/IGMPv1/IGMPv2 queriers.
-----------------------------------------------------------
Note that we do not need to explicitly forward to MLDv2/IGMPv3 queriers, because these protocols have no report suppression: A bridge has no trouble detecting MLDv2/IGMPv3 listeners.
Even though we do not support bridges yet we need to provide the according infrastructure already to not break compatibility later.
Signed-off-by: Linus Lüssing linus.luessing@web.de --- main.c | 7 ++ multicast.c | 200 +++++++++++++++++++++++++++++++++++++++++++++++++++++- multicast.h | 15 +++- packet.h | 4 ++ send.c | 23 +++++++ send.h | 3 + soft-interface.c | 9 ++- types.h | 16 +++++ 8 files changed, 273 insertions(+), 4 deletions(-)
diff --git a/main.c b/main.c index 8f11b67..0f89deb 100644 --- a/main.c +++ b/main.c @@ -111,6 +111,9 @@ int batadv_mesh_init(struct net_device *soft_iface) spin_lock_init(&bat_priv->tt.last_changeset_lock); spin_lock_init(&bat_priv->tt.commit_lock); spin_lock_init(&bat_priv->gw.list_lock); +#ifdef CONFIG_BATMAN_ADV_MCAST + spin_lock_init(&bat_priv->mcast.want_lists_lock); +#endif spin_lock_init(&bat_priv->tvlv.container_list_lock); spin_lock_init(&bat_priv->tvlv.handler_list_lock); spin_lock_init(&bat_priv->softif_vlan_list_lock); @@ -118,6 +121,10 @@ int batadv_mesh_init(struct net_device *soft_iface) INIT_HLIST_HEAD(&bat_priv->forw_bat_list); INIT_HLIST_HEAD(&bat_priv->forw_bcast_list); INIT_HLIST_HEAD(&bat_priv->gw.list); +#ifdef CONFIG_BATMAN_ADV_MCAST + INIT_HLIST_HEAD(&bat_priv->mcast.want_all_ipv4_list); + INIT_HLIST_HEAD(&bat_priv->mcast.want_all_ipv6_list); +#endif INIT_LIST_HEAD(&bat_priv->tt.changes_list); INIT_LIST_HEAD(&bat_priv->tt.req_list); INIT_LIST_HEAD(&bat_priv->tt.roam_list); diff --git a/multicast.c b/multicast.c index cd85378..d6b9a7d 100644 --- a/multicast.c +++ b/multicast.c @@ -382,14 +382,53 @@ static int batadv_mcast_forw_mode_check(struct batadv_priv *bat_priv, }
/** + * batadv_mcast_want_all_count - number of nodes with unspecific mcast interest + * @bat_priv: the bat priv with all the soft interface information + * @ethhdr: ethernet header of a packet + * @want_all_list: pointer to a mcast want list in our bat_priv + * + * Return the number of nodes which want all IPv4 multicast traffic if + * the given ethhdr is from an IPv4 packet or the number of nodes which want + * all IPv6 traffic if it matches an IPv6 packet and set the want_list to the + * according one in our bat_priv. For other frame types leave the want_list + * untouched and return zero. + */ +static int batadv_mcast_want_all_count(struct batadv_priv *bat_priv, + struct ethhdr *ethhdr, + struct hlist_head **want_all_list) +{ + int ret; + + switch (ntohs(ethhdr->h_proto)) { + case ETH_P_IP: + ret = atomic_read(&bat_priv->mcast.num_want_all_ipv4); + if (ret) + *want_all_list = &bat_priv->mcast.want_all_ipv4_list; + break; + case ETH_P_IPV6: + ret = atomic_read(&bat_priv->mcast.num_want_all_ipv6); + if (ret) + *want_all_list = &bat_priv->mcast.want_all_ipv6_list; + break; + default: + /* we shouldn't be here... */ + ret = 0; + } + + return ret; +} + +/** * 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 + * @want_all_list: pointer to a mcast want list in our bat_priv * * 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) +batadv_mcast_forw_mode(struct batadv_priv *bat_priv, struct sk_buff *skb, + struct hlist_head **want_all_list) { struct ethhdr *ethhdr = eth_hdr(skb); int ret; @@ -402,6 +441,7 @@ batadv_mcast_forw_mode(struct batadv_priv *bat_priv, struct sk_buff *skb)
ret = batadv_tt_global_hash_count(bat_priv, ethhdr->h_dest, BATADV_NO_FLAGS); + ret += batadv_mcast_want_all_count(bat_priv, ethhdr, want_all_list);
switch (ret) { case 0: @@ -414,6 +454,124 @@ batadv_mcast_forw_mode(struct batadv_priv *bat_priv, struct sk_buff *skb) }
/** + * batadv_mcast_want_all_ipv4_node_get - get an orig_node with want_all_ipv4 + * @head: list of originators that want all IPv4 multicast traffic + * + * Return the first orig_node from the given want_all_ipv4 list. Increases + * the refcount of the returned orig_node. + */ +static struct batadv_orig_node * +batadv_mcast_want_all_ipv4_node_get(struct hlist_head *head) +{ + struct batadv_orig_node *orig_node = NULL; + + rcu_read_lock(); + hlist_for_each_entry_rcu(orig_node, head, + mcast_want_all_ipv4_node) { + if (atomic_inc_not_zero(&orig_node->refcount)) + break; + } + rcu_read_unlock(); + + return orig_node; +} + +/** + * batadv_mcast_want_all_ipv6_node_get - get an orig_node with want_all_ipv6 + * @head: list of originators that want all IPv6 multicast traffic + * + * Return the first orig_node from the given want_all_ipv6 list. Increases + * the refcount of the returned orig_node. + */ +static struct batadv_orig_node * +batadv_mcast_want_all_ipv6_node_get(struct hlist_head *head) +{ + struct batadv_orig_node *orig_node = NULL; + + rcu_read_lock(); + hlist_for_each_entry_rcu(orig_node, head, + mcast_want_all_ipv6_node) { + if (atomic_inc_not_zero(&orig_node->refcount)) + break; + } + rcu_read_unlock(); + + return orig_node; +} + +/** + * batadv_mcast_want_all_node_get - get an orig_node with an mcast want list + * @want_all_list: list of originators that want all IPv4 or IPv6 mcast traffic + * @bat_priv: the bat priv with all the soft interface information + * + * Return the first orig_node from the given want_all list. Increases the + * refcount of the returned orig_node. + */ +struct batadv_orig_node * +batadv_mcast_want_all_node_get(struct hlist_head *want_all_list, + struct batadv_priv *bat_priv) +{ + if (want_all_list == &bat_priv->mcast.want_all_ipv4_list) + return batadv_mcast_want_all_ipv4_node_get(want_all_list); + else if (want_all_list == &bat_priv->mcast.want_all_ipv6_list) + return batadv_mcast_want_all_ipv6_node_get(want_all_list); + else + return NULL; +} + +/** + * batadv_mcast_list_add - grab a lock and add a node to a head + * @node: the node to add + * @head: the head to add the node to + * @lock: the lock to grab while adding the node to the head + */ +static void batadv_mcast_list_add(struct hlist_node *node, + struct hlist_head *head, + spinlock_t *lock) +{ + spin_lock_bh(lock); + hlist_add_head_rcu(node, head); + spin_unlock_bh(lock); +} + +/** + * batadv_mcast_list_del - grab a lock and delete a node from its list + * @node: the node to delete from its list + * @lock: the lock to grab while deleting the node from its list + */ +static void batadv_mcast_list_del(struct hlist_node *node, spinlock_t *lock) +{ + spin_lock_bh(lock); + hlist_del_rcu(node); + spin_unlock_bh(lock); +} + +/** + * batadv_mcast_list_update - update the list of a flag + * @flag: the flag we want to update the list for + * @node: a list node of an originator + * @head: the list head the node might be added to + * @lock: the lock that synchronizes list modifications + * @new_flags: the new capability bitset of a node + * @old_flags: the current, to be updated bitset of a node + * + * Update the list of the given node/head with the help of the new flag + * information of an originator to contain the nodes which have the given + * flag set. + */ +static void batadv_mcast_list_update(uint8_t flag, + struct hlist_node *node, + struct hlist_head *head, + spinlock_t *lock, + uint8_t new_flags, int old_flags) +{ + if (new_flags & flag && !(old_flags & flag)) + batadv_mcast_list_add(node, head, lock); + else if (!(new_flags & flag) && old_flags & flag) + batadv_mcast_list_del(node, lock); +} + +/** * batadv_mcast_tvlv_ogm_handler_v1 - process incoming multicast tvlv container * @bat_priv: the bat priv with all the soft interface information * @orig: the orig_node of the ogm @@ -464,6 +622,31 @@ static void batadv_mcast_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv, orig->mcast_flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES) atomic_dec(&bat_priv->mcast.num_want_all_unsnoopables);
+ if (mcast_flags & BATADV_MCAST_WANT_ALL_IPV4 && + !(orig->mcast_flags & BATADV_MCAST_WANT_ALL_IPV4)) + atomic_inc(&bat_priv->mcast.num_want_all_ipv4); + else if (!(mcast_flags & BATADV_MCAST_WANT_ALL_IPV4) && + orig->mcast_flags & BATADV_MCAST_WANT_ALL_IPV4) + atomic_dec(&bat_priv->mcast.num_want_all_ipv4); + + if (mcast_flags & BATADV_MCAST_WANT_ALL_IPV6 && + !(orig->mcast_flags & BATADV_MCAST_WANT_ALL_IPV6)) + atomic_inc(&bat_priv->mcast.num_want_all_ipv6); + else if (!(mcast_flags & BATADV_MCAST_WANT_ALL_IPV6) && + orig->mcast_flags & BATADV_MCAST_WANT_ALL_IPV6) + atomic_dec(&bat_priv->mcast.num_want_all_ipv6); + + batadv_mcast_list_update(BATADV_MCAST_WANT_ALL_IPV4, + &orig->mcast_want_all_ipv4_node, + &bat_priv->mcast.want_all_ipv4_list, + &bat_priv->mcast.want_lists_lock, + mcast_flags, orig->mcast_flags); + batadv_mcast_list_update(BATADV_MCAST_WANT_ALL_IPV6, + &orig->mcast_want_all_ipv6_node, + &bat_priv->mcast.want_all_ipv6_list, + &bat_priv->mcast.want_lists_lock, + mcast_flags, orig->mcast_flags); + orig->mcast_flags = mcast_flags; }
@@ -502,4 +685,19 @@ void batadv_mcast_purge_orig(struct batadv_orig_node *orig) atomic_dec(&bat_priv->mcast.num_disabled); if (orig->mcast_flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES) atomic_dec(&bat_priv->mcast.num_want_all_unsnoopables); + if (orig->mcast_flags & BATADV_MCAST_WANT_ALL_IPV4) + atomic_dec(&bat_priv->mcast.num_want_all_ipv4); + if (orig->mcast_flags & BATADV_MCAST_WANT_ALL_IPV6) + atomic_dec(&bat_priv->mcast.num_want_all_ipv6); + + batadv_mcast_list_update(BATADV_MCAST_WANT_ALL_IPV4, + &orig->mcast_want_all_ipv4_node, + &bat_priv->mcast.want_all_ipv4_list, + &bat_priv->mcast.want_lists_lock, + BATADV_NO_FLAGS, orig->mcast_flags); + batadv_mcast_list_update(BATADV_MCAST_WANT_ALL_IPV6, + &orig->mcast_want_all_ipv6_node, + &bat_priv->mcast.want_all_ipv6_list, + &bat_priv->mcast.want_lists_lock, + BATADV_NO_FLAGS, orig->mcast_flags); } diff --git a/multicast.h b/multicast.h index d92f689..3dfc2e9 100644 --- a/multicast.h +++ b/multicast.h @@ -34,7 +34,8 @@ enum batadv_forw_mode { 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); +batadv_mcast_forw_mode(struct batadv_priv *bat_priv, struct sk_buff *skb, + struct hlist_head **want_all_list);
void batadv_mcast_init(struct batadv_priv *bat_priv);
@@ -42,6 +43,9 @@ void batadv_mcast_free(struct batadv_priv *bat_priv);
void batadv_mcast_purge_orig(struct batadv_orig_node *orig_node);
+struct batadv_orig_node * +batadv_mcast_want_all_node_get(struct hlist_head *want_all_list, + struct batadv_priv *bat_priv); #else
static inline void batadv_mcast_mla_update(struct batadv_priv *bat_priv) @@ -50,7 +54,8 @@ static inline void batadv_mcast_mla_update(struct batadv_priv *bat_priv) }
static inline enum batadv_forw_mode -batadv_mcast_forw_mode(struct batadv_priv *bat_priv, struct sk_buff *skb) +batadv_mcast_forw_mode(struct batadv_priv *bat_priv, struct sk_buff *skb, + struct hlist_head **want_all_list) { return BATADV_FORW_ALL; } @@ -70,6 +75,12 @@ static inline void batadv_mcast_purge_orig(struct batadv_orig_node *orig_node) return; }
+static inline struct batadv_orig_node * +batadv_mcast_want_all_node_get(struct hlist_head *want_all_list, + struct batadv_priv *bat_priv) +{ + return NULL; +} #endif /* CONFIG_BATMAN_ADV_MCAST */
#endif /* _NET_BATMAN_ADV_MULTICAST_H_ */ diff --git a/packet.h b/packet.h index 0aada24..feaa336 100644 --- a/packet.h +++ b/packet.h @@ -93,9 +93,13 @@ enum batadv_icmp_packettype { * enum batadv_mcast_flags - flags for multicast capabilities and settings * @BATADV_MCAST_WANT_ALL_UNSNOOPABLES: we want all packets destined for * 224.0.0.0/24 or ff02::1 + * @BATADV_MCAST_WANT_ALL_IPV4: we want all IPv4 multicast packets + * @BATADV_MCAST_WANT_ALL_IPV6: we want all IPv6 multicast packets */ enum batadv_mcast_flags { BATADV_MCAST_WANT_ALL_UNSNOOPABLES = BIT(0), + BATADV_MCAST_WANT_ALL_IPV4 = BIT(1), + BATADV_MCAST_WANT_ALL_IPV6 = BIT(2), };
/* tt data subtypes */ diff --git a/send.c b/send.c index 3d83bf9..0067311 100644 --- a/send.c +++ b/send.c @@ -27,6 +27,7 @@ #include "originator.h" #include "network-coding.h" #include "fragmentation.h" +#include "multicast.h"
static void batadv_send_outstanding_bcast_packet(struct work_struct *work);
@@ -365,6 +366,28 @@ int batadv_send_skb_via_gw(struct batadv_priv *bat_priv, struct sk_buff *skb, orig_node, vid); }
+/** + * batadv_send_skb_via_mcast - send an skb to a node with a WANT_ALL flag + * @bat_priv: the bat priv with all the soft interface information + * @skb: payload to send + * @vid: the vid to be used to search the translation table + * @want_all_list: a list of originators with a WANT_ALL flag + * + * Get an originator node from the want_all_list. Wrap the given skb into a + * batman-adv unicast header and send this frame to this node. + */ +int batadv_send_skb_via_mcast(struct batadv_priv *bat_priv, + struct sk_buff *skb, unsigned short vid, + struct hlist_head *want_all_list) + +{ + struct batadv_orig_node *orig_node; + + orig_node = batadv_mcast_want_all_node_get(want_all_list, bat_priv); + return batadv_send_skb_unicast(bat_priv, skb, BATADV_UNICAST, 0, + orig_node, vid); +} + void batadv_schedule_bat_ogm(struct batadv_hard_iface *hard_iface) { struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface); diff --git a/send.h b/send.h index aaddaa9..e28c0db 100644 --- a/send.h +++ b/send.h @@ -42,6 +42,9 @@ int batadv_send_skb_via_tt_generic(struct batadv_priv *bat_priv, unsigned short vid); int batadv_send_skb_via_gw(struct batadv_priv *bat_priv, struct sk_buff *skb, unsigned short vid); +int batadv_send_skb_via_mcast(struct batadv_priv *bat_priv, + struct sk_buff *skb, unsigned short vid, + struct hlist_head *want_all_list);
/** * batadv_send_skb_via_tt - send an skb via TT lookup diff --git a/soft-interface.c b/soft-interface.c index 706825a..e53f983 100644 --- a/soft-interface.c +++ b/soft-interface.c @@ -157,6 +157,7 @@ static int batadv_interface_tx(struct sk_buff *skb, struct batadv_hard_iface *primary_if = NULL; struct batadv_bcast_packet *bcast_packet; __be16 ethertype = htons(ETH_P_BATMAN); + struct hlist_head *want_all_list = NULL; static const uint8_t stp_addr[ETH_ALEN] = {0x01, 0x80, 0xC2, 0x00, 0x00, 0x00}; static const uint8_t ectp_addr[ETH_ALEN] = {0xCF, 0x00, 0x00, 0x00, @@ -252,7 +253,8 @@ static int 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, + &want_all_list); if (forw_mode == BATADV_FORW_NONE) goto dropped;
@@ -312,6 +314,9 @@ send: if (ret) goto dropped; ret = batadv_send_skb_via_gw(bat_priv, skb, vid); + } else if (want_all_list) { + ret = batadv_send_skb_via_mcast(bat_priv, skb, vid, + want_all_list); } else { if (batadv_dat_snoop_outgoing_arp_request(bat_priv, skb)) @@ -708,6 +713,8 @@ static int batadv_softif_init_late(struct net_device *dev) atomic_set(&bat_priv->multicast_mode, 1); atomic_set(&bat_priv->mcast.num_disabled, 0); 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); #endif atomic_set(&bat_priv->gw_mode, BATADV_GW_MODE_OFF); atomic_set(&bat_priv->gw_sel_class, 20); diff --git a/types.h b/types.h index a9ac674..3905486 100644 --- a/types.h +++ b/types.h @@ -205,6 +205,8 @@ struct batadv_orig_bat_iv { * @last_seen: time when last packet from this node was received * @bcast_seqno_reset: time when the broadcast seqno window was reset * @mcast_flags: multicast flags announced by the orig node + * @mcast_want_all_ipv4_node: a list node for the mcast.want_all_ipv4 list + * @mcast_want_all_ipv6_node: a list node for the mcast.want_all_ipv6 list * @capabilities: announced capabilities of this originator * @capa_initialized: bitfield to remember whether a capability was initialized * @last_ttvn: last seen translation table version number @@ -249,6 +251,8 @@ struct batadv_orig_node { unsigned long bcast_seqno_reset; #ifdef CONFIG_BATMAN_ADV_MCAST uint8_t mcast_flags; + struct hlist_node mcast_want_all_ipv4_node; + struct hlist_node mcast_want_all_ipv6_node; #endif uint8_t capabilities; uint8_t capa_initialized; @@ -619,17 +623,29 @@ struct batadv_priv_dat { /** * struct batadv_priv_mcast - per mesh interface mcast data * @mla_list: list of multicast addresses we are currently announcing via TT + * @want_all_ipv4_list: a list of orig_nodes wanting all IPv4 multicast traffic + * @want_all_ipv6_list: a list of orig_nodes wanting all IPv6 multicast traffic * @flags: the flags we have last sent in our mcast tvlv * @enabled: whether the multicast tvlv is currently enabled * @num_disabled: number of nodes that have no mcast tvlv * @num_want_all_unsnoopables: number of nodes wanting unsnoopable IP traffic + * @num_want_all_ipv4: counter for items in want_all_ipv4_list + * @num_want_all_ipv6: counter for items in want_all_ipv6_list + * @want_lists_lock: lock for protecting modifications to mcast want lists + * (traversals are rcu-locked) */ struct batadv_priv_mcast { struct hlist_head mla_list; + struct hlist_head want_all_ipv4_list; + struct hlist_head want_all_ipv6_list; uint8_t flags; bool enabled; atomic_t num_disabled; atomic_t num_want_all_unsnoopables; + atomic_t num_want_all_ipv4; + atomic_t num_want_all_ipv6; + /* protects want_all_ipv4_list & want_all_ipv6_list */ + spinlock_t want_lists_lock; }; #endif
On Monday 27 January 2014 10:48:36 Linus Lüssing wrote:
/**
- batadv_mcast_want_all_count - number of nodes with unspecific mcast
interest + * @bat_priv: the bat priv with all the soft interface information + * @ethhdr: ethernet header of a packet
- @want_all_list: pointer to a mcast want list in our bat_priv
- Return the number of nodes which want all IPv4 multicast traffic if
- the given ethhdr is from an IPv4 packet or the number of nodes which
want + * all IPv6 traffic if it matches an IPv6 packet and set the want_list to the + * according one in our bat_priv. For other frame types leave the want_list + * untouched and return zero.
- */
+static int batadv_mcast_want_all_count(struct batadv_priv *bat_priv,
struct ethhdr *ethhdr,
struct hlist_head **want_all_list)
+{
- int ret;
- switch (ntohs(ethhdr->h_proto)) {
- case ETH_P_IP:
ret = atomic_read(&bat_priv->mcast.num_want_all_ipv4);
if (ret)
*want_all_list = &bat_priv->mcast.want_all_ipv4_list;
break;
- case ETH_P_IPV6:
ret = atomic_read(&bat_priv->mcast.num_want_all_ipv6);
if (ret)
*want_all_list = &bat_priv->mcast.want_all_ipv6_list;
break;
- default:
/* we shouldn't be here... */
ret = 0;
- }
- return ret;
+}
As far as I can tell the want_all list is returned through 3 different functions to end up in batadv_mcast_want_all_node_get() where the code checks again for IPv4 vs IPv6. Wouldn't be much easier to make a simple IPv4/IPv6 in that function to retrieve the list ? Or better, batadv_mcast_want_all_ipv4_node_get() / batadv_mcast_want_all_ipv6_node_get() get bat_priv passed and use the correct list ? I see no need to pass the list around.
/**
- batadv_mcast_want_all_ipv4_node_get - get an orig_node with
want_all_ipv4 + * @head: list of originators that want all IPv4 multicast traffic + *
- Return the first orig_node from the given want_all_ipv4 list. Increases
- the refcount of the returned orig_node.
- */
+static struct batadv_orig_node * +batadv_mcast_want_all_ipv4_node_get(struct hlist_head *head) +{
- struct batadv_orig_node *orig_node = NULL;
- rcu_read_lock();
- hlist_for_each_entry_rcu(orig_node, head,
mcast_want_all_ipv4_node) {
if (atomic_inc_not_zero(&orig_node->refcount))
break;
- }
- rcu_read_unlock();
- return orig_node;
+}
+/**
- batadv_mcast_want_all_ipv6_node_get - get an orig_node with
want_all_ipv6 + * @head: list of originators that want all IPv6 multicast traffic + *
- Return the first orig_node from the given want_all_ipv6 list. Increases
- the refcount of the returned orig_node.
- */
+static struct batadv_orig_node * +batadv_mcast_want_all_ipv6_node_get(struct hlist_head *head) +{
- struct batadv_orig_node *orig_node = NULL;
- rcu_read_lock();
- hlist_for_each_entry_rcu(orig_node, head,
mcast_want_all_ipv6_node) {
if (atomic_inc_not_zero(&orig_node->refcount))
break;
- }
- rcu_read_unlock();
- return orig_node;
+}
Both functions have the same crucial bug. What will the function return if we have on entry in the list but are unable to increment the refcount ?
+/**
- batadv_mcast_list_add - grab a lock and add a node to a head
- @node: the node to add
- @head: the head to add the node to
- @lock: the lock to grab while adding the node to the head
- */
+static void batadv_mcast_list_add(struct hlist_node *node,
struct hlist_head *head,
spinlock_t *lock)
+{
- spin_lock_bh(lock);
- hlist_add_head_rcu(node, head);
- spin_unlock_bh(lock);
+}
+/**
- batadv_mcast_list_del - grab a lock and delete a node from its list
- @node: the node to delete from its list
- @lock: the lock to grab while deleting the node from its list
- */
+static void batadv_mcast_list_del(struct hlist_node *node, spinlock_t *lock) +{
- spin_lock_bh(lock);
- hlist_del_rcu(node);
- spin_unlock_bh(lock);
+}
+/**
- batadv_mcast_list_update - update the list of a flag
- @flag: the flag we want to update the list for
- @node: a list node of an originator
- @head: the list head the node might be added to
- @lock: the lock that synchronizes list modifications
- @new_flags: the new capability bitset of a node
- @old_flags: the current, to be updated bitset of a node
- Update the list of the given node/head with the help of the new flag
- information of an originator to contain the nodes which have the given
- flag set.
- */
+static void batadv_mcast_list_update(uint8_t flag,
struct hlist_node *node,
struct hlist_head *head,
spinlock_t *lock,
uint8_t new_flags, int old_flags)
+{
- if (new_flags & flag && !(old_flags & flag))
batadv_mcast_list_add(node, head, lock);
- else if (!(new_flags & flag) && old_flags & flag)
batadv_mcast_list_del(node, lock);
+}
Didn't we agree on banishing batadv_mcast_list_update() a while ago ?
+/**
- batadv_mcast_want_all_node_get - get an orig_node with an mcast want
list
- @want_all_list: list of originators that want all IPv4 or IPv6 mcast
traffic + * @bat_priv: the bat priv with all the soft interface information
- Return the first orig_node from the given want_all list. Increases the
- refcount of the returned orig_node.
- */
+struct batadv_orig_node * +batadv_mcast_want_all_node_get(struct hlist_head *want_all_list,
struct batadv_priv *bat_priv)
+{
if (want_all_list == &bat_priv->mcast.want_all_ipv4_list)
return batadv_mcast_want_all_ipv4_node_get(want_all_list);
else if (want_all_list == &bat_priv->mcast.want_all_ipv6_list)
return batadv_mcast_want_all_ipv6_node_get(want_all_list);
else
return NULL;
+}
In case there is a good reason to keep this function: bat_priv should be the first argument.
+/**
- batadv_send_skb_via_mcast - send an skb to a node with a WANT_ALL flag
- @bat_priv: the bat priv with all the soft interface information
- @skb: payload to send
- @vid: the vid to be used to search the translation table
- @want_all_list: a list of originators with a WANT_ALL flag
- Get an originator node from the want_all_list. Wrap the given skb into a
- batman-adv unicast header and send this frame to this node.
- */
+int batadv_send_skb_via_mcast(struct batadv_priv *bat_priv,
struct sk_buff *skb, unsigned short vid,
struct hlist_head *want_all_list)
+{
- struct batadv_orig_node *orig_node;
- orig_node = batadv_mcast_want_all_node_get(want_all_list, bat_priv);
- return batadv_send_skb_unicast(bat_priv, skb, BATADV_UNICAST, 0,
orig_node, vid);
+}
Maybe I am missing the whole point of WANT_ALL but why do we maintain a list of WANT_ALL nodes to only send the packet to the first valid entry in the list?
Cheers, Marek
On Wed, Jan 29, 2014 at 02:53:49PM +0800, Marek Lindner wrote:
On Monday 27 January 2014 10:48:36 Linus Lüssing wrote:
/**
- batadv_mcast_want_all_count - number of nodes with unspecific mcast
interest + * @bat_priv: the bat priv with all the soft interface information + * @ethhdr: ethernet header of a packet
- @want_all_list: pointer to a mcast want list in our bat_priv
- Return the number of nodes which want all IPv4 multicast traffic if
- the given ethhdr is from an IPv4 packet or the number of nodes which
want + * all IPv6 traffic if it matches an IPv6 packet and set the want_list to the + * according one in our bat_priv. For other frame types leave the want_list + * untouched and return zero.
- */
+static int batadv_mcast_want_all_count(struct batadv_priv *bat_priv,
struct ethhdr *ethhdr,
struct hlist_head **want_all_list)
+{
- int ret;
- switch (ntohs(ethhdr->h_proto)) {
- case ETH_P_IP:
ret = atomic_read(&bat_priv->mcast.num_want_all_ipv4);
if (ret)
*want_all_list = &bat_priv->mcast.want_all_ipv4_list;
break;
- case ETH_P_IPV6:
ret = atomic_read(&bat_priv->mcast.num_want_all_ipv6);
if (ret)
*want_all_list = &bat_priv->mcast.want_all_ipv6_list;
break;
- default:
/* we shouldn't be here... */
ret = 0;
- }
- return ret;
+}
As far as I can tell the want_all list is returned through 3 different functions to end up in batadv_mcast_want_all_node_get() where the code checks again for IPv4 vs IPv6. Wouldn't be much easier to make a simple IPv4/IPv6 in that function to retrieve the list ? Or better, batadv_mcast_want_all_ipv4_node_get() / batadv_mcast_want_all_ipv6_node_get() get bat_priv passed and use the correct list ? I see no need to pass the list around.
Sure, we could pass something else around instead of the list, for instance simply ETH_P_IP/ETH_P_IPV6. But we'd need to pass around at least something to batadv_mcast_want_all_node_get() to make it possible for that function to decide whether to call the ipv4 or ipv6 variant.
Or we'd use another approach, see considerations/suggestions at the bottom.
/**
- batadv_mcast_want_all_ipv4_node_get - get an orig_node with
want_all_ipv4 + * @head: list of originators that want all IPv4 multicast traffic + *
- Return the first orig_node from the given want_all_ipv4 list. Increases
- the refcount of the returned orig_node.
- */
+static struct batadv_orig_node * +batadv_mcast_want_all_ipv4_node_get(struct hlist_head *head) +{
- struct batadv_orig_node *orig_node = NULL;
- rcu_read_lock();
- hlist_for_each_entry_rcu(orig_node, head,
mcast_want_all_ipv4_node) {
if (atomic_inc_not_zero(&orig_node->refcount))
break;
- }
- rcu_read_unlock();
- return orig_node;
+}
+/**
- batadv_mcast_want_all_ipv6_node_get - get an orig_node with
want_all_ipv6 + * @head: list of originators that want all IPv6 multicast traffic + *
- Return the first orig_node from the given want_all_ipv6 list. Increases
- the refcount of the returned orig_node.
- */
+static struct batadv_orig_node * +batadv_mcast_want_all_ipv6_node_get(struct hlist_head *head) +{
- struct batadv_orig_node *orig_node = NULL;
- rcu_read_lock();
- hlist_for_each_entry_rcu(orig_node, head,
mcast_want_all_ipv6_node) {
if (atomic_inc_not_zero(&orig_node->refcount))
break;
- }
- rcu_read_unlock();
- return orig_node;
+}
Both functions have the same crucial bug. What will the function return if we have on entry in the list but are unable to increment the refcount ?
Ah - you're right, we should reset *orig_node to NULL in that case to have the skb dropped. Going to fix that, thanks!
+/**
- batadv_mcast_list_add - grab a lock and add a node to a head
- @node: the node to add
- @head: the head to add the node to
- @lock: the lock to grab while adding the node to the head
- */
+static void batadv_mcast_list_add(struct hlist_node *node,
struct hlist_head *head,
spinlock_t *lock)
+{
- spin_lock_bh(lock);
- hlist_add_head_rcu(node, head);
- spin_unlock_bh(lock);
+}
+/**
- batadv_mcast_list_del - grab a lock and delete a node from its list
- @node: the node to delete from its list
- @lock: the lock to grab while deleting the node from its list
- */
+static void batadv_mcast_list_del(struct hlist_node *node, spinlock_t *lock) +{
- spin_lock_bh(lock);
- hlist_del_rcu(node);
- spin_unlock_bh(lock);
+}
+/**
- batadv_mcast_list_update - update the list of a flag
- @flag: the flag we want to update the list for
- @node: a list node of an originator
- @head: the list head the node might be added to
- @lock: the lock that synchronizes list modifications
- @new_flags: the new capability bitset of a node
- @old_flags: the current, to be updated bitset of a node
- Update the list of the given node/head with the help of the new flag
- information of an originator to contain the nodes which have the given
- flag set.
- */
+static void batadv_mcast_list_update(uint8_t flag,
struct hlist_node *node,
struct hlist_head *head,
spinlock_t *lock,
uint8_t new_flags, int old_flags)
+{
- if (new_flags & flag && !(old_flags & flag))
batadv_mcast_list_add(node, head, lock);
- else if (!(new_flags & flag) && old_flags & flag)
batadv_mcast_list_del(node, lock);
+}
Didn't we agree on banishing batadv_mcast_list_update() a while ago ?
I understood we agreed on banishing the counter_update() functions. If you'd like that one to be removed as well, similar to what we did with the counters, then okay. But note that this is going to make batadv_mcast_tvlv_ogm_handler_v1() even longer... (and I was tought by a great guy and at the university to usually try to keep functions to fit on a screen).
+/**
- batadv_mcast_want_all_node_get - get an orig_node with an mcast want
list
- @want_all_list: list of originators that want all IPv4 or IPv6 mcast
traffic + * @bat_priv: the bat priv with all the soft interface information
- Return the first orig_node from the given want_all list. Increases the
- refcount of the returned orig_node.
- */
+struct batadv_orig_node * +batadv_mcast_want_all_node_get(struct hlist_head *want_all_list,
struct batadv_priv *bat_priv)
+{
if (want_all_list == &bat_priv->mcast.want_all_ipv4_list)
return batadv_mcast_want_all_ipv4_node_get(want_all_list);
else if (want_all_list == &bat_priv->mcast.want_all_ipv6_list)
return batadv_mcast_want_all_ipv6_node_get(want_all_list);
else
return NULL;
+}
In case there is a good reason to keep this function: bat_priv should be the first argument.
Oki doki.
+/**
- batadv_send_skb_via_mcast - send an skb to a node with a WANT_ALL flag
- @bat_priv: the bat priv with all the soft interface information
- @skb: payload to send
- @vid: the vid to be used to search the translation table
- @want_all_list: a list of originators with a WANT_ALL flag
- Get an originator node from the want_all_list. Wrap the given skb into a
- batman-adv unicast header and send this frame to this node.
- */
+int batadv_send_skb_via_mcast(struct batadv_priv *bat_priv,
struct sk_buff *skb, unsigned short vid,
struct hlist_head *want_all_list)
+{
- struct batadv_orig_node *orig_node;
- orig_node = batadv_mcast_want_all_node_get(want_all_list, bat_priv);
- return batadv_send_skb_unicast(bat_priv, skb, BATADV_UNICAST, 0,
orig_node, vid);
+}
Maybe I am missing the whole point of WANT_ALL but why do we maintain a list of WANT_ALL nodes to only send the packet to the first valid entry in the list?
Hm, the thing is, there can be multiple nodes with that flag. But most of the time we only end up in this function when there's just a single node in this list. (when there's more than one, we'd usually end up in the broadcast instead of unicasting path)
We could remove the list entirely but then we'd have to loop over all orig_nodes and check their mcast_flags for every packet - which is too costly on the fast path.
We could replace these two lists by two variables holding a single originator address or orig_node each. But then we'd still have to loop over all originators when the numbers of nodes with such a flag decreases to one to find this one node to update the variable with.
But yes, this pointer pointer to a list head is not really nice either... what do you think about returning a pointer pointer to an orig_node with batadv_mcast_want_all_count() already, instead? That way we'd spare checking the IP address family twice, too. Or the list-to-orig_node-variable substitution approach?
Cheers, Linus
Maybe I am missing the whole point of WANT_ALL but why do we maintain a list of WANT_ALL nodes to only send the packet to the first valid entry in the list?
Hm, the thing is, there can be multiple nodes with that flag. But most of the time we only end up in this function when there's just a single node in this list. (when there's more than one, we'd usually end up in the broadcast instead of unicasting path)
As far as I see, send_skb_via_mcast() is only called if forw_mode returned BATADV_FORW_SINGLE, that is there is only one recipient in the list, and there is no "regular" listener in TT.
(I don't see any good reason for calling that "most of the time" and "usually")
We could remove the list entirely but then we'd have to loop over all orig_nodes and check their mcast_flags for every packet - which is too costly on the fast path.
We could replace these two lists by two variables holding a single originator address or orig_node each. But then we'd still have to loop over all originators when the numbers of nodes with such a flag decreases to one to find this one node to update the variable with.
But yes, this pointer pointer to a list head is not really nice either... what do you think about returning a pointer pointer to an orig_node with batadv_mcast_want_all_count() already, instead? That way we'd spare checking the IP address family twice, too. Or the list-to-orig_node-variable substitution approach?
I think that would be good to do (returning an orig_node, that is). And also please return an orig_node if a TT match was found for the single case.
With this we would unify the multicast sending in batadv_interface_tx() as well which would make it more readable. Right now mcast packets are sent via batadv_send_skb_via_mcast() if the want_all_list is present or via batadv_send_skb_via_tt() if want_all_list is not present, which isn't exactly easy to understand. :)
something like
forw_mode = batadv_mcast_forw_mode(bat_priv, skb, &mcast_single_orig);
and then in the unicast branch below
} else if (mcast_single_orig) { ret = batadv_send_skb_unicast(bat_priv, skb, BATADV_UNICAST, 0, mcast_single_orig, vid); }
should do the trick, no?
Thanks, Simon
b.a.t.m.a.n@lists.open-mesh.org