aboutsummaryrefslogtreecommitdiffstats
path: root/net/batman-adv
diff options
context:
space:
mode:
authorLinus Lüssing <linus.luessing@web.de>2014-02-15 11:47:54 -0500
committerAntonio Quartulli <antonio@meshcoding.com>2014-03-22 04:18:58 -0400
commit4c8755d69cbde2ec464a39c932aed0a83f9ff89f (patch)
treeaaf3e50adcccff46e2f8de9d80cd228b4fd3024d /net/batman-adv
parentab49886e3da73b6b35ece21006e191910427bb30 (diff)
batman-adv: Send multicast packets to nodes with a WANT_ALL flag
With this patch a node sends IPv4 multicast packets to nodes which have a BATADV_MCAST_WANT_ALL_IPV4 flag set and IPv6 multicast packets to nodes which have a BATADV_MCAST_WANT_ALL_IPV6 flag set, too. Why is this needed? There are scenarios involving bridges where multicast report snooping and multicast TT announcements are not sufficient, which would lead to packet loss for some nodes otherwise: MLDv1 and IGMPv1/IGMPv2 have a suppression mechanism for multicast listener reports. When we have an MLDv1/IGMPv1/IGMPv2 querier behind a bridge then our snooping bridge is potentially not going to see any reports even though listeners exist because according to RFC4541 such reports are only forwarded to multicast routers: ----------------------------------------------------------- --------------- {Querier}---|Snoop. Switch|----{Listener} --------------- \ ^ ------- | br0 | < ??? ------- \ _-~---~_ _-~/ ~-_ ~ batman-adv \-----{Sender} \~_ cloud ~/ -~~__-__-~_/ I) MLDv1 Query: {Querier} -> flooded II) MLDv1 Report: {Listener} -> {Querier} -> br0 cannot detect the {Listener} => Packets from {Sender} need to be forwarded to all detected listeners and MLDv1/IGMPv1/IGMPv2 queriers. ----------------------------------------------------------- Note that we do not need to explicitly forward to MLDv2/IGMPv3 queriers, because these protocols have no report suppression: A bridge has no trouble detecting MLDv2/IGMPv3 listeners. Even though we do not support bridges yet we need to provide the according infrastructure already to not break compatibility later. Signed-off-by: Linus Lüssing <linus.luessing@web.de> Signed-off-by: Marek Lindner <mareklindner@neomailbox.ch> Signed-off-by: Antonio Quartulli <antonio@meshcoding.com>
Diffstat (limited to 'net/batman-adv')
-rw-r--r--net/batman-adv/main.c2
-rw-r--r--net/batman-adv/multicast.c178
-rw-r--r--net/batman-adv/packet.h4
-rw-r--r--net/batman-adv/send.c1
-rw-r--r--net/batman-adv/soft-interface.c2
-rw-r--r--net/batman-adv/types.h14
6 files changed, 198 insertions, 3 deletions
diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c
index 57b09fa54b14..d1183e882167 100644
--- a/net/batman-adv/main.c
+++ b/net/batman-adv/main.c
@@ -123,6 +123,8 @@ int batadv_mesh_init(struct net_device *soft_iface)
123 INIT_HLIST_HEAD(&bat_priv->gw.list); 123 INIT_HLIST_HEAD(&bat_priv->gw.list);
124#ifdef CONFIG_BATMAN_ADV_MCAST 124#ifdef CONFIG_BATMAN_ADV_MCAST
125 INIT_HLIST_HEAD(&bat_priv->mcast.want_all_unsnoopables_list); 125 INIT_HLIST_HEAD(&bat_priv->mcast.want_all_unsnoopables_list);
126 INIT_HLIST_HEAD(&bat_priv->mcast.want_all_ipv4_list);
127 INIT_HLIST_HEAD(&bat_priv->mcast.want_all_ipv6_list);
126#endif 128#endif
127 INIT_LIST_HEAD(&bat_priv->tt.changes_list); 129 INIT_LIST_HEAD(&bat_priv->tt.changes_list);
128 INIT_LIST_HEAD(&bat_priv->tt.req_list); 130 INIT_LIST_HEAD(&bat_priv->tt.req_list);
diff --git a/net/batman-adv/multicast.c b/net/batman-adv/multicast.c
index a4804fa1ad11..8c7ca811de6e 100644
--- a/net/batman-adv/multicast.c
+++ b/net/batman-adv/multicast.c
@@ -361,6 +361,29 @@ static int batadv_mcast_forw_mode_check(struct batadv_priv *bat_priv,
361} 361}
362 362
363/** 363/**
364 * batadv_mcast_want_all_ip_count - count nodes with unspecific mcast interest
365 * @bat_priv: the bat priv with all the soft interface information
366 * @ethhdr: ethernet header of a packet
367 *
368 * Returns the number of nodes which want all IPv4 multicast traffic if the
369 * given ethhdr is from an IPv4 packet or the number of nodes which want all
370 * IPv6 traffic if it matches an IPv6 packet.
371 */
372static int batadv_mcast_forw_want_all_ip_count(struct batadv_priv *bat_priv,
373 struct ethhdr *ethhdr)
374{
375 switch (ntohs(ethhdr->h_proto)) {
376 case ETH_P_IP:
377 return atomic_read(&bat_priv->mcast.num_want_all_ipv4);
378 case ETH_P_IPV6:
379 return atomic_read(&bat_priv->mcast.num_want_all_ipv6);
380 default:
381 /* we shouldn't be here... */
382 return 0;
383 }
384}
385
386/**
364 * batadv_mcast_forw_tt_node_get - get a multicast tt node 387 * batadv_mcast_forw_tt_node_get - get a multicast tt node
365 * @bat_priv: the bat priv with all the soft interface information 388 * @bat_priv: the bat priv with all the soft interface information
366 * @ethhdr: the ether header containing the multicast destination 389 * @ethhdr: the ether header containing the multicast destination
@@ -377,6 +400,84 @@ batadv_mcast_forw_tt_node_get(struct batadv_priv *bat_priv,
377} 400}
378 401
379/** 402/**
403 * batadv_mcast_want_forw_ipv4_node_get - get a node with an ipv4 flag
404 * @bat_priv: the bat priv with all the soft interface information
405 *
406 * Returns an orig_node which has the BATADV_MCAST_WANT_ALL_IPV4 flag set and
407 * increases its refcount.
408 */
409static struct batadv_orig_node *
410batadv_mcast_forw_ipv4_node_get(struct batadv_priv *bat_priv)
411{
412 struct batadv_orig_node *tmp_orig_node, *orig_node = NULL;
413
414 rcu_read_lock();
415 hlist_for_each_entry_rcu(tmp_orig_node,
416 &bat_priv->mcast.want_all_ipv4_list,
417 mcast_want_all_ipv4_node) {
418 if (!atomic_inc_not_zero(&orig_node->refcount))
419 continue;
420
421 orig_node = tmp_orig_node;
422 break;
423 }
424 rcu_read_unlock();
425
426 return orig_node;
427}
428
429/**
430 * batadv_mcast_want_forw_ipv6_node_get - get a node with an ipv6 flag
431 * @bat_priv: the bat priv with all the soft interface information
432 *
433 * Returns an orig_node which has the BATADV_MCAST_WANT_ALL_IPV6 flag set
434 * and increases its refcount.
435 */
436static struct batadv_orig_node *
437batadv_mcast_forw_ipv6_node_get(struct batadv_priv *bat_priv)
438{
439 struct batadv_orig_node *tmp_orig_node, *orig_node = NULL;
440
441 rcu_read_lock();
442 hlist_for_each_entry_rcu(tmp_orig_node,
443 &bat_priv->mcast.want_all_ipv6_list,
444 mcast_want_all_ipv6_node) {
445 if (!atomic_inc_not_zero(&orig_node->refcount))
446 continue;
447
448 orig_node = tmp_orig_node;
449 break;
450 }
451 rcu_read_unlock();
452
453 return orig_node;
454}
455
456/**
457 * batadv_mcast_want_forw_ip_node_get - get a node with an ipv4/ipv6 flag
458 * @bat_priv: the bat priv with all the soft interface information
459 * @ethhdr: an ethernet header to determine the protocol family from
460 *
461 * Returns an orig_node which has the BATADV_MCAST_WANT_ALL_IPV4 or
462 * BATADV_MCAST_WANT_ALL_IPV6 flag, depending on the provided ethhdr, set and
463 * increases its refcount.
464 */
465static struct batadv_orig_node *
466batadv_mcast_forw_ip_node_get(struct batadv_priv *bat_priv,
467 struct ethhdr *ethhdr)
468{
469 switch (ntohs(ethhdr->h_proto)) {
470 case ETH_P_IP:
471 return batadv_mcast_forw_ipv4_node_get(bat_priv);
472 case ETH_P_IPV6:
473 return batadv_mcast_forw_ipv6_node_get(bat_priv);
474 default:
475 /* we shouldn't be here... */
476 return NULL;
477 }
478}
479
480/**
380 * batadv_mcast_want_forw_unsnoop_node_get - get a node with an unsnoopable flag 481 * batadv_mcast_want_forw_unsnoop_node_get - get a node with an unsnoopable flag
381 * @bat_priv: the bat priv with all the soft interface information 482 * @bat_priv: the bat priv with all the soft interface information
382 * 483 *
@@ -417,7 +518,7 @@ enum batadv_forw_mode
417batadv_mcast_forw_mode(struct batadv_priv *bat_priv, struct sk_buff *skb, 518batadv_mcast_forw_mode(struct batadv_priv *bat_priv, struct sk_buff *skb,
418 struct batadv_orig_node **orig) 519 struct batadv_orig_node **orig)
419{ 520{
420 int ret, tt_count, unsnoop_count, total_count; 521 int ret, tt_count, ip_count, unsnoop_count, total_count;
421 bool is_unsnoopable = false; 522 bool is_unsnoopable = false;
422 struct ethhdr *ethhdr; 523 struct ethhdr *ethhdr;
423 524
@@ -431,15 +532,18 @@ batadv_mcast_forw_mode(struct batadv_priv *bat_priv, struct sk_buff *skb,
431 532
432 tt_count = batadv_tt_global_hash_count(bat_priv, ethhdr->h_dest, 533 tt_count = batadv_tt_global_hash_count(bat_priv, ethhdr->h_dest,
433 BATADV_NO_FLAGS); 534 BATADV_NO_FLAGS);
535 ip_count = batadv_mcast_forw_want_all_ip_count(bat_priv, ethhdr);
434 unsnoop_count = !is_unsnoopable ? 0 : 536 unsnoop_count = !is_unsnoopable ? 0 :
435 atomic_read(&bat_priv->mcast.num_want_all_unsnoopables); 537 atomic_read(&bat_priv->mcast.num_want_all_unsnoopables);
436 538
437 total_count = tt_count + unsnoop_count; 539 total_count = tt_count + ip_count + unsnoop_count;
438 540
439 switch (total_count) { 541 switch (total_count) {
440 case 1: 542 case 1:
441 if (tt_count) 543 if (tt_count)
442 *orig = batadv_mcast_forw_tt_node_get(bat_priv, ethhdr); 544 *orig = batadv_mcast_forw_tt_node_get(bat_priv, ethhdr);
545 else if (ip_count)
546 *orig = batadv_mcast_forw_ip_node_get(bat_priv, ethhdr);
443 else if (unsnoop_count) 547 else if (unsnoop_count)
444 *orig = batadv_mcast_forw_unsnoop_node_get(bat_priv); 548 *orig = batadv_mcast_forw_unsnoop_node_get(bat_priv);
445 549
@@ -488,6 +592,72 @@ static void batadv_mcast_want_unsnoop_update(struct batadv_priv *bat_priv,
488} 592}
489 593
490/** 594/**
595 * batadv_mcast_want_ipv4_update - update want-all-ipv4 counter and list
596 * @bat_priv: the bat priv with all the soft interface information
597 * @orig: the orig_node which multicast state might have changed of
598 * @mcast_flags: flags indicating the new multicast state
599 *
600 * If the BATADV_MCAST_WANT_ALL_IPV4 flag of this originator, orig, has
601 * toggled then this method updates counter and list accordingly.
602 */
603static void batadv_mcast_want_ipv4_update(struct batadv_priv *bat_priv,
604 struct batadv_orig_node *orig,
605 uint8_t mcast_flags)
606{
607 /* switched from flag unset to set */
608 if (mcast_flags & BATADV_MCAST_WANT_ALL_IPV4 &&
609 !(orig->mcast_flags & BATADV_MCAST_WANT_ALL_IPV4)) {
610 atomic_inc(&bat_priv->mcast.num_want_all_ipv4);
611
612 spin_lock_bh(&bat_priv->mcast.want_lists_lock);
613 hlist_add_head_rcu(&orig->mcast_want_all_ipv4_node,
614 &bat_priv->mcast.want_all_ipv4_list);
615 spin_unlock_bh(&bat_priv->mcast.want_lists_lock);
616 /* switched from flag set to unset */
617 } else if (!(mcast_flags & BATADV_MCAST_WANT_ALL_IPV4) &&
618 orig->mcast_flags & BATADV_MCAST_WANT_ALL_IPV4) {
619 atomic_dec(&bat_priv->mcast.num_want_all_ipv4);
620
621 spin_lock_bh(&bat_priv->mcast.want_lists_lock);
622 hlist_del_rcu(&orig->mcast_want_all_ipv4_node);
623 spin_unlock_bh(&bat_priv->mcast.want_lists_lock);
624 }
625}
626
627/**
628 * batadv_mcast_want_ipv6_update - update want-all-ipv6 counter and list
629 * @bat_priv: the bat priv with all the soft interface information
630 * @orig: the orig_node which multicast state might have changed of
631 * @mcast_flags: flags indicating the new multicast state
632 *
633 * If the BATADV_MCAST_WANT_ALL_IPV6 flag of this originator, orig, has
634 * toggled then this method updates counter and list accordingly.
635 */
636static void batadv_mcast_want_ipv6_update(struct batadv_priv *bat_priv,
637 struct batadv_orig_node *orig,
638 uint8_t mcast_flags)
639{
640 /* switched from flag unset to set */
641 if (mcast_flags & BATADV_MCAST_WANT_ALL_IPV6 &&
642 !(orig->mcast_flags & BATADV_MCAST_WANT_ALL_IPV6)) {
643 atomic_inc(&bat_priv->mcast.num_want_all_ipv6);
644
645 spin_lock_bh(&bat_priv->mcast.want_lists_lock);
646 hlist_add_head_rcu(&orig->mcast_want_all_ipv6_node,
647 &bat_priv->mcast.want_all_ipv6_list);
648 spin_unlock_bh(&bat_priv->mcast.want_lists_lock);
649 /* switched from flag set to unset */
650 } else if (!(mcast_flags & BATADV_MCAST_WANT_ALL_IPV6) &&
651 orig->mcast_flags & BATADV_MCAST_WANT_ALL_IPV6) {
652 atomic_dec(&bat_priv->mcast.num_want_all_ipv6);
653
654 spin_lock_bh(&bat_priv->mcast.want_lists_lock);
655 hlist_del_rcu(&orig->mcast_want_all_ipv6_node);
656 spin_unlock_bh(&bat_priv->mcast.want_lists_lock);
657 }
658}
659
660/**
491 * batadv_mcast_tvlv_ogm_handler_v1 - process incoming multicast tvlv container 661 * batadv_mcast_tvlv_ogm_handler_v1 - process incoming multicast tvlv container
492 * @bat_priv: the bat priv with all the soft interface information 662 * @bat_priv: the bat priv with all the soft interface information
493 * @orig: the orig_node of the ogm 663 * @orig: the orig_node of the ogm
@@ -532,6 +702,8 @@ static void batadv_mcast_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv,
532 mcast_flags = *(uint8_t *)tvlv_value; 702 mcast_flags = *(uint8_t *)tvlv_value;
533 703
534 batadv_mcast_want_unsnoop_update(bat_priv, orig, mcast_flags); 704 batadv_mcast_want_unsnoop_update(bat_priv, orig, mcast_flags);
705 batadv_mcast_want_ipv4_update(bat_priv, orig, mcast_flags);
706 batadv_mcast_want_ipv6_update(bat_priv, orig, mcast_flags);
535 707
536 orig->mcast_flags = mcast_flags; 708 orig->mcast_flags = mcast_flags;
537} 709}
@@ -571,4 +743,6 @@ void batadv_mcast_purge_orig(struct batadv_orig_node *orig)
571 atomic_dec(&bat_priv->mcast.num_disabled); 743 atomic_dec(&bat_priv->mcast.num_disabled);
572 744
573 batadv_mcast_want_unsnoop_update(bat_priv, orig, BATADV_NO_FLAGS); 745 batadv_mcast_want_unsnoop_update(bat_priv, orig, BATADV_NO_FLAGS);
746 batadv_mcast_want_ipv4_update(bat_priv, orig, BATADV_NO_FLAGS);
747 batadv_mcast_want_ipv6_update(bat_priv, orig, BATADV_NO_FLAGS);
574} 748}
diff --git a/net/batman-adv/packet.h b/net/batman-adv/packet.h
index d061e26c2045..c7f6eefda681 100644
--- a/net/batman-adv/packet.h
+++ b/net/batman-adv/packet.h
@@ -93,9 +93,13 @@ enum batadv_icmp_packettype {
93 * enum batadv_mcast_flags - flags for multicast capabilities and settings 93 * enum batadv_mcast_flags - flags for multicast capabilities and settings
94 * @BATADV_MCAST_WANT_ALL_UNSNOOPABLES: we want all packets destined for 94 * @BATADV_MCAST_WANT_ALL_UNSNOOPABLES: we want all packets destined for
95 * 224.0.0.0/24 or ff02::1 95 * 224.0.0.0/24 or ff02::1
96 * @BATADV_MCAST_WANT_ALL_IPV4: we want all IPv4 multicast packets
97 * @BATADV_MCAST_WANT_ALL_IPV6: we want all IPv6 multicast packets
96 */ 98 */
97enum batadv_mcast_flags { 99enum batadv_mcast_flags {
98 BATADV_MCAST_WANT_ALL_UNSNOOPABLES = BIT(0), 100 BATADV_MCAST_WANT_ALL_UNSNOOPABLES = BIT(0),
101 BATADV_MCAST_WANT_ALL_IPV4 = BIT(1),
102 BATADV_MCAST_WANT_ALL_IPV6 = BIT(2),
99}; 103};
100 104
101/* tt data subtypes */ 105/* tt data subtypes */
diff --git a/net/batman-adv/send.c b/net/batman-adv/send.c
index 8bee5e8536b7..3d64ed20c393 100644
--- a/net/batman-adv/send.c
+++ b/net/batman-adv/send.c
@@ -27,6 +27,7 @@
27#include "originator.h" 27#include "originator.h"
28#include "network-coding.h" 28#include "network-coding.h"
29#include "fragmentation.h" 29#include "fragmentation.h"
30#include "multicast.h"
30 31
31static void batadv_send_outstanding_bcast_packet(struct work_struct *work); 32static void batadv_send_outstanding_bcast_packet(struct work_struct *work);
32 33
diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c
index db6fecaddb9c..744a59b85e15 100644
--- a/net/batman-adv/soft-interface.c
+++ b/net/batman-adv/soft-interface.c
@@ -711,6 +711,8 @@ static int batadv_softif_init_late(struct net_device *dev)
711 atomic_set(&bat_priv->multicast_mode, 1); 711 atomic_set(&bat_priv->multicast_mode, 1);
712 atomic_set(&bat_priv->mcast.num_disabled, 0); 712 atomic_set(&bat_priv->mcast.num_disabled, 0);
713 atomic_set(&bat_priv->mcast.num_want_all_unsnoopables, 0); 713 atomic_set(&bat_priv->mcast.num_want_all_unsnoopables, 0);
714 atomic_set(&bat_priv->mcast.num_want_all_ipv4, 0);
715 atomic_set(&bat_priv->mcast.num_want_all_ipv6, 0);
714#endif 716#endif
715 atomic_set(&bat_priv->gw_mode, BATADV_GW_MODE_OFF); 717 atomic_set(&bat_priv->gw_mode, BATADV_GW_MODE_OFF);
716 atomic_set(&bat_priv->gw_sel_class, 20); 718 atomic_set(&bat_priv->gw_sel_class, 20);
diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
index 1a674cb19553..d4b923c7229c 100644
--- a/net/batman-adv/types.h
+++ b/net/batman-adv/types.h
@@ -207,6 +207,8 @@ struct batadv_orig_bat_iv {
207 * @mcast_flags: multicast flags announced by the orig node 207 * @mcast_flags: multicast flags announced by the orig node
208 * @mcast_want_all_unsnoop_node: a list node for the 208 * @mcast_want_all_unsnoop_node: a list node for the
209 * mcast.want_all_unsnoopables list 209 * mcast.want_all_unsnoopables list
210 * @mcast_want_all_ipv4_node: a list node for the mcast.want_all_ipv4 list
211 * @mcast_want_all_ipv6_node: a list node for the mcast.want_all_ipv6 list
210 * @capabilities: announced capabilities of this originator 212 * @capabilities: announced capabilities of this originator
211 * @capa_initialized: bitfield to remember whether a capability was initialized 213 * @capa_initialized: bitfield to remember whether a capability was initialized
212 * @last_ttvn: last seen translation table version number 214 * @last_ttvn: last seen translation table version number
@@ -252,6 +254,8 @@ struct batadv_orig_node {
252#ifdef CONFIG_BATMAN_ADV_MCAST 254#ifdef CONFIG_BATMAN_ADV_MCAST
253 uint8_t mcast_flags; 255 uint8_t mcast_flags;
254 struct hlist_node mcast_want_all_unsnoopables_node; 256 struct hlist_node mcast_want_all_unsnoopables_node;
257 struct hlist_node mcast_want_all_ipv4_node;
258 struct hlist_node mcast_want_all_ipv6_node;
255#endif 259#endif
256 uint8_t capabilities; 260 uint8_t capabilities;
257 uint8_t capa_initialized; 261 uint8_t capa_initialized;
@@ -624,21 +628,29 @@ struct batadv_priv_dat {
624 * @mla_list: list of multicast addresses we are currently announcing via TT 628 * @mla_list: list of multicast addresses we are currently announcing via TT
625 * @want_all_unsnoopables_list: a list of orig_nodes wanting all unsnoopable 629 * @want_all_unsnoopables_list: a list of orig_nodes wanting all unsnoopable
626 * multicast traffic 630 * multicast traffic
631 * @want_all_ipv4_list: a list of orig_nodes wanting all IPv4 multicast traffic
632 * @want_all_ipv6_list: a list of orig_nodes wanting all IPv6 multicast traffic
627 * @flags: the flags we have last sent in our mcast tvlv 633 * @flags: the flags we have last sent in our mcast tvlv
628 * @enabled: whether the multicast tvlv is currently enabled 634 * @enabled: whether the multicast tvlv is currently enabled
629 * @num_disabled: number of nodes that have no mcast tvlv 635 * @num_disabled: number of nodes that have no mcast tvlv
630 * @num_want_all_unsnoopables: number of nodes wanting unsnoopable IP traffic 636 * @num_want_all_unsnoopables: number of nodes wanting unsnoopable IP traffic
637 * @num_want_all_ipv4: counter for items in want_all_ipv4_list
638 * @num_want_all_ipv6: counter for items in want_all_ipv6_list
631 * @want_lists_lock: lock for protecting modifications to mcast want lists 639 * @want_lists_lock: lock for protecting modifications to mcast want lists
632 * (traversals are rcu-locked) 640 * (traversals are rcu-locked)
633 */ 641 */
634struct batadv_priv_mcast { 642struct batadv_priv_mcast {
635 struct hlist_head mla_list; 643 struct hlist_head mla_list;
636 struct hlist_head want_all_unsnoopables_list; 644 struct hlist_head want_all_unsnoopables_list;
645 struct hlist_head want_all_ipv4_list;
646 struct hlist_head want_all_ipv6_list;
637 uint8_t flags; 647 uint8_t flags;
638 bool enabled; 648 bool enabled;
639 atomic_t num_disabled; 649 atomic_t num_disabled;
640 atomic_t num_want_all_unsnoopables; 650 atomic_t num_want_all_unsnoopables;
641 /* protects want_all_unsnoopables_list */ 651 atomic_t num_want_all_ipv4;
652 atomic_t num_want_all_ipv6;
653 /* protects want_all_{unsnoopables,ipv4,ipv6}_list */
642 spinlock_t want_lists_lock; 654 spinlock_t want_lists_lock;
643}; 655};
644#endif 656#endif