18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* Copyright (C) 2014-2020 B.A.T.M.A.N. contributors: 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Linus Lüssing 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include "multicast.h" 88c2ecf20Sopenharmony_ci#include "main.h" 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/atomic.h> 118c2ecf20Sopenharmony_ci#include <linux/bitops.h> 128c2ecf20Sopenharmony_ci#include <linux/bug.h> 138c2ecf20Sopenharmony_ci#include <linux/byteorder/generic.h> 148c2ecf20Sopenharmony_ci#include <linux/errno.h> 158c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 168c2ecf20Sopenharmony_ci#include <linux/gfp.h> 178c2ecf20Sopenharmony_ci#include <linux/icmpv6.h> 188c2ecf20Sopenharmony_ci#include <linux/if_bridge.h> 198c2ecf20Sopenharmony_ci#include <linux/if_ether.h> 208c2ecf20Sopenharmony_ci#include <linux/igmp.h> 218c2ecf20Sopenharmony_ci#include <linux/in.h> 228c2ecf20Sopenharmony_ci#include <linux/in6.h> 238c2ecf20Sopenharmony_ci#include <linux/inetdevice.h> 248c2ecf20Sopenharmony_ci#include <linux/ip.h> 258c2ecf20Sopenharmony_ci#include <linux/ipv6.h> 268c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 278c2ecf20Sopenharmony_ci#include <linux/kernel.h> 288c2ecf20Sopenharmony_ci#include <linux/kref.h> 298c2ecf20Sopenharmony_ci#include <linux/list.h> 308c2ecf20Sopenharmony_ci#include <linux/lockdep.h> 318c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 328c2ecf20Sopenharmony_ci#include <linux/netlink.h> 338c2ecf20Sopenharmony_ci#include <linux/printk.h> 348c2ecf20Sopenharmony_ci#include <linux/rculist.h> 358c2ecf20Sopenharmony_ci#include <linux/rcupdate.h> 368c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 378c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 388c2ecf20Sopenharmony_ci#include <linux/slab.h> 398c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 408c2ecf20Sopenharmony_ci#include <linux/stddef.h> 418c2ecf20Sopenharmony_ci#include <linux/string.h> 428c2ecf20Sopenharmony_ci#include <linux/types.h> 438c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 448c2ecf20Sopenharmony_ci#include <net/addrconf.h> 458c2ecf20Sopenharmony_ci#include <net/genetlink.h> 468c2ecf20Sopenharmony_ci#include <net/if_inet6.h> 478c2ecf20Sopenharmony_ci#include <net/ip.h> 488c2ecf20Sopenharmony_ci#include <net/ipv6.h> 498c2ecf20Sopenharmony_ci#include <net/netlink.h> 508c2ecf20Sopenharmony_ci#include <net/sock.h> 518c2ecf20Sopenharmony_ci#include <uapi/linux/batadv_packet.h> 528c2ecf20Sopenharmony_ci#include <uapi/linux/batman_adv.h> 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci#include "bridge_loop_avoidance.h" 558c2ecf20Sopenharmony_ci#include "hard-interface.h" 568c2ecf20Sopenharmony_ci#include "hash.h" 578c2ecf20Sopenharmony_ci#include "log.h" 588c2ecf20Sopenharmony_ci#include "netlink.h" 598c2ecf20Sopenharmony_ci#include "send.h" 608c2ecf20Sopenharmony_ci#include "soft-interface.h" 618c2ecf20Sopenharmony_ci#include "translation-table.h" 628c2ecf20Sopenharmony_ci#include "tvlv.h" 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic void batadv_mcast_mla_update(struct work_struct *work); 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci/** 678c2ecf20Sopenharmony_ci * batadv_mcast_start_timer() - schedule the multicast periodic worker 688c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 698c2ecf20Sopenharmony_ci */ 708c2ecf20Sopenharmony_cistatic void batadv_mcast_start_timer(struct batadv_priv *bat_priv) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci queue_delayed_work(batadv_event_workqueue, &bat_priv->mcast.work, 738c2ecf20Sopenharmony_ci msecs_to_jiffies(BATADV_MCAST_WORK_PERIOD)); 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci/** 778c2ecf20Sopenharmony_ci * batadv_mcast_get_bridge() - get the bridge on top of the softif if it exists 788c2ecf20Sopenharmony_ci * @soft_iface: netdev struct of the mesh interface 798c2ecf20Sopenharmony_ci * 808c2ecf20Sopenharmony_ci * If the given soft interface has a bridge on top then the refcount 818c2ecf20Sopenharmony_ci * of the according net device is increased. 828c2ecf20Sopenharmony_ci * 838c2ecf20Sopenharmony_ci * Return: NULL if no such bridge exists. Otherwise the net device of the 848c2ecf20Sopenharmony_ci * bridge. 858c2ecf20Sopenharmony_ci */ 868c2ecf20Sopenharmony_cistatic struct net_device *batadv_mcast_get_bridge(struct net_device *soft_iface) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci struct net_device *upper = soft_iface; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci rcu_read_lock(); 918c2ecf20Sopenharmony_ci do { 928c2ecf20Sopenharmony_ci upper = netdev_master_upper_dev_get_rcu(upper); 938c2ecf20Sopenharmony_ci } while (upper && !(upper->priv_flags & IFF_EBRIDGE)); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci if (upper) 968c2ecf20Sopenharmony_ci dev_hold(upper); 978c2ecf20Sopenharmony_ci rcu_read_unlock(); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci return upper; 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci/** 1038c2ecf20Sopenharmony_ci * batadv_mcast_mla_rtr_flags_softif_get_ipv4() - get mcast router flags from 1048c2ecf20Sopenharmony_ci * node for IPv4 1058c2ecf20Sopenharmony_ci * @dev: the interface to check 1068c2ecf20Sopenharmony_ci * 1078c2ecf20Sopenharmony_ci * Checks the presence of an IPv4 multicast router on this node. 1088c2ecf20Sopenharmony_ci * 1098c2ecf20Sopenharmony_ci * Caller needs to hold rcu read lock. 1108c2ecf20Sopenharmony_ci * 1118c2ecf20Sopenharmony_ci * Return: BATADV_NO_FLAGS if present, BATADV_MCAST_WANT_NO_RTR4 otherwise. 1128c2ecf20Sopenharmony_ci */ 1138c2ecf20Sopenharmony_cistatic u8 batadv_mcast_mla_rtr_flags_softif_get_ipv4(struct net_device *dev) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci struct in_device *in_dev = __in_dev_get_rcu(dev); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci if (in_dev && IN_DEV_MFORWARD(in_dev)) 1188c2ecf20Sopenharmony_ci return BATADV_NO_FLAGS; 1198c2ecf20Sopenharmony_ci else 1208c2ecf20Sopenharmony_ci return BATADV_MCAST_WANT_NO_RTR4; 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci/** 1248c2ecf20Sopenharmony_ci * batadv_mcast_mla_rtr_flags_softif_get_ipv6() - get mcast router flags from 1258c2ecf20Sopenharmony_ci * node for IPv6 1268c2ecf20Sopenharmony_ci * @dev: the interface to check 1278c2ecf20Sopenharmony_ci * 1288c2ecf20Sopenharmony_ci * Checks the presence of an IPv6 multicast router on this node. 1298c2ecf20Sopenharmony_ci * 1308c2ecf20Sopenharmony_ci * Caller needs to hold rcu read lock. 1318c2ecf20Sopenharmony_ci * 1328c2ecf20Sopenharmony_ci * Return: BATADV_NO_FLAGS if present, BATADV_MCAST_WANT_NO_RTR6 otherwise. 1338c2ecf20Sopenharmony_ci */ 1348c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6_MROUTE) 1358c2ecf20Sopenharmony_cistatic u8 batadv_mcast_mla_rtr_flags_softif_get_ipv6(struct net_device *dev) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci struct inet6_dev *in6_dev = __in6_dev_get(dev); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if (in6_dev && atomic_read(&in6_dev->cnf.mc_forwarding)) 1408c2ecf20Sopenharmony_ci return BATADV_NO_FLAGS; 1418c2ecf20Sopenharmony_ci else 1428c2ecf20Sopenharmony_ci return BATADV_MCAST_WANT_NO_RTR6; 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci#else 1458c2ecf20Sopenharmony_cistatic inline u8 1468c2ecf20Sopenharmony_cibatadv_mcast_mla_rtr_flags_softif_get_ipv6(struct net_device *dev) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci return BATADV_MCAST_WANT_NO_RTR6; 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci#endif 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci/** 1538c2ecf20Sopenharmony_ci * batadv_mcast_mla_rtr_flags_softif_get() - get mcast router flags from node 1548c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 1558c2ecf20Sopenharmony_ci * @bridge: bridge interface on top of the soft_iface if present, 1568c2ecf20Sopenharmony_ci * otherwise pass NULL 1578c2ecf20Sopenharmony_ci * 1588c2ecf20Sopenharmony_ci * Checks the presence of IPv4 and IPv6 multicast routers on this 1598c2ecf20Sopenharmony_ci * node. 1608c2ecf20Sopenharmony_ci * 1618c2ecf20Sopenharmony_ci * Return: 1628c2ecf20Sopenharmony_ci * BATADV_NO_FLAGS: Both an IPv4 and IPv6 multicast router is present 1638c2ecf20Sopenharmony_ci * BATADV_MCAST_WANT_NO_RTR4: No IPv4 multicast router is present 1648c2ecf20Sopenharmony_ci * BATADV_MCAST_WANT_NO_RTR6: No IPv6 multicast router is present 1658c2ecf20Sopenharmony_ci * The former two OR'd: no multicast router is present 1668c2ecf20Sopenharmony_ci */ 1678c2ecf20Sopenharmony_cistatic u8 batadv_mcast_mla_rtr_flags_softif_get(struct batadv_priv *bat_priv, 1688c2ecf20Sopenharmony_ci struct net_device *bridge) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci struct net_device *dev = bridge ? bridge : bat_priv->soft_iface; 1718c2ecf20Sopenharmony_ci u8 flags = BATADV_NO_FLAGS; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci rcu_read_lock(); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci flags |= batadv_mcast_mla_rtr_flags_softif_get_ipv4(dev); 1768c2ecf20Sopenharmony_ci flags |= batadv_mcast_mla_rtr_flags_softif_get_ipv6(dev); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci rcu_read_unlock(); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci return flags; 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci/** 1848c2ecf20Sopenharmony_ci * batadv_mcast_mla_rtr_flags_bridge_get() - get mcast router flags from bridge 1858c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 1868c2ecf20Sopenharmony_ci * @bridge: bridge interface on top of the soft_iface if present, 1878c2ecf20Sopenharmony_ci * otherwise pass NULL 1888c2ecf20Sopenharmony_ci * 1898c2ecf20Sopenharmony_ci * Checks the presence of IPv4 and IPv6 multicast routers behind a bridge. 1908c2ecf20Sopenharmony_ci * 1918c2ecf20Sopenharmony_ci * Return: 1928c2ecf20Sopenharmony_ci * BATADV_NO_FLAGS: Both an IPv4 and IPv6 multicast router is present 1938c2ecf20Sopenharmony_ci * BATADV_MCAST_WANT_NO_RTR4: No IPv4 multicast router is present 1948c2ecf20Sopenharmony_ci * BATADV_MCAST_WANT_NO_RTR6: No IPv6 multicast router is present 1958c2ecf20Sopenharmony_ci * The former two OR'd: no multicast router is present 1968c2ecf20Sopenharmony_ci */ 1978c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 1988c2ecf20Sopenharmony_cistatic u8 batadv_mcast_mla_rtr_flags_bridge_get(struct batadv_priv *bat_priv, 1998c2ecf20Sopenharmony_ci struct net_device *bridge) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci struct list_head bridge_mcast_list = LIST_HEAD_INIT(bridge_mcast_list); 2028c2ecf20Sopenharmony_ci struct net_device *dev = bat_priv->soft_iface; 2038c2ecf20Sopenharmony_ci struct br_ip_list *br_ip_entry, *tmp; 2048c2ecf20Sopenharmony_ci u8 flags = BATADV_MCAST_WANT_NO_RTR6; 2058c2ecf20Sopenharmony_ci int ret; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci if (!bridge) 2088c2ecf20Sopenharmony_ci return BATADV_MCAST_WANT_NO_RTR4 | BATADV_MCAST_WANT_NO_RTR6; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci /* TODO: ask the bridge if a multicast router is present (the bridge 2118c2ecf20Sopenharmony_ci * is capable of performing proper RFC4286 multicast router 2128c2ecf20Sopenharmony_ci * discovery) instead of searching for a ff02::2 listener here 2138c2ecf20Sopenharmony_ci */ 2148c2ecf20Sopenharmony_ci ret = br_multicast_list_adjacent(dev, &bridge_mcast_list); 2158c2ecf20Sopenharmony_ci if (ret < 0) 2168c2ecf20Sopenharmony_ci return BATADV_NO_FLAGS; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci list_for_each_entry_safe(br_ip_entry, tmp, &bridge_mcast_list, list) { 2198c2ecf20Sopenharmony_ci /* the bridge snooping does not maintain IPv4 link-local 2208c2ecf20Sopenharmony_ci * addresses - therefore we won't find any IPv4 multicast router 2218c2ecf20Sopenharmony_ci * address here, only IPv6 ones 2228c2ecf20Sopenharmony_ci */ 2238c2ecf20Sopenharmony_ci if (br_ip_entry->addr.proto == htons(ETH_P_IPV6) && 2248c2ecf20Sopenharmony_ci ipv6_addr_is_ll_all_routers(&br_ip_entry->addr.dst.ip6)) 2258c2ecf20Sopenharmony_ci flags &= ~BATADV_MCAST_WANT_NO_RTR6; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci list_del(&br_ip_entry->list); 2288c2ecf20Sopenharmony_ci kfree(br_ip_entry); 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci return flags; 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci#else 2348c2ecf20Sopenharmony_cistatic inline u8 2358c2ecf20Sopenharmony_cibatadv_mcast_mla_rtr_flags_bridge_get(struct batadv_priv *bat_priv, 2368c2ecf20Sopenharmony_ci struct net_device *bridge) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci if (bridge) 2398c2ecf20Sopenharmony_ci return BATADV_NO_FLAGS; 2408c2ecf20Sopenharmony_ci else 2418c2ecf20Sopenharmony_ci return BATADV_MCAST_WANT_NO_RTR4 | BATADV_MCAST_WANT_NO_RTR6; 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci#endif 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci/** 2468c2ecf20Sopenharmony_ci * batadv_mcast_mla_rtr_flags_get() - get multicast router flags 2478c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 2488c2ecf20Sopenharmony_ci * @bridge: bridge interface on top of the soft_iface if present, 2498c2ecf20Sopenharmony_ci * otherwise pass NULL 2508c2ecf20Sopenharmony_ci * 2518c2ecf20Sopenharmony_ci * Checks the presence of IPv4 and IPv6 multicast routers on this 2528c2ecf20Sopenharmony_ci * node or behind its bridge. 2538c2ecf20Sopenharmony_ci * 2548c2ecf20Sopenharmony_ci * Return: 2558c2ecf20Sopenharmony_ci * BATADV_NO_FLAGS: Both an IPv4 and IPv6 multicast router is present 2568c2ecf20Sopenharmony_ci * BATADV_MCAST_WANT_NO_RTR4: No IPv4 multicast router is present 2578c2ecf20Sopenharmony_ci * BATADV_MCAST_WANT_NO_RTR6: No IPv6 multicast router is present 2588c2ecf20Sopenharmony_ci * The former two OR'd: no multicast router is present 2598c2ecf20Sopenharmony_ci */ 2608c2ecf20Sopenharmony_cistatic u8 batadv_mcast_mla_rtr_flags_get(struct batadv_priv *bat_priv, 2618c2ecf20Sopenharmony_ci struct net_device *bridge) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci u8 flags = BATADV_MCAST_WANT_NO_RTR4 | BATADV_MCAST_WANT_NO_RTR6; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci flags &= batadv_mcast_mla_rtr_flags_softif_get(bat_priv, bridge); 2668c2ecf20Sopenharmony_ci flags &= batadv_mcast_mla_rtr_flags_bridge_get(bat_priv, bridge); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci return flags; 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci/** 2728c2ecf20Sopenharmony_ci * batadv_mcast_mla_flags_get() - get the new multicast flags 2738c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 2748c2ecf20Sopenharmony_ci * 2758c2ecf20Sopenharmony_ci * Return: A set of flags for the current/next TVLV, querier and 2768c2ecf20Sopenharmony_ci * bridge state. 2778c2ecf20Sopenharmony_ci */ 2788c2ecf20Sopenharmony_cistatic struct batadv_mcast_mla_flags 2798c2ecf20Sopenharmony_cibatadv_mcast_mla_flags_get(struct batadv_priv *bat_priv) 2808c2ecf20Sopenharmony_ci{ 2818c2ecf20Sopenharmony_ci struct net_device *dev = bat_priv->soft_iface; 2828c2ecf20Sopenharmony_ci struct batadv_mcast_querier_state *qr4, *qr6; 2838c2ecf20Sopenharmony_ci struct batadv_mcast_mla_flags mla_flags; 2848c2ecf20Sopenharmony_ci struct net_device *bridge; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci bridge = batadv_mcast_get_bridge(dev); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci memset(&mla_flags, 0, sizeof(mla_flags)); 2898c2ecf20Sopenharmony_ci mla_flags.enabled = 1; 2908c2ecf20Sopenharmony_ci mla_flags.tvlv_flags |= batadv_mcast_mla_rtr_flags_get(bat_priv, 2918c2ecf20Sopenharmony_ci bridge); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci if (!bridge) 2948c2ecf20Sopenharmony_ci return mla_flags; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci dev_put(bridge); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci mla_flags.bridged = 1; 2998c2ecf20Sopenharmony_ci qr4 = &mla_flags.querier_ipv4; 3008c2ecf20Sopenharmony_ci qr6 = &mla_flags.querier_ipv6; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci if (!IS_ENABLED(CONFIG_BRIDGE_IGMP_SNOOPING)) 3038c2ecf20Sopenharmony_ci pr_warn_once("No bridge IGMP snooping compiled - multicast optimizations disabled\n"); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci qr4->exists = br_multicast_has_querier_anywhere(dev, ETH_P_IP); 3068c2ecf20Sopenharmony_ci qr4->shadowing = br_multicast_has_querier_adjacent(dev, ETH_P_IP); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci qr6->exists = br_multicast_has_querier_anywhere(dev, ETH_P_IPV6); 3098c2ecf20Sopenharmony_ci qr6->shadowing = br_multicast_has_querier_adjacent(dev, ETH_P_IPV6); 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci mla_flags.tvlv_flags |= BATADV_MCAST_WANT_ALL_UNSNOOPABLES; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci /* 1) If no querier exists at all, then multicast listeners on 3148c2ecf20Sopenharmony_ci * our local TT clients behind the bridge will keep silent. 3158c2ecf20Sopenharmony_ci * 2) If the selected querier is on one of our local TT clients, 3168c2ecf20Sopenharmony_ci * behind the bridge, then this querier might shadow multicast 3178c2ecf20Sopenharmony_ci * listeners on our local TT clients, behind this bridge. 3188c2ecf20Sopenharmony_ci * 3198c2ecf20Sopenharmony_ci * In both cases, we will signalize other batman nodes that 3208c2ecf20Sopenharmony_ci * we need all multicast traffic of the according protocol. 3218c2ecf20Sopenharmony_ci */ 3228c2ecf20Sopenharmony_ci if (!qr4->exists || qr4->shadowing) { 3238c2ecf20Sopenharmony_ci mla_flags.tvlv_flags |= BATADV_MCAST_WANT_ALL_IPV4; 3248c2ecf20Sopenharmony_ci mla_flags.tvlv_flags &= ~BATADV_MCAST_WANT_NO_RTR4; 3258c2ecf20Sopenharmony_ci } 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci if (!qr6->exists || qr6->shadowing) { 3288c2ecf20Sopenharmony_ci mla_flags.tvlv_flags |= BATADV_MCAST_WANT_ALL_IPV6; 3298c2ecf20Sopenharmony_ci mla_flags.tvlv_flags &= ~BATADV_MCAST_WANT_NO_RTR6; 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci return mla_flags; 3338c2ecf20Sopenharmony_ci} 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci/** 3368c2ecf20Sopenharmony_ci * batadv_mcast_mla_is_duplicate() - check whether an address is in a list 3378c2ecf20Sopenharmony_ci * @mcast_addr: the multicast address to check 3388c2ecf20Sopenharmony_ci * @mcast_list: the list with multicast addresses to search in 3398c2ecf20Sopenharmony_ci * 3408c2ecf20Sopenharmony_ci * Return: true if the given address is already in the given list. 3418c2ecf20Sopenharmony_ci * Otherwise returns false. 3428c2ecf20Sopenharmony_ci */ 3438c2ecf20Sopenharmony_cistatic bool batadv_mcast_mla_is_duplicate(u8 *mcast_addr, 3448c2ecf20Sopenharmony_ci struct hlist_head *mcast_list) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci struct batadv_hw_addr *mcast_entry; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci hlist_for_each_entry(mcast_entry, mcast_list, list) 3498c2ecf20Sopenharmony_ci if (batadv_compare_eth(mcast_entry->addr, mcast_addr)) 3508c2ecf20Sopenharmony_ci return true; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci return false; 3538c2ecf20Sopenharmony_ci} 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci/** 3568c2ecf20Sopenharmony_ci * batadv_mcast_mla_softif_get_ipv4() - get softif IPv4 multicast listeners 3578c2ecf20Sopenharmony_ci * @dev: the device to collect multicast addresses from 3588c2ecf20Sopenharmony_ci * @mcast_list: a list to put found addresses into 3598c2ecf20Sopenharmony_ci * @flags: flags indicating the new multicast state 3608c2ecf20Sopenharmony_ci * 3618c2ecf20Sopenharmony_ci * Collects multicast addresses of IPv4 multicast listeners residing 3628c2ecf20Sopenharmony_ci * on this kernel on the given soft interface, dev, in 3638c2ecf20Sopenharmony_ci * the given mcast_list. In general, multicast listeners provided by 3648c2ecf20Sopenharmony_ci * your multicast receiving applications run directly on this node. 3658c2ecf20Sopenharmony_ci * 3668c2ecf20Sopenharmony_ci * Return: -ENOMEM on memory allocation error or the number of 3678c2ecf20Sopenharmony_ci * items added to the mcast_list otherwise. 3688c2ecf20Sopenharmony_ci */ 3698c2ecf20Sopenharmony_cistatic int 3708c2ecf20Sopenharmony_cibatadv_mcast_mla_softif_get_ipv4(struct net_device *dev, 3718c2ecf20Sopenharmony_ci struct hlist_head *mcast_list, 3728c2ecf20Sopenharmony_ci struct batadv_mcast_mla_flags *flags) 3738c2ecf20Sopenharmony_ci{ 3748c2ecf20Sopenharmony_ci struct batadv_hw_addr *new; 3758c2ecf20Sopenharmony_ci struct in_device *in_dev; 3768c2ecf20Sopenharmony_ci u8 mcast_addr[ETH_ALEN]; 3778c2ecf20Sopenharmony_ci struct ip_mc_list *pmc; 3788c2ecf20Sopenharmony_ci int ret = 0; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci if (flags->tvlv_flags & BATADV_MCAST_WANT_ALL_IPV4) 3818c2ecf20Sopenharmony_ci return 0; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci rcu_read_lock(); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci in_dev = __in_dev_get_rcu(dev); 3868c2ecf20Sopenharmony_ci if (!in_dev) { 3878c2ecf20Sopenharmony_ci rcu_read_unlock(); 3888c2ecf20Sopenharmony_ci return 0; 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci for (pmc = rcu_dereference(in_dev->mc_list); pmc; 3928c2ecf20Sopenharmony_ci pmc = rcu_dereference(pmc->next_rcu)) { 3938c2ecf20Sopenharmony_ci if (flags->tvlv_flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES && 3948c2ecf20Sopenharmony_ci ipv4_is_local_multicast(pmc->multiaddr)) 3958c2ecf20Sopenharmony_ci continue; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci if (!(flags->tvlv_flags & BATADV_MCAST_WANT_NO_RTR4) && 3988c2ecf20Sopenharmony_ci !ipv4_is_local_multicast(pmc->multiaddr)) 3998c2ecf20Sopenharmony_ci continue; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci ip_eth_mc_map(pmc->multiaddr, mcast_addr); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci if (batadv_mcast_mla_is_duplicate(mcast_addr, mcast_list)) 4048c2ecf20Sopenharmony_ci continue; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci new = kmalloc(sizeof(*new), GFP_ATOMIC); 4078c2ecf20Sopenharmony_ci if (!new) { 4088c2ecf20Sopenharmony_ci ret = -ENOMEM; 4098c2ecf20Sopenharmony_ci break; 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci ether_addr_copy(new->addr, mcast_addr); 4138c2ecf20Sopenharmony_ci hlist_add_head(&new->list, mcast_list); 4148c2ecf20Sopenharmony_ci ret++; 4158c2ecf20Sopenharmony_ci } 4168c2ecf20Sopenharmony_ci rcu_read_unlock(); 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci return ret; 4198c2ecf20Sopenharmony_ci} 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci/** 4228c2ecf20Sopenharmony_ci * batadv_mcast_mla_softif_get_ipv6() - get softif IPv6 multicast listeners 4238c2ecf20Sopenharmony_ci * @dev: the device to collect multicast addresses from 4248c2ecf20Sopenharmony_ci * @mcast_list: a list to put found addresses into 4258c2ecf20Sopenharmony_ci * @flags: flags indicating the new multicast state 4268c2ecf20Sopenharmony_ci * 4278c2ecf20Sopenharmony_ci * Collects multicast addresses of IPv6 multicast listeners residing 4288c2ecf20Sopenharmony_ci * on this kernel on the given soft interface, dev, in 4298c2ecf20Sopenharmony_ci * the given mcast_list. In general, multicast listeners provided by 4308c2ecf20Sopenharmony_ci * your multicast receiving applications run directly on this node. 4318c2ecf20Sopenharmony_ci * 4328c2ecf20Sopenharmony_ci * Return: -ENOMEM on memory allocation error or the number of 4338c2ecf20Sopenharmony_ci * items added to the mcast_list otherwise. 4348c2ecf20Sopenharmony_ci */ 4358c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 4368c2ecf20Sopenharmony_cistatic int 4378c2ecf20Sopenharmony_cibatadv_mcast_mla_softif_get_ipv6(struct net_device *dev, 4388c2ecf20Sopenharmony_ci struct hlist_head *mcast_list, 4398c2ecf20Sopenharmony_ci struct batadv_mcast_mla_flags *flags) 4408c2ecf20Sopenharmony_ci{ 4418c2ecf20Sopenharmony_ci struct batadv_hw_addr *new; 4428c2ecf20Sopenharmony_ci struct inet6_dev *in6_dev; 4438c2ecf20Sopenharmony_ci u8 mcast_addr[ETH_ALEN]; 4448c2ecf20Sopenharmony_ci struct ifmcaddr6 *pmc6; 4458c2ecf20Sopenharmony_ci int ret = 0; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci if (flags->tvlv_flags & BATADV_MCAST_WANT_ALL_IPV6) 4488c2ecf20Sopenharmony_ci return 0; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci rcu_read_lock(); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci in6_dev = __in6_dev_get(dev); 4538c2ecf20Sopenharmony_ci if (!in6_dev) { 4548c2ecf20Sopenharmony_ci rcu_read_unlock(); 4558c2ecf20Sopenharmony_ci return 0; 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci read_lock_bh(&in6_dev->lock); 4598c2ecf20Sopenharmony_ci for (pmc6 = in6_dev->mc_list; pmc6; pmc6 = pmc6->next) { 4608c2ecf20Sopenharmony_ci if (IPV6_ADDR_MC_SCOPE(&pmc6->mca_addr) < 4618c2ecf20Sopenharmony_ci IPV6_ADDR_SCOPE_LINKLOCAL) 4628c2ecf20Sopenharmony_ci continue; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci if (flags->tvlv_flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES && 4658c2ecf20Sopenharmony_ci ipv6_addr_is_ll_all_nodes(&pmc6->mca_addr)) 4668c2ecf20Sopenharmony_ci continue; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci if (!(flags->tvlv_flags & BATADV_MCAST_WANT_NO_RTR6) && 4698c2ecf20Sopenharmony_ci IPV6_ADDR_MC_SCOPE(&pmc6->mca_addr) > 4708c2ecf20Sopenharmony_ci IPV6_ADDR_SCOPE_LINKLOCAL) 4718c2ecf20Sopenharmony_ci continue; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci ipv6_eth_mc_map(&pmc6->mca_addr, mcast_addr); 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci if (batadv_mcast_mla_is_duplicate(mcast_addr, mcast_list)) 4768c2ecf20Sopenharmony_ci continue; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci new = kmalloc(sizeof(*new), GFP_ATOMIC); 4798c2ecf20Sopenharmony_ci if (!new) { 4808c2ecf20Sopenharmony_ci ret = -ENOMEM; 4818c2ecf20Sopenharmony_ci break; 4828c2ecf20Sopenharmony_ci } 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci ether_addr_copy(new->addr, mcast_addr); 4858c2ecf20Sopenharmony_ci hlist_add_head(&new->list, mcast_list); 4868c2ecf20Sopenharmony_ci ret++; 4878c2ecf20Sopenharmony_ci } 4888c2ecf20Sopenharmony_ci read_unlock_bh(&in6_dev->lock); 4898c2ecf20Sopenharmony_ci rcu_read_unlock(); 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci return ret; 4928c2ecf20Sopenharmony_ci} 4938c2ecf20Sopenharmony_ci#else 4948c2ecf20Sopenharmony_cistatic inline int 4958c2ecf20Sopenharmony_cibatadv_mcast_mla_softif_get_ipv6(struct net_device *dev, 4968c2ecf20Sopenharmony_ci struct hlist_head *mcast_list, 4978c2ecf20Sopenharmony_ci struct batadv_mcast_mla_flags *flags) 4988c2ecf20Sopenharmony_ci{ 4998c2ecf20Sopenharmony_ci return 0; 5008c2ecf20Sopenharmony_ci} 5018c2ecf20Sopenharmony_ci#endif 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci/** 5048c2ecf20Sopenharmony_ci * batadv_mcast_mla_softif_get() - get softif multicast listeners 5058c2ecf20Sopenharmony_ci * @dev: the device to collect multicast addresses from 5068c2ecf20Sopenharmony_ci * @mcast_list: a list to put found addresses into 5078c2ecf20Sopenharmony_ci * @flags: flags indicating the new multicast state 5088c2ecf20Sopenharmony_ci * 5098c2ecf20Sopenharmony_ci * Collects multicast addresses of multicast listeners residing 5108c2ecf20Sopenharmony_ci * on this kernel on the given soft interface, dev, in 5118c2ecf20Sopenharmony_ci * the given mcast_list. In general, multicast listeners provided by 5128c2ecf20Sopenharmony_ci * your multicast receiving applications run directly on this node. 5138c2ecf20Sopenharmony_ci * 5148c2ecf20Sopenharmony_ci * If there is a bridge interface on top of dev, collect from that one 5158c2ecf20Sopenharmony_ci * instead. Just like with IP addresses and routes, multicast listeners 5168c2ecf20Sopenharmony_ci * will(/should) register to the bridge interface instead of an 5178c2ecf20Sopenharmony_ci * enslaved bat0. 5188c2ecf20Sopenharmony_ci * 5198c2ecf20Sopenharmony_ci * Return: -ENOMEM on memory allocation error or the number of 5208c2ecf20Sopenharmony_ci * items added to the mcast_list otherwise. 5218c2ecf20Sopenharmony_ci */ 5228c2ecf20Sopenharmony_cistatic int 5238c2ecf20Sopenharmony_cibatadv_mcast_mla_softif_get(struct net_device *dev, 5248c2ecf20Sopenharmony_ci struct hlist_head *mcast_list, 5258c2ecf20Sopenharmony_ci struct batadv_mcast_mla_flags *flags) 5268c2ecf20Sopenharmony_ci{ 5278c2ecf20Sopenharmony_ci struct net_device *bridge = batadv_mcast_get_bridge(dev); 5288c2ecf20Sopenharmony_ci int ret4, ret6 = 0; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci if (bridge) 5318c2ecf20Sopenharmony_ci dev = bridge; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci ret4 = batadv_mcast_mla_softif_get_ipv4(dev, mcast_list, flags); 5348c2ecf20Sopenharmony_ci if (ret4 < 0) 5358c2ecf20Sopenharmony_ci goto out; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci ret6 = batadv_mcast_mla_softif_get_ipv6(dev, mcast_list, flags); 5388c2ecf20Sopenharmony_ci if (ret6 < 0) { 5398c2ecf20Sopenharmony_ci ret4 = 0; 5408c2ecf20Sopenharmony_ci goto out; 5418c2ecf20Sopenharmony_ci } 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ciout: 5448c2ecf20Sopenharmony_ci if (bridge) 5458c2ecf20Sopenharmony_ci dev_put(bridge); 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci return ret4 + ret6; 5488c2ecf20Sopenharmony_ci} 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci/** 5518c2ecf20Sopenharmony_ci * batadv_mcast_mla_br_addr_cpy() - copy a bridge multicast address 5528c2ecf20Sopenharmony_ci * @dst: destination to write to - a multicast MAC address 5538c2ecf20Sopenharmony_ci * @src: source to read from - a multicast IP address 5548c2ecf20Sopenharmony_ci * 5558c2ecf20Sopenharmony_ci * Converts a given multicast IPv4/IPv6 address from a bridge 5568c2ecf20Sopenharmony_ci * to its matching multicast MAC address and copies it into the given 5578c2ecf20Sopenharmony_ci * destination buffer. 5588c2ecf20Sopenharmony_ci * 5598c2ecf20Sopenharmony_ci * Caller needs to make sure the destination buffer can hold 5608c2ecf20Sopenharmony_ci * at least ETH_ALEN bytes. 5618c2ecf20Sopenharmony_ci */ 5628c2ecf20Sopenharmony_cistatic void batadv_mcast_mla_br_addr_cpy(char *dst, const struct br_ip *src) 5638c2ecf20Sopenharmony_ci{ 5648c2ecf20Sopenharmony_ci if (src->proto == htons(ETH_P_IP)) 5658c2ecf20Sopenharmony_ci ip_eth_mc_map(src->dst.ip4, dst); 5668c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 5678c2ecf20Sopenharmony_ci else if (src->proto == htons(ETH_P_IPV6)) 5688c2ecf20Sopenharmony_ci ipv6_eth_mc_map(&src->dst.ip6, dst); 5698c2ecf20Sopenharmony_ci#endif 5708c2ecf20Sopenharmony_ci else 5718c2ecf20Sopenharmony_ci eth_zero_addr(dst); 5728c2ecf20Sopenharmony_ci} 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci/** 5758c2ecf20Sopenharmony_ci * batadv_mcast_mla_bridge_get() - get bridged-in multicast listeners 5768c2ecf20Sopenharmony_ci * @dev: a bridge slave whose bridge to collect multicast addresses from 5778c2ecf20Sopenharmony_ci * @mcast_list: a list to put found addresses into 5788c2ecf20Sopenharmony_ci * @flags: flags indicating the new multicast state 5798c2ecf20Sopenharmony_ci * 5808c2ecf20Sopenharmony_ci * Collects multicast addresses of multicast listeners residing 5818c2ecf20Sopenharmony_ci * on foreign, non-mesh devices which we gave access to our mesh via 5828c2ecf20Sopenharmony_ci * a bridge on top of the given soft interface, dev, in the given 5838c2ecf20Sopenharmony_ci * mcast_list. 5848c2ecf20Sopenharmony_ci * 5858c2ecf20Sopenharmony_ci * Return: -ENOMEM on memory allocation error or the number of 5868c2ecf20Sopenharmony_ci * items added to the mcast_list otherwise. 5878c2ecf20Sopenharmony_ci */ 5888c2ecf20Sopenharmony_cistatic int batadv_mcast_mla_bridge_get(struct net_device *dev, 5898c2ecf20Sopenharmony_ci struct hlist_head *mcast_list, 5908c2ecf20Sopenharmony_ci struct batadv_mcast_mla_flags *flags) 5918c2ecf20Sopenharmony_ci{ 5928c2ecf20Sopenharmony_ci struct list_head bridge_mcast_list = LIST_HEAD_INIT(bridge_mcast_list); 5938c2ecf20Sopenharmony_ci struct br_ip_list *br_ip_entry, *tmp; 5948c2ecf20Sopenharmony_ci u8 tvlv_flags = flags->tvlv_flags; 5958c2ecf20Sopenharmony_ci struct batadv_hw_addr *new; 5968c2ecf20Sopenharmony_ci u8 mcast_addr[ETH_ALEN]; 5978c2ecf20Sopenharmony_ci int ret; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci /* we don't need to detect these devices/listeners, the IGMP/MLD 6008c2ecf20Sopenharmony_ci * snooping code of the Linux bridge already does that for us 6018c2ecf20Sopenharmony_ci */ 6028c2ecf20Sopenharmony_ci ret = br_multicast_list_adjacent(dev, &bridge_mcast_list); 6038c2ecf20Sopenharmony_ci if (ret < 0) 6048c2ecf20Sopenharmony_ci goto out; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci list_for_each_entry(br_ip_entry, &bridge_mcast_list, list) { 6078c2ecf20Sopenharmony_ci if (br_ip_entry->addr.proto == htons(ETH_P_IP)) { 6088c2ecf20Sopenharmony_ci if (tvlv_flags & BATADV_MCAST_WANT_ALL_IPV4) 6098c2ecf20Sopenharmony_ci continue; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci if (tvlv_flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES && 6128c2ecf20Sopenharmony_ci ipv4_is_local_multicast(br_ip_entry->addr.dst.ip4)) 6138c2ecf20Sopenharmony_ci continue; 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci if (!(tvlv_flags & BATADV_MCAST_WANT_NO_RTR4) && 6168c2ecf20Sopenharmony_ci !ipv4_is_local_multicast(br_ip_entry->addr.dst.ip4)) 6178c2ecf20Sopenharmony_ci continue; 6188c2ecf20Sopenharmony_ci } 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 6218c2ecf20Sopenharmony_ci if (br_ip_entry->addr.proto == htons(ETH_P_IPV6)) { 6228c2ecf20Sopenharmony_ci if (tvlv_flags & BATADV_MCAST_WANT_ALL_IPV6) 6238c2ecf20Sopenharmony_ci continue; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci if (tvlv_flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES && 6268c2ecf20Sopenharmony_ci ipv6_addr_is_ll_all_nodes(&br_ip_entry->addr.dst.ip6)) 6278c2ecf20Sopenharmony_ci continue; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci if (!(tvlv_flags & BATADV_MCAST_WANT_NO_RTR6) && 6308c2ecf20Sopenharmony_ci IPV6_ADDR_MC_SCOPE(&br_ip_entry->addr.dst.ip6) > 6318c2ecf20Sopenharmony_ci IPV6_ADDR_SCOPE_LINKLOCAL) 6328c2ecf20Sopenharmony_ci continue; 6338c2ecf20Sopenharmony_ci } 6348c2ecf20Sopenharmony_ci#endif 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci batadv_mcast_mla_br_addr_cpy(mcast_addr, &br_ip_entry->addr); 6378c2ecf20Sopenharmony_ci if (batadv_mcast_mla_is_duplicate(mcast_addr, mcast_list)) 6388c2ecf20Sopenharmony_ci continue; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci new = kmalloc(sizeof(*new), GFP_ATOMIC); 6418c2ecf20Sopenharmony_ci if (!new) { 6428c2ecf20Sopenharmony_ci ret = -ENOMEM; 6438c2ecf20Sopenharmony_ci break; 6448c2ecf20Sopenharmony_ci } 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci ether_addr_copy(new->addr, mcast_addr); 6478c2ecf20Sopenharmony_ci hlist_add_head(&new->list, mcast_list); 6488c2ecf20Sopenharmony_ci } 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ciout: 6518c2ecf20Sopenharmony_ci list_for_each_entry_safe(br_ip_entry, tmp, &bridge_mcast_list, list) { 6528c2ecf20Sopenharmony_ci list_del(&br_ip_entry->list); 6538c2ecf20Sopenharmony_ci kfree(br_ip_entry); 6548c2ecf20Sopenharmony_ci } 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci return ret; 6578c2ecf20Sopenharmony_ci} 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci/** 6608c2ecf20Sopenharmony_ci * batadv_mcast_mla_list_free() - free a list of multicast addresses 6618c2ecf20Sopenharmony_ci * @mcast_list: the list to free 6628c2ecf20Sopenharmony_ci * 6638c2ecf20Sopenharmony_ci * Removes and frees all items in the given mcast_list. 6648c2ecf20Sopenharmony_ci */ 6658c2ecf20Sopenharmony_cistatic void batadv_mcast_mla_list_free(struct hlist_head *mcast_list) 6668c2ecf20Sopenharmony_ci{ 6678c2ecf20Sopenharmony_ci struct batadv_hw_addr *mcast_entry; 6688c2ecf20Sopenharmony_ci struct hlist_node *tmp; 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci hlist_for_each_entry_safe(mcast_entry, tmp, mcast_list, list) { 6718c2ecf20Sopenharmony_ci hlist_del(&mcast_entry->list); 6728c2ecf20Sopenharmony_ci kfree(mcast_entry); 6738c2ecf20Sopenharmony_ci } 6748c2ecf20Sopenharmony_ci} 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci/** 6778c2ecf20Sopenharmony_ci * batadv_mcast_mla_tt_retract() - clean up multicast listener announcements 6788c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 6798c2ecf20Sopenharmony_ci * @mcast_list: a list of addresses which should _not_ be removed 6808c2ecf20Sopenharmony_ci * 6818c2ecf20Sopenharmony_ci * Retracts the announcement of any multicast listener from the 6828c2ecf20Sopenharmony_ci * translation table except the ones listed in the given mcast_list. 6838c2ecf20Sopenharmony_ci * 6848c2ecf20Sopenharmony_ci * If mcast_list is NULL then all are retracted. 6858c2ecf20Sopenharmony_ci */ 6868c2ecf20Sopenharmony_cistatic void batadv_mcast_mla_tt_retract(struct batadv_priv *bat_priv, 6878c2ecf20Sopenharmony_ci struct hlist_head *mcast_list) 6888c2ecf20Sopenharmony_ci{ 6898c2ecf20Sopenharmony_ci struct batadv_hw_addr *mcast_entry; 6908c2ecf20Sopenharmony_ci struct hlist_node *tmp; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci hlist_for_each_entry_safe(mcast_entry, tmp, &bat_priv->mcast.mla_list, 6938c2ecf20Sopenharmony_ci list) { 6948c2ecf20Sopenharmony_ci if (mcast_list && 6958c2ecf20Sopenharmony_ci batadv_mcast_mla_is_duplicate(mcast_entry->addr, 6968c2ecf20Sopenharmony_ci mcast_list)) 6978c2ecf20Sopenharmony_ci continue; 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci batadv_tt_local_remove(bat_priv, mcast_entry->addr, 7008c2ecf20Sopenharmony_ci BATADV_NO_FLAGS, 7018c2ecf20Sopenharmony_ci "mcast TT outdated", false); 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci hlist_del(&mcast_entry->list); 7048c2ecf20Sopenharmony_ci kfree(mcast_entry); 7058c2ecf20Sopenharmony_ci } 7068c2ecf20Sopenharmony_ci} 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci/** 7098c2ecf20Sopenharmony_ci * batadv_mcast_mla_tt_add() - add multicast listener announcements 7108c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 7118c2ecf20Sopenharmony_ci * @mcast_list: a list of addresses which are going to get added 7128c2ecf20Sopenharmony_ci * 7138c2ecf20Sopenharmony_ci * Adds multicast listener announcements from the given mcast_list to the 7148c2ecf20Sopenharmony_ci * translation table if they have not been added yet. 7158c2ecf20Sopenharmony_ci */ 7168c2ecf20Sopenharmony_cistatic void batadv_mcast_mla_tt_add(struct batadv_priv *bat_priv, 7178c2ecf20Sopenharmony_ci struct hlist_head *mcast_list) 7188c2ecf20Sopenharmony_ci{ 7198c2ecf20Sopenharmony_ci struct batadv_hw_addr *mcast_entry; 7208c2ecf20Sopenharmony_ci struct hlist_node *tmp; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci if (!mcast_list) 7238c2ecf20Sopenharmony_ci return; 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci hlist_for_each_entry_safe(mcast_entry, tmp, mcast_list, list) { 7268c2ecf20Sopenharmony_ci if (batadv_mcast_mla_is_duplicate(mcast_entry->addr, 7278c2ecf20Sopenharmony_ci &bat_priv->mcast.mla_list)) 7288c2ecf20Sopenharmony_ci continue; 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci if (!batadv_tt_local_add(bat_priv->soft_iface, 7318c2ecf20Sopenharmony_ci mcast_entry->addr, BATADV_NO_FLAGS, 7328c2ecf20Sopenharmony_ci BATADV_NULL_IFINDEX, BATADV_NO_MARK)) 7338c2ecf20Sopenharmony_ci continue; 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci hlist_del(&mcast_entry->list); 7368c2ecf20Sopenharmony_ci hlist_add_head(&mcast_entry->list, &bat_priv->mcast.mla_list); 7378c2ecf20Sopenharmony_ci } 7388c2ecf20Sopenharmony_ci} 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci/** 7418c2ecf20Sopenharmony_ci * batadv_mcast_querier_log() - debug output regarding the querier status on 7428c2ecf20Sopenharmony_ci * link 7438c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 7448c2ecf20Sopenharmony_ci * @str_proto: a string for the querier protocol (e.g. "IGMP" or "MLD") 7458c2ecf20Sopenharmony_ci * @old_state: the previous querier state on our link 7468c2ecf20Sopenharmony_ci * @new_state: the new querier state on our link 7478c2ecf20Sopenharmony_ci * 7488c2ecf20Sopenharmony_ci * Outputs debug messages to the logging facility with log level 'mcast' 7498c2ecf20Sopenharmony_ci * regarding changes to the querier status on the link which are relevant 7508c2ecf20Sopenharmony_ci * to our multicast optimizations. 7518c2ecf20Sopenharmony_ci * 7528c2ecf20Sopenharmony_ci * Usually this is about whether a querier appeared or vanished in 7538c2ecf20Sopenharmony_ci * our mesh or whether the querier is in the suboptimal position of being 7548c2ecf20Sopenharmony_ci * behind our local bridge segment: Snooping switches will directly 7558c2ecf20Sopenharmony_ci * forward listener reports to the querier, therefore batman-adv and 7568c2ecf20Sopenharmony_ci * the bridge will potentially not see these listeners - the querier is 7578c2ecf20Sopenharmony_ci * potentially shadowing listeners from us then. 7588c2ecf20Sopenharmony_ci * 7598c2ecf20Sopenharmony_ci * This is only interesting for nodes with a bridge on top of their 7608c2ecf20Sopenharmony_ci * soft interface. 7618c2ecf20Sopenharmony_ci */ 7628c2ecf20Sopenharmony_cistatic void 7638c2ecf20Sopenharmony_cibatadv_mcast_querier_log(struct batadv_priv *bat_priv, char *str_proto, 7648c2ecf20Sopenharmony_ci struct batadv_mcast_querier_state *old_state, 7658c2ecf20Sopenharmony_ci struct batadv_mcast_querier_state *new_state) 7668c2ecf20Sopenharmony_ci{ 7678c2ecf20Sopenharmony_ci if (!old_state->exists && new_state->exists) 7688c2ecf20Sopenharmony_ci batadv_info(bat_priv->soft_iface, "%s Querier appeared\n", 7698c2ecf20Sopenharmony_ci str_proto); 7708c2ecf20Sopenharmony_ci else if (old_state->exists && !new_state->exists) 7718c2ecf20Sopenharmony_ci batadv_info(bat_priv->soft_iface, 7728c2ecf20Sopenharmony_ci "%s Querier disappeared - multicast optimizations disabled\n", 7738c2ecf20Sopenharmony_ci str_proto); 7748c2ecf20Sopenharmony_ci else if (!bat_priv->mcast.mla_flags.bridged && !new_state->exists) 7758c2ecf20Sopenharmony_ci batadv_info(bat_priv->soft_iface, 7768c2ecf20Sopenharmony_ci "No %s Querier present - multicast optimizations disabled\n", 7778c2ecf20Sopenharmony_ci str_proto); 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci if (new_state->exists) { 7808c2ecf20Sopenharmony_ci if ((!old_state->shadowing && new_state->shadowing) || 7818c2ecf20Sopenharmony_ci (!old_state->exists && new_state->shadowing)) 7828c2ecf20Sopenharmony_ci batadv_dbg(BATADV_DBG_MCAST, bat_priv, 7838c2ecf20Sopenharmony_ci "%s Querier is behind our bridged segment: Might shadow listeners\n", 7848c2ecf20Sopenharmony_ci str_proto); 7858c2ecf20Sopenharmony_ci else if (old_state->shadowing && !new_state->shadowing) 7868c2ecf20Sopenharmony_ci batadv_dbg(BATADV_DBG_MCAST, bat_priv, 7878c2ecf20Sopenharmony_ci "%s Querier is not behind our bridged segment\n", 7888c2ecf20Sopenharmony_ci str_proto); 7898c2ecf20Sopenharmony_ci } 7908c2ecf20Sopenharmony_ci} 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci/** 7938c2ecf20Sopenharmony_ci * batadv_mcast_bridge_log() - debug output for topology changes in bridged 7948c2ecf20Sopenharmony_ci * setups 7958c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 7968c2ecf20Sopenharmony_ci * @new_flags: flags indicating the new multicast state 7978c2ecf20Sopenharmony_ci * 7988c2ecf20Sopenharmony_ci * If no bridges are ever used on this node, then this function does nothing. 7998c2ecf20Sopenharmony_ci * 8008c2ecf20Sopenharmony_ci * Otherwise this function outputs debug information to the 'mcast' log level 8018c2ecf20Sopenharmony_ci * which might be relevant to our multicast optimizations. 8028c2ecf20Sopenharmony_ci * 8038c2ecf20Sopenharmony_ci * More precisely, it outputs information when a bridge interface is added or 8048c2ecf20Sopenharmony_ci * removed from a soft interface. And when a bridge is present, it further 8058c2ecf20Sopenharmony_ci * outputs information about the querier state which is relevant for the 8068c2ecf20Sopenharmony_ci * multicast flags this node is going to set. 8078c2ecf20Sopenharmony_ci */ 8088c2ecf20Sopenharmony_cistatic void 8098c2ecf20Sopenharmony_cibatadv_mcast_bridge_log(struct batadv_priv *bat_priv, 8108c2ecf20Sopenharmony_ci struct batadv_mcast_mla_flags *new_flags) 8118c2ecf20Sopenharmony_ci{ 8128c2ecf20Sopenharmony_ci struct batadv_mcast_mla_flags *old_flags = &bat_priv->mcast.mla_flags; 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci if (!old_flags->bridged && new_flags->bridged) 8158c2ecf20Sopenharmony_ci batadv_dbg(BATADV_DBG_MCAST, bat_priv, 8168c2ecf20Sopenharmony_ci "Bridge added: Setting Unsnoopables(U)-flag\n"); 8178c2ecf20Sopenharmony_ci else if (old_flags->bridged && !new_flags->bridged) 8188c2ecf20Sopenharmony_ci batadv_dbg(BATADV_DBG_MCAST, bat_priv, 8198c2ecf20Sopenharmony_ci "Bridge removed: Unsetting Unsnoopables(U)-flag\n"); 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci if (new_flags->bridged) { 8228c2ecf20Sopenharmony_ci batadv_mcast_querier_log(bat_priv, "IGMP", 8238c2ecf20Sopenharmony_ci &old_flags->querier_ipv4, 8248c2ecf20Sopenharmony_ci &new_flags->querier_ipv4); 8258c2ecf20Sopenharmony_ci batadv_mcast_querier_log(bat_priv, "MLD", 8268c2ecf20Sopenharmony_ci &old_flags->querier_ipv6, 8278c2ecf20Sopenharmony_ci &new_flags->querier_ipv6); 8288c2ecf20Sopenharmony_ci } 8298c2ecf20Sopenharmony_ci} 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci/** 8328c2ecf20Sopenharmony_ci * batadv_mcast_flags_logs() - output debug information about mcast flag changes 8338c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 8348c2ecf20Sopenharmony_ci * @flags: TVLV flags indicating the new multicast state 8358c2ecf20Sopenharmony_ci * 8368c2ecf20Sopenharmony_ci * Whenever the multicast TVLV flags this node announces change, this function 8378c2ecf20Sopenharmony_ci * should be used to notify userspace about the change. 8388c2ecf20Sopenharmony_ci */ 8398c2ecf20Sopenharmony_cistatic void batadv_mcast_flags_log(struct batadv_priv *bat_priv, u8 flags) 8408c2ecf20Sopenharmony_ci{ 8418c2ecf20Sopenharmony_ci bool old_enabled = bat_priv->mcast.mla_flags.enabled; 8428c2ecf20Sopenharmony_ci u8 old_flags = bat_priv->mcast.mla_flags.tvlv_flags; 8438c2ecf20Sopenharmony_ci char str_old_flags[] = "[.... . ]"; 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci sprintf(str_old_flags, "[%c%c%c%s%s]", 8468c2ecf20Sopenharmony_ci (old_flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES) ? 'U' : '.', 8478c2ecf20Sopenharmony_ci (old_flags & BATADV_MCAST_WANT_ALL_IPV4) ? '4' : '.', 8488c2ecf20Sopenharmony_ci (old_flags & BATADV_MCAST_WANT_ALL_IPV6) ? '6' : '.', 8498c2ecf20Sopenharmony_ci !(old_flags & BATADV_MCAST_WANT_NO_RTR4) ? "R4" : ". ", 8508c2ecf20Sopenharmony_ci !(old_flags & BATADV_MCAST_WANT_NO_RTR6) ? "R6" : ". "); 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci batadv_dbg(BATADV_DBG_MCAST, bat_priv, 8538c2ecf20Sopenharmony_ci "Changing multicast flags from '%s' to '[%c%c%c%s%s]'\n", 8548c2ecf20Sopenharmony_ci old_enabled ? str_old_flags : "<undefined>", 8558c2ecf20Sopenharmony_ci (flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES) ? 'U' : '.', 8568c2ecf20Sopenharmony_ci (flags & BATADV_MCAST_WANT_ALL_IPV4) ? '4' : '.', 8578c2ecf20Sopenharmony_ci (flags & BATADV_MCAST_WANT_ALL_IPV6) ? '6' : '.', 8588c2ecf20Sopenharmony_ci !(flags & BATADV_MCAST_WANT_NO_RTR4) ? "R4" : ". ", 8598c2ecf20Sopenharmony_ci !(flags & BATADV_MCAST_WANT_NO_RTR6) ? "R6" : ". "); 8608c2ecf20Sopenharmony_ci} 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci/** 8638c2ecf20Sopenharmony_ci * batadv_mcast_mla_flags_update() - update multicast flags 8648c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 8658c2ecf20Sopenharmony_ci * @flags: flags indicating the new multicast state 8668c2ecf20Sopenharmony_ci * 8678c2ecf20Sopenharmony_ci * Updates the own multicast tvlv with our current multicast related settings, 8688c2ecf20Sopenharmony_ci * capabilities and inabilities. 8698c2ecf20Sopenharmony_ci */ 8708c2ecf20Sopenharmony_cistatic void 8718c2ecf20Sopenharmony_cibatadv_mcast_mla_flags_update(struct batadv_priv *bat_priv, 8728c2ecf20Sopenharmony_ci struct batadv_mcast_mla_flags *flags) 8738c2ecf20Sopenharmony_ci{ 8748c2ecf20Sopenharmony_ci struct batadv_tvlv_mcast_data mcast_data; 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci if (!memcmp(flags, &bat_priv->mcast.mla_flags, sizeof(*flags))) 8778c2ecf20Sopenharmony_ci return; 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci batadv_mcast_bridge_log(bat_priv, flags); 8808c2ecf20Sopenharmony_ci batadv_mcast_flags_log(bat_priv, flags->tvlv_flags); 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci mcast_data.flags = flags->tvlv_flags; 8838c2ecf20Sopenharmony_ci memset(mcast_data.reserved, 0, sizeof(mcast_data.reserved)); 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci batadv_tvlv_container_register(bat_priv, BATADV_TVLV_MCAST, 2, 8868c2ecf20Sopenharmony_ci &mcast_data, sizeof(mcast_data)); 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci bat_priv->mcast.mla_flags = *flags; 8898c2ecf20Sopenharmony_ci} 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci/** 8928c2ecf20Sopenharmony_ci * __batadv_mcast_mla_update() - update the own MLAs 8938c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 8948c2ecf20Sopenharmony_ci * 8958c2ecf20Sopenharmony_ci * Updates the own multicast listener announcements in the translation 8968c2ecf20Sopenharmony_ci * table as well as the own, announced multicast tvlv container. 8978c2ecf20Sopenharmony_ci * 8988c2ecf20Sopenharmony_ci * Note that non-conflicting reads and writes to bat_priv->mcast.mla_list 8998c2ecf20Sopenharmony_ci * in batadv_mcast_mla_tt_retract() and batadv_mcast_mla_tt_add() are 9008c2ecf20Sopenharmony_ci * ensured by the non-parallel execution of the worker this function 9018c2ecf20Sopenharmony_ci * belongs to. 9028c2ecf20Sopenharmony_ci */ 9038c2ecf20Sopenharmony_cistatic void __batadv_mcast_mla_update(struct batadv_priv *bat_priv) 9048c2ecf20Sopenharmony_ci{ 9058c2ecf20Sopenharmony_ci struct net_device *soft_iface = bat_priv->soft_iface; 9068c2ecf20Sopenharmony_ci struct hlist_head mcast_list = HLIST_HEAD_INIT; 9078c2ecf20Sopenharmony_ci struct batadv_mcast_mla_flags flags; 9088c2ecf20Sopenharmony_ci int ret; 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci flags = batadv_mcast_mla_flags_get(bat_priv); 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci ret = batadv_mcast_mla_softif_get(soft_iface, &mcast_list, &flags); 9138c2ecf20Sopenharmony_ci if (ret < 0) 9148c2ecf20Sopenharmony_ci goto out; 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci ret = batadv_mcast_mla_bridge_get(soft_iface, &mcast_list, &flags); 9178c2ecf20Sopenharmony_ci if (ret < 0) 9188c2ecf20Sopenharmony_ci goto out; 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci spin_lock(&bat_priv->mcast.mla_lock); 9218c2ecf20Sopenharmony_ci batadv_mcast_mla_tt_retract(bat_priv, &mcast_list); 9228c2ecf20Sopenharmony_ci batadv_mcast_mla_tt_add(bat_priv, &mcast_list); 9238c2ecf20Sopenharmony_ci batadv_mcast_mla_flags_update(bat_priv, &flags); 9248c2ecf20Sopenharmony_ci spin_unlock(&bat_priv->mcast.mla_lock); 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ciout: 9278c2ecf20Sopenharmony_ci batadv_mcast_mla_list_free(&mcast_list); 9288c2ecf20Sopenharmony_ci} 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci/** 9318c2ecf20Sopenharmony_ci * batadv_mcast_mla_update() - update the own MLAs 9328c2ecf20Sopenharmony_ci * @work: kernel work struct 9338c2ecf20Sopenharmony_ci * 9348c2ecf20Sopenharmony_ci * Updates the own multicast listener announcements in the translation 9358c2ecf20Sopenharmony_ci * table as well as the own, announced multicast tvlv container. 9368c2ecf20Sopenharmony_ci * 9378c2ecf20Sopenharmony_ci * In the end, reschedules the work timer. 9388c2ecf20Sopenharmony_ci */ 9398c2ecf20Sopenharmony_cistatic void batadv_mcast_mla_update(struct work_struct *work) 9408c2ecf20Sopenharmony_ci{ 9418c2ecf20Sopenharmony_ci struct delayed_work *delayed_work; 9428c2ecf20Sopenharmony_ci struct batadv_priv_mcast *priv_mcast; 9438c2ecf20Sopenharmony_ci struct batadv_priv *bat_priv; 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci delayed_work = to_delayed_work(work); 9468c2ecf20Sopenharmony_ci priv_mcast = container_of(delayed_work, struct batadv_priv_mcast, work); 9478c2ecf20Sopenharmony_ci bat_priv = container_of(priv_mcast, struct batadv_priv, mcast); 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci __batadv_mcast_mla_update(bat_priv); 9508c2ecf20Sopenharmony_ci batadv_mcast_start_timer(bat_priv); 9518c2ecf20Sopenharmony_ci} 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci/** 9548c2ecf20Sopenharmony_ci * batadv_mcast_is_report_ipv4() - check for IGMP reports 9558c2ecf20Sopenharmony_ci * @skb: the ethernet frame destined for the mesh 9568c2ecf20Sopenharmony_ci * 9578c2ecf20Sopenharmony_ci * This call might reallocate skb data. 9588c2ecf20Sopenharmony_ci * 9598c2ecf20Sopenharmony_ci * Checks whether the given frame is a valid IGMP report. 9608c2ecf20Sopenharmony_ci * 9618c2ecf20Sopenharmony_ci * Return: If so then true, otherwise false. 9628c2ecf20Sopenharmony_ci */ 9638c2ecf20Sopenharmony_cistatic bool batadv_mcast_is_report_ipv4(struct sk_buff *skb) 9648c2ecf20Sopenharmony_ci{ 9658c2ecf20Sopenharmony_ci if (ip_mc_check_igmp(skb) < 0) 9668c2ecf20Sopenharmony_ci return false; 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci switch (igmp_hdr(skb)->type) { 9698c2ecf20Sopenharmony_ci case IGMP_HOST_MEMBERSHIP_REPORT: 9708c2ecf20Sopenharmony_ci case IGMPV2_HOST_MEMBERSHIP_REPORT: 9718c2ecf20Sopenharmony_ci case IGMPV3_HOST_MEMBERSHIP_REPORT: 9728c2ecf20Sopenharmony_ci return true; 9738c2ecf20Sopenharmony_ci } 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci return false; 9768c2ecf20Sopenharmony_ci} 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci/** 9798c2ecf20Sopenharmony_ci * batadv_mcast_forw_mode_check_ipv4() - check for optimized forwarding 9808c2ecf20Sopenharmony_ci * potential 9818c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 9828c2ecf20Sopenharmony_ci * @skb: the IPv4 packet to check 9838c2ecf20Sopenharmony_ci * @is_unsnoopable: stores whether the destination is snoopable 9848c2ecf20Sopenharmony_ci * @is_routable: stores whether the destination is routable 9858c2ecf20Sopenharmony_ci * 9868c2ecf20Sopenharmony_ci * Checks whether the given IPv4 packet has the potential to be forwarded with a 9878c2ecf20Sopenharmony_ci * mode more optimal than classic flooding. 9888c2ecf20Sopenharmony_ci * 9898c2ecf20Sopenharmony_ci * Return: If so then 0. Otherwise -EINVAL or -ENOMEM in case of memory 9908c2ecf20Sopenharmony_ci * allocation failure. 9918c2ecf20Sopenharmony_ci */ 9928c2ecf20Sopenharmony_cistatic int batadv_mcast_forw_mode_check_ipv4(struct batadv_priv *bat_priv, 9938c2ecf20Sopenharmony_ci struct sk_buff *skb, 9948c2ecf20Sopenharmony_ci bool *is_unsnoopable, 9958c2ecf20Sopenharmony_ci int *is_routable) 9968c2ecf20Sopenharmony_ci{ 9978c2ecf20Sopenharmony_ci struct iphdr *iphdr; 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci /* We might fail due to out-of-memory -> drop it */ 10008c2ecf20Sopenharmony_ci if (!pskb_may_pull(skb, sizeof(struct ethhdr) + sizeof(*iphdr))) 10018c2ecf20Sopenharmony_ci return -ENOMEM; 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci if (batadv_mcast_is_report_ipv4(skb)) 10048c2ecf20Sopenharmony_ci return -EINVAL; 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci iphdr = ip_hdr(skb); 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci /* link-local multicast listeners behind a bridge are 10098c2ecf20Sopenharmony_ci * not snoopable (see RFC4541, section 2.1.2.2) 10108c2ecf20Sopenharmony_ci */ 10118c2ecf20Sopenharmony_ci if (ipv4_is_local_multicast(iphdr->daddr)) 10128c2ecf20Sopenharmony_ci *is_unsnoopable = true; 10138c2ecf20Sopenharmony_ci else 10148c2ecf20Sopenharmony_ci *is_routable = ETH_P_IP; 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci return 0; 10178c2ecf20Sopenharmony_ci} 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci/** 10208c2ecf20Sopenharmony_ci * batadv_mcast_is_report_ipv6() - check for MLD reports 10218c2ecf20Sopenharmony_ci * @skb: the ethernet frame destined for the mesh 10228c2ecf20Sopenharmony_ci * 10238c2ecf20Sopenharmony_ci * This call might reallocate skb data. 10248c2ecf20Sopenharmony_ci * 10258c2ecf20Sopenharmony_ci * Checks whether the given frame is a valid MLD report. 10268c2ecf20Sopenharmony_ci * 10278c2ecf20Sopenharmony_ci * Return: If so then true, otherwise false. 10288c2ecf20Sopenharmony_ci */ 10298c2ecf20Sopenharmony_cistatic bool batadv_mcast_is_report_ipv6(struct sk_buff *skb) 10308c2ecf20Sopenharmony_ci{ 10318c2ecf20Sopenharmony_ci if (ipv6_mc_check_mld(skb) < 0) 10328c2ecf20Sopenharmony_ci return false; 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci switch (icmp6_hdr(skb)->icmp6_type) { 10358c2ecf20Sopenharmony_ci case ICMPV6_MGM_REPORT: 10368c2ecf20Sopenharmony_ci case ICMPV6_MLD2_REPORT: 10378c2ecf20Sopenharmony_ci return true; 10388c2ecf20Sopenharmony_ci } 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci return false; 10418c2ecf20Sopenharmony_ci} 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci/** 10448c2ecf20Sopenharmony_ci * batadv_mcast_forw_mode_check_ipv6() - check for optimized forwarding 10458c2ecf20Sopenharmony_ci * potential 10468c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 10478c2ecf20Sopenharmony_ci * @skb: the IPv6 packet to check 10488c2ecf20Sopenharmony_ci * @is_unsnoopable: stores whether the destination is snoopable 10498c2ecf20Sopenharmony_ci * @is_routable: stores whether the destination is routable 10508c2ecf20Sopenharmony_ci * 10518c2ecf20Sopenharmony_ci * Checks whether the given IPv6 packet has the potential to be forwarded with a 10528c2ecf20Sopenharmony_ci * mode more optimal than classic flooding. 10538c2ecf20Sopenharmony_ci * 10548c2ecf20Sopenharmony_ci * Return: If so then 0. Otherwise -EINVAL is or -ENOMEM if we are out of memory 10558c2ecf20Sopenharmony_ci */ 10568c2ecf20Sopenharmony_cistatic int batadv_mcast_forw_mode_check_ipv6(struct batadv_priv *bat_priv, 10578c2ecf20Sopenharmony_ci struct sk_buff *skb, 10588c2ecf20Sopenharmony_ci bool *is_unsnoopable, 10598c2ecf20Sopenharmony_ci int *is_routable) 10608c2ecf20Sopenharmony_ci{ 10618c2ecf20Sopenharmony_ci struct ipv6hdr *ip6hdr; 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci /* We might fail due to out-of-memory -> drop it */ 10648c2ecf20Sopenharmony_ci if (!pskb_may_pull(skb, sizeof(struct ethhdr) + sizeof(*ip6hdr))) 10658c2ecf20Sopenharmony_ci return -ENOMEM; 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci if (batadv_mcast_is_report_ipv6(skb)) 10688c2ecf20Sopenharmony_ci return -EINVAL; 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci ip6hdr = ipv6_hdr(skb); 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci if (IPV6_ADDR_MC_SCOPE(&ip6hdr->daddr) < IPV6_ADDR_SCOPE_LINKLOCAL) 10738c2ecf20Sopenharmony_ci return -EINVAL; 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci /* link-local-all-nodes multicast listeners behind a bridge are 10768c2ecf20Sopenharmony_ci * not snoopable (see RFC4541, section 3, paragraph 3) 10778c2ecf20Sopenharmony_ci */ 10788c2ecf20Sopenharmony_ci if (ipv6_addr_is_ll_all_nodes(&ip6hdr->daddr)) 10798c2ecf20Sopenharmony_ci *is_unsnoopable = true; 10808c2ecf20Sopenharmony_ci else if (IPV6_ADDR_MC_SCOPE(&ip6hdr->daddr) > IPV6_ADDR_SCOPE_LINKLOCAL) 10818c2ecf20Sopenharmony_ci *is_routable = ETH_P_IPV6; 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci return 0; 10848c2ecf20Sopenharmony_ci} 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci/** 10878c2ecf20Sopenharmony_ci * batadv_mcast_forw_mode_check() - check for optimized forwarding potential 10888c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 10898c2ecf20Sopenharmony_ci * @skb: the multicast frame to check 10908c2ecf20Sopenharmony_ci * @is_unsnoopable: stores whether the destination is snoopable 10918c2ecf20Sopenharmony_ci * @is_routable: stores whether the destination is routable 10928c2ecf20Sopenharmony_ci * 10938c2ecf20Sopenharmony_ci * Checks whether the given multicast ethernet frame has the potential to be 10948c2ecf20Sopenharmony_ci * forwarded with a mode more optimal than classic flooding. 10958c2ecf20Sopenharmony_ci * 10968c2ecf20Sopenharmony_ci * Return: If so then 0. Otherwise -EINVAL is or -ENOMEM if we are out of memory 10978c2ecf20Sopenharmony_ci */ 10988c2ecf20Sopenharmony_cistatic int batadv_mcast_forw_mode_check(struct batadv_priv *bat_priv, 10998c2ecf20Sopenharmony_ci struct sk_buff *skb, 11008c2ecf20Sopenharmony_ci bool *is_unsnoopable, 11018c2ecf20Sopenharmony_ci int *is_routable) 11028c2ecf20Sopenharmony_ci{ 11038c2ecf20Sopenharmony_ci struct ethhdr *ethhdr = eth_hdr(skb); 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci if (!atomic_read(&bat_priv->multicast_mode)) 11068c2ecf20Sopenharmony_ci return -EINVAL; 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci switch (ntohs(ethhdr->h_proto)) { 11098c2ecf20Sopenharmony_ci case ETH_P_IP: 11108c2ecf20Sopenharmony_ci return batadv_mcast_forw_mode_check_ipv4(bat_priv, skb, 11118c2ecf20Sopenharmony_ci is_unsnoopable, 11128c2ecf20Sopenharmony_ci is_routable); 11138c2ecf20Sopenharmony_ci case ETH_P_IPV6: 11148c2ecf20Sopenharmony_ci if (!IS_ENABLED(CONFIG_IPV6)) 11158c2ecf20Sopenharmony_ci return -EINVAL; 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci return batadv_mcast_forw_mode_check_ipv6(bat_priv, skb, 11188c2ecf20Sopenharmony_ci is_unsnoopable, 11198c2ecf20Sopenharmony_ci is_routable); 11208c2ecf20Sopenharmony_ci default: 11218c2ecf20Sopenharmony_ci return -EINVAL; 11228c2ecf20Sopenharmony_ci } 11238c2ecf20Sopenharmony_ci} 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci/** 11268c2ecf20Sopenharmony_ci * batadv_mcast_forw_want_all_ip_count() - count nodes with unspecific mcast 11278c2ecf20Sopenharmony_ci * interest 11288c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 11298c2ecf20Sopenharmony_ci * @ethhdr: ethernet header of a packet 11308c2ecf20Sopenharmony_ci * 11318c2ecf20Sopenharmony_ci * Return: the number of nodes which want all IPv4 multicast traffic if the 11328c2ecf20Sopenharmony_ci * given ethhdr is from an IPv4 packet or the number of nodes which want all 11338c2ecf20Sopenharmony_ci * IPv6 traffic if it matches an IPv6 packet. 11348c2ecf20Sopenharmony_ci */ 11358c2ecf20Sopenharmony_cistatic int batadv_mcast_forw_want_all_ip_count(struct batadv_priv *bat_priv, 11368c2ecf20Sopenharmony_ci struct ethhdr *ethhdr) 11378c2ecf20Sopenharmony_ci{ 11388c2ecf20Sopenharmony_ci switch (ntohs(ethhdr->h_proto)) { 11398c2ecf20Sopenharmony_ci case ETH_P_IP: 11408c2ecf20Sopenharmony_ci return atomic_read(&bat_priv->mcast.num_want_all_ipv4); 11418c2ecf20Sopenharmony_ci case ETH_P_IPV6: 11428c2ecf20Sopenharmony_ci return atomic_read(&bat_priv->mcast.num_want_all_ipv6); 11438c2ecf20Sopenharmony_ci default: 11448c2ecf20Sopenharmony_ci /* we shouldn't be here... */ 11458c2ecf20Sopenharmony_ci return 0; 11468c2ecf20Sopenharmony_ci } 11478c2ecf20Sopenharmony_ci} 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci/** 11508c2ecf20Sopenharmony_ci * batadv_mcast_forw_rtr_count() - count nodes with a multicast router 11518c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 11528c2ecf20Sopenharmony_ci * @protocol: the ethernet protocol type to count multicast routers for 11538c2ecf20Sopenharmony_ci * 11548c2ecf20Sopenharmony_ci * Return: the number of nodes which want all routable IPv4 multicast traffic 11558c2ecf20Sopenharmony_ci * if the protocol is ETH_P_IP or the number of nodes which want all routable 11568c2ecf20Sopenharmony_ci * IPv6 traffic if the protocol is ETH_P_IPV6. Otherwise returns 0. 11578c2ecf20Sopenharmony_ci */ 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_cistatic int batadv_mcast_forw_rtr_count(struct batadv_priv *bat_priv, 11608c2ecf20Sopenharmony_ci int protocol) 11618c2ecf20Sopenharmony_ci{ 11628c2ecf20Sopenharmony_ci switch (protocol) { 11638c2ecf20Sopenharmony_ci case ETH_P_IP: 11648c2ecf20Sopenharmony_ci return atomic_read(&bat_priv->mcast.num_want_all_rtr4); 11658c2ecf20Sopenharmony_ci case ETH_P_IPV6: 11668c2ecf20Sopenharmony_ci return atomic_read(&bat_priv->mcast.num_want_all_rtr6); 11678c2ecf20Sopenharmony_ci default: 11688c2ecf20Sopenharmony_ci return 0; 11698c2ecf20Sopenharmony_ci } 11708c2ecf20Sopenharmony_ci} 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci/** 11738c2ecf20Sopenharmony_ci * batadv_mcast_forw_tt_node_get() - get a multicast tt node 11748c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 11758c2ecf20Sopenharmony_ci * @ethhdr: the ether header containing the multicast destination 11768c2ecf20Sopenharmony_ci * 11778c2ecf20Sopenharmony_ci * Return: an orig_node matching the multicast address provided by ethhdr 11788c2ecf20Sopenharmony_ci * via a translation table lookup. This increases the returned nodes refcount. 11798c2ecf20Sopenharmony_ci */ 11808c2ecf20Sopenharmony_cistatic struct batadv_orig_node * 11818c2ecf20Sopenharmony_cibatadv_mcast_forw_tt_node_get(struct batadv_priv *bat_priv, 11828c2ecf20Sopenharmony_ci struct ethhdr *ethhdr) 11838c2ecf20Sopenharmony_ci{ 11848c2ecf20Sopenharmony_ci return batadv_transtable_search(bat_priv, NULL, ethhdr->h_dest, 11858c2ecf20Sopenharmony_ci BATADV_NO_FLAGS); 11868c2ecf20Sopenharmony_ci} 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci/** 11898c2ecf20Sopenharmony_ci * batadv_mcast_forw_ipv4_node_get() - get a node with an ipv4 flag 11908c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 11918c2ecf20Sopenharmony_ci * 11928c2ecf20Sopenharmony_ci * Return: an orig_node which has the BATADV_MCAST_WANT_ALL_IPV4 flag set and 11938c2ecf20Sopenharmony_ci * increases its refcount. 11948c2ecf20Sopenharmony_ci */ 11958c2ecf20Sopenharmony_cistatic struct batadv_orig_node * 11968c2ecf20Sopenharmony_cibatadv_mcast_forw_ipv4_node_get(struct batadv_priv *bat_priv) 11978c2ecf20Sopenharmony_ci{ 11988c2ecf20Sopenharmony_ci struct batadv_orig_node *tmp_orig_node, *orig_node = NULL; 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_ci rcu_read_lock(); 12018c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(tmp_orig_node, 12028c2ecf20Sopenharmony_ci &bat_priv->mcast.want_all_ipv4_list, 12038c2ecf20Sopenharmony_ci mcast_want_all_ipv4_node) { 12048c2ecf20Sopenharmony_ci if (!kref_get_unless_zero(&tmp_orig_node->refcount)) 12058c2ecf20Sopenharmony_ci continue; 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_ci orig_node = tmp_orig_node; 12088c2ecf20Sopenharmony_ci break; 12098c2ecf20Sopenharmony_ci } 12108c2ecf20Sopenharmony_ci rcu_read_unlock(); 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci return orig_node; 12138c2ecf20Sopenharmony_ci} 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci/** 12168c2ecf20Sopenharmony_ci * batadv_mcast_forw_ipv6_node_get() - get a node with an ipv6 flag 12178c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 12188c2ecf20Sopenharmony_ci * 12198c2ecf20Sopenharmony_ci * Return: an orig_node which has the BATADV_MCAST_WANT_ALL_IPV6 flag set 12208c2ecf20Sopenharmony_ci * and increases its refcount. 12218c2ecf20Sopenharmony_ci */ 12228c2ecf20Sopenharmony_cistatic struct batadv_orig_node * 12238c2ecf20Sopenharmony_cibatadv_mcast_forw_ipv6_node_get(struct batadv_priv *bat_priv) 12248c2ecf20Sopenharmony_ci{ 12258c2ecf20Sopenharmony_ci struct batadv_orig_node *tmp_orig_node, *orig_node = NULL; 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci rcu_read_lock(); 12288c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(tmp_orig_node, 12298c2ecf20Sopenharmony_ci &bat_priv->mcast.want_all_ipv6_list, 12308c2ecf20Sopenharmony_ci mcast_want_all_ipv6_node) { 12318c2ecf20Sopenharmony_ci if (!kref_get_unless_zero(&tmp_orig_node->refcount)) 12328c2ecf20Sopenharmony_ci continue; 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci orig_node = tmp_orig_node; 12358c2ecf20Sopenharmony_ci break; 12368c2ecf20Sopenharmony_ci } 12378c2ecf20Sopenharmony_ci rcu_read_unlock(); 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_ci return orig_node; 12408c2ecf20Sopenharmony_ci} 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ci/** 12438c2ecf20Sopenharmony_ci * batadv_mcast_forw_ip_node_get() - get a node with an ipv4/ipv6 flag 12448c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 12458c2ecf20Sopenharmony_ci * @ethhdr: an ethernet header to determine the protocol family from 12468c2ecf20Sopenharmony_ci * 12478c2ecf20Sopenharmony_ci * Return: an orig_node which has the BATADV_MCAST_WANT_ALL_IPV4 or 12488c2ecf20Sopenharmony_ci * BATADV_MCAST_WANT_ALL_IPV6 flag, depending on the provided ethhdr, sets and 12498c2ecf20Sopenharmony_ci * increases its refcount. 12508c2ecf20Sopenharmony_ci */ 12518c2ecf20Sopenharmony_cistatic struct batadv_orig_node * 12528c2ecf20Sopenharmony_cibatadv_mcast_forw_ip_node_get(struct batadv_priv *bat_priv, 12538c2ecf20Sopenharmony_ci struct ethhdr *ethhdr) 12548c2ecf20Sopenharmony_ci{ 12558c2ecf20Sopenharmony_ci switch (ntohs(ethhdr->h_proto)) { 12568c2ecf20Sopenharmony_ci case ETH_P_IP: 12578c2ecf20Sopenharmony_ci return batadv_mcast_forw_ipv4_node_get(bat_priv); 12588c2ecf20Sopenharmony_ci case ETH_P_IPV6: 12598c2ecf20Sopenharmony_ci return batadv_mcast_forw_ipv6_node_get(bat_priv); 12608c2ecf20Sopenharmony_ci default: 12618c2ecf20Sopenharmony_ci /* we shouldn't be here... */ 12628c2ecf20Sopenharmony_ci return NULL; 12638c2ecf20Sopenharmony_ci } 12648c2ecf20Sopenharmony_ci} 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci/** 12678c2ecf20Sopenharmony_ci * batadv_mcast_forw_unsnoop_node_get() - get a node with an unsnoopable flag 12688c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 12698c2ecf20Sopenharmony_ci * 12708c2ecf20Sopenharmony_ci * Return: an orig_node which has the BATADV_MCAST_WANT_ALL_UNSNOOPABLES flag 12718c2ecf20Sopenharmony_ci * set and increases its refcount. 12728c2ecf20Sopenharmony_ci */ 12738c2ecf20Sopenharmony_cistatic struct batadv_orig_node * 12748c2ecf20Sopenharmony_cibatadv_mcast_forw_unsnoop_node_get(struct batadv_priv *bat_priv) 12758c2ecf20Sopenharmony_ci{ 12768c2ecf20Sopenharmony_ci struct batadv_orig_node *tmp_orig_node, *orig_node = NULL; 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_ci rcu_read_lock(); 12798c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(tmp_orig_node, 12808c2ecf20Sopenharmony_ci &bat_priv->mcast.want_all_unsnoopables_list, 12818c2ecf20Sopenharmony_ci mcast_want_all_unsnoopables_node) { 12828c2ecf20Sopenharmony_ci if (!kref_get_unless_zero(&tmp_orig_node->refcount)) 12838c2ecf20Sopenharmony_ci continue; 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_ci orig_node = tmp_orig_node; 12868c2ecf20Sopenharmony_ci break; 12878c2ecf20Sopenharmony_ci } 12888c2ecf20Sopenharmony_ci rcu_read_unlock(); 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_ci return orig_node; 12918c2ecf20Sopenharmony_ci} 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci/** 12948c2ecf20Sopenharmony_ci * batadv_mcast_forw_rtr4_node_get() - get a node with an ipv4 mcast router flag 12958c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 12968c2ecf20Sopenharmony_ci * 12978c2ecf20Sopenharmony_ci * Return: an orig_node which has the BATADV_MCAST_WANT_NO_RTR4 flag unset and 12988c2ecf20Sopenharmony_ci * increases its refcount. 12998c2ecf20Sopenharmony_ci */ 13008c2ecf20Sopenharmony_cistatic struct batadv_orig_node * 13018c2ecf20Sopenharmony_cibatadv_mcast_forw_rtr4_node_get(struct batadv_priv *bat_priv) 13028c2ecf20Sopenharmony_ci{ 13038c2ecf20Sopenharmony_ci struct batadv_orig_node *tmp_orig_node, *orig_node = NULL; 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ci rcu_read_lock(); 13068c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(tmp_orig_node, 13078c2ecf20Sopenharmony_ci &bat_priv->mcast.want_all_rtr4_list, 13088c2ecf20Sopenharmony_ci mcast_want_all_rtr4_node) { 13098c2ecf20Sopenharmony_ci if (!kref_get_unless_zero(&tmp_orig_node->refcount)) 13108c2ecf20Sopenharmony_ci continue; 13118c2ecf20Sopenharmony_ci 13128c2ecf20Sopenharmony_ci orig_node = tmp_orig_node; 13138c2ecf20Sopenharmony_ci break; 13148c2ecf20Sopenharmony_ci } 13158c2ecf20Sopenharmony_ci rcu_read_unlock(); 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_ci return orig_node; 13188c2ecf20Sopenharmony_ci} 13198c2ecf20Sopenharmony_ci 13208c2ecf20Sopenharmony_ci/** 13218c2ecf20Sopenharmony_ci * batadv_mcast_forw_rtr6_node_get() - get a node with an ipv6 mcast router flag 13228c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 13238c2ecf20Sopenharmony_ci * 13248c2ecf20Sopenharmony_ci * Return: an orig_node which has the BATADV_MCAST_WANT_NO_RTR6 flag unset 13258c2ecf20Sopenharmony_ci * and increases its refcount. 13268c2ecf20Sopenharmony_ci */ 13278c2ecf20Sopenharmony_cistatic struct batadv_orig_node * 13288c2ecf20Sopenharmony_cibatadv_mcast_forw_rtr6_node_get(struct batadv_priv *bat_priv) 13298c2ecf20Sopenharmony_ci{ 13308c2ecf20Sopenharmony_ci struct batadv_orig_node *tmp_orig_node, *orig_node = NULL; 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_ci rcu_read_lock(); 13338c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(tmp_orig_node, 13348c2ecf20Sopenharmony_ci &bat_priv->mcast.want_all_rtr6_list, 13358c2ecf20Sopenharmony_ci mcast_want_all_rtr6_node) { 13368c2ecf20Sopenharmony_ci if (!kref_get_unless_zero(&tmp_orig_node->refcount)) 13378c2ecf20Sopenharmony_ci continue; 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_ci orig_node = tmp_orig_node; 13408c2ecf20Sopenharmony_ci break; 13418c2ecf20Sopenharmony_ci } 13428c2ecf20Sopenharmony_ci rcu_read_unlock(); 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_ci return orig_node; 13458c2ecf20Sopenharmony_ci} 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_ci/** 13488c2ecf20Sopenharmony_ci * batadv_mcast_forw_rtr_node_get() - get a node with an ipv4/ipv6 router flag 13498c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 13508c2ecf20Sopenharmony_ci * @ethhdr: an ethernet header to determine the protocol family from 13518c2ecf20Sopenharmony_ci * 13528c2ecf20Sopenharmony_ci * Return: an orig_node which has no BATADV_MCAST_WANT_NO_RTR4 or 13538c2ecf20Sopenharmony_ci * BATADV_MCAST_WANT_NO_RTR6 flag, depending on the provided ethhdr, set and 13548c2ecf20Sopenharmony_ci * increases its refcount. 13558c2ecf20Sopenharmony_ci */ 13568c2ecf20Sopenharmony_cistatic struct batadv_orig_node * 13578c2ecf20Sopenharmony_cibatadv_mcast_forw_rtr_node_get(struct batadv_priv *bat_priv, 13588c2ecf20Sopenharmony_ci struct ethhdr *ethhdr) 13598c2ecf20Sopenharmony_ci{ 13608c2ecf20Sopenharmony_ci switch (ntohs(ethhdr->h_proto)) { 13618c2ecf20Sopenharmony_ci case ETH_P_IP: 13628c2ecf20Sopenharmony_ci return batadv_mcast_forw_rtr4_node_get(bat_priv); 13638c2ecf20Sopenharmony_ci case ETH_P_IPV6: 13648c2ecf20Sopenharmony_ci return batadv_mcast_forw_rtr6_node_get(bat_priv); 13658c2ecf20Sopenharmony_ci default: 13668c2ecf20Sopenharmony_ci /* we shouldn't be here... */ 13678c2ecf20Sopenharmony_ci return NULL; 13688c2ecf20Sopenharmony_ci } 13698c2ecf20Sopenharmony_ci} 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_ci/** 13728c2ecf20Sopenharmony_ci * batadv_mcast_forw_mode() - check on how to forward a multicast packet 13738c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 13748c2ecf20Sopenharmony_ci * @skb: The multicast packet to check 13758c2ecf20Sopenharmony_ci * @orig: an originator to be set to forward the skb to 13768c2ecf20Sopenharmony_ci * @is_routable: stores whether the destination is routable 13778c2ecf20Sopenharmony_ci * 13788c2ecf20Sopenharmony_ci * Return: the forwarding mode as enum batadv_forw_mode and in case of 13798c2ecf20Sopenharmony_ci * BATADV_FORW_SINGLE set the orig to the single originator the skb 13808c2ecf20Sopenharmony_ci * should be forwarded to. 13818c2ecf20Sopenharmony_ci */ 13828c2ecf20Sopenharmony_cienum batadv_forw_mode 13838c2ecf20Sopenharmony_cibatadv_mcast_forw_mode(struct batadv_priv *bat_priv, struct sk_buff *skb, 13848c2ecf20Sopenharmony_ci struct batadv_orig_node **orig, int *is_routable) 13858c2ecf20Sopenharmony_ci{ 13868c2ecf20Sopenharmony_ci int ret, tt_count, ip_count, unsnoop_count, total_count; 13878c2ecf20Sopenharmony_ci bool is_unsnoopable = false; 13888c2ecf20Sopenharmony_ci unsigned int mcast_fanout; 13898c2ecf20Sopenharmony_ci struct ethhdr *ethhdr; 13908c2ecf20Sopenharmony_ci int rtr_count = 0; 13918c2ecf20Sopenharmony_ci 13928c2ecf20Sopenharmony_ci ret = batadv_mcast_forw_mode_check(bat_priv, skb, &is_unsnoopable, 13938c2ecf20Sopenharmony_ci is_routable); 13948c2ecf20Sopenharmony_ci if (ret == -ENOMEM) 13958c2ecf20Sopenharmony_ci return BATADV_FORW_NONE; 13968c2ecf20Sopenharmony_ci else if (ret < 0) 13978c2ecf20Sopenharmony_ci return BATADV_FORW_ALL; 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ci ethhdr = eth_hdr(skb); 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_ci tt_count = batadv_tt_global_hash_count(bat_priv, ethhdr->h_dest, 14028c2ecf20Sopenharmony_ci BATADV_NO_FLAGS); 14038c2ecf20Sopenharmony_ci ip_count = batadv_mcast_forw_want_all_ip_count(bat_priv, ethhdr); 14048c2ecf20Sopenharmony_ci unsnoop_count = !is_unsnoopable ? 0 : 14058c2ecf20Sopenharmony_ci atomic_read(&bat_priv->mcast.num_want_all_unsnoopables); 14068c2ecf20Sopenharmony_ci rtr_count = batadv_mcast_forw_rtr_count(bat_priv, *is_routable); 14078c2ecf20Sopenharmony_ci 14088c2ecf20Sopenharmony_ci total_count = tt_count + ip_count + unsnoop_count + rtr_count; 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci switch (total_count) { 14118c2ecf20Sopenharmony_ci case 1: 14128c2ecf20Sopenharmony_ci if (tt_count) 14138c2ecf20Sopenharmony_ci *orig = batadv_mcast_forw_tt_node_get(bat_priv, ethhdr); 14148c2ecf20Sopenharmony_ci else if (ip_count) 14158c2ecf20Sopenharmony_ci *orig = batadv_mcast_forw_ip_node_get(bat_priv, ethhdr); 14168c2ecf20Sopenharmony_ci else if (unsnoop_count) 14178c2ecf20Sopenharmony_ci *orig = batadv_mcast_forw_unsnoop_node_get(bat_priv); 14188c2ecf20Sopenharmony_ci else if (rtr_count) 14198c2ecf20Sopenharmony_ci *orig = batadv_mcast_forw_rtr_node_get(bat_priv, 14208c2ecf20Sopenharmony_ci ethhdr); 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_ci if (*orig) 14238c2ecf20Sopenharmony_ci return BATADV_FORW_SINGLE; 14248c2ecf20Sopenharmony_ci 14258c2ecf20Sopenharmony_ci fallthrough; 14268c2ecf20Sopenharmony_ci case 0: 14278c2ecf20Sopenharmony_ci return BATADV_FORW_NONE; 14288c2ecf20Sopenharmony_ci default: 14298c2ecf20Sopenharmony_ci mcast_fanout = atomic_read(&bat_priv->multicast_fanout); 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_ci if (!unsnoop_count && total_count <= mcast_fanout) 14328c2ecf20Sopenharmony_ci return BATADV_FORW_SOME; 14338c2ecf20Sopenharmony_ci } 14348c2ecf20Sopenharmony_ci 14358c2ecf20Sopenharmony_ci return BATADV_FORW_ALL; 14368c2ecf20Sopenharmony_ci} 14378c2ecf20Sopenharmony_ci 14388c2ecf20Sopenharmony_ci/** 14398c2ecf20Sopenharmony_ci * batadv_mcast_forw_send_orig() - send a multicast packet to an originator 14408c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 14418c2ecf20Sopenharmony_ci * @skb: the multicast packet to send 14428c2ecf20Sopenharmony_ci * @vid: the vlan identifier 14438c2ecf20Sopenharmony_ci * @orig_node: the originator to send the packet to 14448c2ecf20Sopenharmony_ci * 14458c2ecf20Sopenharmony_ci * Return: NET_XMIT_DROP in case of error or NET_XMIT_SUCCESS otherwise. 14468c2ecf20Sopenharmony_ci */ 14478c2ecf20Sopenharmony_ciint batadv_mcast_forw_send_orig(struct batadv_priv *bat_priv, 14488c2ecf20Sopenharmony_ci struct sk_buff *skb, 14498c2ecf20Sopenharmony_ci unsigned short vid, 14508c2ecf20Sopenharmony_ci struct batadv_orig_node *orig_node) 14518c2ecf20Sopenharmony_ci{ 14528c2ecf20Sopenharmony_ci /* Avoid sending multicast-in-unicast packets to other BLA 14538c2ecf20Sopenharmony_ci * gateways - they already got the frame from the LAN side 14548c2ecf20Sopenharmony_ci * we share with them. 14558c2ecf20Sopenharmony_ci * TODO: Refactor to take BLA into account earlier, to avoid 14568c2ecf20Sopenharmony_ci * reducing the mcast_fanout count. 14578c2ecf20Sopenharmony_ci */ 14588c2ecf20Sopenharmony_ci if (batadv_bla_is_backbone_gw_orig(bat_priv, orig_node->orig, vid)) { 14598c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 14608c2ecf20Sopenharmony_ci return NET_XMIT_SUCCESS; 14618c2ecf20Sopenharmony_ci } 14628c2ecf20Sopenharmony_ci 14638c2ecf20Sopenharmony_ci return batadv_send_skb_unicast(bat_priv, skb, BATADV_UNICAST, 0, 14648c2ecf20Sopenharmony_ci orig_node, vid); 14658c2ecf20Sopenharmony_ci} 14668c2ecf20Sopenharmony_ci 14678c2ecf20Sopenharmony_ci/** 14688c2ecf20Sopenharmony_ci * batadv_mcast_forw_tt() - forwards a packet to multicast listeners 14698c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 14708c2ecf20Sopenharmony_ci * @skb: the multicast packet to transmit 14718c2ecf20Sopenharmony_ci * @vid: the vlan identifier 14728c2ecf20Sopenharmony_ci * 14738c2ecf20Sopenharmony_ci * Sends copies of a frame with multicast destination to any multicast 14748c2ecf20Sopenharmony_ci * listener registered in the translation table. A transmission is performed 14758c2ecf20Sopenharmony_ci * via a batman-adv unicast packet for each such destination node. 14768c2ecf20Sopenharmony_ci * 14778c2ecf20Sopenharmony_ci * Return: NET_XMIT_DROP on memory allocation failure, NET_XMIT_SUCCESS 14788c2ecf20Sopenharmony_ci * otherwise. 14798c2ecf20Sopenharmony_ci */ 14808c2ecf20Sopenharmony_cistatic int 14818c2ecf20Sopenharmony_cibatadv_mcast_forw_tt(struct batadv_priv *bat_priv, struct sk_buff *skb, 14828c2ecf20Sopenharmony_ci unsigned short vid) 14838c2ecf20Sopenharmony_ci{ 14848c2ecf20Sopenharmony_ci int ret = NET_XMIT_SUCCESS; 14858c2ecf20Sopenharmony_ci struct sk_buff *newskb; 14868c2ecf20Sopenharmony_ci 14878c2ecf20Sopenharmony_ci struct batadv_tt_orig_list_entry *orig_entry; 14888c2ecf20Sopenharmony_ci 14898c2ecf20Sopenharmony_ci struct batadv_tt_global_entry *tt_global; 14908c2ecf20Sopenharmony_ci const u8 *addr = eth_hdr(skb)->h_dest; 14918c2ecf20Sopenharmony_ci 14928c2ecf20Sopenharmony_ci tt_global = batadv_tt_global_hash_find(bat_priv, addr, vid); 14938c2ecf20Sopenharmony_ci if (!tt_global) 14948c2ecf20Sopenharmony_ci goto out; 14958c2ecf20Sopenharmony_ci 14968c2ecf20Sopenharmony_ci rcu_read_lock(); 14978c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(orig_entry, &tt_global->orig_list, list) { 14988c2ecf20Sopenharmony_ci newskb = skb_copy(skb, GFP_ATOMIC); 14998c2ecf20Sopenharmony_ci if (!newskb) { 15008c2ecf20Sopenharmony_ci ret = NET_XMIT_DROP; 15018c2ecf20Sopenharmony_ci break; 15028c2ecf20Sopenharmony_ci } 15038c2ecf20Sopenharmony_ci 15048c2ecf20Sopenharmony_ci batadv_mcast_forw_send_orig(bat_priv, newskb, vid, 15058c2ecf20Sopenharmony_ci orig_entry->orig_node); 15068c2ecf20Sopenharmony_ci } 15078c2ecf20Sopenharmony_ci rcu_read_unlock(); 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_ci batadv_tt_global_entry_put(tt_global); 15108c2ecf20Sopenharmony_ci 15118c2ecf20Sopenharmony_ciout: 15128c2ecf20Sopenharmony_ci return ret; 15138c2ecf20Sopenharmony_ci} 15148c2ecf20Sopenharmony_ci 15158c2ecf20Sopenharmony_ci/** 15168c2ecf20Sopenharmony_ci * batadv_mcast_forw_want_all_ipv4() - forward to nodes with want-all-ipv4 15178c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 15188c2ecf20Sopenharmony_ci * @skb: the multicast packet to transmit 15198c2ecf20Sopenharmony_ci * @vid: the vlan identifier 15208c2ecf20Sopenharmony_ci * 15218c2ecf20Sopenharmony_ci * Sends copies of a frame with multicast destination to any node with a 15228c2ecf20Sopenharmony_ci * BATADV_MCAST_WANT_ALL_IPV4 flag set. A transmission is performed via a 15238c2ecf20Sopenharmony_ci * batman-adv unicast packet for each such destination node. 15248c2ecf20Sopenharmony_ci * 15258c2ecf20Sopenharmony_ci * Return: NET_XMIT_DROP on memory allocation failure, NET_XMIT_SUCCESS 15268c2ecf20Sopenharmony_ci * otherwise. 15278c2ecf20Sopenharmony_ci */ 15288c2ecf20Sopenharmony_cistatic int 15298c2ecf20Sopenharmony_cibatadv_mcast_forw_want_all_ipv4(struct batadv_priv *bat_priv, 15308c2ecf20Sopenharmony_ci struct sk_buff *skb, unsigned short vid) 15318c2ecf20Sopenharmony_ci{ 15328c2ecf20Sopenharmony_ci struct batadv_orig_node *orig_node; 15338c2ecf20Sopenharmony_ci int ret = NET_XMIT_SUCCESS; 15348c2ecf20Sopenharmony_ci struct sk_buff *newskb; 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_ci rcu_read_lock(); 15378c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(orig_node, 15388c2ecf20Sopenharmony_ci &bat_priv->mcast.want_all_ipv4_list, 15398c2ecf20Sopenharmony_ci mcast_want_all_ipv4_node) { 15408c2ecf20Sopenharmony_ci newskb = skb_copy(skb, GFP_ATOMIC); 15418c2ecf20Sopenharmony_ci if (!newskb) { 15428c2ecf20Sopenharmony_ci ret = NET_XMIT_DROP; 15438c2ecf20Sopenharmony_ci break; 15448c2ecf20Sopenharmony_ci } 15458c2ecf20Sopenharmony_ci 15468c2ecf20Sopenharmony_ci batadv_mcast_forw_send_orig(bat_priv, newskb, vid, orig_node); 15478c2ecf20Sopenharmony_ci } 15488c2ecf20Sopenharmony_ci rcu_read_unlock(); 15498c2ecf20Sopenharmony_ci return ret; 15508c2ecf20Sopenharmony_ci} 15518c2ecf20Sopenharmony_ci 15528c2ecf20Sopenharmony_ci/** 15538c2ecf20Sopenharmony_ci * batadv_mcast_forw_want_all_ipv6() - forward to nodes with want-all-ipv6 15548c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 15558c2ecf20Sopenharmony_ci * @skb: The multicast packet to transmit 15568c2ecf20Sopenharmony_ci * @vid: the vlan identifier 15578c2ecf20Sopenharmony_ci * 15588c2ecf20Sopenharmony_ci * Sends copies of a frame with multicast destination to any node with a 15598c2ecf20Sopenharmony_ci * BATADV_MCAST_WANT_ALL_IPV6 flag set. A transmission is performed via a 15608c2ecf20Sopenharmony_ci * batman-adv unicast packet for each such destination node. 15618c2ecf20Sopenharmony_ci * 15628c2ecf20Sopenharmony_ci * Return: NET_XMIT_DROP on memory allocation failure, NET_XMIT_SUCCESS 15638c2ecf20Sopenharmony_ci * otherwise. 15648c2ecf20Sopenharmony_ci */ 15658c2ecf20Sopenharmony_cistatic int 15668c2ecf20Sopenharmony_cibatadv_mcast_forw_want_all_ipv6(struct batadv_priv *bat_priv, 15678c2ecf20Sopenharmony_ci struct sk_buff *skb, unsigned short vid) 15688c2ecf20Sopenharmony_ci{ 15698c2ecf20Sopenharmony_ci struct batadv_orig_node *orig_node; 15708c2ecf20Sopenharmony_ci int ret = NET_XMIT_SUCCESS; 15718c2ecf20Sopenharmony_ci struct sk_buff *newskb; 15728c2ecf20Sopenharmony_ci 15738c2ecf20Sopenharmony_ci rcu_read_lock(); 15748c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(orig_node, 15758c2ecf20Sopenharmony_ci &bat_priv->mcast.want_all_ipv6_list, 15768c2ecf20Sopenharmony_ci mcast_want_all_ipv6_node) { 15778c2ecf20Sopenharmony_ci newskb = skb_copy(skb, GFP_ATOMIC); 15788c2ecf20Sopenharmony_ci if (!newskb) { 15798c2ecf20Sopenharmony_ci ret = NET_XMIT_DROP; 15808c2ecf20Sopenharmony_ci break; 15818c2ecf20Sopenharmony_ci } 15828c2ecf20Sopenharmony_ci 15838c2ecf20Sopenharmony_ci batadv_mcast_forw_send_orig(bat_priv, newskb, vid, orig_node); 15848c2ecf20Sopenharmony_ci } 15858c2ecf20Sopenharmony_ci rcu_read_unlock(); 15868c2ecf20Sopenharmony_ci return ret; 15878c2ecf20Sopenharmony_ci} 15888c2ecf20Sopenharmony_ci 15898c2ecf20Sopenharmony_ci/** 15908c2ecf20Sopenharmony_ci * batadv_mcast_forw_want_all() - forward packet to nodes in a want-all list 15918c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 15928c2ecf20Sopenharmony_ci * @skb: the multicast packet to transmit 15938c2ecf20Sopenharmony_ci * @vid: the vlan identifier 15948c2ecf20Sopenharmony_ci * 15958c2ecf20Sopenharmony_ci * Sends copies of a frame with multicast destination to any node with a 15968c2ecf20Sopenharmony_ci * BATADV_MCAST_WANT_ALL_IPV4 or BATADV_MCAST_WANT_ALL_IPV6 flag set. A 15978c2ecf20Sopenharmony_ci * transmission is performed via a batman-adv unicast packet for each such 15988c2ecf20Sopenharmony_ci * destination node. 15998c2ecf20Sopenharmony_ci * 16008c2ecf20Sopenharmony_ci * Return: NET_XMIT_DROP on memory allocation failure or if the protocol family 16018c2ecf20Sopenharmony_ci * is neither IPv4 nor IPv6. NET_XMIT_SUCCESS otherwise. 16028c2ecf20Sopenharmony_ci */ 16038c2ecf20Sopenharmony_cistatic int 16048c2ecf20Sopenharmony_cibatadv_mcast_forw_want_all(struct batadv_priv *bat_priv, 16058c2ecf20Sopenharmony_ci struct sk_buff *skb, unsigned short vid) 16068c2ecf20Sopenharmony_ci{ 16078c2ecf20Sopenharmony_ci switch (ntohs(eth_hdr(skb)->h_proto)) { 16088c2ecf20Sopenharmony_ci case ETH_P_IP: 16098c2ecf20Sopenharmony_ci return batadv_mcast_forw_want_all_ipv4(bat_priv, skb, vid); 16108c2ecf20Sopenharmony_ci case ETH_P_IPV6: 16118c2ecf20Sopenharmony_ci return batadv_mcast_forw_want_all_ipv6(bat_priv, skb, vid); 16128c2ecf20Sopenharmony_ci default: 16138c2ecf20Sopenharmony_ci /* we shouldn't be here... */ 16148c2ecf20Sopenharmony_ci return NET_XMIT_DROP; 16158c2ecf20Sopenharmony_ci } 16168c2ecf20Sopenharmony_ci} 16178c2ecf20Sopenharmony_ci 16188c2ecf20Sopenharmony_ci/** 16198c2ecf20Sopenharmony_ci * batadv_mcast_forw_want_all_rtr4() - forward to nodes with want-all-rtr4 16208c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 16218c2ecf20Sopenharmony_ci * @skb: the multicast packet to transmit 16228c2ecf20Sopenharmony_ci * @vid: the vlan identifier 16238c2ecf20Sopenharmony_ci * 16248c2ecf20Sopenharmony_ci * Sends copies of a frame with multicast destination to any node with a 16258c2ecf20Sopenharmony_ci * BATADV_MCAST_WANT_NO_RTR4 flag unset. A transmission is performed via a 16268c2ecf20Sopenharmony_ci * batman-adv unicast packet for each such destination node. 16278c2ecf20Sopenharmony_ci * 16288c2ecf20Sopenharmony_ci * Return: NET_XMIT_DROP on memory allocation failure, NET_XMIT_SUCCESS 16298c2ecf20Sopenharmony_ci * otherwise. 16308c2ecf20Sopenharmony_ci */ 16318c2ecf20Sopenharmony_cistatic int 16328c2ecf20Sopenharmony_cibatadv_mcast_forw_want_all_rtr4(struct batadv_priv *bat_priv, 16338c2ecf20Sopenharmony_ci struct sk_buff *skb, unsigned short vid) 16348c2ecf20Sopenharmony_ci{ 16358c2ecf20Sopenharmony_ci struct batadv_orig_node *orig_node; 16368c2ecf20Sopenharmony_ci int ret = NET_XMIT_SUCCESS; 16378c2ecf20Sopenharmony_ci struct sk_buff *newskb; 16388c2ecf20Sopenharmony_ci 16398c2ecf20Sopenharmony_ci rcu_read_lock(); 16408c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(orig_node, 16418c2ecf20Sopenharmony_ci &bat_priv->mcast.want_all_rtr4_list, 16428c2ecf20Sopenharmony_ci mcast_want_all_rtr4_node) { 16438c2ecf20Sopenharmony_ci newskb = skb_copy(skb, GFP_ATOMIC); 16448c2ecf20Sopenharmony_ci if (!newskb) { 16458c2ecf20Sopenharmony_ci ret = NET_XMIT_DROP; 16468c2ecf20Sopenharmony_ci break; 16478c2ecf20Sopenharmony_ci } 16488c2ecf20Sopenharmony_ci 16498c2ecf20Sopenharmony_ci batadv_mcast_forw_send_orig(bat_priv, newskb, vid, orig_node); 16508c2ecf20Sopenharmony_ci } 16518c2ecf20Sopenharmony_ci rcu_read_unlock(); 16528c2ecf20Sopenharmony_ci return ret; 16538c2ecf20Sopenharmony_ci} 16548c2ecf20Sopenharmony_ci 16558c2ecf20Sopenharmony_ci/** 16568c2ecf20Sopenharmony_ci * batadv_mcast_forw_want_all_rtr6() - forward to nodes with want-all-rtr6 16578c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 16588c2ecf20Sopenharmony_ci * @skb: The multicast packet to transmit 16598c2ecf20Sopenharmony_ci * @vid: the vlan identifier 16608c2ecf20Sopenharmony_ci * 16618c2ecf20Sopenharmony_ci * Sends copies of a frame with multicast destination to any node with a 16628c2ecf20Sopenharmony_ci * BATADV_MCAST_WANT_NO_RTR6 flag unset. A transmission is performed via a 16638c2ecf20Sopenharmony_ci * batman-adv unicast packet for each such destination node. 16648c2ecf20Sopenharmony_ci * 16658c2ecf20Sopenharmony_ci * Return: NET_XMIT_DROP on memory allocation failure, NET_XMIT_SUCCESS 16668c2ecf20Sopenharmony_ci * otherwise. 16678c2ecf20Sopenharmony_ci */ 16688c2ecf20Sopenharmony_cistatic int 16698c2ecf20Sopenharmony_cibatadv_mcast_forw_want_all_rtr6(struct batadv_priv *bat_priv, 16708c2ecf20Sopenharmony_ci struct sk_buff *skb, unsigned short vid) 16718c2ecf20Sopenharmony_ci{ 16728c2ecf20Sopenharmony_ci struct batadv_orig_node *orig_node; 16738c2ecf20Sopenharmony_ci int ret = NET_XMIT_SUCCESS; 16748c2ecf20Sopenharmony_ci struct sk_buff *newskb; 16758c2ecf20Sopenharmony_ci 16768c2ecf20Sopenharmony_ci rcu_read_lock(); 16778c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(orig_node, 16788c2ecf20Sopenharmony_ci &bat_priv->mcast.want_all_rtr6_list, 16798c2ecf20Sopenharmony_ci mcast_want_all_rtr6_node) { 16808c2ecf20Sopenharmony_ci newskb = skb_copy(skb, GFP_ATOMIC); 16818c2ecf20Sopenharmony_ci if (!newskb) { 16828c2ecf20Sopenharmony_ci ret = NET_XMIT_DROP; 16838c2ecf20Sopenharmony_ci break; 16848c2ecf20Sopenharmony_ci } 16858c2ecf20Sopenharmony_ci 16868c2ecf20Sopenharmony_ci batadv_mcast_forw_send_orig(bat_priv, newskb, vid, orig_node); 16878c2ecf20Sopenharmony_ci } 16888c2ecf20Sopenharmony_ci rcu_read_unlock(); 16898c2ecf20Sopenharmony_ci return ret; 16908c2ecf20Sopenharmony_ci} 16918c2ecf20Sopenharmony_ci 16928c2ecf20Sopenharmony_ci/** 16938c2ecf20Sopenharmony_ci * batadv_mcast_forw_want_rtr() - forward packet to nodes in a want-all-rtr list 16948c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 16958c2ecf20Sopenharmony_ci * @skb: the multicast packet to transmit 16968c2ecf20Sopenharmony_ci * @vid: the vlan identifier 16978c2ecf20Sopenharmony_ci * 16988c2ecf20Sopenharmony_ci * Sends copies of a frame with multicast destination to any node with a 16998c2ecf20Sopenharmony_ci * BATADV_MCAST_WANT_NO_RTR4 or BATADV_MCAST_WANT_NO_RTR6 flag unset. A 17008c2ecf20Sopenharmony_ci * transmission is performed via a batman-adv unicast packet for each such 17018c2ecf20Sopenharmony_ci * destination node. 17028c2ecf20Sopenharmony_ci * 17038c2ecf20Sopenharmony_ci * Return: NET_XMIT_DROP on memory allocation failure or if the protocol family 17048c2ecf20Sopenharmony_ci * is neither IPv4 nor IPv6. NET_XMIT_SUCCESS otherwise. 17058c2ecf20Sopenharmony_ci */ 17068c2ecf20Sopenharmony_cistatic int 17078c2ecf20Sopenharmony_cibatadv_mcast_forw_want_rtr(struct batadv_priv *bat_priv, 17088c2ecf20Sopenharmony_ci struct sk_buff *skb, unsigned short vid) 17098c2ecf20Sopenharmony_ci{ 17108c2ecf20Sopenharmony_ci switch (ntohs(eth_hdr(skb)->h_proto)) { 17118c2ecf20Sopenharmony_ci case ETH_P_IP: 17128c2ecf20Sopenharmony_ci return batadv_mcast_forw_want_all_rtr4(bat_priv, skb, vid); 17138c2ecf20Sopenharmony_ci case ETH_P_IPV6: 17148c2ecf20Sopenharmony_ci return batadv_mcast_forw_want_all_rtr6(bat_priv, skb, vid); 17158c2ecf20Sopenharmony_ci default: 17168c2ecf20Sopenharmony_ci /* we shouldn't be here... */ 17178c2ecf20Sopenharmony_ci return NET_XMIT_DROP; 17188c2ecf20Sopenharmony_ci } 17198c2ecf20Sopenharmony_ci} 17208c2ecf20Sopenharmony_ci 17218c2ecf20Sopenharmony_ci/** 17228c2ecf20Sopenharmony_ci * batadv_mcast_forw_send() - send packet to any detected multicast recipient 17238c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 17248c2ecf20Sopenharmony_ci * @skb: the multicast packet to transmit 17258c2ecf20Sopenharmony_ci * @vid: the vlan identifier 17268c2ecf20Sopenharmony_ci * @is_routable: stores whether the destination is routable 17278c2ecf20Sopenharmony_ci * 17288c2ecf20Sopenharmony_ci * Sends copies of a frame with multicast destination to any node that signaled 17298c2ecf20Sopenharmony_ci * interest in it, that is either via the translation table or the according 17308c2ecf20Sopenharmony_ci * want-all flags. A transmission is performed via a batman-adv unicast packet 17318c2ecf20Sopenharmony_ci * for each such destination node. 17328c2ecf20Sopenharmony_ci * 17338c2ecf20Sopenharmony_ci * The given skb is consumed/freed. 17348c2ecf20Sopenharmony_ci * 17358c2ecf20Sopenharmony_ci * Return: NET_XMIT_DROP on memory allocation failure or if the protocol family 17368c2ecf20Sopenharmony_ci * is neither IPv4 nor IPv6. NET_XMIT_SUCCESS otherwise. 17378c2ecf20Sopenharmony_ci */ 17388c2ecf20Sopenharmony_ciint batadv_mcast_forw_send(struct batadv_priv *bat_priv, struct sk_buff *skb, 17398c2ecf20Sopenharmony_ci unsigned short vid, int is_routable) 17408c2ecf20Sopenharmony_ci{ 17418c2ecf20Sopenharmony_ci int ret; 17428c2ecf20Sopenharmony_ci 17438c2ecf20Sopenharmony_ci ret = batadv_mcast_forw_tt(bat_priv, skb, vid); 17448c2ecf20Sopenharmony_ci if (ret != NET_XMIT_SUCCESS) { 17458c2ecf20Sopenharmony_ci kfree_skb(skb); 17468c2ecf20Sopenharmony_ci return ret; 17478c2ecf20Sopenharmony_ci } 17488c2ecf20Sopenharmony_ci 17498c2ecf20Sopenharmony_ci ret = batadv_mcast_forw_want_all(bat_priv, skb, vid); 17508c2ecf20Sopenharmony_ci if (ret != NET_XMIT_SUCCESS) { 17518c2ecf20Sopenharmony_ci kfree_skb(skb); 17528c2ecf20Sopenharmony_ci return ret; 17538c2ecf20Sopenharmony_ci } 17548c2ecf20Sopenharmony_ci 17558c2ecf20Sopenharmony_ci if (!is_routable) 17568c2ecf20Sopenharmony_ci goto skip_mc_router; 17578c2ecf20Sopenharmony_ci 17588c2ecf20Sopenharmony_ci ret = batadv_mcast_forw_want_rtr(bat_priv, skb, vid); 17598c2ecf20Sopenharmony_ci if (ret != NET_XMIT_SUCCESS) { 17608c2ecf20Sopenharmony_ci kfree_skb(skb); 17618c2ecf20Sopenharmony_ci return ret; 17628c2ecf20Sopenharmony_ci } 17638c2ecf20Sopenharmony_ci 17648c2ecf20Sopenharmony_ciskip_mc_router: 17658c2ecf20Sopenharmony_ci consume_skb(skb); 17668c2ecf20Sopenharmony_ci return ret; 17678c2ecf20Sopenharmony_ci} 17688c2ecf20Sopenharmony_ci 17698c2ecf20Sopenharmony_ci/** 17708c2ecf20Sopenharmony_ci * batadv_mcast_want_unsnoop_update() - update unsnoop counter and list 17718c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 17728c2ecf20Sopenharmony_ci * @orig: the orig_node which multicast state might have changed of 17738c2ecf20Sopenharmony_ci * @mcast_flags: flags indicating the new multicast state 17748c2ecf20Sopenharmony_ci * 17758c2ecf20Sopenharmony_ci * If the BATADV_MCAST_WANT_ALL_UNSNOOPABLES flag of this originator, 17768c2ecf20Sopenharmony_ci * orig, has toggled then this method updates the counter and the list 17778c2ecf20Sopenharmony_ci * accordingly. 17788c2ecf20Sopenharmony_ci * 17798c2ecf20Sopenharmony_ci * Caller needs to hold orig->mcast_handler_lock. 17808c2ecf20Sopenharmony_ci */ 17818c2ecf20Sopenharmony_cistatic void batadv_mcast_want_unsnoop_update(struct batadv_priv *bat_priv, 17828c2ecf20Sopenharmony_ci struct batadv_orig_node *orig, 17838c2ecf20Sopenharmony_ci u8 mcast_flags) 17848c2ecf20Sopenharmony_ci{ 17858c2ecf20Sopenharmony_ci struct hlist_node *node = &orig->mcast_want_all_unsnoopables_node; 17868c2ecf20Sopenharmony_ci struct hlist_head *head = &bat_priv->mcast.want_all_unsnoopables_list; 17878c2ecf20Sopenharmony_ci 17888c2ecf20Sopenharmony_ci lockdep_assert_held(&orig->mcast_handler_lock); 17898c2ecf20Sopenharmony_ci 17908c2ecf20Sopenharmony_ci /* switched from flag unset to set */ 17918c2ecf20Sopenharmony_ci if (mcast_flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES && 17928c2ecf20Sopenharmony_ci !(orig->mcast_flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES)) { 17938c2ecf20Sopenharmony_ci atomic_inc(&bat_priv->mcast.num_want_all_unsnoopables); 17948c2ecf20Sopenharmony_ci 17958c2ecf20Sopenharmony_ci spin_lock_bh(&bat_priv->mcast.want_lists_lock); 17968c2ecf20Sopenharmony_ci /* flag checks above + mcast_handler_lock prevents this */ 17978c2ecf20Sopenharmony_ci WARN_ON(!hlist_unhashed(node)); 17988c2ecf20Sopenharmony_ci 17998c2ecf20Sopenharmony_ci hlist_add_head_rcu(node, head); 18008c2ecf20Sopenharmony_ci spin_unlock_bh(&bat_priv->mcast.want_lists_lock); 18018c2ecf20Sopenharmony_ci /* switched from flag set to unset */ 18028c2ecf20Sopenharmony_ci } else if (!(mcast_flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES) && 18038c2ecf20Sopenharmony_ci orig->mcast_flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES) { 18048c2ecf20Sopenharmony_ci atomic_dec(&bat_priv->mcast.num_want_all_unsnoopables); 18058c2ecf20Sopenharmony_ci 18068c2ecf20Sopenharmony_ci spin_lock_bh(&bat_priv->mcast.want_lists_lock); 18078c2ecf20Sopenharmony_ci /* flag checks above + mcast_handler_lock prevents this */ 18088c2ecf20Sopenharmony_ci WARN_ON(hlist_unhashed(node)); 18098c2ecf20Sopenharmony_ci 18108c2ecf20Sopenharmony_ci hlist_del_init_rcu(node); 18118c2ecf20Sopenharmony_ci spin_unlock_bh(&bat_priv->mcast.want_lists_lock); 18128c2ecf20Sopenharmony_ci } 18138c2ecf20Sopenharmony_ci} 18148c2ecf20Sopenharmony_ci 18158c2ecf20Sopenharmony_ci/** 18168c2ecf20Sopenharmony_ci * batadv_mcast_want_ipv4_update() - update want-all-ipv4 counter and list 18178c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 18188c2ecf20Sopenharmony_ci * @orig: the orig_node which multicast state might have changed of 18198c2ecf20Sopenharmony_ci * @mcast_flags: flags indicating the new multicast state 18208c2ecf20Sopenharmony_ci * 18218c2ecf20Sopenharmony_ci * If the BATADV_MCAST_WANT_ALL_IPV4 flag of this originator, orig, has 18228c2ecf20Sopenharmony_ci * toggled then this method updates the counter and the list accordingly. 18238c2ecf20Sopenharmony_ci * 18248c2ecf20Sopenharmony_ci * Caller needs to hold orig->mcast_handler_lock. 18258c2ecf20Sopenharmony_ci */ 18268c2ecf20Sopenharmony_cistatic void batadv_mcast_want_ipv4_update(struct batadv_priv *bat_priv, 18278c2ecf20Sopenharmony_ci struct batadv_orig_node *orig, 18288c2ecf20Sopenharmony_ci u8 mcast_flags) 18298c2ecf20Sopenharmony_ci{ 18308c2ecf20Sopenharmony_ci struct hlist_node *node = &orig->mcast_want_all_ipv4_node; 18318c2ecf20Sopenharmony_ci struct hlist_head *head = &bat_priv->mcast.want_all_ipv4_list; 18328c2ecf20Sopenharmony_ci 18338c2ecf20Sopenharmony_ci lockdep_assert_held(&orig->mcast_handler_lock); 18348c2ecf20Sopenharmony_ci 18358c2ecf20Sopenharmony_ci /* switched from flag unset to set */ 18368c2ecf20Sopenharmony_ci if (mcast_flags & BATADV_MCAST_WANT_ALL_IPV4 && 18378c2ecf20Sopenharmony_ci !(orig->mcast_flags & BATADV_MCAST_WANT_ALL_IPV4)) { 18388c2ecf20Sopenharmony_ci atomic_inc(&bat_priv->mcast.num_want_all_ipv4); 18398c2ecf20Sopenharmony_ci 18408c2ecf20Sopenharmony_ci spin_lock_bh(&bat_priv->mcast.want_lists_lock); 18418c2ecf20Sopenharmony_ci /* flag checks above + mcast_handler_lock prevents this */ 18428c2ecf20Sopenharmony_ci WARN_ON(!hlist_unhashed(node)); 18438c2ecf20Sopenharmony_ci 18448c2ecf20Sopenharmony_ci hlist_add_head_rcu(node, head); 18458c2ecf20Sopenharmony_ci spin_unlock_bh(&bat_priv->mcast.want_lists_lock); 18468c2ecf20Sopenharmony_ci /* switched from flag set to unset */ 18478c2ecf20Sopenharmony_ci } else if (!(mcast_flags & BATADV_MCAST_WANT_ALL_IPV4) && 18488c2ecf20Sopenharmony_ci orig->mcast_flags & BATADV_MCAST_WANT_ALL_IPV4) { 18498c2ecf20Sopenharmony_ci atomic_dec(&bat_priv->mcast.num_want_all_ipv4); 18508c2ecf20Sopenharmony_ci 18518c2ecf20Sopenharmony_ci spin_lock_bh(&bat_priv->mcast.want_lists_lock); 18528c2ecf20Sopenharmony_ci /* flag checks above + mcast_handler_lock prevents this */ 18538c2ecf20Sopenharmony_ci WARN_ON(hlist_unhashed(node)); 18548c2ecf20Sopenharmony_ci 18558c2ecf20Sopenharmony_ci hlist_del_init_rcu(node); 18568c2ecf20Sopenharmony_ci spin_unlock_bh(&bat_priv->mcast.want_lists_lock); 18578c2ecf20Sopenharmony_ci } 18588c2ecf20Sopenharmony_ci} 18598c2ecf20Sopenharmony_ci 18608c2ecf20Sopenharmony_ci/** 18618c2ecf20Sopenharmony_ci * batadv_mcast_want_ipv6_update() - update want-all-ipv6 counter and list 18628c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 18638c2ecf20Sopenharmony_ci * @orig: the orig_node which multicast state might have changed of 18648c2ecf20Sopenharmony_ci * @mcast_flags: flags indicating the new multicast state 18658c2ecf20Sopenharmony_ci * 18668c2ecf20Sopenharmony_ci * If the BATADV_MCAST_WANT_ALL_IPV6 flag of this originator, orig, has 18678c2ecf20Sopenharmony_ci * toggled then this method updates the counter and the list accordingly. 18688c2ecf20Sopenharmony_ci * 18698c2ecf20Sopenharmony_ci * Caller needs to hold orig->mcast_handler_lock. 18708c2ecf20Sopenharmony_ci */ 18718c2ecf20Sopenharmony_cistatic void batadv_mcast_want_ipv6_update(struct batadv_priv *bat_priv, 18728c2ecf20Sopenharmony_ci struct batadv_orig_node *orig, 18738c2ecf20Sopenharmony_ci u8 mcast_flags) 18748c2ecf20Sopenharmony_ci{ 18758c2ecf20Sopenharmony_ci struct hlist_node *node = &orig->mcast_want_all_ipv6_node; 18768c2ecf20Sopenharmony_ci struct hlist_head *head = &bat_priv->mcast.want_all_ipv6_list; 18778c2ecf20Sopenharmony_ci 18788c2ecf20Sopenharmony_ci lockdep_assert_held(&orig->mcast_handler_lock); 18798c2ecf20Sopenharmony_ci 18808c2ecf20Sopenharmony_ci /* switched from flag unset to set */ 18818c2ecf20Sopenharmony_ci if (mcast_flags & BATADV_MCAST_WANT_ALL_IPV6 && 18828c2ecf20Sopenharmony_ci !(orig->mcast_flags & BATADV_MCAST_WANT_ALL_IPV6)) { 18838c2ecf20Sopenharmony_ci atomic_inc(&bat_priv->mcast.num_want_all_ipv6); 18848c2ecf20Sopenharmony_ci 18858c2ecf20Sopenharmony_ci spin_lock_bh(&bat_priv->mcast.want_lists_lock); 18868c2ecf20Sopenharmony_ci /* flag checks above + mcast_handler_lock prevents this */ 18878c2ecf20Sopenharmony_ci WARN_ON(!hlist_unhashed(node)); 18888c2ecf20Sopenharmony_ci 18898c2ecf20Sopenharmony_ci hlist_add_head_rcu(node, head); 18908c2ecf20Sopenharmony_ci spin_unlock_bh(&bat_priv->mcast.want_lists_lock); 18918c2ecf20Sopenharmony_ci /* switched from flag set to unset */ 18928c2ecf20Sopenharmony_ci } else if (!(mcast_flags & BATADV_MCAST_WANT_ALL_IPV6) && 18938c2ecf20Sopenharmony_ci orig->mcast_flags & BATADV_MCAST_WANT_ALL_IPV6) { 18948c2ecf20Sopenharmony_ci atomic_dec(&bat_priv->mcast.num_want_all_ipv6); 18958c2ecf20Sopenharmony_ci 18968c2ecf20Sopenharmony_ci spin_lock_bh(&bat_priv->mcast.want_lists_lock); 18978c2ecf20Sopenharmony_ci /* flag checks above + mcast_handler_lock prevents this */ 18988c2ecf20Sopenharmony_ci WARN_ON(hlist_unhashed(node)); 18998c2ecf20Sopenharmony_ci 19008c2ecf20Sopenharmony_ci hlist_del_init_rcu(node); 19018c2ecf20Sopenharmony_ci spin_unlock_bh(&bat_priv->mcast.want_lists_lock); 19028c2ecf20Sopenharmony_ci } 19038c2ecf20Sopenharmony_ci} 19048c2ecf20Sopenharmony_ci 19058c2ecf20Sopenharmony_ci/** 19068c2ecf20Sopenharmony_ci * batadv_mcast_want_rtr4_update() - update want-all-rtr4 counter and list 19078c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 19088c2ecf20Sopenharmony_ci * @orig: the orig_node which multicast state might have changed of 19098c2ecf20Sopenharmony_ci * @mcast_flags: flags indicating the new multicast state 19108c2ecf20Sopenharmony_ci * 19118c2ecf20Sopenharmony_ci * If the BATADV_MCAST_WANT_NO_RTR4 flag of this originator, orig, has 19128c2ecf20Sopenharmony_ci * toggled then this method updates the counter and the list accordingly. 19138c2ecf20Sopenharmony_ci * 19148c2ecf20Sopenharmony_ci * Caller needs to hold orig->mcast_handler_lock. 19158c2ecf20Sopenharmony_ci */ 19168c2ecf20Sopenharmony_cistatic void batadv_mcast_want_rtr4_update(struct batadv_priv *bat_priv, 19178c2ecf20Sopenharmony_ci struct batadv_orig_node *orig, 19188c2ecf20Sopenharmony_ci u8 mcast_flags) 19198c2ecf20Sopenharmony_ci{ 19208c2ecf20Sopenharmony_ci struct hlist_node *node = &orig->mcast_want_all_rtr4_node; 19218c2ecf20Sopenharmony_ci struct hlist_head *head = &bat_priv->mcast.want_all_rtr4_list; 19228c2ecf20Sopenharmony_ci 19238c2ecf20Sopenharmony_ci lockdep_assert_held(&orig->mcast_handler_lock); 19248c2ecf20Sopenharmony_ci 19258c2ecf20Sopenharmony_ci /* switched from flag set to unset */ 19268c2ecf20Sopenharmony_ci if (!(mcast_flags & BATADV_MCAST_WANT_NO_RTR4) && 19278c2ecf20Sopenharmony_ci orig->mcast_flags & BATADV_MCAST_WANT_NO_RTR4) { 19288c2ecf20Sopenharmony_ci atomic_inc(&bat_priv->mcast.num_want_all_rtr4); 19298c2ecf20Sopenharmony_ci 19308c2ecf20Sopenharmony_ci spin_lock_bh(&bat_priv->mcast.want_lists_lock); 19318c2ecf20Sopenharmony_ci /* flag checks above + mcast_handler_lock prevents this */ 19328c2ecf20Sopenharmony_ci WARN_ON(!hlist_unhashed(node)); 19338c2ecf20Sopenharmony_ci 19348c2ecf20Sopenharmony_ci hlist_add_head_rcu(node, head); 19358c2ecf20Sopenharmony_ci spin_unlock_bh(&bat_priv->mcast.want_lists_lock); 19368c2ecf20Sopenharmony_ci /* switched from flag unset to set */ 19378c2ecf20Sopenharmony_ci } else if (mcast_flags & BATADV_MCAST_WANT_NO_RTR4 && 19388c2ecf20Sopenharmony_ci !(orig->mcast_flags & BATADV_MCAST_WANT_NO_RTR4)) { 19398c2ecf20Sopenharmony_ci atomic_dec(&bat_priv->mcast.num_want_all_rtr4); 19408c2ecf20Sopenharmony_ci 19418c2ecf20Sopenharmony_ci spin_lock_bh(&bat_priv->mcast.want_lists_lock); 19428c2ecf20Sopenharmony_ci /* flag checks above + mcast_handler_lock prevents this */ 19438c2ecf20Sopenharmony_ci WARN_ON(hlist_unhashed(node)); 19448c2ecf20Sopenharmony_ci 19458c2ecf20Sopenharmony_ci hlist_del_init_rcu(node); 19468c2ecf20Sopenharmony_ci spin_unlock_bh(&bat_priv->mcast.want_lists_lock); 19478c2ecf20Sopenharmony_ci } 19488c2ecf20Sopenharmony_ci} 19498c2ecf20Sopenharmony_ci 19508c2ecf20Sopenharmony_ci/** 19518c2ecf20Sopenharmony_ci * batadv_mcast_want_rtr6_update() - update want-all-rtr6 counter and list 19528c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 19538c2ecf20Sopenharmony_ci * @orig: the orig_node which multicast state might have changed of 19548c2ecf20Sopenharmony_ci * @mcast_flags: flags indicating the new multicast state 19558c2ecf20Sopenharmony_ci * 19568c2ecf20Sopenharmony_ci * If the BATADV_MCAST_WANT_NO_RTR6 flag of this originator, orig, has 19578c2ecf20Sopenharmony_ci * toggled then this method updates the counter and the list accordingly. 19588c2ecf20Sopenharmony_ci * 19598c2ecf20Sopenharmony_ci * Caller needs to hold orig->mcast_handler_lock. 19608c2ecf20Sopenharmony_ci */ 19618c2ecf20Sopenharmony_cistatic void batadv_mcast_want_rtr6_update(struct batadv_priv *bat_priv, 19628c2ecf20Sopenharmony_ci struct batadv_orig_node *orig, 19638c2ecf20Sopenharmony_ci u8 mcast_flags) 19648c2ecf20Sopenharmony_ci{ 19658c2ecf20Sopenharmony_ci struct hlist_node *node = &orig->mcast_want_all_rtr6_node; 19668c2ecf20Sopenharmony_ci struct hlist_head *head = &bat_priv->mcast.want_all_rtr6_list; 19678c2ecf20Sopenharmony_ci 19688c2ecf20Sopenharmony_ci lockdep_assert_held(&orig->mcast_handler_lock); 19698c2ecf20Sopenharmony_ci 19708c2ecf20Sopenharmony_ci /* switched from flag set to unset */ 19718c2ecf20Sopenharmony_ci if (!(mcast_flags & BATADV_MCAST_WANT_NO_RTR6) && 19728c2ecf20Sopenharmony_ci orig->mcast_flags & BATADV_MCAST_WANT_NO_RTR6) { 19738c2ecf20Sopenharmony_ci atomic_inc(&bat_priv->mcast.num_want_all_rtr6); 19748c2ecf20Sopenharmony_ci 19758c2ecf20Sopenharmony_ci spin_lock_bh(&bat_priv->mcast.want_lists_lock); 19768c2ecf20Sopenharmony_ci /* flag checks above + mcast_handler_lock prevents this */ 19778c2ecf20Sopenharmony_ci WARN_ON(!hlist_unhashed(node)); 19788c2ecf20Sopenharmony_ci 19798c2ecf20Sopenharmony_ci hlist_add_head_rcu(node, head); 19808c2ecf20Sopenharmony_ci spin_unlock_bh(&bat_priv->mcast.want_lists_lock); 19818c2ecf20Sopenharmony_ci /* switched from flag unset to set */ 19828c2ecf20Sopenharmony_ci } else if (mcast_flags & BATADV_MCAST_WANT_NO_RTR6 && 19838c2ecf20Sopenharmony_ci !(orig->mcast_flags & BATADV_MCAST_WANT_NO_RTR6)) { 19848c2ecf20Sopenharmony_ci atomic_dec(&bat_priv->mcast.num_want_all_rtr6); 19858c2ecf20Sopenharmony_ci 19868c2ecf20Sopenharmony_ci spin_lock_bh(&bat_priv->mcast.want_lists_lock); 19878c2ecf20Sopenharmony_ci /* flag checks above + mcast_handler_lock prevents this */ 19888c2ecf20Sopenharmony_ci WARN_ON(hlist_unhashed(node)); 19898c2ecf20Sopenharmony_ci 19908c2ecf20Sopenharmony_ci hlist_del_init_rcu(node); 19918c2ecf20Sopenharmony_ci spin_unlock_bh(&bat_priv->mcast.want_lists_lock); 19928c2ecf20Sopenharmony_ci } 19938c2ecf20Sopenharmony_ci} 19948c2ecf20Sopenharmony_ci 19958c2ecf20Sopenharmony_ci/** 19968c2ecf20Sopenharmony_ci * batadv_mcast_tvlv_flags_get() - get multicast flags from an OGM TVLV 19978c2ecf20Sopenharmony_ci * @enabled: whether the originator has multicast TVLV support enabled 19988c2ecf20Sopenharmony_ci * @tvlv_value: tvlv buffer containing the multicast flags 19998c2ecf20Sopenharmony_ci * @tvlv_value_len: tvlv buffer length 20008c2ecf20Sopenharmony_ci * 20018c2ecf20Sopenharmony_ci * Return: multicast flags for the given tvlv buffer 20028c2ecf20Sopenharmony_ci */ 20038c2ecf20Sopenharmony_cistatic u8 20048c2ecf20Sopenharmony_cibatadv_mcast_tvlv_flags_get(bool enabled, void *tvlv_value, u16 tvlv_value_len) 20058c2ecf20Sopenharmony_ci{ 20068c2ecf20Sopenharmony_ci u8 mcast_flags = BATADV_NO_FLAGS; 20078c2ecf20Sopenharmony_ci 20088c2ecf20Sopenharmony_ci if (enabled && tvlv_value && tvlv_value_len >= sizeof(mcast_flags)) 20098c2ecf20Sopenharmony_ci mcast_flags = *(u8 *)tvlv_value; 20108c2ecf20Sopenharmony_ci 20118c2ecf20Sopenharmony_ci if (!enabled) { 20128c2ecf20Sopenharmony_ci mcast_flags |= BATADV_MCAST_WANT_ALL_IPV4; 20138c2ecf20Sopenharmony_ci mcast_flags |= BATADV_MCAST_WANT_ALL_IPV6; 20148c2ecf20Sopenharmony_ci } 20158c2ecf20Sopenharmony_ci 20168c2ecf20Sopenharmony_ci /* remove redundant flags to avoid sending duplicate packets later */ 20178c2ecf20Sopenharmony_ci if (mcast_flags & BATADV_MCAST_WANT_ALL_IPV4) 20188c2ecf20Sopenharmony_ci mcast_flags |= BATADV_MCAST_WANT_NO_RTR4; 20198c2ecf20Sopenharmony_ci 20208c2ecf20Sopenharmony_ci if (mcast_flags & BATADV_MCAST_WANT_ALL_IPV6) 20218c2ecf20Sopenharmony_ci mcast_flags |= BATADV_MCAST_WANT_NO_RTR6; 20228c2ecf20Sopenharmony_ci 20238c2ecf20Sopenharmony_ci return mcast_flags; 20248c2ecf20Sopenharmony_ci} 20258c2ecf20Sopenharmony_ci 20268c2ecf20Sopenharmony_ci/** 20278c2ecf20Sopenharmony_ci * batadv_mcast_tvlv_ogm_handler() - process incoming multicast tvlv container 20288c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 20298c2ecf20Sopenharmony_ci * @orig: the orig_node of the ogm 20308c2ecf20Sopenharmony_ci * @flags: flags indicating the tvlv state (see batadv_tvlv_handler_flags) 20318c2ecf20Sopenharmony_ci * @tvlv_value: tvlv buffer containing the multicast data 20328c2ecf20Sopenharmony_ci * @tvlv_value_len: tvlv buffer length 20338c2ecf20Sopenharmony_ci */ 20348c2ecf20Sopenharmony_cistatic void batadv_mcast_tvlv_ogm_handler(struct batadv_priv *bat_priv, 20358c2ecf20Sopenharmony_ci struct batadv_orig_node *orig, 20368c2ecf20Sopenharmony_ci u8 flags, 20378c2ecf20Sopenharmony_ci void *tvlv_value, 20388c2ecf20Sopenharmony_ci u16 tvlv_value_len) 20398c2ecf20Sopenharmony_ci{ 20408c2ecf20Sopenharmony_ci bool orig_mcast_enabled = !(flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND); 20418c2ecf20Sopenharmony_ci u8 mcast_flags; 20428c2ecf20Sopenharmony_ci 20438c2ecf20Sopenharmony_ci mcast_flags = batadv_mcast_tvlv_flags_get(orig_mcast_enabled, 20448c2ecf20Sopenharmony_ci tvlv_value, tvlv_value_len); 20458c2ecf20Sopenharmony_ci 20468c2ecf20Sopenharmony_ci spin_lock_bh(&orig->mcast_handler_lock); 20478c2ecf20Sopenharmony_ci 20488c2ecf20Sopenharmony_ci if (orig_mcast_enabled && 20498c2ecf20Sopenharmony_ci !test_bit(BATADV_ORIG_CAPA_HAS_MCAST, &orig->capabilities)) { 20508c2ecf20Sopenharmony_ci set_bit(BATADV_ORIG_CAPA_HAS_MCAST, &orig->capabilities); 20518c2ecf20Sopenharmony_ci } else if (!orig_mcast_enabled && 20528c2ecf20Sopenharmony_ci test_bit(BATADV_ORIG_CAPA_HAS_MCAST, &orig->capabilities)) { 20538c2ecf20Sopenharmony_ci clear_bit(BATADV_ORIG_CAPA_HAS_MCAST, &orig->capabilities); 20548c2ecf20Sopenharmony_ci } 20558c2ecf20Sopenharmony_ci 20568c2ecf20Sopenharmony_ci set_bit(BATADV_ORIG_CAPA_HAS_MCAST, &orig->capa_initialized); 20578c2ecf20Sopenharmony_ci 20588c2ecf20Sopenharmony_ci batadv_mcast_want_unsnoop_update(bat_priv, orig, mcast_flags); 20598c2ecf20Sopenharmony_ci batadv_mcast_want_ipv4_update(bat_priv, orig, mcast_flags); 20608c2ecf20Sopenharmony_ci batadv_mcast_want_ipv6_update(bat_priv, orig, mcast_flags); 20618c2ecf20Sopenharmony_ci batadv_mcast_want_rtr4_update(bat_priv, orig, mcast_flags); 20628c2ecf20Sopenharmony_ci batadv_mcast_want_rtr6_update(bat_priv, orig, mcast_flags); 20638c2ecf20Sopenharmony_ci 20648c2ecf20Sopenharmony_ci orig->mcast_flags = mcast_flags; 20658c2ecf20Sopenharmony_ci spin_unlock_bh(&orig->mcast_handler_lock); 20668c2ecf20Sopenharmony_ci} 20678c2ecf20Sopenharmony_ci 20688c2ecf20Sopenharmony_ci/** 20698c2ecf20Sopenharmony_ci * batadv_mcast_init() - initialize the multicast optimizations structures 20708c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 20718c2ecf20Sopenharmony_ci */ 20728c2ecf20Sopenharmony_civoid batadv_mcast_init(struct batadv_priv *bat_priv) 20738c2ecf20Sopenharmony_ci{ 20748c2ecf20Sopenharmony_ci batadv_tvlv_handler_register(bat_priv, batadv_mcast_tvlv_ogm_handler, 20758c2ecf20Sopenharmony_ci NULL, BATADV_TVLV_MCAST, 2, 20768c2ecf20Sopenharmony_ci BATADV_TVLV_HANDLER_OGM_CIFNOTFND); 20778c2ecf20Sopenharmony_ci 20788c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&bat_priv->mcast.work, batadv_mcast_mla_update); 20798c2ecf20Sopenharmony_ci batadv_mcast_start_timer(bat_priv); 20808c2ecf20Sopenharmony_ci} 20818c2ecf20Sopenharmony_ci 20828c2ecf20Sopenharmony_ci#ifdef CONFIG_BATMAN_ADV_DEBUGFS 20838c2ecf20Sopenharmony_ci/** 20848c2ecf20Sopenharmony_ci * batadv_mcast_flags_print_header() - print own mcast flags to debugfs table 20858c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 20868c2ecf20Sopenharmony_ci * @seq: debugfs table seq_file struct 20878c2ecf20Sopenharmony_ci * 20888c2ecf20Sopenharmony_ci * Prints our own multicast flags including a more specific reason why 20898c2ecf20Sopenharmony_ci * they are set, that is prints the bridge and querier state too, to 20908c2ecf20Sopenharmony_ci * the debugfs table specified via @seq. 20918c2ecf20Sopenharmony_ci */ 20928c2ecf20Sopenharmony_cistatic void batadv_mcast_flags_print_header(struct batadv_priv *bat_priv, 20938c2ecf20Sopenharmony_ci struct seq_file *seq) 20948c2ecf20Sopenharmony_ci{ 20958c2ecf20Sopenharmony_ci struct batadv_mcast_mla_flags *mla_flags = &bat_priv->mcast.mla_flags; 20968c2ecf20Sopenharmony_ci char querier4, querier6, shadowing4, shadowing6; 20978c2ecf20Sopenharmony_ci bool bridged = mla_flags->bridged; 20988c2ecf20Sopenharmony_ci u8 flags = mla_flags->tvlv_flags; 20998c2ecf20Sopenharmony_ci 21008c2ecf20Sopenharmony_ci if (bridged) { 21018c2ecf20Sopenharmony_ci querier4 = mla_flags->querier_ipv4.exists ? '.' : '4'; 21028c2ecf20Sopenharmony_ci querier6 = mla_flags->querier_ipv6.exists ? '.' : '6'; 21038c2ecf20Sopenharmony_ci shadowing4 = mla_flags->querier_ipv4.shadowing ? '4' : '.'; 21048c2ecf20Sopenharmony_ci shadowing6 = mla_flags->querier_ipv6.shadowing ? '6' : '.'; 21058c2ecf20Sopenharmony_ci } else { 21068c2ecf20Sopenharmony_ci querier4 = '?'; 21078c2ecf20Sopenharmony_ci querier6 = '?'; 21088c2ecf20Sopenharmony_ci shadowing4 = '?'; 21098c2ecf20Sopenharmony_ci shadowing6 = '?'; 21108c2ecf20Sopenharmony_ci } 21118c2ecf20Sopenharmony_ci 21128c2ecf20Sopenharmony_ci seq_printf(seq, "Multicast flags (own flags: [%c%c%c%s%s])\n", 21138c2ecf20Sopenharmony_ci (flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES) ? 'U' : '.', 21148c2ecf20Sopenharmony_ci (flags & BATADV_MCAST_WANT_ALL_IPV4) ? '4' : '.', 21158c2ecf20Sopenharmony_ci (flags & BATADV_MCAST_WANT_ALL_IPV6) ? '6' : '.', 21168c2ecf20Sopenharmony_ci !(flags & BATADV_MCAST_WANT_NO_RTR4) ? "R4" : ". ", 21178c2ecf20Sopenharmony_ci !(flags & BATADV_MCAST_WANT_NO_RTR6) ? "R6" : ". "); 21188c2ecf20Sopenharmony_ci seq_printf(seq, "* Bridged [U]\t\t\t\t%c\n", bridged ? 'U' : '.'); 21198c2ecf20Sopenharmony_ci seq_printf(seq, "* No IGMP/MLD Querier [4/6]:\t\t%c/%c\n", 21208c2ecf20Sopenharmony_ci querier4, querier6); 21218c2ecf20Sopenharmony_ci seq_printf(seq, "* Shadowing IGMP/MLD Querier [4/6]:\t%c/%c\n", 21228c2ecf20Sopenharmony_ci shadowing4, shadowing6); 21238c2ecf20Sopenharmony_ci seq_puts(seq, "-------------------------------------------\n"); 21248c2ecf20Sopenharmony_ci seq_printf(seq, " %-10s %s\n", "Originator", "Flags"); 21258c2ecf20Sopenharmony_ci} 21268c2ecf20Sopenharmony_ci 21278c2ecf20Sopenharmony_ci/** 21288c2ecf20Sopenharmony_ci * batadv_mcast_flags_seq_print_text() - print the mcast flags of other nodes 21298c2ecf20Sopenharmony_ci * @seq: seq file to print on 21308c2ecf20Sopenharmony_ci * @offset: not used 21318c2ecf20Sopenharmony_ci * 21328c2ecf20Sopenharmony_ci * This prints a table of (primary) originators and their according 21338c2ecf20Sopenharmony_ci * multicast flags, including (in the header) our own. 21348c2ecf20Sopenharmony_ci * 21358c2ecf20Sopenharmony_ci * Return: always 0 21368c2ecf20Sopenharmony_ci */ 21378c2ecf20Sopenharmony_ciint batadv_mcast_flags_seq_print_text(struct seq_file *seq, void *offset) 21388c2ecf20Sopenharmony_ci{ 21398c2ecf20Sopenharmony_ci struct net_device *net_dev = (struct net_device *)seq->private; 21408c2ecf20Sopenharmony_ci struct batadv_priv *bat_priv = netdev_priv(net_dev); 21418c2ecf20Sopenharmony_ci struct batadv_hard_iface *primary_if; 21428c2ecf20Sopenharmony_ci struct batadv_hashtable *hash = bat_priv->orig_hash; 21438c2ecf20Sopenharmony_ci struct batadv_orig_node *orig_node; 21448c2ecf20Sopenharmony_ci struct hlist_head *head; 21458c2ecf20Sopenharmony_ci u8 flags; 21468c2ecf20Sopenharmony_ci u32 i; 21478c2ecf20Sopenharmony_ci 21488c2ecf20Sopenharmony_ci primary_if = batadv_seq_print_text_primary_if_get(seq); 21498c2ecf20Sopenharmony_ci if (!primary_if) 21508c2ecf20Sopenharmony_ci return 0; 21518c2ecf20Sopenharmony_ci 21528c2ecf20Sopenharmony_ci batadv_mcast_flags_print_header(bat_priv, seq); 21538c2ecf20Sopenharmony_ci 21548c2ecf20Sopenharmony_ci for (i = 0; i < hash->size; i++) { 21558c2ecf20Sopenharmony_ci head = &hash->table[i]; 21568c2ecf20Sopenharmony_ci 21578c2ecf20Sopenharmony_ci rcu_read_lock(); 21588c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(orig_node, head, hash_entry) { 21598c2ecf20Sopenharmony_ci if (!test_bit(BATADV_ORIG_CAPA_HAS_MCAST, 21608c2ecf20Sopenharmony_ci &orig_node->capa_initialized)) 21618c2ecf20Sopenharmony_ci continue; 21628c2ecf20Sopenharmony_ci 21638c2ecf20Sopenharmony_ci if (!test_bit(BATADV_ORIG_CAPA_HAS_MCAST, 21648c2ecf20Sopenharmony_ci &orig_node->capabilities)) { 21658c2ecf20Sopenharmony_ci seq_printf(seq, "%pM -\n", orig_node->orig); 21668c2ecf20Sopenharmony_ci continue; 21678c2ecf20Sopenharmony_ci } 21688c2ecf20Sopenharmony_ci 21698c2ecf20Sopenharmony_ci flags = orig_node->mcast_flags; 21708c2ecf20Sopenharmony_ci 21718c2ecf20Sopenharmony_ci seq_printf(seq, "%pM [%c%c%c%s%s]\n", orig_node->orig, 21728c2ecf20Sopenharmony_ci (flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES) 21738c2ecf20Sopenharmony_ci ? 'U' : '.', 21748c2ecf20Sopenharmony_ci (flags & BATADV_MCAST_WANT_ALL_IPV4) 21758c2ecf20Sopenharmony_ci ? '4' : '.', 21768c2ecf20Sopenharmony_ci (flags & BATADV_MCAST_WANT_ALL_IPV6) 21778c2ecf20Sopenharmony_ci ? '6' : '.', 21788c2ecf20Sopenharmony_ci !(flags & BATADV_MCAST_WANT_NO_RTR4) 21798c2ecf20Sopenharmony_ci ? "R4" : ". ", 21808c2ecf20Sopenharmony_ci !(flags & BATADV_MCAST_WANT_NO_RTR6) 21818c2ecf20Sopenharmony_ci ? "R6" : ". "); 21828c2ecf20Sopenharmony_ci } 21838c2ecf20Sopenharmony_ci rcu_read_unlock(); 21848c2ecf20Sopenharmony_ci } 21858c2ecf20Sopenharmony_ci 21868c2ecf20Sopenharmony_ci batadv_hardif_put(primary_if); 21878c2ecf20Sopenharmony_ci 21888c2ecf20Sopenharmony_ci return 0; 21898c2ecf20Sopenharmony_ci} 21908c2ecf20Sopenharmony_ci#endif 21918c2ecf20Sopenharmony_ci 21928c2ecf20Sopenharmony_ci/** 21938c2ecf20Sopenharmony_ci * batadv_mcast_mesh_info_put() - put multicast info into a netlink message 21948c2ecf20Sopenharmony_ci * @msg: buffer for the message 21958c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 21968c2ecf20Sopenharmony_ci * 21978c2ecf20Sopenharmony_ci * Return: 0 or error code. 21988c2ecf20Sopenharmony_ci */ 21998c2ecf20Sopenharmony_ciint batadv_mcast_mesh_info_put(struct sk_buff *msg, 22008c2ecf20Sopenharmony_ci struct batadv_priv *bat_priv) 22018c2ecf20Sopenharmony_ci{ 22028c2ecf20Sopenharmony_ci u32 flags = bat_priv->mcast.mla_flags.tvlv_flags; 22038c2ecf20Sopenharmony_ci u32 flags_priv = BATADV_NO_FLAGS; 22048c2ecf20Sopenharmony_ci 22058c2ecf20Sopenharmony_ci if (bat_priv->mcast.mla_flags.bridged) { 22068c2ecf20Sopenharmony_ci flags_priv |= BATADV_MCAST_FLAGS_BRIDGED; 22078c2ecf20Sopenharmony_ci 22088c2ecf20Sopenharmony_ci if (bat_priv->mcast.mla_flags.querier_ipv4.exists) 22098c2ecf20Sopenharmony_ci flags_priv |= BATADV_MCAST_FLAGS_QUERIER_IPV4_EXISTS; 22108c2ecf20Sopenharmony_ci if (bat_priv->mcast.mla_flags.querier_ipv6.exists) 22118c2ecf20Sopenharmony_ci flags_priv |= BATADV_MCAST_FLAGS_QUERIER_IPV6_EXISTS; 22128c2ecf20Sopenharmony_ci if (bat_priv->mcast.mla_flags.querier_ipv4.shadowing) 22138c2ecf20Sopenharmony_ci flags_priv |= BATADV_MCAST_FLAGS_QUERIER_IPV4_SHADOWING; 22148c2ecf20Sopenharmony_ci if (bat_priv->mcast.mla_flags.querier_ipv6.shadowing) 22158c2ecf20Sopenharmony_ci flags_priv |= BATADV_MCAST_FLAGS_QUERIER_IPV6_SHADOWING; 22168c2ecf20Sopenharmony_ci } 22178c2ecf20Sopenharmony_ci 22188c2ecf20Sopenharmony_ci if (nla_put_u32(msg, BATADV_ATTR_MCAST_FLAGS, flags) || 22198c2ecf20Sopenharmony_ci nla_put_u32(msg, BATADV_ATTR_MCAST_FLAGS_PRIV, flags_priv)) 22208c2ecf20Sopenharmony_ci return -EMSGSIZE; 22218c2ecf20Sopenharmony_ci 22228c2ecf20Sopenharmony_ci return 0; 22238c2ecf20Sopenharmony_ci} 22248c2ecf20Sopenharmony_ci 22258c2ecf20Sopenharmony_ci/** 22268c2ecf20Sopenharmony_ci * batadv_mcast_flags_dump_entry() - dump one entry of the multicast flags table 22278c2ecf20Sopenharmony_ci * to a netlink socket 22288c2ecf20Sopenharmony_ci * @msg: buffer for the message 22298c2ecf20Sopenharmony_ci * @portid: netlink port 22308c2ecf20Sopenharmony_ci * @cb: Control block containing additional options 22318c2ecf20Sopenharmony_ci * @orig_node: originator to dump the multicast flags of 22328c2ecf20Sopenharmony_ci * 22338c2ecf20Sopenharmony_ci * Return: 0 or error code. 22348c2ecf20Sopenharmony_ci */ 22358c2ecf20Sopenharmony_cistatic int 22368c2ecf20Sopenharmony_cibatadv_mcast_flags_dump_entry(struct sk_buff *msg, u32 portid, 22378c2ecf20Sopenharmony_ci struct netlink_callback *cb, 22388c2ecf20Sopenharmony_ci struct batadv_orig_node *orig_node) 22398c2ecf20Sopenharmony_ci{ 22408c2ecf20Sopenharmony_ci void *hdr; 22418c2ecf20Sopenharmony_ci 22428c2ecf20Sopenharmony_ci hdr = genlmsg_put(msg, portid, cb->nlh->nlmsg_seq, 22438c2ecf20Sopenharmony_ci &batadv_netlink_family, NLM_F_MULTI, 22448c2ecf20Sopenharmony_ci BATADV_CMD_GET_MCAST_FLAGS); 22458c2ecf20Sopenharmony_ci if (!hdr) 22468c2ecf20Sopenharmony_ci return -ENOBUFS; 22478c2ecf20Sopenharmony_ci 22488c2ecf20Sopenharmony_ci genl_dump_check_consistent(cb, hdr); 22498c2ecf20Sopenharmony_ci 22508c2ecf20Sopenharmony_ci if (nla_put(msg, BATADV_ATTR_ORIG_ADDRESS, ETH_ALEN, 22518c2ecf20Sopenharmony_ci orig_node->orig)) { 22528c2ecf20Sopenharmony_ci genlmsg_cancel(msg, hdr); 22538c2ecf20Sopenharmony_ci return -EMSGSIZE; 22548c2ecf20Sopenharmony_ci } 22558c2ecf20Sopenharmony_ci 22568c2ecf20Sopenharmony_ci if (test_bit(BATADV_ORIG_CAPA_HAS_MCAST, 22578c2ecf20Sopenharmony_ci &orig_node->capabilities)) { 22588c2ecf20Sopenharmony_ci if (nla_put_u32(msg, BATADV_ATTR_MCAST_FLAGS, 22598c2ecf20Sopenharmony_ci orig_node->mcast_flags)) { 22608c2ecf20Sopenharmony_ci genlmsg_cancel(msg, hdr); 22618c2ecf20Sopenharmony_ci return -EMSGSIZE; 22628c2ecf20Sopenharmony_ci } 22638c2ecf20Sopenharmony_ci } 22648c2ecf20Sopenharmony_ci 22658c2ecf20Sopenharmony_ci genlmsg_end(msg, hdr); 22668c2ecf20Sopenharmony_ci return 0; 22678c2ecf20Sopenharmony_ci} 22688c2ecf20Sopenharmony_ci 22698c2ecf20Sopenharmony_ci/** 22708c2ecf20Sopenharmony_ci * batadv_mcast_flags_dump_bucket() - dump one bucket of the multicast flags 22718c2ecf20Sopenharmony_ci * table to a netlink socket 22728c2ecf20Sopenharmony_ci * @msg: buffer for the message 22738c2ecf20Sopenharmony_ci * @portid: netlink port 22748c2ecf20Sopenharmony_ci * @cb: Control block containing additional options 22758c2ecf20Sopenharmony_ci * @hash: hash to dump 22768c2ecf20Sopenharmony_ci * @bucket: bucket index to dump 22778c2ecf20Sopenharmony_ci * @idx_skip: How many entries to skip 22788c2ecf20Sopenharmony_ci * 22798c2ecf20Sopenharmony_ci * Return: 0 or error code. 22808c2ecf20Sopenharmony_ci */ 22818c2ecf20Sopenharmony_cistatic int 22828c2ecf20Sopenharmony_cibatadv_mcast_flags_dump_bucket(struct sk_buff *msg, u32 portid, 22838c2ecf20Sopenharmony_ci struct netlink_callback *cb, 22848c2ecf20Sopenharmony_ci struct batadv_hashtable *hash, 22858c2ecf20Sopenharmony_ci unsigned int bucket, long *idx_skip) 22868c2ecf20Sopenharmony_ci{ 22878c2ecf20Sopenharmony_ci struct batadv_orig_node *orig_node; 22888c2ecf20Sopenharmony_ci long idx = 0; 22898c2ecf20Sopenharmony_ci 22908c2ecf20Sopenharmony_ci spin_lock_bh(&hash->list_locks[bucket]); 22918c2ecf20Sopenharmony_ci cb->seq = atomic_read(&hash->generation) << 1 | 1; 22928c2ecf20Sopenharmony_ci 22938c2ecf20Sopenharmony_ci hlist_for_each_entry(orig_node, &hash->table[bucket], hash_entry) { 22948c2ecf20Sopenharmony_ci if (!test_bit(BATADV_ORIG_CAPA_HAS_MCAST, 22958c2ecf20Sopenharmony_ci &orig_node->capa_initialized)) 22968c2ecf20Sopenharmony_ci continue; 22978c2ecf20Sopenharmony_ci 22988c2ecf20Sopenharmony_ci if (idx < *idx_skip) 22998c2ecf20Sopenharmony_ci goto skip; 23008c2ecf20Sopenharmony_ci 23018c2ecf20Sopenharmony_ci if (batadv_mcast_flags_dump_entry(msg, portid, cb, orig_node)) { 23028c2ecf20Sopenharmony_ci spin_unlock_bh(&hash->list_locks[bucket]); 23038c2ecf20Sopenharmony_ci *idx_skip = idx; 23048c2ecf20Sopenharmony_ci 23058c2ecf20Sopenharmony_ci return -EMSGSIZE; 23068c2ecf20Sopenharmony_ci } 23078c2ecf20Sopenharmony_ci 23088c2ecf20Sopenharmony_ciskip: 23098c2ecf20Sopenharmony_ci idx++; 23108c2ecf20Sopenharmony_ci } 23118c2ecf20Sopenharmony_ci spin_unlock_bh(&hash->list_locks[bucket]); 23128c2ecf20Sopenharmony_ci 23138c2ecf20Sopenharmony_ci return 0; 23148c2ecf20Sopenharmony_ci} 23158c2ecf20Sopenharmony_ci 23168c2ecf20Sopenharmony_ci/** 23178c2ecf20Sopenharmony_ci * __batadv_mcast_flags_dump() - dump multicast flags table to a netlink socket 23188c2ecf20Sopenharmony_ci * @msg: buffer for the message 23198c2ecf20Sopenharmony_ci * @portid: netlink port 23208c2ecf20Sopenharmony_ci * @cb: Control block containing additional options 23218c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 23228c2ecf20Sopenharmony_ci * @bucket: current bucket to dump 23238c2ecf20Sopenharmony_ci * @idx: index in current bucket to the next entry to dump 23248c2ecf20Sopenharmony_ci * 23258c2ecf20Sopenharmony_ci * Return: 0 or error code. 23268c2ecf20Sopenharmony_ci */ 23278c2ecf20Sopenharmony_cistatic int 23288c2ecf20Sopenharmony_ci__batadv_mcast_flags_dump(struct sk_buff *msg, u32 portid, 23298c2ecf20Sopenharmony_ci struct netlink_callback *cb, 23308c2ecf20Sopenharmony_ci struct batadv_priv *bat_priv, long *bucket, long *idx) 23318c2ecf20Sopenharmony_ci{ 23328c2ecf20Sopenharmony_ci struct batadv_hashtable *hash = bat_priv->orig_hash; 23338c2ecf20Sopenharmony_ci long bucket_tmp = *bucket; 23348c2ecf20Sopenharmony_ci long idx_tmp = *idx; 23358c2ecf20Sopenharmony_ci 23368c2ecf20Sopenharmony_ci while (bucket_tmp < hash->size) { 23378c2ecf20Sopenharmony_ci if (batadv_mcast_flags_dump_bucket(msg, portid, cb, hash, 23388c2ecf20Sopenharmony_ci bucket_tmp, &idx_tmp)) 23398c2ecf20Sopenharmony_ci break; 23408c2ecf20Sopenharmony_ci 23418c2ecf20Sopenharmony_ci bucket_tmp++; 23428c2ecf20Sopenharmony_ci idx_tmp = 0; 23438c2ecf20Sopenharmony_ci } 23448c2ecf20Sopenharmony_ci 23458c2ecf20Sopenharmony_ci *bucket = bucket_tmp; 23468c2ecf20Sopenharmony_ci *idx = idx_tmp; 23478c2ecf20Sopenharmony_ci 23488c2ecf20Sopenharmony_ci return msg->len; 23498c2ecf20Sopenharmony_ci} 23508c2ecf20Sopenharmony_ci 23518c2ecf20Sopenharmony_ci/** 23528c2ecf20Sopenharmony_ci * batadv_mcast_netlink_get_primary() - get primary interface from netlink 23538c2ecf20Sopenharmony_ci * callback 23548c2ecf20Sopenharmony_ci * @cb: netlink callback structure 23558c2ecf20Sopenharmony_ci * @primary_if: the primary interface pointer to return the result in 23568c2ecf20Sopenharmony_ci * 23578c2ecf20Sopenharmony_ci * Return: 0 or error code. 23588c2ecf20Sopenharmony_ci */ 23598c2ecf20Sopenharmony_cistatic int 23608c2ecf20Sopenharmony_cibatadv_mcast_netlink_get_primary(struct netlink_callback *cb, 23618c2ecf20Sopenharmony_ci struct batadv_hard_iface **primary_if) 23628c2ecf20Sopenharmony_ci{ 23638c2ecf20Sopenharmony_ci struct batadv_hard_iface *hard_iface = NULL; 23648c2ecf20Sopenharmony_ci struct net *net = sock_net(cb->skb->sk); 23658c2ecf20Sopenharmony_ci struct net_device *soft_iface; 23668c2ecf20Sopenharmony_ci struct batadv_priv *bat_priv; 23678c2ecf20Sopenharmony_ci int ifindex; 23688c2ecf20Sopenharmony_ci int ret = 0; 23698c2ecf20Sopenharmony_ci 23708c2ecf20Sopenharmony_ci ifindex = batadv_netlink_get_ifindex(cb->nlh, BATADV_ATTR_MESH_IFINDEX); 23718c2ecf20Sopenharmony_ci if (!ifindex) 23728c2ecf20Sopenharmony_ci return -EINVAL; 23738c2ecf20Sopenharmony_ci 23748c2ecf20Sopenharmony_ci soft_iface = dev_get_by_index(net, ifindex); 23758c2ecf20Sopenharmony_ci if (!soft_iface || !batadv_softif_is_valid(soft_iface)) { 23768c2ecf20Sopenharmony_ci ret = -ENODEV; 23778c2ecf20Sopenharmony_ci goto out; 23788c2ecf20Sopenharmony_ci } 23798c2ecf20Sopenharmony_ci 23808c2ecf20Sopenharmony_ci bat_priv = netdev_priv(soft_iface); 23818c2ecf20Sopenharmony_ci 23828c2ecf20Sopenharmony_ci hard_iface = batadv_primary_if_get_selected(bat_priv); 23838c2ecf20Sopenharmony_ci if (!hard_iface || hard_iface->if_status != BATADV_IF_ACTIVE) { 23848c2ecf20Sopenharmony_ci ret = -ENOENT; 23858c2ecf20Sopenharmony_ci goto out; 23868c2ecf20Sopenharmony_ci } 23878c2ecf20Sopenharmony_ci 23888c2ecf20Sopenharmony_ciout: 23898c2ecf20Sopenharmony_ci if (soft_iface) 23908c2ecf20Sopenharmony_ci dev_put(soft_iface); 23918c2ecf20Sopenharmony_ci 23928c2ecf20Sopenharmony_ci if (!ret && primary_if) 23938c2ecf20Sopenharmony_ci *primary_if = hard_iface; 23948c2ecf20Sopenharmony_ci else if (hard_iface) 23958c2ecf20Sopenharmony_ci batadv_hardif_put(hard_iface); 23968c2ecf20Sopenharmony_ci 23978c2ecf20Sopenharmony_ci return ret; 23988c2ecf20Sopenharmony_ci} 23998c2ecf20Sopenharmony_ci 24008c2ecf20Sopenharmony_ci/** 24018c2ecf20Sopenharmony_ci * batadv_mcast_flags_dump() - dump multicast flags table to a netlink socket 24028c2ecf20Sopenharmony_ci * @msg: buffer for the message 24038c2ecf20Sopenharmony_ci * @cb: callback structure containing arguments 24048c2ecf20Sopenharmony_ci * 24058c2ecf20Sopenharmony_ci * Return: message length. 24068c2ecf20Sopenharmony_ci */ 24078c2ecf20Sopenharmony_ciint batadv_mcast_flags_dump(struct sk_buff *msg, struct netlink_callback *cb) 24088c2ecf20Sopenharmony_ci{ 24098c2ecf20Sopenharmony_ci struct batadv_hard_iface *primary_if = NULL; 24108c2ecf20Sopenharmony_ci int portid = NETLINK_CB(cb->skb).portid; 24118c2ecf20Sopenharmony_ci struct batadv_priv *bat_priv; 24128c2ecf20Sopenharmony_ci long *bucket = &cb->args[0]; 24138c2ecf20Sopenharmony_ci long *idx = &cb->args[1]; 24148c2ecf20Sopenharmony_ci int ret; 24158c2ecf20Sopenharmony_ci 24168c2ecf20Sopenharmony_ci ret = batadv_mcast_netlink_get_primary(cb, &primary_if); 24178c2ecf20Sopenharmony_ci if (ret) 24188c2ecf20Sopenharmony_ci return ret; 24198c2ecf20Sopenharmony_ci 24208c2ecf20Sopenharmony_ci bat_priv = netdev_priv(primary_if->soft_iface); 24218c2ecf20Sopenharmony_ci ret = __batadv_mcast_flags_dump(msg, portid, cb, bat_priv, bucket, idx); 24228c2ecf20Sopenharmony_ci 24238c2ecf20Sopenharmony_ci batadv_hardif_put(primary_if); 24248c2ecf20Sopenharmony_ci return ret; 24258c2ecf20Sopenharmony_ci} 24268c2ecf20Sopenharmony_ci 24278c2ecf20Sopenharmony_ci/** 24288c2ecf20Sopenharmony_ci * batadv_mcast_free() - free the multicast optimizations structures 24298c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 24308c2ecf20Sopenharmony_ci */ 24318c2ecf20Sopenharmony_civoid batadv_mcast_free(struct batadv_priv *bat_priv) 24328c2ecf20Sopenharmony_ci{ 24338c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&bat_priv->mcast.work); 24348c2ecf20Sopenharmony_ci 24358c2ecf20Sopenharmony_ci batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_MCAST, 2); 24368c2ecf20Sopenharmony_ci batadv_tvlv_handler_unregister(bat_priv, BATADV_TVLV_MCAST, 2); 24378c2ecf20Sopenharmony_ci 24388c2ecf20Sopenharmony_ci /* safely calling outside of worker, as worker was canceled above */ 24398c2ecf20Sopenharmony_ci batadv_mcast_mla_tt_retract(bat_priv, NULL); 24408c2ecf20Sopenharmony_ci} 24418c2ecf20Sopenharmony_ci 24428c2ecf20Sopenharmony_ci/** 24438c2ecf20Sopenharmony_ci * batadv_mcast_purge_orig() - reset originator global mcast state modifications 24448c2ecf20Sopenharmony_ci * @orig: the originator which is going to get purged 24458c2ecf20Sopenharmony_ci */ 24468c2ecf20Sopenharmony_civoid batadv_mcast_purge_orig(struct batadv_orig_node *orig) 24478c2ecf20Sopenharmony_ci{ 24488c2ecf20Sopenharmony_ci struct batadv_priv *bat_priv = orig->bat_priv; 24498c2ecf20Sopenharmony_ci 24508c2ecf20Sopenharmony_ci spin_lock_bh(&orig->mcast_handler_lock); 24518c2ecf20Sopenharmony_ci 24528c2ecf20Sopenharmony_ci batadv_mcast_want_unsnoop_update(bat_priv, orig, BATADV_NO_FLAGS); 24538c2ecf20Sopenharmony_ci batadv_mcast_want_ipv4_update(bat_priv, orig, BATADV_NO_FLAGS); 24548c2ecf20Sopenharmony_ci batadv_mcast_want_ipv6_update(bat_priv, orig, BATADV_NO_FLAGS); 24558c2ecf20Sopenharmony_ci batadv_mcast_want_rtr4_update(bat_priv, orig, 24568c2ecf20Sopenharmony_ci BATADV_MCAST_WANT_NO_RTR4); 24578c2ecf20Sopenharmony_ci batadv_mcast_want_rtr6_update(bat_priv, orig, 24588c2ecf20Sopenharmony_ci BATADV_MCAST_WANT_NO_RTR6); 24598c2ecf20Sopenharmony_ci 24608c2ecf20Sopenharmony_ci spin_unlock_bh(&orig->mcast_handler_lock); 24618c2ecf20Sopenharmony_ci} 2462