|
@@ -25,17 +25,23 @@
|
|
#include <linux/errno.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/etherdevice.h>
|
|
#include <linux/etherdevice.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/fs.h>
|
|
|
|
+#include <linux/icmpv6.h>
|
|
|
|
+#include <linux/if_bridge.h>
|
|
#include <linux/if_ether.h>
|
|
#include <linux/if_ether.h>
|
|
-#include <linux/in6.h>
|
|
|
|
|
|
+#include <linux/igmp.h>
|
|
#include <linux/in.h>
|
|
#include <linux/in.h>
|
|
|
|
+#include <linux/in6.h>
|
|
#include <linux/ip.h>
|
|
#include <linux/ip.h>
|
|
#include <linux/ipv6.h>
|
|
#include <linux/ipv6.h>
|
|
|
|
+#include <linux/kernel.h>
|
|
#include <linux/kref.h>
|
|
#include <linux/kref.h>
|
|
#include <linux/list.h>
|
|
#include <linux/list.h>
|
|
#include <linux/lockdep.h>
|
|
#include <linux/lockdep.h>
|
|
#include <linux/netdevice.h>
|
|
#include <linux/netdevice.h>
|
|
|
|
+#include <linux/printk.h>
|
|
#include <linux/rculist.h>
|
|
#include <linux/rculist.h>
|
|
#include <linux/rcupdate.h>
|
|
#include <linux/rcupdate.h>
|
|
|
|
+#include <linux/seq_file.h>
|
|
#include <linux/skbuff.h>
|
|
#include <linux/skbuff.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/spinlock.h>
|
|
@@ -43,18 +49,55 @@
|
|
#include <linux/string.h>
|
|
#include <linux/string.h>
|
|
#include <linux/types.h>
|
|
#include <linux/types.h>
|
|
#include <net/addrconf.h>
|
|
#include <net/addrconf.h>
|
|
|
|
+#include <net/if_inet6.h>
|
|
|
|
+#include <net/ip.h>
|
|
#include <net/ipv6.h>
|
|
#include <net/ipv6.h>
|
|
|
|
|
|
|
|
+#include "hard-interface.h"
|
|
|
|
+#include "hash.h"
|
|
#include "packet.h"
|
|
#include "packet.h"
|
|
#include "translation-table.h"
|
|
#include "translation-table.h"
|
|
|
|
|
|
|
|
+/**
|
|
|
|
+ * batadv_mcast_get_bridge - get the bridge on top of the softif if it exists
|
|
|
|
+ * @soft_iface: netdev struct of the mesh interface
|
|
|
|
+ *
|
|
|
|
+ * If the given soft interface has a bridge on top then the refcount
|
|
|
|
+ * of the according net device is increased.
|
|
|
|
+ *
|
|
|
|
+ * Return: NULL if no such bridge exists. Otherwise the net device of the
|
|
|
|
+ * bridge.
|
|
|
|
+ */
|
|
|
|
+static struct net_device *batadv_mcast_get_bridge(struct net_device *soft_iface)
|
|
|
|
+{
|
|
|
|
+ struct net_device *upper = 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_mla_softif_get - get softif multicast listeners
|
|
* batadv_mcast_mla_softif_get - get softif multicast listeners
|
|
* @dev: the device to collect multicast addresses from
|
|
* @dev: the device to collect multicast addresses from
|
|
* @mcast_list: a list to put found addresses into
|
|
* @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.
|
|
|
|
|
|
+ * Collects multicast addresses of multicast listeners residing
|
|
|
|
+ * on this kernel on the given soft interface, dev, in
|
|
|
|
+ * the given mcast_list. In general, multicast listeners provided by
|
|
|
|
+ * your multicast receiving applications run directly on this node.
|
|
|
|
+ *
|
|
|
|
+ * If there is a bridge interface on top of dev, collects from that one
|
|
|
|
+ * instead. Just like with IP addresses and routes, multicast listeners
|
|
|
|
+ * will(/should) register to the bridge interface instead of an
|
|
|
|
+ * enslaved bat0.
|
|
*
|
|
*
|
|
* Return: -ENOMEM on memory allocation error or the number of
|
|
* Return: -ENOMEM on memory allocation error or the number of
|
|
* items added to the mcast_list otherwise.
|
|
* items added to the mcast_list otherwise.
|
|
@@ -62,12 +105,13 @@
|
|
static int batadv_mcast_mla_softif_get(struct net_device *dev,
|
|
static int batadv_mcast_mla_softif_get(struct net_device *dev,
|
|
struct hlist_head *mcast_list)
|
|
struct hlist_head *mcast_list)
|
|
{
|
|
{
|
|
|
|
+ struct net_device *bridge = batadv_mcast_get_bridge(dev);
|
|
struct netdev_hw_addr *mc_list_entry;
|
|
struct netdev_hw_addr *mc_list_entry;
|
|
struct batadv_hw_addr *new;
|
|
struct batadv_hw_addr *new;
|
|
int ret = 0;
|
|
int ret = 0;
|
|
|
|
|
|
- netif_addr_lock_bh(dev);
|
|
|
|
- netdev_for_each_mc_addr(mc_list_entry, dev) {
|
|
|
|
|
|
+ netif_addr_lock_bh(bridge ? bridge : dev);
|
|
|
|
+ netdev_for_each_mc_addr(mc_list_entry, bridge ? bridge : dev) {
|
|
new = kmalloc(sizeof(*new), GFP_ATOMIC);
|
|
new = kmalloc(sizeof(*new), GFP_ATOMIC);
|
|
if (!new) {
|
|
if (!new) {
|
|
ret = -ENOMEM;
|
|
ret = -ENOMEM;
|
|
@@ -78,7 +122,10 @@ static int batadv_mcast_mla_softif_get(struct net_device *dev,
|
|
hlist_add_head(&new->list, mcast_list);
|
|
hlist_add_head(&new->list, mcast_list);
|
|
ret++;
|
|
ret++;
|
|
}
|
|
}
|
|
- netif_addr_unlock_bh(dev);
|
|
|
|
|
|
+ netif_addr_unlock_bh(bridge ? bridge : dev);
|
|
|
|
+
|
|
|
|
+ if (bridge)
|
|
|
|
+ dev_put(bridge);
|
|
|
|
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
@@ -103,6 +150,83 @@ static bool batadv_mcast_mla_is_duplicate(u8 *mcast_addr,
|
|
return false;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/**
|
|
|
|
+ * batadv_mcast_mla_br_addr_cpy - copy a bridge multicast address
|
|
|
|
+ * @dst: destination to write to - a multicast MAC address
|
|
|
|
+ * @src: source to read from - a multicast IP address
|
|
|
|
+ *
|
|
|
|
+ * Converts a given multicast IPv4/IPv6 address from a bridge
|
|
|
|
+ * to its matching multicast MAC address and copies it into the given
|
|
|
|
+ * destination buffer.
|
|
|
|
+ *
|
|
|
|
+ * Caller needs to make sure the destination buffer can hold
|
|
|
|
+ * at least ETH_ALEN bytes.
|
|
|
|
+ */
|
|
|
|
+static void batadv_mcast_mla_br_addr_cpy(char *dst, const struct br_ip *src)
|
|
|
|
+{
|
|
|
|
+ if (src->proto == htons(ETH_P_IP))
|
|
|
|
+ ip_eth_mc_map(src->u.ip4, dst);
|
|
|
|
+#if IS_ENABLED(CONFIG_IPV6)
|
|
|
|
+ else if (src->proto == htons(ETH_P_IPV6))
|
|
|
|
+ ipv6_eth_mc_map(&src->u.ip6, dst);
|
|
|
|
+#endif
|
|
|
|
+ else
|
|
|
|
+ eth_zero_addr(dst);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * batadv_mcast_mla_bridge_get - get bridged-in multicast listeners
|
|
|
|
+ * @dev: a bridge slave whose bridge to collect multicast addresses from
|
|
|
|
+ * @mcast_list: a list to put found addresses into
|
|
|
|
+ *
|
|
|
|
+ * Collects multicast addresses of multicast listeners residing
|
|
|
|
+ * on foreign, non-mesh devices which we gave access to our mesh via
|
|
|
|
+ * a bridge on top of 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_bridge_get(struct net_device *dev,
|
|
|
|
+ struct hlist_head *mcast_list)
|
|
|
|
+{
|
|
|
|
+ struct list_head bridge_mcast_list = LIST_HEAD_INIT(bridge_mcast_list);
|
|
|
|
+ struct br_ip_list *br_ip_entry, *tmp;
|
|
|
|
+ struct batadv_hw_addr *new;
|
|
|
|
+ u8 mcast_addr[ETH_ALEN];
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ /* we don't need to detect these devices/listeners, the IGMP/MLD
|
|
|
|
+ * snooping code of the Linux bridge already does that for us
|
|
|
|
+ */
|
|
|
|
+ ret = br_multicast_list_adjacent(dev, &bridge_mcast_list);
|
|
|
|
+ if (ret < 0)
|
|
|
|
+ goto out;
|
|
|
|
+
|
|
|
|
+ list_for_each_entry(br_ip_entry, &bridge_mcast_list, list) {
|
|
|
|
+ batadv_mcast_mla_br_addr_cpy(mcast_addr, &br_ip_entry->addr);
|
|
|
|
+ if (batadv_mcast_mla_is_duplicate(mcast_addr, mcast_list))
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ new = kmalloc(sizeof(*new), GFP_ATOMIC);
|
|
|
|
+ if (!new) {
|
|
|
|
+ ret = -ENOMEM;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ether_addr_copy(new->addr, mcast_addr);
|
|
|
|
+ hlist_add_head(&new->list, mcast_list);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+out:
|
|
|
|
+ list_for_each_entry_safe(br_ip_entry, tmp, &bridge_mcast_list, list) {
|
|
|
|
+ list_del(&br_ip_entry->list);
|
|
|
|
+ kfree(br_ip_entry);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* batadv_mcast_mla_list_free - free a list of multicast addresses
|
|
* batadv_mcast_mla_list_free - free a list of multicast addresses
|
|
* @bat_priv: the bat priv with all the soft interface information
|
|
* @bat_priv: the bat priv with all the soft interface information
|
|
@@ -213,6 +337,122 @@ static bool batadv_mcast_has_bridge(struct batadv_priv *bat_priv)
|
|
return upper;
|
|
return upper;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/**
|
|
|
|
+ * batadv_mcast_querier_log - debug output regarding the querier status on link
|
|
|
|
+ * @bat_priv: the bat priv with all the soft interface information
|
|
|
|
+ * @str_proto: a string for the querier protocol (e.g. "IGMP" or "MLD")
|
|
|
|
+ * @old_state: the previous querier state on our link
|
|
|
|
+ * @new_state: the new querier state on our link
|
|
|
|
+ *
|
|
|
|
+ * Outputs debug messages to the logging facility with log level 'mcast'
|
|
|
|
+ * regarding changes to the querier status on the link which are relevant
|
|
|
|
+ * to our multicast optimizations.
|
|
|
|
+ *
|
|
|
|
+ * Usually this is about whether a querier appeared or vanished in
|
|
|
|
+ * our mesh or whether the querier is in the suboptimal position of being
|
|
|
|
+ * behind our local bridge segment: Snooping switches will directly
|
|
|
|
+ * forward listener reports to the querier, therefore batman-adv and
|
|
|
|
+ * the bridge will potentially not see these listeners - the querier is
|
|
|
|
+ * potentially shadowing listeners from us then.
|
|
|
|
+ *
|
|
|
|
+ * This is only interesting for nodes with a bridge on top of their
|
|
|
|
+ * soft interface.
|
|
|
|
+ */
|
|
|
|
+static void
|
|
|
|
+batadv_mcast_querier_log(struct batadv_priv *bat_priv, char *str_proto,
|
|
|
|
+ struct batadv_mcast_querier_state *old_state,
|
|
|
|
+ struct batadv_mcast_querier_state *new_state)
|
|
|
|
+{
|
|
|
|
+ if (!old_state->exists && new_state->exists)
|
|
|
|
+ batadv_info(bat_priv->soft_iface, "%s Querier appeared\n",
|
|
|
|
+ str_proto);
|
|
|
|
+ else if (old_state->exists && !new_state->exists)
|
|
|
|
+ batadv_info(bat_priv->soft_iface,
|
|
|
|
+ "%s Querier disappeared - multicast optimizations disabled\n",
|
|
|
|
+ str_proto);
|
|
|
|
+ else if (!bat_priv->mcast.bridged && !new_state->exists)
|
|
|
|
+ batadv_info(bat_priv->soft_iface,
|
|
|
|
+ "No %s Querier present - multicast optimizations disabled\n",
|
|
|
|
+ str_proto);
|
|
|
|
+
|
|
|
|
+ if (new_state->exists) {
|
|
|
|
+ if ((!old_state->shadowing && new_state->shadowing) ||
|
|
|
|
+ (!old_state->exists && new_state->shadowing))
|
|
|
|
+ batadv_dbg(BATADV_DBG_MCAST, bat_priv,
|
|
|
|
+ "%s Querier is behind our bridged segment: Might shadow listeners\n",
|
|
|
|
+ str_proto);
|
|
|
|
+ else if (old_state->shadowing && !new_state->shadowing)
|
|
|
|
+ batadv_dbg(BATADV_DBG_MCAST, bat_priv,
|
|
|
|
+ "%s Querier is not behind our bridged segment\n",
|
|
|
|
+ str_proto);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * batadv_mcast_bridge_log - debug output for topology changes in bridged setups
|
|
|
|
+ * @bat_priv: the bat priv with all the soft interface information
|
|
|
|
+ * @bridged: a flag about whether the soft interface is currently bridged or not
|
|
|
|
+ * @querier_ipv4: (maybe) new status of a potential, selected IGMP querier
|
|
|
|
+ * @querier_ipv6: (maybe) new status of a potential, selected MLD querier
|
|
|
|
+ *
|
|
|
|
+ * If no bridges are ever used on this node, then this function does nothing.
|
|
|
|
+ *
|
|
|
|
+ * Otherwise this function outputs debug information to the 'mcast' log level
|
|
|
|
+ * which might be relevant to our multicast optimizations.
|
|
|
|
+ *
|
|
|
|
+ * More precisely, it outputs information when a bridge interface is added or
|
|
|
|
+ * removed from a soft interface. And when a bridge is present, it further
|
|
|
|
+ * outputs information about the querier state which is relevant for the
|
|
|
|
+ * multicast flags this node is going to set.
|
|
|
|
+ */
|
|
|
|
+static void
|
|
|
|
+batadv_mcast_bridge_log(struct batadv_priv *bat_priv, bool bridged,
|
|
|
|
+ struct batadv_mcast_querier_state *querier_ipv4,
|
|
|
|
+ struct batadv_mcast_querier_state *querier_ipv6)
|
|
|
|
+{
|
|
|
|
+ if (!bat_priv->mcast.bridged && bridged)
|
|
|
|
+ batadv_dbg(BATADV_DBG_MCAST, bat_priv,
|
|
|
|
+ "Bridge added: Setting Unsnoopables(U)-flag\n");
|
|
|
|
+ else if (bat_priv->mcast.bridged && !bridged)
|
|
|
|
+ batadv_dbg(BATADV_DBG_MCAST, bat_priv,
|
|
|
|
+ "Bridge removed: Unsetting Unsnoopables(U)-flag\n");
|
|
|
|
+
|
|
|
|
+ if (bridged) {
|
|
|
|
+ batadv_mcast_querier_log(bat_priv, "IGMP",
|
|
|
|
+ &bat_priv->mcast.querier_ipv4,
|
|
|
|
+ querier_ipv4);
|
|
|
|
+ batadv_mcast_querier_log(bat_priv, "MLD",
|
|
|
|
+ &bat_priv->mcast.querier_ipv6,
|
|
|
|
+ querier_ipv6);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * batadv_mcast_flags_logs - output debug information about mcast flag changes
|
|
|
|
+ * @bat_priv: the bat priv with all the soft interface information
|
|
|
|
+ * @flags: flags indicating the new multicast state
|
|
|
|
+ *
|
|
|
|
+ * Whenever the multicast flags this nodes announces changes (@mcast_flags vs.
|
|
|
|
+ * bat_priv->mcast.flags), this notifies userspace via the 'mcast' log level.
|
|
|
|
+ */
|
|
|
|
+static void batadv_mcast_flags_log(struct batadv_priv *bat_priv, u8 flags)
|
|
|
|
+{
|
|
|
|
+ u8 old_flags = bat_priv->mcast.flags;
|
|
|
|
+ char str_old_flags[] = "[...]";
|
|
|
|
+
|
|
|
|
+ sprintf(str_old_flags, "[%c%c%c]",
|
|
|
|
+ (old_flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES) ? 'U' : '.',
|
|
|
|
+ (old_flags & BATADV_MCAST_WANT_ALL_IPV4) ? '4' : '.',
|
|
|
|
+ (old_flags & BATADV_MCAST_WANT_ALL_IPV6) ? '6' : '.');
|
|
|
|
+
|
|
|
|
+ batadv_dbg(BATADV_DBG_MCAST, bat_priv,
|
|
|
|
+ "Changing multicast flags from '%s' to '[%c%c%c]'\n",
|
|
|
|
+ bat_priv->mcast.enabled ? str_old_flags : "<undefined>",
|
|
|
|
+ (flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES) ? 'U' : '.',
|
|
|
|
+ (flags & BATADV_MCAST_WANT_ALL_IPV4) ? '4' : '.',
|
|
|
|
+ (flags & BATADV_MCAST_WANT_ALL_IPV6) ? '6' : '.');
|
|
|
|
+}
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* batadv_mcast_mla_tvlv_update - update multicast tvlv
|
|
* batadv_mcast_mla_tvlv_update - update multicast tvlv
|
|
* @bat_priv: the bat priv with all the soft interface information
|
|
* @bat_priv: the bat priv with all the soft interface information
|
|
@@ -220,38 +460,73 @@ static bool batadv_mcast_has_bridge(struct batadv_priv *bat_priv)
|
|
* Updates the own multicast tvlv with our current multicast related settings,
|
|
* Updates the own multicast tvlv with our current multicast related settings,
|
|
* capabilities and inabilities.
|
|
* capabilities and inabilities.
|
|
*
|
|
*
|
|
- * Return: true if the tvlv container is registered afterwards. Otherwise
|
|
|
|
- * returns false.
|
|
|
|
|
|
+ * Return: false if we want all IPv4 && IPv6 multicast traffic and true
|
|
|
|
+ * otherwise.
|
|
*/
|
|
*/
|
|
static bool batadv_mcast_mla_tvlv_update(struct batadv_priv *bat_priv)
|
|
static bool batadv_mcast_mla_tvlv_update(struct batadv_priv *bat_priv)
|
|
{
|
|
{
|
|
struct batadv_tvlv_mcast_data mcast_data;
|
|
struct batadv_tvlv_mcast_data mcast_data;
|
|
|
|
+ struct batadv_mcast_querier_state querier4 = {false, false};
|
|
|
|
+ struct batadv_mcast_querier_state querier6 = {false, false};
|
|
|
|
+ struct net_device *dev = bat_priv->soft_iface;
|
|
|
|
+ bool bridged;
|
|
|
|
|
|
mcast_data.flags = BATADV_NO_FLAGS;
|
|
mcast_data.flags = BATADV_NO_FLAGS;
|
|
memset(mcast_data.reserved, 0, sizeof(mcast_data.reserved));
|
|
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)
|
|
|
|
|
|
+ bridged = batadv_mcast_has_bridge(bat_priv);
|
|
|
|
+ if (!bridged)
|
|
|
|
+ goto update;
|
|
|
|
+
|
|
|
|
+#if !IS_ENABLED(CONFIG_BRIDGE_IGMP_SNOOPING)
|
|
|
|
+ pr_warn_once("No bridge IGMP snooping compiled - multicast optimizations disabled\n");
|
|
|
|
+#endif
|
|
|
|
+
|
|
|
|
+ querier4.exists = br_multicast_has_querier_anywhere(dev, ETH_P_IP);
|
|
|
|
+ querier4.shadowing = br_multicast_has_querier_adjacent(dev, ETH_P_IP);
|
|
|
|
+
|
|
|
|
+ querier6.exists = br_multicast_has_querier_anywhere(dev, ETH_P_IPV6);
|
|
|
|
+ querier6.shadowing = br_multicast_has_querier_adjacent(dev, ETH_P_IPV6);
|
|
|
|
+
|
|
|
|
+ mcast_data.flags |= BATADV_MCAST_WANT_ALL_UNSNOOPABLES;
|
|
|
|
+
|
|
|
|
+ /* 1) If no querier exists at all, then multicast listeners on
|
|
|
|
+ * our local TT clients behind the bridge will keep silent.
|
|
|
|
+ * 2) If the selected querier is on one of our local TT clients,
|
|
|
|
+ * behind the bridge, then this querier might shadow multicast
|
|
|
|
+ * listeners on our local TT clients, behind this bridge.
|
|
|
|
+ *
|
|
|
|
+ * In both cases, we will signalize other batman nodes that
|
|
|
|
+ * we need all multicast traffic of the according protocol.
|
|
*/
|
|
*/
|
|
- 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;
|
|
|
|
- }
|
|
|
|
|
|
+ if (!querier4.exists || querier4.shadowing)
|
|
|
|
+ mcast_data.flags |= BATADV_MCAST_WANT_ALL_IPV4;
|
|
|
|
|
|
- return false;
|
|
|
|
- }
|
|
|
|
|
|
+ if (!querier6.exists || querier6.shadowing)
|
|
|
|
+ mcast_data.flags |= BATADV_MCAST_WANT_ALL_IPV6;
|
|
|
|
+
|
|
|
|
+update:
|
|
|
|
+ batadv_mcast_bridge_log(bat_priv, bridged, &querier4, &querier6);
|
|
|
|
+
|
|
|
|
+ bat_priv->mcast.querier_ipv4.exists = querier4.exists;
|
|
|
|
+ bat_priv->mcast.querier_ipv4.shadowing = querier4.shadowing;
|
|
|
|
+
|
|
|
|
+ bat_priv->mcast.querier_ipv6.exists = querier6.exists;
|
|
|
|
+ bat_priv->mcast.querier_ipv6.shadowing = querier6.shadowing;
|
|
|
|
+
|
|
|
|
+ bat_priv->mcast.bridged = bridged;
|
|
|
|
|
|
if (!bat_priv->mcast.enabled ||
|
|
if (!bat_priv->mcast.enabled ||
|
|
mcast_data.flags != bat_priv->mcast.flags) {
|
|
mcast_data.flags != bat_priv->mcast.flags) {
|
|
- batadv_tvlv_container_register(bat_priv, BATADV_TVLV_MCAST, 1,
|
|
|
|
|
|
+ batadv_mcast_flags_log(bat_priv, mcast_data.flags);
|
|
|
|
+ batadv_tvlv_container_register(bat_priv, BATADV_TVLV_MCAST, 2,
|
|
&mcast_data, sizeof(mcast_data));
|
|
&mcast_data, sizeof(mcast_data));
|
|
bat_priv->mcast.flags = mcast_data.flags;
|
|
bat_priv->mcast.flags = mcast_data.flags;
|
|
bat_priv->mcast.enabled = true;
|
|
bat_priv->mcast.enabled = true;
|
|
}
|
|
}
|
|
|
|
|
|
- return true;
|
|
|
|
|
|
+ return !(mcast_data.flags &
|
|
|
|
+ (BATADV_MCAST_WANT_ALL_IPV4 + BATADV_MCAST_WANT_ALL_IPV6));
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -274,6 +549,10 @@ void batadv_mcast_mla_update(struct batadv_priv *bat_priv)
|
|
if (ret < 0)
|
|
if (ret < 0)
|
|
goto out;
|
|
goto out;
|
|
|
|
|
|
|
|
+ ret = batadv_mcast_mla_bridge_get(soft_iface, &mcast_list);
|
|
|
|
+ if (ret < 0)
|
|
|
|
+ goto out;
|
|
|
|
+
|
|
update:
|
|
update:
|
|
batadv_mcast_mla_tt_retract(bat_priv, &mcast_list);
|
|
batadv_mcast_mla_tt_retract(bat_priv, &mcast_list);
|
|
batadv_mcast_mla_tt_add(bat_priv, &mcast_list);
|
|
batadv_mcast_mla_tt_add(bat_priv, &mcast_list);
|
|
@@ -282,6 +561,31 @@ out:
|
|
batadv_mcast_mla_list_free(bat_priv, &mcast_list);
|
|
batadv_mcast_mla_list_free(bat_priv, &mcast_list);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/**
|
|
|
|
+ * batadv_mcast_is_report_ipv4 - check for IGMP reports
|
|
|
|
+ * @skb: the ethernet frame destined for the mesh
|
|
|
|
+ *
|
|
|
|
+ * This call might reallocate skb data.
|
|
|
|
+ *
|
|
|
|
+ * Checks whether the given frame is a valid IGMP report.
|
|
|
|
+ *
|
|
|
|
+ * Return: If so then true, otherwise false.
|
|
|
|
+ */
|
|
|
|
+static bool batadv_mcast_is_report_ipv4(struct sk_buff *skb)
|
|
|
|
+{
|
|
|
|
+ if (ip_mc_check_igmp(skb, NULL) < 0)
|
|
|
|
+ return false;
|
|
|
|
+
|
|
|
|
+ switch (igmp_hdr(skb)->type) {
|
|
|
|
+ case IGMP_HOST_MEMBERSHIP_REPORT:
|
|
|
|
+ case IGMPV2_HOST_MEMBERSHIP_REPORT:
|
|
|
|
+ case IGMPV3_HOST_MEMBERSHIP_REPORT:
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return false;
|
|
|
|
+}
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* batadv_mcast_forw_mode_check_ipv4 - check for optimized forwarding potential
|
|
* batadv_mcast_forw_mode_check_ipv4 - check for optimized forwarding potential
|
|
* @bat_priv: the bat priv with all the soft interface information
|
|
* @bat_priv: the bat priv with all the soft interface information
|
|
@@ -304,6 +608,9 @@ static int batadv_mcast_forw_mode_check_ipv4(struct batadv_priv *bat_priv,
|
|
if (!pskb_may_pull(skb, sizeof(struct ethhdr) + sizeof(*iphdr)))
|
|
if (!pskb_may_pull(skb, sizeof(struct ethhdr) + sizeof(*iphdr)))
|
|
return -ENOMEM;
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
+ if (batadv_mcast_is_report_ipv4(skb))
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
iphdr = ip_hdr(skb);
|
|
iphdr = ip_hdr(skb);
|
|
|
|
|
|
/* TODO: Implement Multicast Router Discovery (RFC4286),
|
|
/* TODO: Implement Multicast Router Discovery (RFC4286),
|
|
@@ -320,6 +627,31 @@ static int batadv_mcast_forw_mode_check_ipv4(struct batadv_priv *bat_priv,
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+#if IS_ENABLED(CONFIG_IPV6)
|
|
|
|
+/**
|
|
|
|
+ * batadv_mcast_is_report_ipv6 - check for MLD reports
|
|
|
|
+ * @skb: the ethernet frame destined for the mesh
|
|
|
|
+ *
|
|
|
|
+ * This call might reallocate skb data.
|
|
|
|
+ *
|
|
|
|
+ * Checks whether the given frame is a valid MLD report.
|
|
|
|
+ *
|
|
|
|
+ * Return: If so then true, otherwise false.
|
|
|
|
+ */
|
|
|
|
+static bool batadv_mcast_is_report_ipv6(struct sk_buff *skb)
|
|
|
|
+{
|
|
|
|
+ if (ipv6_mc_check_mld(skb, NULL) < 0)
|
|
|
|
+ return false;
|
|
|
|
+
|
|
|
|
+ switch (icmp6_hdr(skb)->icmp6_type) {
|
|
|
|
+ case ICMPV6_MGM_REPORT:
|
|
|
|
+ case ICMPV6_MLD2_REPORT:
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return false;
|
|
|
|
+}
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* batadv_mcast_forw_mode_check_ipv6 - check for optimized forwarding potential
|
|
* batadv_mcast_forw_mode_check_ipv6 - check for optimized forwarding potential
|
|
* @bat_priv: the bat priv with all the soft interface information
|
|
* @bat_priv: the bat priv with all the soft interface information
|
|
@@ -341,6 +673,9 @@ static int batadv_mcast_forw_mode_check_ipv6(struct batadv_priv *bat_priv,
|
|
if (!pskb_may_pull(skb, sizeof(struct ethhdr) + sizeof(*ip6hdr)))
|
|
if (!pskb_may_pull(skb, sizeof(struct ethhdr) + sizeof(*ip6hdr)))
|
|
return -ENOMEM;
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
+ if (batadv_mcast_is_report_ipv6(skb))
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
ip6hdr = ipv6_hdr(skb);
|
|
ip6hdr = ipv6_hdr(skb);
|
|
|
|
|
|
/* TODO: Implement Multicast Router Discovery (RFC4286),
|
|
/* TODO: Implement Multicast Router Discovery (RFC4286),
|
|
@@ -357,6 +692,7 @@ static int batadv_mcast_forw_mode_check_ipv6(struct batadv_priv *bat_priv,
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
+#endif
|
|
|
|
|
|
/**
|
|
/**
|
|
* batadv_mcast_forw_mode_check - check for optimized forwarding potential
|
|
* batadv_mcast_forw_mode_check - check for optimized forwarding potential
|
|
@@ -385,9 +721,11 @@ static int batadv_mcast_forw_mode_check(struct batadv_priv *bat_priv,
|
|
case ETH_P_IP:
|
|
case ETH_P_IP:
|
|
return batadv_mcast_forw_mode_check_ipv4(bat_priv, skb,
|
|
return batadv_mcast_forw_mode_check_ipv4(bat_priv, skb,
|
|
is_unsnoopable);
|
|
is_unsnoopable);
|
|
|
|
+#if IS_ENABLED(CONFIG_IPV6)
|
|
case ETH_P_IPV6:
|
|
case ETH_P_IPV6:
|
|
return batadv_mcast_forw_mode_check_ipv6(bat_priv, skb,
|
|
return batadv_mcast_forw_mode_check_ipv6(bat_priv, skb,
|
|
is_unsnoopable);
|
|
is_unsnoopable);
|
|
|
|
+#endif
|
|
default:
|
|
default:
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
@@ -728,18 +1066,18 @@ static void batadv_mcast_want_ipv6_update(struct batadv_priv *bat_priv,
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * batadv_mcast_tvlv_ogm_handler_v1 - process incoming multicast tvlv container
|
|
|
|
|
|
+ * batadv_mcast_tvlv_ogm_handler - process incoming multicast tvlv container
|
|
* @bat_priv: the bat priv with all the soft interface information
|
|
* @bat_priv: the bat priv with all the soft interface information
|
|
* @orig: the orig_node of the ogm
|
|
* @orig: the orig_node of the ogm
|
|
* @flags: flags indicating the tvlv state (see batadv_tvlv_handler_flags)
|
|
* @flags: flags indicating the tvlv state (see batadv_tvlv_handler_flags)
|
|
* @tvlv_value: tvlv buffer containing the multicast data
|
|
* @tvlv_value: tvlv buffer containing the multicast data
|
|
* @tvlv_value_len: tvlv buffer length
|
|
* @tvlv_value_len: tvlv buffer length
|
|
*/
|
|
*/
|
|
-static void batadv_mcast_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv,
|
|
|
|
- struct batadv_orig_node *orig,
|
|
|
|
- u8 flags,
|
|
|
|
- void *tvlv_value,
|
|
|
|
- u16 tvlv_value_len)
|
|
|
|
|
|
+static void batadv_mcast_tvlv_ogm_handler(struct batadv_priv *bat_priv,
|
|
|
|
+ struct batadv_orig_node *orig,
|
|
|
|
+ u8 flags,
|
|
|
|
+ void *tvlv_value,
|
|
|
|
+ u16 tvlv_value_len)
|
|
{
|
|
{
|
|
bool orig_mcast_enabled = !(flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND);
|
|
bool orig_mcast_enabled = !(flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND);
|
|
u8 mcast_flags = BATADV_NO_FLAGS;
|
|
u8 mcast_flags = BATADV_NO_FLAGS;
|
|
@@ -789,19 +1127,120 @@ static void batadv_mcast_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv,
|
|
*/
|
|
*/
|
|
void batadv_mcast_init(struct batadv_priv *bat_priv)
|
|
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_register(bat_priv, batadv_mcast_tvlv_ogm_handler,
|
|
|
|
+ NULL, BATADV_TVLV_MCAST, 2,
|
|
BATADV_TVLV_HANDLER_OGM_CIFNOTFND);
|
|
BATADV_TVLV_HANDLER_OGM_CIFNOTFND);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/**
|
|
|
|
+ * batadv_mcast_flags_print_header - print own mcast flags to debugfs table
|
|
|
|
+ * @bat_priv: the bat priv with all the soft interface information
|
|
|
|
+ * @seq: debugfs table seq_file struct
|
|
|
|
+ *
|
|
|
|
+ * Prints our own multicast flags including a more specific reason why
|
|
|
|
+ * they are set, that is prints the bridge and querier state too, to
|
|
|
|
+ * the debugfs table specified via @seq.
|
|
|
|
+ */
|
|
|
|
+static void batadv_mcast_flags_print_header(struct batadv_priv *bat_priv,
|
|
|
|
+ struct seq_file *seq)
|
|
|
|
+{
|
|
|
|
+ u8 flags = bat_priv->mcast.flags;
|
|
|
|
+ char querier4, querier6, shadowing4, shadowing6;
|
|
|
|
+ bool bridged = bat_priv->mcast.bridged;
|
|
|
|
+
|
|
|
|
+ if (bridged) {
|
|
|
|
+ querier4 = bat_priv->mcast.querier_ipv4.exists ? '.' : '4';
|
|
|
|
+ querier6 = bat_priv->mcast.querier_ipv6.exists ? '.' : '6';
|
|
|
|
+ shadowing4 = bat_priv->mcast.querier_ipv4.shadowing ? '4' : '.';
|
|
|
|
+ shadowing6 = bat_priv->mcast.querier_ipv6.shadowing ? '6' : '.';
|
|
|
|
+ } else {
|
|
|
|
+ querier4 = '?';
|
|
|
|
+ querier6 = '?';
|
|
|
|
+ shadowing4 = '?';
|
|
|
|
+ shadowing6 = '?';
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ seq_printf(seq, "Multicast flags (own flags: [%c%c%c])\n",
|
|
|
|
+ (flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES) ? 'U' : '.',
|
|
|
|
+ (flags & BATADV_MCAST_WANT_ALL_IPV4) ? '4' : '.',
|
|
|
|
+ (flags & BATADV_MCAST_WANT_ALL_IPV6) ? '6' : '.');
|
|
|
|
+ seq_printf(seq, "* Bridged [U]\t\t\t\t%c\n", bridged ? 'U' : '.');
|
|
|
|
+ seq_printf(seq, "* No IGMP/MLD Querier [4/6]:\t\t%c/%c\n",
|
|
|
|
+ querier4, querier6);
|
|
|
|
+ seq_printf(seq, "* Shadowing IGMP/MLD Querier [4/6]:\t%c/%c\n",
|
|
|
|
+ shadowing4, shadowing6);
|
|
|
|
+ seq_puts(seq, "-------------------------------------------\n");
|
|
|
|
+ seq_printf(seq, " %-10s %s\n", "Originator", "Flags");
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * batadv_mcast_flags_seq_print_text - print the mcast flags of other nodes
|
|
|
|
+ * @seq: seq file to print on
|
|
|
|
+ * @offset: not used
|
|
|
|
+ *
|
|
|
|
+ * This prints a table of (primary) originators and their according
|
|
|
|
+ * multicast flags, including (in the header) our own.
|
|
|
|
+ *
|
|
|
|
+ * Return: always 0
|
|
|
|
+ */
|
|
|
|
+int batadv_mcast_flags_seq_print_text(struct seq_file *seq, void *offset)
|
|
|
|
+{
|
|
|
|
+ struct net_device *net_dev = (struct net_device *)seq->private;
|
|
|
|
+ struct batadv_priv *bat_priv = netdev_priv(net_dev);
|
|
|
|
+ struct batadv_hard_iface *primary_if;
|
|
|
|
+ struct batadv_hashtable *hash = bat_priv->orig_hash;
|
|
|
|
+ struct batadv_orig_node *orig_node;
|
|
|
|
+ struct hlist_head *head;
|
|
|
|
+ u8 flags;
|
|
|
|
+ u32 i;
|
|
|
|
+
|
|
|
|
+ primary_if = batadv_seq_print_text_primary_if_get(seq);
|
|
|
|
+ if (!primary_if)
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ batadv_mcast_flags_print_header(bat_priv, seq);
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < hash->size; i++) {
|
|
|
|
+ head = &hash->table[i];
|
|
|
|
+
|
|
|
|
+ rcu_read_lock();
|
|
|
|
+ hlist_for_each_entry_rcu(orig_node, head, hash_entry) {
|
|
|
|
+ if (!test_bit(BATADV_ORIG_CAPA_HAS_MCAST,
|
|
|
|
+ &orig_node->capa_initialized))
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ if (!test_bit(BATADV_ORIG_CAPA_HAS_MCAST,
|
|
|
|
+ &orig_node->capabilities)) {
|
|
|
|
+ seq_printf(seq, "%pM -\n", orig_node->orig);
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ flags = orig_node->mcast_flags;
|
|
|
|
+
|
|
|
|
+ seq_printf(seq, "%pM [%c%c%c]\n", orig_node->orig,
|
|
|
|
+ (flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES)
|
|
|
|
+ ? 'U' : '.',
|
|
|
|
+ (flags & BATADV_MCAST_WANT_ALL_IPV4)
|
|
|
|
+ ? '4' : '.',
|
|
|
|
+ (flags & BATADV_MCAST_WANT_ALL_IPV6)
|
|
|
|
+ ? '6' : '.');
|
|
|
|
+ }
|
|
|
|
+ rcu_read_unlock();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ batadv_hardif_put(primary_if);
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* batadv_mcast_free - free the multicast optimizations structures
|
|
* batadv_mcast_free - free the multicast optimizations structures
|
|
* @bat_priv: the bat priv with all the soft interface information
|
|
* @bat_priv: the bat priv with all the soft interface information
|
|
*/
|
|
*/
|
|
void batadv_mcast_free(struct batadv_priv *bat_priv)
|
|
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_tvlv_container_unregister(bat_priv, BATADV_TVLV_MCAST, 2);
|
|
|
|
+ batadv_tvlv_handler_unregister(bat_priv, BATADV_TVLV_MCAST, 2);
|
|
|
|
|
|
spin_lock_bh(&bat_priv->tt.commit_lock);
|
|
spin_lock_bh(&bat_priv->tt.commit_lock);
|
|
batadv_mcast_mla_tt_retract(bat_priv, NULL);
|
|
batadv_mcast_mla_tt_retract(bat_priv, NULL);
|