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.c | 21 +++++ compat.h | 18 ++++ gen-compat-autoconf.sh | 1 + main.c | 6 ++ main.h | 9 ++ multicast.c | 213 ++++++++++++++++++++++++++++++++++++++++++++++++ multicast.h | 43 ++++++++++ soft-interface.c | 3 + sysfs.c | 6 ++ translation-table.c | 22 ++++- types.h | 12 +++ 13 files changed, 353 insertions(+), 4 deletions(-) create mode 100644 multicast.c create mode 100644 multicast.h
diff --git a/Makefile b/Makefile index 407cdc4..d7c6fa6 100644 --- a/Makefile +++ b/Makefile @@ -27,6 +27,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_OPTIMIZATIONS=y
PWD:=$(shell pwd) KERNELPATH ?= /lib/modules/$(shell uname -r)/build diff --git a/Makefile.kbuild b/Makefile.kbuild index 8ddbfe6..d3efe3a 100644 --- a/Makefile.kbuild +++ b/Makefile.kbuild @@ -38,3 +38,4 @@ batman-adv-y += soft-interface.o batman-adv-y += sysfs.o batman-adv-y += translation-table.o batman-adv-y += unicast.o +batman-adv-$(CONFIG_BATMAN_ADV_MCAST_OPTIMIZATIONS) += multicast.o diff --git a/compat.c b/compat.c index 1f3a39d..27df197 100644 --- a/compat.c +++ b/compat.c @@ -25,6 +25,27 @@ #include <linux/version.h> #include "main.h"
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 38) + +void batadv_for_each_pmc_rcu_init(struct in_device *in_dev, + struct ip_mc_list **pmc) +{ + read_lock(&in_dev->mc_list_lock); + *pmc = in_dev->mc_list; +} + +bool batadv_for_each_pmc_rcu_check(struct in_device *in_dev, + struct ip_mc_list *pmc) +{ + if (pmc == NULL) + read_unlock(&in_dev->mc_list_lock); + + return (pmc != NULL); +} + +#endif /* < KERNEL_VERSION(2, 6, 38) */ + + #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 0, 0)
void batadv_free_rcu_gw_node(struct rcu_head *rcu) diff --git a/compat.h b/compat.h index 8bd4f62..de1556a 100644 --- a/compat.h +++ b/compat.h @@ -152,6 +152,24 @@ static inline int batadv_param_set_copystring(const char *val,
#endif /* < KERNEL_VERSION(2, 6, 37) */
+ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 38) + +void batadv_for_each_pmc_rcu_init(struct in_device *in_dev, + struct ip_mc_list **pmc); +bool batadv_for_each_pmc_rcu_check(struct in_device *in_dev, + struct ip_mc_list *pmc); + +#undef for_each_pmc_rcu +#define for_each_pmc_rcu(in_dev, pmc) \ + for (batadv_for_each_pmc_rcu_init(in_dev, &pmc); \ + batadv_for_each_pmc_rcu_check(in_dev, pmc); \ + pmc = NULL) \ + for (; pmc != NULL; pmc = pmc->next) + +#endif /* < KERNEL_VERSION(2, 6, 38) */ + + #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39)
#define kstrtoul strict_strtoul diff --git a/gen-compat-autoconf.sh b/gen-compat-autoconf.sh index 78573e4..b56ac29 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_OPTIMIZATIONS' ${CONFIG_BATMAN_ADV_MCAST_OPTIMIZATIONS:="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 af89c09..1c82c18 100644 --- a/main.c +++ b/main.c @@ -33,6 +33,7 @@ #include "bridge_loop_avoidance.h" #include "distributed-arp-table.h" #include "unicast.h" +#include "multicast.h" #include "gateway_common.h" #include "hash.h" #include "bat_algo.h" @@ -115,6 +116,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_OPTIMIZATIONS + INIT_LIST_HEAD(&bat_priv->mcast.mla_list); +#endif INIT_HLIST_HEAD(&bat_priv->tvlv.container_list); INIT_HLIST_HEAD(&bat_priv->tvlv.handler_list);
@@ -168,6 +172,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 795345f..c865a9c 100644 --- a/main.h +++ b/main.h @@ -141,6 +141,14 @@ enum batadv_uev_type { /* Append 'batman-adv: ' before kernel messages */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+/* identical to the one used in net/ipv4/igmp.c + * TODO: remove redundancy, put in some header file + */ +#define for_each_pmc_rcu(in_dev, pmc) \ + for (pmc = rcu_dereference(in_dev->mc_list); \ + pmc != NULL; \ + pmc = rcu_dereference(pmc->next_rcu)) + /* Kernel headers */
#include <linux/mutex.h> /* mutex */ @@ -155,6 +163,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..56a1128 --- /dev/null +++ b/multicast.c @@ -0,0 +1,213 @@ +/* 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include "main.h" +#include "originator.h" +#include "hard-interface.h" +#include "translation-table.h" + +/** + * batadv_mcast_mla_local_collect - collect local 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 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_local_collect(struct net_device *dev, + struct list_head *mcast_list) +{ + struct netdev_hw_addr *mc_list_entry, *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); + list_add(&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 list_head *mcast_list) +{ + struct netdev_hw_addr *mcast_entry; + + list_for_each_entry(mcast_entry, mcast_list, list) + if (batadv_compare_eth(mcast_entry->addr, mcast_addr)) + return true; + + return false; +} + +/** + * batadv_mcast_mla_collect_free - free a list of multicast addresses + * @mcast_list: the list to free + * + * Remove and free all items in the given mcast_list. + */ +void batadv_mcast_mla_collect_free(struct list_head *mcast_list) +{ + struct netdev_hw_addr *mcast_entry, *tmp; + + list_for_each_entry_safe(mcast_entry, tmp, mcast_list, list) { + list_del(&mcast_entry->list); + kfree(mcast_entry); + } +} + +/** + * batadv_mcast_mla_tt_clean - 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_clean(struct batadv_priv *bat_priv, + struct list_head *mcast_list) +{ + struct netdev_hw_addr *mcast_entry, *tmp; + + list_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, + "mcast TT outdated", false); + + list_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 list_head *mcast_list) +{ + struct netdev_hw_addr *mcast_entry, *tmp; + + if (!mcast_list) + return; + + list_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; + + batadv_tt_local_add(bat_priv->soft_iface, mcast_entry->addr, + BATADV_NULL_IFINDEX); + list_move_tail(&mcast_entry->list, &bat_priv->mcast.mla_list); + } +} + +/** + * batadv_mcast_mla_tt_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. Also take care of registering or unregistering the multicast + * tvlv depending on whether the user activated or deactivated + * multicast optimizations. + */ +void batadv_mcast_mla_tt_update(struct batadv_priv *bat_priv) +{ + struct batadv_hard_iface *primary_if; + struct net_device *soft_iface; + struct list_head mcast_list; + int ret; + static bool enabled; + + INIT_LIST_HEAD(&mcast_list); + + primary_if = batadv_primary_if_get_selected(bat_priv); + if (!primary_if) + goto out; + + soft_iface = primary_if->soft_iface; + + /* Avoid attaching MLAs, if multicast optimization is disabled + * or there is a bridge on top of our soft interface (TODO) + */ + if (!atomic_read(&bat_priv->mcast_group_awareness) || + bat_priv->soft_iface->priv_flags & IFF_BRIDGE_PORT) { + if (enabled) + enabled = false; + + goto update; + } + + if (!enabled) + enabled = true; + + ret = batadv_mcast_mla_local_collect(soft_iface, &mcast_list); + if (ret < 0) + goto out; + +update: + batadv_mcast_mla_tt_clean(bat_priv, &mcast_list); + batadv_mcast_mla_tt_add(bat_priv, &mcast_list); + +out: + if (primary_if) + batadv_hardif_free_ref(primary_if); + + batadv_mcast_mla_collect_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_clean(bat_priv, NULL); +} diff --git a/multicast.h b/multicast.h new file mode 100644 index 0000000..3b68e3b --- /dev/null +++ b/multicast.h @@ -0,0 +1,43 @@ +/* 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _NET_BATMAN_ADV_MULTICAST_H_ +#define _NET_BATMAN_ADV_MULTICAST_H_ + +#ifdef CONFIG_BATMAN_ADV_MCAST_OPTIMIZATIONS + +void batadv_mcast_mla_tt_update(struct batadv_priv *bat_priv); + +void batadv_mcast_free(struct batadv_priv *bat_priv); + +#else + +static inline void batadv_mcast_mla_tt_update(struct batadv_priv *bat_priv) +{ + return; +} + +static inline void batadv_mcast_free(struct batadv_priv *bat_priv) +{ + return; +} + +#endif /* CONFIG_BATMAN_ADV_MCAST_OPTIMIZATIONS */ + +#endif /* _NET_BATMAN_ADV_MULTICAST_H_ */ diff --git a/soft-interface.c b/soft-interface.c index a6ab9e4..b97dfa2 100644 --- a/soft-interface.c +++ b/soft-interface.c @@ -457,6 +457,9 @@ 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_OPTIMIZATIONS + atomic_set(&bat_priv->mcast_group_awareness, 1); +#endif atomic_set(&bat_priv->ap_isolation, 0); atomic_set(&bat_priv->gw_mode, BATADV_GW_MODE_OFF); atomic_set(&bat_priv->gw_sel_class, 20); diff --git a/sysfs.c b/sysfs.c index cbbb77b..76390d0 100644 --- a/sysfs.c +++ b/sysfs.c @@ -369,6 +369,9 @@ BATADV_ATTR_SIF_UINT(gw_sel_class, S_IRUGO | S_IWUSR, 1, BATADV_TQ_MAX_VALUE, batadv_post_gw_deselect); static BATADV_ATTR(gw_bandwidth, S_IRUGO | S_IWUSR, batadv_show_gw_bwidth, batadv_store_gw_bwidth); +#ifdef CONFIG_BATMAN_ADV_MCAST_OPTIMIZATIONS +BATADV_ATTR_SIF_BOOL(mcast_group_awareness, 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 @@ -386,6 +389,9 @@ static struct batadv_attribute *batadv_mesh_attrs[] = { #ifdef CONFIG_BATMAN_ADV_DAT &batadv_attr_distributed_arp_table, #endif +#ifdef CONFIG_BATMAN_ADV_MCAST_OPTIMIZATIONS + &batadv_attr_mcast_group_awareness, +#endif &batadv_attr_fragmentation, &batadv_attr_ap_isolation, &batadv_attr_routing_algo, diff --git a/translation-table.c b/translation-table.c index df6b5cd..be5cfe1 100644 --- a/translation-table.c +++ b/translation-table.c @@ -26,6 +26,7 @@ #include "originator.h" #include "routing.h" #include "bridge_loop_avoidance.h" +#include "multicast.h"
#include <linux/crc32c.h>
@@ -274,14 +275,16 @@ void 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 hlist_head *head; struct batadv_tt_orig_list_entry *orig_entry; int hash_added; bool roamed_back = false;
tt_local = batadv_tt_local_hash_find(bat_priv, addr); - tt_global = batadv_tt_global_hash_find(bat_priv, addr); + + if (!is_multicast_ether_addr(addr)) + tt_global = batadv_tt_global_hash_find(bat_priv, addr);
if (tt_local) { tt_local->last_seen = jiffies; @@ -332,8 +335,11 @@ void 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, @@ -919,6 +925,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, @@ -2346,6 +2357,9 @@ void batadv_tt_local_commit_changes(struct batadv_priv *bat_priv) { uint16_t changed_num = 0;
+ /* Update multicast addresses in local translation table */ + batadv_mcast_mla_tt_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 c84f5cc..5d73a75 100644 --- a/types.h +++ b/types.h @@ -473,6 +473,12 @@ struct batadv_priv_dat { }; #endif
+#ifdef CONFIG_BATMAN_ADV_MCAST_OPTIMIZATIONS +struct batadv_priv_mcast { + struct list_head mla_list; +}; +#endif + /** * struct batadv_priv_nc - per mesh interface network coding private data * @work: work queue callback item for cleanup @@ -561,6 +567,9 @@ struct batadv_priv { #ifdef CONFIG_BATMAN_ADV_DAT atomic_t distributed_arp_table; #endif +#ifdef CONFIG_BATMAN_ADV_MCAST_OPTIMIZATIONS + atomic_t mcast_group_awareness; +#endif atomic_t gw_mode; atomic_t gw_sel_class; atomic_t orig_interval; @@ -595,6 +604,9 @@ struct batadv_priv { #ifdef CONFIG_BATMAN_ADV_DAT struct batadv_priv_dat dat; #endif +#ifdef CONFIG_BATMAN_ADV_MCAST_OPTIMIZATIONS + struct batadv_priv_mcast mcast; +#endif #ifdef CONFIG_BATMAN_ADV_NC atomic_t network_coding; struct batadv_priv_nc nc;