From patchwork Sun May 26 16:48:28 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Linus_L=C3=BCssing?= X-Patchwork-Id: 17931 X-Patchwork-Delegate: sw@simonwunderlich.de Return-Path: X-Original-To: patchwork@open-mesh.org Delivered-To: patchwork@open-mesh.org Received: from open-mesh.org (localhost [IPv6:::1]) by open-mesh.org (Postfix) with ESMTP id 3D472820B4; Sun, 26 May 2019 18:48:48 +0200 (CEST) Received: from mail.aperture-lab.de (mail.aperture-lab.de [138.201.29.205]) by open-mesh.org (Postfix) with ESMTPS id C988B80901 for ; Sun, 26 May 2019 18:48:45 +0200 (CEST) From: =?utf-8?q?Linus_L=C3=BCssing?= DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=c0d3.blue; s=2018; t=1558889325; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=mvMYqhmMBR4cxigy5vXahD6G13Qrc27Cq4UCAfaPB3o=; b=h/LSygIBvyd8Do7QD/E6u1ZeAtFS4AYWxFrL96VFiIZEsaPWuG94H6Vvw2rdbVU2Od7MJI 4mNFCATlT/p9ehjU4wTmG/ZVquLGaVO+8DSFJXo6HB94JcXSNX/CRkuuCDdXhZz+VAkdSH ZsTzYmLqVqDIfZgp8UbI0hUXIMv9Iuy31N/roqOL0yb8hqDlsjsP9sIHoN1xueLAxwcWPM g16+7/Lez3LdS28hp9CVtdC5ANMcUem1UEZT0BqbzPmocdaSQKkyJdDbAQvQdp2Y/tXX4f +u087UVCfP3+0/pp2VlTA1qXbs/8uLjR2NhSJ3EQaip2jkBJKFAeCQeJPDPTCA== To: b.a.t.m.a.n@lists.open-mesh.org Subject: [PATCH v3 1/2] batman-adv: mcast: detect, distribute and maintain multicast router presence Date: Sun, 26 May 2019 18:48:28 +0200 Message-Id: <20190526164829.4247-2-linus.luessing@c0d3.blue> In-Reply-To: <20190526164829.4247-1-linus.luessing@c0d3.blue> References: <20190526164829.4247-1-linus.luessing@c0d3.blue> MIME-Version: 1.0 ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=c0d3.blue; s=2018; t=1558889325; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=mvMYqhmMBR4cxigy5vXahD6G13Qrc27Cq4UCAfaPB3o=; b=rejIS5DKPdIZDcQvTqtvz4ZnUUIWHDIVajOxaj5YSkwm4/U0R7+tuWacSIaD4nNWC582uR XCpH95HUrJTOVZrnk//695NNF09OqX7L7xk0gLnUJO4HJdiRHVnIK3CFLtRR4C6bvM8jKy Eio9iMHopn3au767ExLTNRjRwWSIqUK0Z92ouCQZSAfKQgLmNXNeXR4T070XYT93rGmrue fHWFWR0aVfj2xkSFH1zRdSLHH/omPzJg+0CdHar2Ds8XG5KCt14BsCFVo3zEW57uHOImr5 DxPo1oC8VL9qgnhRR06xt2Wo4TVjNRQvXja+dDJeaS/eZn4uyTlXO/i0HVB92A== ARC-Seal: i=1; s=2018; d=c0d3.blue; t=1558889325; a=rsa-sha256; cv=none; b=tCGtnZXc1CrIzDfRs/UQqkeNLV5pkLDFojAHHBkik8dhmbra0kKF7mbyjcjgrPZHQJ8SH9 WRn4JcUk4gRKC8XHE+9aVhzpHwX845LCDBalItAqoN3VHYvVxMtM4eU/ee+r9pJERxiTgT xs6JM+BulaDLw5O3boXm0lkuee/jl6K/E74NTL/FaY2v/DP0jsLsovt3tQBO1TyT+etytd qQxNVM+vhl880vmfP+TWx9esPrJOzG3usS/u44sLTG64YL4XvZ1yr2u1f0zevTAgsF43rk 6lGxzeCSJ1l9yPzNFnRMS3LfVHaC9fyvv8lxbM7oRlfHMnTuCRw0MVY/6o9ZZQ== ARC-Authentication-Results: i=1; ORIGINATING; auth=pass smtp.auth=linus.luessing@c0d3.blue smtp.mailfrom=linus.luessing@c0d3.blue Authentication-Results: ORIGINATING; auth=pass smtp.auth=linus.luessing@c0d3.blue smtp.mailfrom=linus.luessing@c0d3.blue X-BeenThere: b.a.t.m.a.n@lists.open-mesh.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: The list for a Better Approach To Mobile Ad-hoc Networking List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: The list for a Better Approach To Mobile Ad-hoc Networking Errors-To: b.a.t.m.a.n-bounces@lists.open-mesh.org Sender: "B.A.T.M.A.N" To be able to apply our group aware multicast optimizations to packets with a scope greater than link-local we need to not only keep track of multicast listeners but also multicast routers. With this patch a node detects the presence of multicast routers on its segment by checking if /proc/sys/net/ipv{4,6}/conf//mc_forwarding is set for one thing. This option is enabled by multicast routing daemons and needed for the kernel's multicast routing tables to receive and route packets. For another thing if a bridge is configured on top of bat0 then the presence of an IPv6 multicast router behind this bridge is currently detected by checking for an IPv6 multicast "All Routers Address" (ff02::2). This should later be replaced by querying the bridge, which performs proper, RFC4286 compliant Multicast Router Discovery (our simplified approach includes more hosts than necessary, most notably not just multicast routers but also unicast ones and is not applicable for IPv4). If a multicast router is detected then this is signalized via the new BATADV_MCAST_WANT_ALL_RTR4 and BATADV_MCAST_WANT_ALL_RTR6 multicast tvlv flags (which are sent flipped over the wire for backwards compatibility - so that old nodes will always have them "set"). Signed-off-by: Linus Lüssing --- include/uapi/linux/batadv_packet.h | 8 + net/batman-adv/multicast.c | 414 +++++++++++++++++++++++++---- net/batman-adv/types.h | 29 ++ 3 files changed, 400 insertions(+), 51 deletions(-) diff --git a/include/uapi/linux/batadv_packet.h b/include/uapi/linux/batadv_packet.h index 4ebc2135..a38ce796 100644 --- a/include/uapi/linux/batadv_packet.h +++ b/include/uapi/linux/batadv_packet.h @@ -107,12 +107,20 @@ enum batadv_icmp_packettype { * @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 + * (both link-local and routeable ones) * @BATADV_MCAST_WANT_ALL_IPV6: we want all IPv6 multicast packets + * (both link-local and routable ones) + * @BATADV_MCAST_WANT_ALL_RTR4: we want all routeable IPv4 multicast packets + * (send flipped on the wire) + * @BATADV_MCAST_WANT_ALL_RTR6: we want all routeable IPv6 multicast packets + * (send flipped on the wire) */ enum batadv_mcast_flags { BATADV_MCAST_WANT_ALL_UNSNOOPABLES = 1UL << 0, BATADV_MCAST_WANT_ALL_IPV4 = 1UL << 1, BATADV_MCAST_WANT_ALL_IPV6 = 1UL << 2, + BATADV_MCAST_WANT_ALL_RTR4 = 1UL << 3, + BATADV_MCAST_WANT_ALL_RTR6 = 1UL << 4, }; /* tt data subtypes */ diff --git a/net/batman-adv/multicast.c b/net/batman-adv/multicast.c index 5940c2df..c170c08f 100644 --- a/net/batman-adv/multicast.c +++ b/net/batman-adv/multicast.c @@ -73,26 +73,200 @@ static void batadv_mcast_start_timer(struct batadv_priv *bat_priv) } /** - * batadv_mcast_has_bridge() - check whether the soft-iface is bridged - * @bat_priv: the bat priv with all the soft interface information + * batadv_mcast_get_bridge() - get the bridge on top of the softif if it exists + * @soft_iface: netdev struct of the mesh interface * - * Checks whether there is a bridge on top of our soft interface. + * If the given soft interface has a bridge on top then the refcount + * of the according net device is increased. * - * Return: true if there is a bridge, false otherwise. + * Return: NULL if no such bridge exists. Otherwise the net device of the + * bridge. */ -static bool batadv_mcast_has_bridge(struct batadv_priv *bat_priv) +static struct net_device *batadv_mcast_get_bridge(struct net_device *soft_iface) { - struct net_device *upper = bat_priv->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_rtr_flags_softif_get_ipv4() - get mcast router flags from + * node for IPv4 + * @dev: the interface to check + * + * Checks the presence of an IPv4 multicast router on this node. + * + * Caller needs to hold rcu read lock. + * + * Return: BATADV_MCAST_WANT_ALL_RTR4 if present, BATADV_NO_FLAGS otherwise. + */ +static u8 batadv_mcast_mla_rtr_flags_softif_get_ipv4(struct net_device *dev) +{ + struct in_device *in_dev = __in_dev_get_rcu(dev); + + if (in_dev && IN_DEV_MFORWARD(in_dev)) + return BATADV_MCAST_WANT_ALL_RTR4; + else + return BATADV_NO_FLAGS; +} + +/** + * batadv_mcast_mla_rtr_flags_softif_get_ipv6() - get mcast router flags from + * node for IPv6 + * @dev: the interface to check + * + * Checks the presence of an IPv6 multicast router on this node. + * + * Caller needs to hold rcu read lock. + * + * Return: BATADV_MCAST_WANT_ALL_RTR6 if present, BATADV_NO_FLAGS otherwise. + */ +#if IS_ENABLED(CONFIG_IPV6_MROUTE) +static u8 batadv_mcast_mla_rtr_flags_softif_get_ipv6(struct net_device *dev) +{ + struct inet6_dev *in6_dev = __in6_dev_get(dev); + + if (in6_dev && in6_dev->cnf.mc_forwarding) + return BATADV_MCAST_WANT_ALL_RTR6; + else + return BATADV_NO_FLAGS; +} +#else +static inline u8 +batadv_mcast_mla_rtr_flags_softif_get_ipv6(struct net_device *dev) +{ + return BATADV_NO_FLAGS; +} +#endif + +/** + * batadv_mcast_mla_rtr_flags_softif_get() - get mcast router flags from node + * @bat_priv: the bat priv with all the soft interface information + * @bridge: bridge interface on top of the soft_iface if present, + * otherwise pass NULL + * + * Checks the presence of IPv4 and IPv6 multicast routers on this + * node. + * + * Return: + * BATADV_NO_FLAGS: no multicast router is present + * BATADV_MCAST_WANT_ALL_RTR4: An IPv4 multicast router is present + * BATADV_MCAST_WANT_ALL_RTR6: An IPv6 multicast router is present + * The former two OR'd: Both an IPv4 and IPv6 multicast router is present + */ +static u8 batadv_mcast_mla_rtr_flags_softif_get(struct batadv_priv *bat_priv, + struct net_device *bridge) +{ + struct net_device *dev = bridge ? bridge : bat_priv->soft_iface; + u8 flags = BATADV_NO_FLAGS; + + rcu_read_lock(); + + flags |= batadv_mcast_mla_rtr_flags_softif_get_ipv4(dev); + flags |= batadv_mcast_mla_rtr_flags_softif_get_ipv6(dev); + + rcu_read_unlock(); + + return flags; +} + +/** + * batadv_mcast_mla_rtr_flags_bridge_get() - get mcast router flags from bridge + * @bat_priv: the bat priv with all the soft interface information + * @bridge: bridge interface on top of the soft_iface if present, + * otherwise pass NULL + * + * Checks the presence of IPv4 and IPv6 multicast routers behind a bridge. + * + * Return: + * BATADV_NO_FLAGS: no multicast router is present + * BATADV_MCAST_WANT_ALL_RTR4: An IPv4 multicast router is present + * BATADV_MCAST_WANT_ALL_RTR6: An IPv6 multicast router is present + * The former two OR'd: Both an IPv4 and IPv6 multicast router is present + */ +#if IS_ENABLED(CONFIG_IPV6) +static u8 batadv_mcast_mla_rtr_flags_bridge_get(struct batadv_priv *bat_priv, + struct net_device *bridge) +{ + struct list_head bridge_mcast_list = LIST_HEAD_INIT(bridge_mcast_list); + struct net_device *dev = bat_priv->soft_iface; + u8 flags = BATADV_MCAST_WANT_ALL_RTR4; + struct br_ip_list *br_ip_entry, *tmp; + int ret; + + if (!bridge) + return BATADV_NO_FLAGS; + + /* TODO: ask the bridge if a multicast router is present (the bridge + * is capable of performing proper RFC4286 multicast multicast router + * discovery) instead of searching for a ff02::2 listener here + */ + ret = br_multicast_list_adjacent(dev, &bridge_mcast_list); + if (ret < 0) + return flags | BATADV_MCAST_WANT_ALL_RTR6; + + list_for_each_entry_safe(br_ip_entry, tmp, &bridge_mcast_list, list) { + /* the bridge snooping does not maintain IPv4 link-local + * addresses - therefore we won't find any IPv4 multicast router + * address here, only IPv6 ones + */ + if (br_ip_entry->addr.proto == htons(ETH_P_IPV6) && + ipv6_addr_is_ll_all_routers(&br_ip_entry->addr.u.ip6)) + flags |= BATADV_MCAST_WANT_ALL_RTR6; + + list_del(&br_ip_entry->list); + kfree(br_ip_entry); + } + + return flags; +} +#else +static inline u8 +batadv_mcast_mla_rtr_flags_bridge_get(struct batadv_priv *bat_priv, + struct net_device *bridge) +{ + if (bridge) + return BATADV_MCAST_WANT_ALL_RTR4 | BATADV_MCAST_WANT_ALL_RTR6; + else + return BATADV_NO_FLAGS; +} +#endif + +/** + * batadv_mcast_mla_rtr_flags_get() - get multicast router flags + * @bat_priv: the bat priv with all the soft interface information + * @bridge: bridge interface on top of the soft_iface if present, + * otherwise pass NULL + * + * Checks the presence of IPv4 and IPv6 multicast routers on this + * node or behind its bridge. + * + * Return: + * BATADV_NO_FLAGS: no multicast router is present + * BATADV_MCAST_WANT_ALL_RTR4: An IPv4 multicast router is present + * BATADV_MCAST_WANT_ALL_RTR6: An IPv6 multicast router is present + * The former two OR'd: Both an IPv4 and IPv6 multicast router is present + */ +static u8 batadv_mcast_mla_rtr_flags_get(struct batadv_priv *bat_priv, + struct net_device *bridge) +{ + u8 flags = BATADV_NO_FLAGS; + + flags |= batadv_mcast_mla_rtr_flags_softif_get(bat_priv, bridge); + flags |= batadv_mcast_mla_rtr_flags_bridge_get(bat_priv, bridge); + + return flags; +} + /** * batadv_mcast_mla_flags_get() - get the new multicast flags * @bat_priv: the bat priv with all the soft interface information @@ -106,13 +280,20 @@ batadv_mcast_mla_flags_get(struct batadv_priv *bat_priv) struct net_device *dev = bat_priv->soft_iface; struct batadv_mcast_querier_state *qr4, *qr6; struct batadv_mcast_mla_flags mla_flags; + struct net_device *bridge; + + bridge = batadv_mcast_get_bridge(dev); memset(&mla_flags, 0, sizeof(mla_flags)); mla_flags.enabled = 1; + mla_flags.tvlv_flags |= batadv_mcast_mla_rtr_flags_get(bat_priv, + bridge); - if (!batadv_mcast_has_bridge(bat_priv)) + if (!bridge) return mla_flags; + dev_put(bridge); + mla_flags.bridged = 1; qr4 = &mla_flags.querier_ipv4; qr6 = &mla_flags.querier_ipv6; @@ -146,32 +327,6 @@ batadv_mcast_mla_flags_get(struct batadv_priv *bat_priv) return mla_flags; } -/** - * 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_is_duplicate() - check whether an address is in a list * @mcast_addr: the multicast address to check @@ -231,6 +386,10 @@ batadv_mcast_mla_softif_get_ipv4(struct net_device *dev, ipv4_is_local_multicast(pmc->multiaddr)) continue; + if (flags->tvlv_flags & BATADV_MCAST_WANT_ALL_RTR4 && + !ipv4_is_local_multicast(pmc->multiaddr)) + continue; + ip_eth_mc_map(pmc->multiaddr, mcast_addr); if (batadv_mcast_mla_is_duplicate(mcast_addr, mcast_list)) @@ -295,6 +454,11 @@ batadv_mcast_mla_softif_get_ipv6(struct net_device *dev, ipv6_addr_is_ll_all_nodes(&pmc6->mca_addr)) continue; + if (flags->tvlv_flags & BATADV_MCAST_WANT_ALL_RTR6 && + IPV6_ADDR_MC_SCOPE(&pmc6->mca_addr) > + IPV6_ADDR_SCOPE_LINKLOCAL) + continue; + ipv6_eth_mc_map(&pmc6->mca_addr, mcast_addr); if (batadv_mcast_mla_is_duplicate(mcast_addr, mcast_list)) @@ -436,6 +600,10 @@ static int batadv_mcast_mla_bridge_get(struct net_device *dev, if (tvlv_flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES && ipv4_is_local_multicast(br_ip_entry->addr.u.ip4)) continue; + + if (tvlv_flags & BATADV_MCAST_WANT_ALL_RTR4 && + !ipv4_is_local_multicast(br_ip_entry->addr.u.ip4)) + continue; } #if IS_ENABLED(CONFIG_IPV6) @@ -446,6 +614,11 @@ static int batadv_mcast_mla_bridge_get(struct net_device *dev, if (tvlv_flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES && ipv6_addr_is_ll_all_nodes(&br_ip_entry->addr.u.ip6)) continue; + + if (tvlv_flags & BATADV_MCAST_WANT_ALL_RTR6 && + IPV6_ADDR_MC_SCOPE(&br_ip_entry->addr.u.ip6) > + IPV6_ADDR_SCOPE_LINKLOCAL) + continue; } #endif @@ -656,19 +829,23 @@ static void batadv_mcast_flags_log(struct batadv_priv *bat_priv, u8 flags) { bool old_enabled = bat_priv->mcast.mla_flags.enabled; u8 old_flags = bat_priv->mcast.mla_flags.tvlv_flags; - char str_old_flags[] = "[...]"; + char str_old_flags[] = "[.... . ]"; - sprintf(str_old_flags, "[%c%c%c]", + sprintf(str_old_flags, "[%c%c%c%s%s]", (old_flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES) ? 'U' : '.', (old_flags & BATADV_MCAST_WANT_ALL_IPV4) ? '4' : '.', - (old_flags & BATADV_MCAST_WANT_ALL_IPV6) ? '6' : '.'); + (old_flags & BATADV_MCAST_WANT_ALL_IPV6) ? '6' : '.', + (old_flags & BATADV_MCAST_WANT_ALL_RTR4) ? "R4" : ". ", + (old_flags & BATADV_MCAST_WANT_ALL_RTR6) ? "R6" : ". "); batadv_dbg(BATADV_DBG_MCAST, bat_priv, - "Changing multicast flags from '%s' to '[%c%c%c]'\n", + "Changing multicast flags from '%s' to '[%c%c%c%s%s]'\n", old_enabled ? str_old_flags : "", (flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES) ? 'U' : '.', (flags & BATADV_MCAST_WANT_ALL_IPV4) ? '4' : '.', - (flags & BATADV_MCAST_WANT_ALL_IPV6) ? '6' : '.'); + (flags & BATADV_MCAST_WANT_ALL_IPV6) ? '6' : '.', + (flags & BATADV_MCAST_WANT_ALL_RTR4) ? "R4" : ". ", + (flags & BATADV_MCAST_WANT_ALL_RTR6) ? "R6" : ". "); } /** @@ -691,7 +868,12 @@ batadv_mcast_mla_flags_update(struct batadv_priv *bat_priv, batadv_mcast_bridge_log(bat_priv, flags); batadv_mcast_flags_log(bat_priv, flags->tvlv_flags); + /* toggle WANT_ALL_RTR flags as they are sent flipped on the + * wire for backwards compatibility + */ mcast_data.flags = flags->tvlv_flags; + mcast_data.flags ^= BATADV_MCAST_WANT_ALL_RTR4; + mcast_data.flags ^= BATADV_MCAST_WANT_ALL_RTR6; memset(mcast_data.reserved, 0, sizeof(mcast_data.reserved)); batadv_tvlv_container_register(bat_priv, BATADV_TVLV_MCAST, 2, @@ -1460,6 +1642,132 @@ static void batadv_mcast_want_ipv6_update(struct batadv_priv *bat_priv, } } +/** + * batadv_mcast_want_rtr4_update() - update want-all-rtr4 counter and list + * @bat_priv: the bat priv with all the soft interface information + * @orig: the orig_node which multicast state might have changed of + * @mcast_flags: flags indicating the new multicast state + * + * If the BATADV_MCAST_WANT_ALL_RTR4 flag of this originator, orig, has + * toggled then this method updates counter and list accordingly. + * + * Caller needs to hold orig->mcast_handler_lock. + */ +static void batadv_mcast_want_rtr4_update(struct batadv_priv *bat_priv, + struct batadv_orig_node *orig, + u8 mcast_flags) +{ + struct hlist_node *node = &orig->mcast_want_all_rtr4_node; + struct hlist_head *head = &bat_priv->mcast.want_all_rtr4_list; + + lockdep_assert_held(&orig->mcast_handler_lock); + + /* switched from flag unset to set */ + if (mcast_flags & BATADV_MCAST_WANT_ALL_RTR4 && + !(orig->mcast_flags & BATADV_MCAST_WANT_ALL_RTR4)) { + atomic_inc(&bat_priv->mcast.num_want_all_rtr4); + + spin_lock_bh(&bat_priv->mcast.want_lists_lock); + /* flag checks above + mcast_handler_lock prevents this */ + WARN_ON(!hlist_unhashed(node)); + + hlist_add_head_rcu(node, head); + spin_unlock_bh(&bat_priv->mcast.want_lists_lock); + /* switched from flag set to unset */ + } else if (!(mcast_flags & BATADV_MCAST_WANT_ALL_RTR4) && + orig->mcast_flags & BATADV_MCAST_WANT_ALL_RTR4) { + atomic_dec(&bat_priv->mcast.num_want_all_rtr4); + + spin_lock_bh(&bat_priv->mcast.want_lists_lock); + /* flag checks above + mcast_handler_lock prevents this */ + WARN_ON(hlist_unhashed(node)); + + hlist_del_init_rcu(node); + spin_unlock_bh(&bat_priv->mcast.want_lists_lock); + } +} + +/** + * batadv_mcast_want_rtr6_update() - update want-all-rtr6 counter and list + * @bat_priv: the bat priv with all the soft interface information + * @orig: the orig_node which multicast state might have changed of + * @mcast_flags: flags indicating the new multicast state + * + * If the BATADV_MCAST_WANT_ALL_RTR6 flag of this originator, orig, has + * toggled then this method updates counter and list accordingly. + * + * Caller needs to hold orig->mcast_handler_lock. + */ +static void batadv_mcast_want_rtr6_update(struct batadv_priv *bat_priv, + struct batadv_orig_node *orig, + u8 mcast_flags) +{ + struct hlist_node *node = &orig->mcast_want_all_rtr6_node; + struct hlist_head *head = &bat_priv->mcast.want_all_rtr6_list; + + lockdep_assert_held(&orig->mcast_handler_lock); + + /* switched from flag unset to set */ + if (mcast_flags & BATADV_MCAST_WANT_ALL_RTR6 && + !(orig->mcast_flags & BATADV_MCAST_WANT_ALL_RTR6)) { + atomic_inc(&bat_priv->mcast.num_want_all_rtr6); + + spin_lock_bh(&bat_priv->mcast.want_lists_lock); + /* flag checks above + mcast_handler_lock prevents this */ + WARN_ON(!hlist_unhashed(node)); + + hlist_add_head_rcu(node, head); + spin_unlock_bh(&bat_priv->mcast.want_lists_lock); + /* switched from flag set to unset */ + } else if (!(mcast_flags & BATADV_MCAST_WANT_ALL_RTR6) && + orig->mcast_flags & BATADV_MCAST_WANT_ALL_RTR6) { + atomic_dec(&bat_priv->mcast.num_want_all_rtr6); + + spin_lock_bh(&bat_priv->mcast.want_lists_lock); + /* flag checks above + mcast_handler_lock prevents this */ + WARN_ON(hlist_unhashed(node)); + + hlist_del_init_rcu(node); + spin_unlock_bh(&bat_priv->mcast.want_lists_lock); + } +} + +/** + * batadv_mcast_tvlv_flags_get() - get multicast flags from an OGM TVLV + * @enabled: whether the originator has multicast TVLV support enabled + * @tvlv_value: tvlv buffer containing the multicast flags + * @tvlv_value_len: tvlv buffer length + * + * Return: multicast flags for the given tvlv buffer + */ +static u8 +batadv_mcast_tvlv_flags_get(bool enabled, void *tvlv_value, u16 tvlv_value_len) +{ + u8 mcast_flags = BATADV_NO_FLAGS; + + if (enabled && tvlv_value && tvlv_value_len >= sizeof(mcast_flags)) + mcast_flags = *(u8 *)tvlv_value; + + if (!enabled) { + mcast_flags |= BATADV_MCAST_WANT_ALL_IPV4; + mcast_flags |= BATADV_MCAST_WANT_ALL_IPV6; + } + + /* unflip: WANT_ALL_RTR flags are sent flipped on the wire for + * backwards compatibiltiy + */ + mcast_flags ^= BATADV_MCAST_WANT_ALL_RTR4 | BATADV_MCAST_WANT_ALL_RTR6; + + /* remove redundant flags to avoid sending duplicate packets later */ + if (mcast_flags & BATADV_MCAST_WANT_ALL_IPV4) + mcast_flags &= ~BATADV_MCAST_WANT_ALL_RTR4; + + if (mcast_flags & BATADV_MCAST_WANT_ALL_IPV6) + mcast_flags &= ~BATADV_MCAST_WANT_ALL_RTR6; + + return mcast_flags; +} + /** * batadv_mcast_tvlv_ogm_handler() - process incoming multicast tvlv container * @bat_priv: the bat priv with all the soft interface information @@ -1475,16 +1783,10 @@ static void batadv_mcast_tvlv_ogm_handler(struct batadv_priv *bat_priv, u16 tvlv_value_len) { bool orig_mcast_enabled = !(flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND); - u8 mcast_flags = BATADV_NO_FLAGS; + u8 mcast_flags; - if (orig_mcast_enabled && tvlv_value && - tvlv_value_len >= sizeof(mcast_flags)) - mcast_flags = *(u8 *)tvlv_value; - - if (!orig_mcast_enabled) { - mcast_flags |= BATADV_MCAST_WANT_ALL_IPV4; - mcast_flags |= BATADV_MCAST_WANT_ALL_IPV6; - } + mcast_flags = batadv_mcast_tvlv_flags_get(orig_mcast_enabled, + tvlv_value, tvlv_value_len); spin_lock_bh(&orig->mcast_handler_lock); @@ -1501,6 +1803,8 @@ static void batadv_mcast_tvlv_ogm_handler(struct batadv_priv *bat_priv, batadv_mcast_want_unsnoop_update(bat_priv, orig, mcast_flags); batadv_mcast_want_ipv4_update(bat_priv, orig, mcast_flags); batadv_mcast_want_ipv6_update(bat_priv, orig, mcast_flags); + batadv_mcast_want_rtr4_update(bat_priv, orig, mcast_flags); + batadv_mcast_want_rtr6_update(bat_priv, orig, mcast_flags); orig->mcast_flags = mcast_flags; spin_unlock_bh(&orig->mcast_handler_lock); @@ -1550,10 +1854,12 @@ static void batadv_mcast_flags_print_header(struct batadv_priv *bat_priv, shadowing6 = '?'; } - seq_printf(seq, "Multicast flags (own flags: [%c%c%c])\n", + seq_printf(seq, "Multicast flags (own flags: [%c%c%c%s%s])\n", (flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES) ? 'U' : '.', (flags & BATADV_MCAST_WANT_ALL_IPV4) ? '4' : '.', - (flags & BATADV_MCAST_WANT_ALL_IPV6) ? '6' : '.'); + (flags & BATADV_MCAST_WANT_ALL_IPV6) ? '6' : '.', + (flags & BATADV_MCAST_WANT_ALL_RTR4) ? "R4" : ". ", + (flags & BATADV_MCAST_WANT_ALL_RTR6) ? "R6" : ". "); 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); @@ -1607,13 +1913,17 @@ int batadv_mcast_flags_seq_print_text(struct seq_file *seq, void *offset) flags = orig_node->mcast_flags; - seq_printf(seq, "%pM [%c%c%c]\n", orig_node->orig, + seq_printf(seq, "%pM [%c%c%c%s%s]\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' : '.'); + ? '6' : '.', + (flags & BATADV_MCAST_WANT_ALL_RTR4) + ? "R4" : ". ", + (flags & BATADV_MCAST_WANT_ALL_RTR6) + ? "R6" : ". "); } rcu_read_unlock(); } @@ -1887,6 +2197,8 @@ void batadv_mcast_purge_orig(struct batadv_orig_node *orig) batadv_mcast_want_unsnoop_update(bat_priv, orig, BATADV_NO_FLAGS); batadv_mcast_want_ipv4_update(bat_priv, orig, BATADV_NO_FLAGS); batadv_mcast_want_ipv6_update(bat_priv, orig, BATADV_NO_FLAGS); + batadv_mcast_want_rtr4_update(bat_priv, orig, BATADV_NO_FLAGS); + batadv_mcast_want_rtr6_update(bat_priv, orig, BATADV_NO_FLAGS); spin_unlock_bh(&orig->mcast_handler_lock); } diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 72f65b37..ba159d4e 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -404,6 +404,17 @@ struct batadv_orig_node { * list */ struct hlist_node mcast_want_all_ipv6_node; + + /** + * @mcast_want_all_rtr4_node: a list node for the mcast.want_all_rtr4 + * list + */ + struct hlist_node mcast_want_all_rtr4_node; + /** + * @mcast_want_all_rtr6_node: a list node for the mcast.want_all_rtr6 + * list + */ + struct hlist_node mcast_want_all_rtr6_node; #endif /** @capabilities: announced capabilities of this originator */ @@ -1218,6 +1229,18 @@ struct batadv_priv_mcast { */ struct hlist_head want_all_ipv6_list; + /** + * @want_all_rtr4_list: a list of orig_nodes wanting all routeable IPv4 + * multicast traffic + */ + struct hlist_head want_all_rtr4_list; + + /** + * @want_all_rtr6_list: a list of orig_nodes wanting all routeable IPv6 + * multicast traffic + */ + struct hlist_head want_all_rtr6_list; + /** * @mla_flags: flags for the querier, bridge and tvlv state */ @@ -1240,6 +1263,12 @@ struct batadv_priv_mcast { /** @num_want_all_ipv6: counter for items in want_all_ipv6_list */ atomic_t num_want_all_ipv6; + /** @num_want_all_rtr4: counter for items in want_all_rtr4_list */ + atomic_t num_want_all_rtr4; + + /** @num_want_all_rtr6: counter for items in want_all_rtr6_list */ + atomic_t num_want_all_rtr6; + /** * @want_lists_lock: lock for protecting modifications to mcasts * want_all_{unsnoopables,ipv4,ipv6}_list (traversals are rcu-locked) From patchwork Sun May 26 16:48:29 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Linus_L=C3=BCssing?= X-Patchwork-Id: 17932 X-Patchwork-Delegate: sw@simonwunderlich.de Return-Path: X-Original-To: patchwork@open-mesh.org Delivered-To: patchwork@open-mesh.org Received: from open-mesh.org (localhost [IPv6:::1]) by open-mesh.org (Postfix) with ESMTP id 96BE382696; Sun, 26 May 2019 18:48:49 +0200 (CEST) Received: from mail.aperture-lab.de (mail.aperture-lab.de [IPv6:2a01:4f8:171:314c::100:a1]) by open-mesh.org (Postfix) with ESMTPS id 1072C81F95 for ; Sun, 26 May 2019 18:48:47 +0200 (CEST) From: =?utf-8?q?Linus_L=C3=BCssing?= DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=c0d3.blue; s=2018; t=1558889326; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=lJs3TGKe37MakQjVwY9ZmATVM4h9rQqkUXQP+3DKrl4=; b=WlpBUgcV+GB/U8bJpSoFV/AXqMkh9fu///ERl/HhbAVO67P+v/WGJTJsTtRfa6qfTg3fAm xIJmXB3qH0VuFpgchC4TWOjIOOaGbLzigqnB3tU/L7ew5rN9XBhWUZPNYCoYTMMWwmQIyL lc6c14hP+E0Nt81YStQuqkrCllP//OykWncRcJR6WFvJ+IRp0azuimF+QHYwU1YCVzH/Xv XjcmLTRrwkSNwhkFswz+HARai2j2k2dxRALozPimuU1OOY7O1ZoNY/Q/EUZnj0sknMTl8v gB/kZOWSS92ykyRhrDqDXroD7t5d2/GaBrAnYghxAPGe33T1YPrwfNutXrvgZQ== To: b.a.t.m.a.n@lists.open-mesh.org Subject: [PATCH v3 2/2] batman-adv: mcast: apply optimizations for routeable packets, too Date: Sun, 26 May 2019 18:48:29 +0200 Message-Id: <20190526164829.4247-3-linus.luessing@c0d3.blue> In-Reply-To: <20190526164829.4247-1-linus.luessing@c0d3.blue> References: <20190526164829.4247-1-linus.luessing@c0d3.blue> MIME-Version: 1.0 ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=c0d3.blue; s=2018; t=1558889326; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=lJs3TGKe37MakQjVwY9ZmATVM4h9rQqkUXQP+3DKrl4=; b=mhHNddWPqVG/NJx25Q6LpXkqd2aX3gzDBgveJrTyD6y5ROVlXd4Xpyrz4nBLgNIVTLm815 JPXvVi3chQKJbalSOIDd/AtAuEtvEPa9YzUMW70HYPsmUF1WqsjbF8CShMjprD28IkfTau w2QcNjDF57gMJox0YBJojiyHdmbUeNJH0u1/LlOg+GU8Sw+kTt6WYU4cPvoUfbjMCAoDjp otpeZXSkfJJmbuu0JsWdSUsZKjolD6CPsikC8PYqN2x4qoI8xLJS2iIT5Cz86Fa+l+WGDa 3MPbVIWrPzBoTn6ysW7YmQEjCbWpu6Ucn3+z4sld2mx1l5ELf1wH5n6h41KYpw== ARC-Seal: i=1; s=2018; d=c0d3.blue; t=1558889326; a=rsa-sha256; cv=none; b=KVS3rDWU0evZOnSNA0KDuPuz0X8f6RZzGTezmDuQ2A442hrMYd9PwmEqitbiKeguvjuz9O hox/qElDrSIQV91njnQ5gnA0/q2W/ekuH7yRtffV1L0kQUP2flh16Lu29MrUfob33GWF9n zC8pu/3boIjdYzL8SyJYAN4DTkYXYeR6wVWIGWYjcdc92jPLB8ypyyDpQNF/rRkMRsny3L LYSpHn/cGfXD3MueICNezxlBPVwyfpWLYJB6NRaJjTib5G7MLBf8HpvDHgVLKCwa3MKV2i 3tI2jKvyU1SxsPzG+01ELvFDQHFsg3nupwmCjEmXXKTL/JLQzEwl1Nr91dukww== ARC-Authentication-Results: i=1; ORIGINATING; auth=pass smtp.auth=linus.luessing@c0d3.blue smtp.mailfrom=linus.luessing@c0d3.blue Authentication-Results: ORIGINATING; auth=pass smtp.auth=linus.luessing@c0d3.blue smtp.mailfrom=linus.luessing@c0d3.blue X-BeenThere: b.a.t.m.a.n@lists.open-mesh.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: The list for a Better Approach To Mobile Ad-hoc Networking List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: The list for a Better Approach To Mobile Ad-hoc Networking Errors-To: b.a.t.m.a.n-bounces@lists.open-mesh.org Sender: "B.A.T.M.A.N" Now that we not only track the presence of multicast listeners but also multicast routers we can safely apply group-aware multicast-to-unicast forwarding to packets with a destination address of scope greater than link-local as well. Signed-off-by: Linus Lüssing --- net/batman-adv/multicast.c | 259 ++++++++++++++++++++++++++++++++++--- 1 file changed, 241 insertions(+), 18 deletions(-) diff --git a/net/batman-adv/multicast.c b/net/batman-adv/multicast.c index c170c08f..ea341670 100644 --- a/net/batman-adv/multicast.c +++ b/net/batman-adv/multicast.c @@ -975,6 +975,7 @@ static bool batadv_mcast_is_report_ipv4(struct sk_buff *skb) * @bat_priv: the bat priv with all the soft interface information * @skb: the IPv4 packet to check * @is_unsnoopable: stores whether the destination is snoopable + * @is_routeable: stores whether the destination is routeable * * Checks whether the given IPv4 packet has the potential to be forwarded with a * mode more optimal than classic flooding. @@ -984,7 +985,8 @@ static bool batadv_mcast_is_report_ipv4(struct sk_buff *skb) */ static int batadv_mcast_forw_mode_check_ipv4(struct batadv_priv *bat_priv, struct sk_buff *skb, - bool *is_unsnoopable) + bool *is_unsnoopable, + int *is_routeable) { struct iphdr *iphdr; @@ -997,16 +999,13 @@ static int batadv_mcast_forw_mode_check_ipv4(struct batadv_priv *bat_priv, 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; - /* link-local multicast listeners behind a bridge are * not snoopable (see RFC4541, section 2.1.2.2) */ - *is_unsnoopable = true; + if (ipv4_is_local_multicast(iphdr->daddr)) + *is_unsnoopable = true; + else + *is_routeable = ETH_P_IP; return 0; } @@ -1041,6 +1040,7 @@ static bool batadv_mcast_is_report_ipv6(struct sk_buff *skb) * @bat_priv: the bat priv with all the soft interface information * @skb: the IPv6 packet to check * @is_unsnoopable: stores whether the destination is snoopable + * @is_routeable: stores whether the destination is routeable * * Checks whether the given IPv6 packet has the potential to be forwarded with a * mode more optimal than classic flooding. @@ -1049,7 +1049,8 @@ static bool batadv_mcast_is_report_ipv6(struct sk_buff *skb) */ static int batadv_mcast_forw_mode_check_ipv6(struct batadv_priv *bat_priv, struct sk_buff *skb, - bool *is_unsnoopable) + bool *is_unsnoopable, + int *is_routeable) { struct ipv6hdr *ip6hdr; @@ -1062,10 +1063,7 @@ static int batadv_mcast_forw_mode_check_ipv6(struct batadv_priv *bat_priv, 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) + if (IPV6_ADDR_MC_SCOPE(&ip6hdr->daddr) < IPV6_ADDR_SCOPE_LINKLOCAL) return -EINVAL; /* link-local-all-nodes multicast listeners behind a bridge are @@ -1073,6 +1071,8 @@ static int batadv_mcast_forw_mode_check_ipv6(struct batadv_priv *bat_priv, */ if (ipv6_addr_is_ll_all_nodes(&ip6hdr->daddr)) *is_unsnoopable = true; + else if (IPV6_ADDR_MC_SCOPE(&ip6hdr->daddr) > IPV6_ADDR_SCOPE_LINKLOCAL) + *is_routeable = ETH_P_IPV6; return 0; } @@ -1082,6 +1082,7 @@ static int batadv_mcast_forw_mode_check_ipv6(struct batadv_priv *bat_priv, * @bat_priv: the bat priv with all the soft interface information * @skb: the multicast frame to check * @is_unsnoopable: stores whether the destination is snoopable + * @is_routeable: stores whether the destination is routeable * * Checks whether the given multicast ethernet frame has the potential to be * forwarded with a mode more optimal than classic flooding. @@ -1090,7 +1091,8 @@ static int batadv_mcast_forw_mode_check_ipv6(struct batadv_priv *bat_priv, */ static int batadv_mcast_forw_mode_check(struct batadv_priv *bat_priv, struct sk_buff *skb, - bool *is_unsnoopable) + bool *is_unsnoopable, + int *is_routeable) { struct ethhdr *ethhdr = eth_hdr(skb); @@ -1100,13 +1102,15 @@ static int batadv_mcast_forw_mode_check(struct batadv_priv *bat_priv, switch (ntohs(ethhdr->h_proto)) { case ETH_P_IP: return batadv_mcast_forw_mode_check_ipv4(bat_priv, skb, - is_unsnoopable); + is_unsnoopable, + is_routeable); case ETH_P_IPV6: if (!IS_ENABLED(CONFIG_IPV6)) return -EINVAL; return batadv_mcast_forw_mode_check_ipv6(bat_priv, skb, - is_unsnoopable); + is_unsnoopable, + is_routeable); default: return -EINVAL; } @@ -1136,6 +1140,29 @@ static int batadv_mcast_forw_want_all_ip_count(struct batadv_priv *bat_priv, } } +/** + * batadv_mcast_forw_rtr_count() - count nodes with a multicast router + * @bat_priv: the bat priv with all the soft interface information + * @protocol: the ethernet protocol type to count multicast routers for + * + * Return: the number of nodes which want all routeable IPv4 multicast traffic + * if the protocol is ETH_P_IP or the number of nodes which want all routeable + * IPv6 traffic if the protocol is ETH_P_IPV6. Otherwise returns 0. + */ + +static int batadv_mcast_forw_rtr_count(struct batadv_priv *bat_priv, + int protocol) +{ + switch (protocol) { + case ETH_P_IP: + return atomic_read(&bat_priv->mcast.num_want_all_rtr4); + case ETH_P_IPV6: + return atomic_read(&bat_priv->mcast.num_want_all_rtr6); + default: + return 0; + } +} + /** * batadv_mcast_forw_tt_node_get() - get a multicast tt node * @bat_priv: the bat priv with all the soft interface information @@ -1257,6 +1284,84 @@ batadv_mcast_forw_unsnoop_node_get(struct batadv_priv *bat_priv) return orig_node; } +/** + * batadv_mcast_forw_rtr4_node_get() - get a node with an ipv4 mcast router flag + * @bat_priv: the bat priv with all the soft interface information + * + * Return: an orig_node which has the BATADV_MCAST_WANT_ALL_RTR4 flag set and + * increases its refcount. + */ +static struct batadv_orig_node * +batadv_mcast_forw_rtr4_node_get(struct batadv_priv *bat_priv) +{ + struct batadv_orig_node *tmp_orig_node, *orig_node = NULL; + + rcu_read_lock(); + hlist_for_each_entry_rcu(tmp_orig_node, + &bat_priv->mcast.want_all_rtr4_list, + mcast_want_all_rtr4_node) { + if (!kref_get_unless_zero(&tmp_orig_node->refcount)) + continue; + + orig_node = tmp_orig_node; + break; + } + rcu_read_unlock(); + + return orig_node; +} + +/** + * batadv_mcast_forw_rtr6_node_get() - get a node with an ipv6 mcast router flag + * @bat_priv: the bat priv with all the soft interface information + * + * Return: an orig_node which has the BATADV_MCAST_WANT_ALL_RTR6 flag set + * and increases its refcount. + */ +static struct batadv_orig_node * +batadv_mcast_forw_rtr6_node_get(struct batadv_priv *bat_priv) +{ + struct batadv_orig_node *tmp_orig_node, *orig_node = NULL; + + rcu_read_lock(); + hlist_for_each_entry_rcu(tmp_orig_node, + &bat_priv->mcast.want_all_rtr6_list, + mcast_want_all_rtr6_node) { + if (!kref_get_unless_zero(&tmp_orig_node->refcount)) + continue; + + orig_node = tmp_orig_node; + break; + } + rcu_read_unlock(); + + return orig_node; +} + +/** + * batadv_mcast_forw_rtr_node_get() - get a node with an ipv4/ipv6 router flag + * @bat_priv: the bat priv with all the soft interface information + * @ethhdr: an ethernet header to determine the protocol family from + * + * Return: an orig_node which has the BATADV_MCAST_WANT_ALL_RTR4 or + * BATADV_MCAST_WANT_ALL_RTR6 flag, depending on the provided ethhdr, set and + * increases its refcount. + */ +static struct batadv_orig_node * +batadv_mcast_forw_rtr_node_get(struct batadv_priv *bat_priv, + struct ethhdr *ethhdr) +{ + switch (ntohs(ethhdr->h_proto)) { + case ETH_P_IP: + return batadv_mcast_forw_rtr4_node_get(bat_priv); + case ETH_P_IPV6: + return batadv_mcast_forw_rtr6_node_get(bat_priv); + default: + /* we shouldn't be here... */ + return NULL; + } +} + /** * batadv_mcast_forw_mode() - check on how to forward a multicast packet * @bat_priv: the bat priv with all the soft interface information @@ -1275,8 +1380,11 @@ batadv_mcast_forw_mode(struct batadv_priv *bat_priv, struct sk_buff *skb, bool is_unsnoopable = false; unsigned int mcast_fanout; struct ethhdr *ethhdr; + int is_routeable = 0; + int rtr_count = 0; - ret = batadv_mcast_forw_mode_check(bat_priv, skb, &is_unsnoopable); + ret = batadv_mcast_forw_mode_check(bat_priv, skb, &is_unsnoopable, + &is_routeable); if (ret == -ENOMEM) return BATADV_FORW_NONE; else if (ret < 0) @@ -1289,8 +1397,9 @@ batadv_mcast_forw_mode(struct batadv_priv *bat_priv, struct sk_buff *skb, ip_count = batadv_mcast_forw_want_all_ip_count(bat_priv, ethhdr); unsnoop_count = !is_unsnoopable ? 0 : atomic_read(&bat_priv->mcast.num_want_all_unsnoopables); + rtr_count = batadv_mcast_forw_rtr_count(bat_priv, is_routeable); - total_count = tt_count + ip_count + unsnoop_count; + total_count = tt_count + ip_count + unsnoop_count + rtr_count; switch (total_count) { case 1: @@ -1300,6 +1409,9 @@ batadv_mcast_forw_mode(struct batadv_priv *bat_priv, struct sk_buff *skb, *orig = batadv_mcast_forw_ip_node_get(bat_priv, ethhdr); else if (unsnoop_count) *orig = batadv_mcast_forw_unsnoop_node_get(bat_priv); + else if (rtr_count) + *orig = batadv_mcast_forw_rtr_node_get(bat_priv, + ethhdr); if (*orig) return BATADV_FORW_SINGLE; @@ -1470,6 +1582,111 @@ batadv_mcast_forw_want_all(struct batadv_priv *bat_priv, } } +/** + * batadv_mcast_forw_want_all_rtr4() - forward to nodes with want-all-rtr4 + * @bat_priv: the bat priv with all the soft interface information + * @skb: the multicast packet to transmit + * @vid: the vlan identifier + * + * Sends copies of a frame with multicast destination to any node with a + * BATADV_MCAST_WANT_ALL_RTR4 flag set. A transmission is performed via a + * batman-adv unicast packet for each such destination node. + * + * Return: NET_XMIT_DROP on memory allocation failure, NET_XMIT_SUCCESS + * otherwise. + */ +static int +batadv_mcast_forw_want_all_rtr4(struct batadv_priv *bat_priv, + struct sk_buff *skb, unsigned short vid) +{ + struct batadv_orig_node *orig_node; + int ret = NET_XMIT_SUCCESS; + struct sk_buff *newskb; + + rcu_read_lock(); + hlist_for_each_entry_rcu(orig_node, + &bat_priv->mcast.want_all_rtr4_list, + mcast_want_all_rtr4_node) { + newskb = skb_copy(skb, GFP_ATOMIC); + if (!newskb) { + ret = NET_XMIT_DROP; + break; + } + + batadv_send_skb_unicast(bat_priv, newskb, BATADV_UNICAST, 0, + orig_node, vid); + } + rcu_read_unlock(); + return ret; +} + +/** + * batadv_mcast_forw_want_all_rtr6() - forward to nodes with want-all-rtr6 + * @bat_priv: the bat priv with all the soft interface information + * @skb: The multicast packet to transmit + * @vid: the vlan identifier + * + * Sends copies of a frame with multicast destination to any node with a + * BATADV_MCAST_WANT_ALL_RTR6 flag set. A transmission is performed via a + * batman-adv unicast packet for each such destination node. + * + * Return: NET_XMIT_DROP on memory allocation failure, NET_XMIT_SUCCESS + * otherwise. + */ +static int +batadv_mcast_forw_want_all_rtr6(struct batadv_priv *bat_priv, + struct sk_buff *skb, unsigned short vid) +{ + struct batadv_orig_node *orig_node; + int ret = NET_XMIT_SUCCESS; + struct sk_buff *newskb; + + rcu_read_lock(); + hlist_for_each_entry_rcu(orig_node, + &bat_priv->mcast.want_all_rtr6_list, + mcast_want_all_rtr6_node) { + newskb = skb_copy(skb, GFP_ATOMIC); + if (!newskb) { + ret = NET_XMIT_DROP; + break; + } + + batadv_send_skb_unicast(bat_priv, newskb, BATADV_UNICAST, 0, + orig_node, vid); + } + rcu_read_unlock(); + return ret; +} + +/** + * batadv_mcast_forw_want_rtr() - forward packet to nodes in a want-all-rtr list + * @bat_priv: the bat priv with all the soft interface information + * @skb: the multicast packet to transmit + * @vid: the vlan identifier + * + * Sends copies of a frame with multicast destination to any node with a + * BATADV_MCAST_WANT_ALL_RTR4 or BATADV_MCAST_WANT_ALL_RTR6 flag set. A + * transmission is performed via a batman-adv unicast packet for each such + * destination node. + * + * Return: NET_XMIT_DROP on memory allocation failure or if the protocol family + * is neither IPv4 nor IPv6. NET_XMIT_SUCCESS otherwise. + */ +static int +batadv_mcast_forw_want_rtr(struct batadv_priv *bat_priv, + struct sk_buff *skb, unsigned short vid) +{ + switch (ntohs(eth_hdr(skb)->h_proto)) { + case ETH_P_IP: + return batadv_mcast_forw_want_all_rtr4(bat_priv, skb, vid); + case ETH_P_IPV6: + return batadv_mcast_forw_want_all_rtr6(bat_priv, skb, vid); + default: + /* we shouldn't be here... */ + return NET_XMIT_DROP; + } +} + /** * batadv_mcast_forw_send() - send packet to any detected multicast recpient * @bat_priv: the bat priv with all the soft interface information @@ -1503,6 +1720,12 @@ int batadv_mcast_forw_send(struct batadv_priv *bat_priv, struct sk_buff *skb, return ret; } + ret = batadv_mcast_forw_want_rtr(bat_priv, skb, vid); + if (ret != NET_XMIT_SUCCESS) { + kfree_skb(skb); + return ret; + } + consume_skb(skb); return ret; }