[1/3] batman-adv: Multicast Listener Announcements via Translation Table

Message ID 1368293014-30742-2-git-send-email-linus.luessing@web.de (mailing list archive)
State Superseded, archived
Headers

Commit Message

Linus Lüssing May 11, 2013, 5:23 p.m. UTC
  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            |  222 ++++++++++++++++++++++++++++++++++++++++++++++++
 multicast.h            |   43 ++++++++++
 soft-interface.c       |    3 +
 sysfs.c                |    6 ++
 translation-table.c    |   17 +++-
 types.h                |   12 +++
 13 files changed, 358 insertions(+), 3 deletions(-)
 create mode 100644 multicast.c
 create mode 100644 multicast.h
  

Comments

Antonio Quartulli May 11, 2013, 10:55 p.m. UTC | #1
Hey Linus,

here you have some comments inline

On Sat, May 11, 2013 at 07:23:25PM +0200, Linus Lüssing wrote:
> diff --git a/main.h b/main.h
> index 795345f..39c683d 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
>  
> +#define UINT8_MAX 255

I think you should define it as 255U, otherwise it would get the int type and
may generate warnings somewhere (or also unexpected result sometime..)

> +
> +/* identical to the one used in net/ipv4/igmp.c */
> +#define for_each_pmc_rcu(in_dev, pmc)				\
> +	for (pmc = rcu_dereference(in_dev->mc_list);		\
> +	     pmc != NULL;					\
> +	     pmc = rcu_dereference(pmc->next_rcu))
> +

Since it is exactly the same code reported in net/ipv4/igmp.c and since it is a
define, don't you thin kit is worth trying to send a patch to netdev to move the
define in a proper header file so that we/everybody can re-use it?

> +
> +/**
> + * batadv_mcast_mla_local_collect - Collects local multicast listeners

please start the description with a small letter (like we do everywhere..just
keep the same style)

> + * @dev:		The device to collect multicast addresses from

don't put tabs between the name of the arg and the description.
don't use the capital letter. Just:

@dev: the device..

would be nice :)

> +static int batadv_mcast_mla_local_collect(struct net_device *dev,
> +					  struct list_head *mcast_list,
> +					  int num_mla_max) {

The opening parenthesis of a function must go at the beginning of the next line.

> +	struct netdev_hw_addr *mc_list_entry, *new;
> +	int num_mla = 0, ret = 0;
> +
> +	netif_addr_lock_bh(dev);
> +	netdev_for_each_mc_addr(mc_list_entry, dev) {
> +		if (num_mla >= num_mla_max) {
> +			pr_warn("Too many local multicast listener announcements here, just adding %i\n",
> +				num_mla_max);

why pr_warn and not just a debug message? Is this a symptom of a possible
malfunctioning?


> +			break;
> +		}
> +
> +		new = kmalloc(sizeof(struct netdev_hw_addr), GFP_ATOMIC);

please use sizeof(*new) here. we agreed on this some time ago. This helps
because if we decide to change the type of new in the future then we do not need
to change all the kmalloc invocations.

> +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 (!memcmp(mcast_entry->addr, mcast_addr, ETH_ALEN))

in main.h we have batadv_compare_eth(), you can re-use it here

> +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 (batadv_mcast_mla_is_duplicate(mcast_entry->addr,
> +						  mcast_list))
> +			continue;
> +
> +		batadv_tt_local_remove(bat_priv, mcast_entry->addr,
> +				       "mcast TT outdated", 0);
                                                           ^^^

parameter is bool, please use "false" instead of 0

> +static void batadv_mcast_mla_tt_add(struct net_device *soft_iface,
> +				    struct list_head *mcast_list)
> +{
> +	struct netdev_hw_addr *mcast_entry, *tmp;
> +	struct batadv_priv *bat_priv = netdev_priv(soft_iface);

why not simply pass bat_priv as first argument (as you do in all the other
functions) and then use bat_priv->soft_iface when needed ?
(this soft_iface member has been added recently to save code for lazy people :))

> +/**
> + * batadv_mcast_mla_tt_update - Updates the own MLAs
> + * @bat_priv: the bat priv with all the soft interface information
> + *
> + * Updates the own multicast listener announcements in the translation

For some reason we agreed on not using the third person while describing the
function. For consistency I'd suggest you to do the same your kernel doc.

> + * table. Also takes 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) */

/* comments should
 * be closed
 * like this
 */

 /* not like
  * this */

(this is something checkpatch should also tell you (but I guess only when your
code is in the net/ folder inside the kernel tree...yes it is a netdev rule only
:))

> +/**
> + * batadv_mcast_free - Frees the multicast optimizaitons structures
> + * @bat_priv: the bat priv with all the soft interface information
> + */
> +void batadv_mcast_free(struct batadv_priv *bat_priv)
> +{
> +	struct list_head mcast_list;
> +
> +	INIT_LIST_HEAD(&mcast_list);
> +
> +	batadv_mcast_mla_tt_clean(bat_priv, &mcast_list);

can't you make batadv_mcast_mla_tt_clean() take a NULL second argument and in
that case skip the check in the loop? would be cleaner I guess? what do you
think?


> diff --git a/translation-table.c b/translation-table.c
> index df6b5cd..37e7d47 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>
>  
> @@ -281,7 +282,8 @@ void batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr,
>  	bool roamed_back = false;
>  
>  	tt_local = batadv_tt_local_hash_find(bat_priv, addr);
> -	tt_global = batadv_tt_global_hash_find(bat_priv, addr);
> +	tt_global = is_multicast_ether_addr(addr) ? NULL :
> +		    batadv_tt_global_hash_find(bat_priv, addr);

what about:

tt_global = NULL; (you can put this directly in the declaration)
if (!is_multicast_ether_addr(addr))
	tt_global = batadv_tt_global_hash_find(bat_priv, addr);

it is a bit easier to read, no?

>  
>  	if (tt_local) {
>  		tt_local->last_seen = jiffies;
> @@ -332,8 +334,10 @@ 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 */

comment style like above

> +	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 +923,10 @@ 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 +2354,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;
> -- 
> 1.7.10.4


Cheers,
  
Linus Lüssing May 16, 2013, 6:16 p.m. UTC | #2
Hi Antonio,

On Sun, May 12, 2013 at 12:55:25AM +0200, Antonio Quartulli wrote:
> Hey Linus,
> 
> here you have some comments inline
> 
> On Sat, May 11, 2013 at 07:23:25PM +0200, Linus Lüssing wrote:
> > diff --git a/main.h b/main.h
> > index 795345f..39c683d 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
> >  
> > +#define UINT8_MAX 255
> 
> I think you should define it as 255U, otherwise it would get the int type and
> may generate warnings somewhere (or also unexpected result sometime..)
ok

> 
> > +
> > +/* identical to the one used in net/ipv4/igmp.c */
> > +#define for_each_pmc_rcu(in_dev, pmc)				\
> > +	for (pmc = rcu_dereference(in_dev->mc_list);		\
> > +	     pmc != NULL;					\
> > +	     pmc = rcu_dereference(pmc->next_rcu))
> > +
> 
> Since it is exactly the same code reported in net/ipv4/igmp.c and since it is a
> define, don't you thin kit is worth trying to send a patch to netdev to move the
> define in a proper header file so that we/everybody can re-use it?

Yes. Hm, I think I'd prefer adding the potential user in netdev
first and then moving it to a header file. I'll add an explicit
'TODO' in the comment.

> 
> > +
> > +/**
> > + * batadv_mcast_mla_local_collect - Collects local multicast listeners
> 
> please start the description with a small letter (like we do everywhere..just
> keep the same style)
> 
> > + * @dev:		The device to collect multicast addresses from
> 
> don't put tabs between the name of the arg and the description.
> don't use the capital letter. Just:
> 
> @dev: the device..
> 
> would be nice :)

ok

> 
> > +static int batadv_mcast_mla_local_collect(struct net_device *dev,
> > +					  struct list_head *mcast_list,
> > +					  int num_mla_max) {
> 
> The opening parenthesis of a function must go at the beginning of the next line.
> 
> > +	struct netdev_hw_addr *mc_list_entry, *new;
> > +	int num_mla = 0, ret = 0;
> > +
> > +	netif_addr_lock_bh(dev);
> > +	netdev_for_each_mc_addr(mc_list_entry, dev) {
> > +		if (num_mla >= num_mla_max) {
> > +			pr_warn("Too many local multicast listener announcements here, just adding %i\n",
> > +				num_mla_max);
> 
> why pr_warn and not just a debug message? Is this a symptom of a possible
> malfunctioning?

It was intended as a warning for a malfunction. But actually - I
think I can remove it and the limitation of number of entries,
it's more a relic of the old announcing approach. It should be up
to the TT mechanism to issue such warnings now instead.

> 
> 
> > +			break;
> > +		}
> > +
> > +		new = kmalloc(sizeof(struct netdev_hw_addr), GFP_ATOMIC);
> 
> please use sizeof(*new) here. we agreed on this some time ago. This helps
> because if we decide to change the type of new in the future then we do not need
> to change all the kmalloc invocations.

ok

> 
> > +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 (!memcmp(mcast_entry->addr, mcast_addr, ETH_ALEN))
> 
> in main.h we have batadv_compare_eth(), you can re-use it here

ok

> 
> > +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 (batadv_mcast_mla_is_duplicate(mcast_entry->addr,
> > +						  mcast_list))
> > +			continue;
> > +
> > +		batadv_tt_local_remove(bat_priv, mcast_entry->addr,
> > +				       "mcast TT outdated", 0);
>                                                            ^^^
> 
> parameter is bool, please use "false" instead of 0

ok

> 
> > +static void batadv_mcast_mla_tt_add(struct net_device *soft_iface,
> > +				    struct list_head *mcast_list)
> > +{
> > +	struct netdev_hw_addr *mcast_entry, *tmp;
> > +	struct batadv_priv *bat_priv = netdev_priv(soft_iface);
> 
> why not simply pass bat_priv as first argument (as you do in all the other
> functions) and then use bat_priv->soft_iface when needed ?
> (this soft_iface member has been added recently to save code for lazy people :))

ok

> 
> > +/**
> > + * batadv_mcast_mla_tt_update - Updates the own MLAs
> > + * @bat_priv: the bat priv with all the soft interface information
> > + *
> > + * Updates the own multicast listener announcements in the translation
> 
> For some reason we agreed on not using the third person while describing the
> function. For consistency I'd suggest you to do the same your kernel doc.

I don't quite understand what you mean, talking in the first or
second person seems strange, like "I update the multicast..." or
"You update the multicast...". Do you have an example?

> 
> > + * table. Also takes 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) */
> 
> /* comments should
>  * be closed
>  * like this
>  */
> 
>  /* not like
>   * this */
> 
> (this is something checkpatch should also tell you (but I guess only when your
> code is in the net/ folder inside the kernel tree...yes it is a netdev rule only
> :))

Ah! Didn't know about that, thanks. Yes, I was only checking raw
git-format-patch'ed patch files from the batman repository.

> 
> > +/**
> > + * batadv_mcast_free - Frees the multicast optimizaitons structures
> > + * @bat_priv: the bat priv with all the soft interface information
> > + */
> > +void batadv_mcast_free(struct batadv_priv *bat_priv)
> > +{
> > +	struct list_head mcast_list;
> > +
> > +	INIT_LIST_HEAD(&mcast_list);
> > +
> > +	batadv_mcast_mla_tt_clean(bat_priv, &mcast_list);
> 
> can't you make batadv_mcast_mla_tt_clean() take a NULL second argument and in
> that case skip the check in the loop? would be cleaner I guess? what do you
> think?

Sounds good, will change that!

> 
> 
> > diff --git a/translation-table.c b/translation-table.c
> > index df6b5cd..37e7d47 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>
> >  
> > @@ -281,7 +282,8 @@ void batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr,
> >  	bool roamed_back = false;
> >  
> >  	tt_local = batadv_tt_local_hash_find(bat_priv, addr);
> > -	tt_global = batadv_tt_global_hash_find(bat_priv, addr);
> > +	tt_global = is_multicast_ether_addr(addr) ? NULL :
> > +		    batadv_tt_global_hash_find(bat_priv, addr);
> 
> what about:
> 
> tt_global = NULL; (you can put this directly in the declaration)
> if (!is_multicast_ether_addr(addr))
> 	tt_global = batadv_tt_global_hash_find(bat_priv, addr);
> 
> it is a bit easier to read, no?

Yes.

> 
> >  
> >  	if (tt_local) {
> >  		tt_local->last_seen = jiffies;
> > @@ -332,8 +334,10 @@ 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 */
> 
> comment style like above
> 
> > +	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 +923,10 @@ 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 +2354,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;
> > -- 
> > 1.7.10.4
> 
> 
> Cheers,
> 
> 
> -- 
> Antonio Quartulli
> 
> ..each of us alone is worth nothing..
> Ernesto "Che" Guevara


Cheers, Linus
  
Antonio Quartulli May 16, 2013, 7:36 p.m. UTC | #3
On Thu, May 16, 2013 at 08:16:40PM +0200, Linus Lüssing wrote:
> > > +/**
> > > + * batadv_mcast_mla_tt_update - Updates the own MLAs
> > > + * @bat_priv: the bat priv with all the soft interface information
> > > + *
> > > + * Updates the own multicast listener announcements in the translation
> > 
> > For some reason we agreed on not using the third person while describing the
> > function. For consistency I'd suggest you to do the same your kernel doc.
> 
> I don't quite understand what you mean, talking in the first or
> second person seems strange, like "I update the multicast..." or
> "You update the multicast...". Do you have an example?

It should look like:
"Update the own multicast listener announcements in the translation"


Cheers,
  

Patch

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..39c683d 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
 
+#define UINT8_MAX 255
+
+/* identical to the one used in net/ipv4/igmp.c */
+#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..88bde61
--- /dev/null
+++ b/multicast.c
@@ -0,0 +1,222 @@ 
+/* 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"
+
+/* should match batadv_ogm_packet's mcast_num_mla */
+#define BATADV_MLA_MAX UINT8_MAX
+
+/**
+ * batadv_mcast_mla_local_collect - Collects local multicast listeners
+ * @dev:		The device to collect multicast addresses from
+ * @mcast_list:		A list to put found addresses into
+ * @num_mla_max:	The maximum number of items to add
+ *
+ * Collects up to num_mla_max multicast addresses of the local multicast
+ * listeners on the given interface, dev, in the given mcast_list.
+ *
+ * Returns -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,
+					  int num_mla_max) {
+	struct netdev_hw_addr *mc_list_entry, *new;
+	int num_mla = 0, ret = 0;
+
+	netif_addr_lock_bh(dev);
+	netdev_for_each_mc_addr(mc_list_entry, dev) {
+		if (num_mla >= num_mla_max) {
+			pr_warn("Too many local multicast listener announcements here, just adding %i\n",
+				num_mla_max);
+			break;
+		}
+
+		new = kmalloc(sizeof(struct netdev_hw_addr), GFP_ATOMIC);
+		if (!new) {
+			ret = -ENOMEM;
+			break;
+		}
+
+		memcpy(&new->addr, &mc_list_entry->addr, ETH_ALEN);
+		list_add(&new->list, mcast_list);
+		num_mla++;
+	}
+	netif_addr_unlock_bh(dev);
+
+	return ret < 0 ? ret : num_mla;
+}
+
+/**
+ * batadv_mcast_mla_is_duplicate - Checks whether an address is in a list
+ * @mcast_addr:	The multicast address to check
+ * @mcast_list:	The list with multicast addresses to search in
+ *
+ * Returns 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 (!memcmp(mcast_entry->addr, mcast_addr, ETH_ALEN))
+			return true;
+
+	return false;
+}
+
+/**
+ * batadv_mcast_mla_collect_free - Frees a list of multicast addresses
+ * @mcast_list:		The list to free
+ *
+ * Removes and frees 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 - Cleans 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
+ *
+ * This method retracts the announcement of any multicast listener from the
+ * translation table except the ones listed in the given mcast_list.
+ */
+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 (batadv_mcast_mla_is_duplicate(mcast_entry->addr,
+						  mcast_list))
+			continue;
+
+		batadv_tt_local_remove(bat_priv, mcast_entry->addr,
+				       "mcast TT outdated", 0);
+
+		list_del(&mcast_entry->list);
+		kfree(mcast_entry);
+	}
+}
+
+/**
+ * batadv_mcast_mla_tt_add - Adds multicast listener announcements
+ * @soft_iface: the soft interface where MLAs get added to
+ * @mcast_list: A list of addresses which are going to get added
+ *
+ * Adds 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 net_device *soft_iface,
+				    struct list_head *mcast_list)
+{
+	struct netdev_hw_addr *mcast_entry, *tmp;
+	struct batadv_priv *bat_priv = netdev_priv(soft_iface);
+
+	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(soft_iface, mcast_entry->addr,
+				    BATADV_NULL_IFINDEX);
+		list_move_tail(&mcast_entry->list, &bat_priv->mcast.mla_list);
+	}
+}
+
+/**
+ * batadv_mcast_mla_tt_update - Updates the own MLAs
+ * @bat_priv: the bat priv with all the soft interface information
+ *
+ * Updates the own multicast listener announcements in the translation
+ * table. Also takes 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,
+					     BATADV_MLA_MAX);
+	if (ret < 0)
+		goto out;
+
+update:
+	batadv_mcast_mla_tt_clean(bat_priv, &mcast_list);
+	batadv_mcast_mla_tt_add(soft_iface, &mcast_list);
+
+out:
+	if (primary_if)
+		batadv_hardif_free_ref(primary_if);
+
+	batadv_mcast_mla_collect_free(&mcast_list);
+}
+
+/**
+ * batadv_mcast_free - Frees the multicast optimizaitons structures
+ * @bat_priv: the bat priv with all the soft interface information
+ */
+void batadv_mcast_free(struct batadv_priv *bat_priv)
+{
+	struct list_head mcast_list;
+
+	INIT_LIST_HEAD(&mcast_list);
+
+	batadv_mcast_mla_tt_clean(bat_priv, &mcast_list);
+}
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..37e7d47 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>
 
@@ -281,7 +282,8 @@  void batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr,
 	bool roamed_back = false;
 
 	tt_local = batadv_tt_local_hash_find(bat_priv, addr);
-	tt_global = batadv_tt_global_hash_find(bat_priv, addr);
+	tt_global = is_multicast_ether_addr(addr) ? NULL :
+		    batadv_tt_global_hash_find(bat_priv, addr);
 
 	if (tt_local) {
 		tt_local->last_seen = jiffies;
@@ -332,8 +334,10 @@  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 +923,10 @@  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 +2354,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;