Merge tag 'batadv-net-for-davem-20190509' of git://git.open-mesh.org/linux-merge
[sfrench/cifs-2.6.git] / net / batman-adv / multicast.c
index 1b985ab89c0879e7be782f11db6e52e5fc4f6332..ec54e236e345432496df8f55b2e00fbad92f3444 100644 (file)
@@ -2,18 +2,6 @@
 /* Copyright (C) 2014-2019  B.A.T.M.A.N. contributors:
  *
  * Linus Lüssing
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
 #include "multicast.h"
@@ -66,6 +54,7 @@
 #include "hash.h"
 #include "log.h"
 #include "netlink.h"
+#include "send.h"
 #include "soft-interface.h"
 #include "translation-table.h"
 #include "tvlv.h"
@@ -986,6 +975,7 @@ batadv_mcast_forw_mode(struct batadv_priv *bat_priv, struct sk_buff *skb,
 {
        int ret, tt_count, ip_count, unsnoop_count, total_count;
        bool is_unsnoopable = false;
+       unsigned int mcast_fanout;
        struct ethhdr *ethhdr;
 
        ret = batadv_mcast_forw_mode_check(bat_priv, skb, &is_unsnoopable);
@@ -1020,8 +1010,203 @@ batadv_mcast_forw_mode(struct batadv_priv *bat_priv, struct sk_buff *skb,
        case 0:
                return BATADV_FORW_NONE;
        default:
-               return BATADV_FORW_ALL;
+               mcast_fanout = atomic_read(&bat_priv->multicast_fanout);
+
+               if (!unsnoop_count && total_count <= mcast_fanout)
+                       return BATADV_FORW_SOME;
        }
+
+       return BATADV_FORW_ALL;
+}
+
+/**
+ * batadv_mcast_forw_tt() - forwards a packet to multicast listeners
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: the multicast packet to transmit
+ * @vid: the vlan identifier
+ *
+ * Sends copies of a frame with multicast destination to any multicast
+ * listener registered in the translation table. A transmission is performed
+ * via a batman-adv unicast packet for each such destination node.
+ *
+ * Return: NET_XMIT_DROP on memory allocation failure, NET_XMIT_SUCCESS
+ * otherwise.
+ */
+static int
+batadv_mcast_forw_tt(struct batadv_priv *bat_priv, struct sk_buff *skb,
+                    unsigned short vid)
+{
+       int ret = NET_XMIT_SUCCESS;
+       struct sk_buff *newskb;
+
+       struct batadv_tt_orig_list_entry *orig_entry;
+
+       struct batadv_tt_global_entry *tt_global;
+       const u8 *addr = eth_hdr(skb)->h_dest;
+
+       tt_global = batadv_tt_global_hash_find(bat_priv, addr, vid);
+       if (!tt_global)
+               goto out;
+
+       rcu_read_lock();
+       hlist_for_each_entry_rcu(orig_entry, &tt_global->orig_list, list) {
+               newskb = skb_copy(skb, GFP_ATOMIC);
+               if (!newskb) {
+                       ret = NET_XMIT_DROP;
+                       break;
+               }
+
+               batadv_send_skb_unicast(bat_priv, newskb, BATADV_UNICAST, 0,
+                                       orig_entry->orig_node, vid);
+       }
+       rcu_read_unlock();
+
+       batadv_tt_global_entry_put(tt_global);
+
+out:
+       return ret;
+}
+
+/**
+ * batadv_mcast_forw_want_all_ipv4() - forward to nodes with want-all-ipv4
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: the multicast packet to transmit
+ * @vid: the vlan identifier
+ *
+ * Sends copies of a frame with multicast destination to any node with a
+ * BATADV_MCAST_WANT_ALL_IPV4 flag set. A transmission is performed via a
+ * batman-adv unicast packet for each such destination node.
+ *
+ * Return: NET_XMIT_DROP on memory allocation failure, NET_XMIT_SUCCESS
+ * otherwise.
+ */
+static int
+batadv_mcast_forw_want_all_ipv4(struct batadv_priv *bat_priv,
+                               struct sk_buff *skb, unsigned short vid)
+{
+       struct batadv_orig_node *orig_node;
+       int ret = NET_XMIT_SUCCESS;
+       struct sk_buff *newskb;
+
+       rcu_read_lock();
+       hlist_for_each_entry_rcu(orig_node,
+                                &bat_priv->mcast.want_all_ipv4_list,
+                                mcast_want_all_ipv4_node) {
+               newskb = skb_copy(skb, GFP_ATOMIC);
+               if (!newskb) {
+                       ret = NET_XMIT_DROP;
+                       break;
+               }
+
+               batadv_send_skb_unicast(bat_priv, newskb, BATADV_UNICAST, 0,
+                                       orig_node, vid);
+       }
+       rcu_read_unlock();
+       return ret;
+}
+
+/**
+ * batadv_mcast_forw_want_all_ipv6() - forward to nodes with want-all-ipv6
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: The multicast packet to transmit
+ * @vid: the vlan identifier
+ *
+ * Sends copies of a frame with multicast destination to any node with a
+ * BATADV_MCAST_WANT_ALL_IPV6 flag set. A transmission is performed via a
+ * batman-adv unicast packet for each such destination node.
+ *
+ * Return: NET_XMIT_DROP on memory allocation failure, NET_XMIT_SUCCESS
+ * otherwise.
+ */
+static int
+batadv_mcast_forw_want_all_ipv6(struct batadv_priv *bat_priv,
+                               struct sk_buff *skb, unsigned short vid)
+{
+       struct batadv_orig_node *orig_node;
+       int ret = NET_XMIT_SUCCESS;
+       struct sk_buff *newskb;
+
+       rcu_read_lock();
+       hlist_for_each_entry_rcu(orig_node,
+                                &bat_priv->mcast.want_all_ipv6_list,
+                                mcast_want_all_ipv6_node) {
+               newskb = skb_copy(skb, GFP_ATOMIC);
+               if (!newskb) {
+                       ret = NET_XMIT_DROP;
+                       break;
+               }
+
+               batadv_send_skb_unicast(bat_priv, newskb, BATADV_UNICAST, 0,
+                                       orig_node, vid);
+       }
+       rcu_read_unlock();
+       return ret;
+}
+
+/**
+ * batadv_mcast_forw_want_all() - forward packet to nodes in a want-all list
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: the multicast packet to transmit
+ * @vid: the vlan identifier
+ *
+ * Sends copies of a frame with multicast destination to any node with a
+ * BATADV_MCAST_WANT_ALL_IPV4 or BATADV_MCAST_WANT_ALL_IPV6 flag set. A
+ * transmission is performed via a batman-adv unicast packet for each such
+ * destination node.
+ *
+ * Return: NET_XMIT_DROP on memory allocation failure or if the protocol family
+ * is neither IPv4 nor IPv6. NET_XMIT_SUCCESS otherwise.
+ */
+static int
+batadv_mcast_forw_want_all(struct batadv_priv *bat_priv,
+                          struct sk_buff *skb, unsigned short vid)
+{
+       switch (ntohs(eth_hdr(skb)->h_proto)) {
+       case ETH_P_IP:
+               return batadv_mcast_forw_want_all_ipv4(bat_priv, skb, vid);
+       case ETH_P_IPV6:
+               return batadv_mcast_forw_want_all_ipv6(bat_priv, skb, vid);
+       default:
+               /* we shouldn't be here... */
+               return NET_XMIT_DROP;
+       }
+}
+
+/**
+ * batadv_mcast_forw_send() - send packet to any detected multicast recpient
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: the multicast packet to transmit
+ * @vid: the vlan identifier
+ *
+ * Sends copies of a frame with multicast destination to any node that signaled
+ * interest in it, that is either via the translation table or the according
+ * want-all flags. A transmission is performed via a batman-adv unicast packet
+ * for each such destination node.
+ *
+ * The given skb is consumed/freed.
+ *
+ * Return: NET_XMIT_DROP on memory allocation failure or if the protocol family
+ * is neither IPv4 nor IPv6. NET_XMIT_SUCCESS otherwise.
+ */
+int batadv_mcast_forw_send(struct batadv_priv *bat_priv, struct sk_buff *skb,
+                          unsigned short vid)
+{
+       int ret;
+
+       ret = batadv_mcast_forw_tt(bat_priv, skb, vid);
+       if (ret != NET_XMIT_SUCCESS) {
+               kfree_skb(skb);
+               return ret;
+       }
+
+       ret = batadv_mcast_forw_want_all(bat_priv, skb, vid);
+       if (ret != NET_XMIT_SUCCESS) {
+               kfree_skb(skb);
+               return ret;
+       }
+
+       consume_skb(skb);
+       return ret;
 }
 
 /**