Commit ab49886e3da73b6b35ece21006e191910427bb30

Authored by Linus Lüssing
Committed by Antonio Quartulli
1 parent 1d8ab8d3c1

batman-adv: Add IPv4 link-local/IPv6-ll-all-nodes multicast support

With this patch a node may additionally perform the dropping or
unicasting behaviour for a link-local IPv4 and link-local-all-nodes
IPv6 multicast packet, too.

The extra counter and BATADV_MCAST_WANT_ALL_UNSNOOPABLES flag is needed
because with a future bridge snooping support integration a node with a
bridge on top of its soft interface is not able to reliably detect its
multicast listeners for IPv4 link-local and the IPv6
link-local-all-nodes addresses anymore (see RFC4541, section 2.1.2.2
and section 3).

Even though this new flag does make "no difference" now, it'll ensure
a seamless integration of multicast bridge support without needing to
break compatibility later.

Also note, that even with multicast bridge support it won't be possible
to optimize 224.0.0.x and ff02::1 towards nodes with bridges, they will
always receive these ranges.

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>

Showing 6 changed files with 156 additions and 8 deletions Side-by-side Diff

net/batman-adv/main.c
... ... @@ -111,6 +111,9 @@
111 111 spin_lock_init(&bat_priv->tt.last_changeset_lock);
112 112 spin_lock_init(&bat_priv->tt.commit_lock);
113 113 spin_lock_init(&bat_priv->gw.list_lock);
  114 +#ifdef CONFIG_BATMAN_ADV_MCAST
  115 + spin_lock_init(&bat_priv->mcast.want_lists_lock);
  116 +#endif
114 117 spin_lock_init(&bat_priv->tvlv.container_list_lock);
115 118 spin_lock_init(&bat_priv->tvlv.handler_list_lock);
116 119 spin_lock_init(&bat_priv->softif_vlan_list_lock);
... ... @@ -118,6 +121,9 @@
118 121 INIT_HLIST_HEAD(&bat_priv->forw_bat_list);
119 122 INIT_HLIST_HEAD(&bat_priv->forw_bcast_list);
120 123 INIT_HLIST_HEAD(&bat_priv->gw.list);
  124 +#ifdef CONFIG_BATMAN_ADV_MCAST
  125 + INIT_HLIST_HEAD(&bat_priv->mcast.want_all_unsnoopables_list);
  126 +#endif
121 127 INIT_LIST_HEAD(&bat_priv->tt.changes_list);
122 128 INIT_LIST_HEAD(&bat_priv->tt.req_list);
123 129 INIT_LIST_HEAD(&bat_priv->tt.roam_list);
net/batman-adv/main.h
... ... @@ -177,6 +177,7 @@
177 177 #include <linux/slab.h>
178 178 #include <net/sock.h> /* struct sock */
179 179 #include <net/addrconf.h> /* ipv6 address stuff */
  180 +#include <linux/ip.h>
180 181 #include <net/rtnetlink.h>
181 182 #include <linux/jiffies.h>
182 183 #include <linux/seq_file.h>
net/batman-adv/multicast.c
... ... @@ -248,9 +248,48 @@
248 248 }
249 249  
250 250 /**
  251 + * batadv_mcast_forw_mode_check_ipv4 - check for optimized forwarding potential
  252 + * @bat_priv: the bat priv with all the soft interface information
  253 + * @skb: the IPv4 packet to check
  254 + * @is_unsnoopable: stores whether the destination is snoopable
  255 + *
  256 + * Checks whether the given IPv4 packet has the potential to be forwarded with a
  257 + * mode more optimal than classic flooding.
  258 + *
  259 + * If so then returns 0. Otherwise -EINVAL is returned or -ENOMEM in case of
  260 + * memory allocation failure.
  261 + */
  262 +static int batadv_mcast_forw_mode_check_ipv4(struct batadv_priv *bat_priv,
  263 + struct sk_buff *skb,
  264 + bool *is_unsnoopable)
  265 +{
  266 + struct iphdr *iphdr;
  267 +
  268 + /* We might fail due to out-of-memory -> drop it */
  269 + if (!pskb_may_pull(skb, sizeof(struct ethhdr) + sizeof(*iphdr)))
  270 + return -ENOMEM;
  271 +
  272 + iphdr = ip_hdr(skb);
  273 +
  274 + /* TODO: Implement Multicast Router Discovery (RFC4286),
  275 + * then allow scope > link local, too
  276 + */
  277 + if (!ipv4_is_local_multicast(iphdr->daddr))
  278 + return -EINVAL;
  279 +
  280 + /* link-local multicast listeners behind a bridge are
  281 + * not snoopable (see RFC4541, section 2.1.2.2)
  282 + */
  283 + *is_unsnoopable = true;
  284 +
  285 + return 0;
  286 +}
  287 +
  288 +/**
251 289 * batadv_mcast_forw_mode_check_ipv6 - check for optimized forwarding potential
252 290 * @bat_priv: the bat priv with all the soft interface information
253 291 * @skb: the IPv6 packet to check
  292 + * @is_unsnoopable: stores whether the destination is snoopable
254 293 *
255 294 * Checks whether the given IPv6 packet has the potential to be forwarded with a
256 295 * mode more optimal than classic flooding.
... ... @@ -259,7 +298,8 @@
259 298 * of memory.
260 299 */
261 300 static int batadv_mcast_forw_mode_check_ipv6(struct batadv_priv *bat_priv,
262   - struct sk_buff *skb)
  301 + struct sk_buff *skb,
  302 + bool *is_unsnoopable)
263 303 {
264 304 struct ipv6hdr *ip6hdr;
265 305  
... ... @@ -279,7 +319,7 @@
279 319 * not snoopable (see RFC4541, section 3, paragraph 3)
280 320 */
281 321 if (ipv6_addr_is_ll_all_nodes(&ip6hdr->daddr))
282   - return -EINVAL;
  322 + *is_unsnoopable = true;
283 323  
284 324 return 0;
285 325 }
... ... @@ -288,6 +328,7 @@
288 328 * batadv_mcast_forw_mode_check - check for optimized forwarding potential
289 329 * @bat_priv: the bat priv with all the soft interface information
290 330 * @skb: the multicast frame to check
  331 + * @is_unsnoopable: stores whether the destination is snoopable
291 332 *
292 333 * Checks whether the given multicast ethernet frame has the potential to be
293 334 * forwarded with a mode more optimal than classic flooding.
... ... @@ -296,7 +337,8 @@
296 337 * of memory.
297 338 */
298 339 static int batadv_mcast_forw_mode_check(struct batadv_priv *bat_priv,
299   - struct sk_buff *skb)
  340 + struct sk_buff *skb,
  341 + bool *is_unsnoopable)
300 342 {
301 343 struct ethhdr *ethhdr = eth_hdr(skb);
302 344  
303 345  
... ... @@ -307,8 +349,12 @@
307 349 return -EINVAL;
308 350  
309 351 switch (ntohs(ethhdr->h_proto)) {
  352 + case ETH_P_IP:
  353 + return batadv_mcast_forw_mode_check_ipv4(bat_priv, skb,
  354 + is_unsnoopable);
310 355 case ETH_P_IPV6:
311   - return batadv_mcast_forw_mode_check_ipv6(bat_priv, skb);
  356 + return batadv_mcast_forw_mode_check_ipv6(bat_priv, skb,
  357 + is_unsnoopable);
312 358 default:
313 359 return -EINVAL;
314 360 }
... ... @@ -331,6 +377,33 @@
331 377 }
332 378  
333 379 /**
  380 + * 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
  382 + *
  383 + * Returns an orig_node which has the BATADV_MCAST_WANT_ALL_UNSNOOPABLES flag
  384 + * set and increases its refcount.
  385 + */
  386 +static struct batadv_orig_node *
  387 +batadv_mcast_forw_unsnoop_node_get(struct batadv_priv *bat_priv)
  388 +{
  389 + struct batadv_orig_node *tmp_orig_node, *orig_node = NULL;
  390 +
  391 + rcu_read_lock();
  392 + hlist_for_each_entry_rcu(tmp_orig_node,
  393 + &bat_priv->mcast.want_all_unsnoopables_list,
  394 + mcast_want_all_unsnoopables_node) {
  395 + if (!atomic_inc_not_zero(&orig_node->refcount))
  396 + continue;
  397 +
  398 + orig_node = tmp_orig_node;
  399 + break;
  400 + }
  401 + rcu_read_unlock();
  402 +
  403 + return orig_node;
  404 +}
  405 +
  406 +/**
334 407 * batadv_mcast_forw_mode - check on how to forward a multicast packet
335 408 * @bat_priv: the bat priv with all the soft interface information
336 409 * @skb: The multicast packet to check
337 410  
338 411  
... ... @@ -344,10 +417,11 @@
344 417 batadv_mcast_forw_mode(struct batadv_priv *bat_priv, struct sk_buff *skb,
345 418 struct batadv_orig_node **orig)
346 419 {
  420 + int ret, tt_count, unsnoop_count, total_count;
  421 + bool is_unsnoopable = false;
347 422 struct ethhdr *ethhdr;
348   - int ret, tt_count;
349 423  
350   - ret = batadv_mcast_forw_mode_check(bat_priv, skb);
  424 + ret = batadv_mcast_forw_mode_check(bat_priv, skb, &is_unsnoopable);
351 425 if (ret == -ENOMEM)
352 426 return BATADV_FORW_NONE;
353 427 else if (ret < 0)
354 428  
355 429  
... ... @@ -357,10 +431,18 @@
357 431  
358 432 tt_count = batadv_tt_global_hash_count(bat_priv, ethhdr->h_dest,
359 433 BATADV_NO_FLAGS);
  434 + unsnoop_count = !is_unsnoopable ? 0 :
  435 + atomic_read(&bat_priv->mcast.num_want_all_unsnoopables);
360 436  
361   - switch (tt_count) {
  437 + total_count = tt_count + unsnoop_count;
  438 +
  439 + switch (total_count) {
362 440 case 1:
363   - *orig = batadv_mcast_forw_tt_node_get(bat_priv, ethhdr);
  441 + if (tt_count)
  442 + *orig = batadv_mcast_forw_tt_node_get(bat_priv, ethhdr);
  443 + else if (unsnoop_count)
  444 + *orig = batadv_mcast_forw_unsnoop_node_get(bat_priv);
  445 +
364 446 if (*orig)
365 447 return BATADV_FORW_SINGLE;
366 448  
... ... @@ -373,6 +455,39 @@
373 455 }
374 456  
375 457 /**
  458 + * batadv_mcast_want_unsnoop_update - update unsnoop counter and list
  459 + * @bat_priv: the bat priv with all the soft interface information
  460 + * @orig: the orig_node which multicast state might have changed of
  461 + * @mcast_flags: flags indicating the new multicast state
  462 + *
  463 + * If the BATADV_MCAST_WANT_ALL_UNSNOOPABLES flag of this originator,
  464 + * orig, has toggled then this method updates counter and list accordingly.
  465 + */
  466 +static void batadv_mcast_want_unsnoop_update(struct batadv_priv *bat_priv,
  467 + struct batadv_orig_node *orig,
  468 + uint8_t mcast_flags)
  469 +{
  470 + /* switched from flag unset to set */
  471 + if (mcast_flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES &&
  472 + !(orig->mcast_flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES)) {
  473 + atomic_inc(&bat_priv->mcast.num_want_all_unsnoopables);
  474 +
  475 + spin_lock_bh(&bat_priv->mcast.want_lists_lock);
  476 + hlist_add_head_rcu(&orig->mcast_want_all_unsnoopables_node,
  477 + &bat_priv->mcast.want_all_unsnoopables_list);
  478 + spin_unlock_bh(&bat_priv->mcast.want_lists_lock);
  479 + /* switched from flag set to unset */
  480 + } else if (!(mcast_flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES) &&
  481 + orig->mcast_flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES) {
  482 + atomic_dec(&bat_priv->mcast.num_want_all_unsnoopables);
  483 +
  484 + spin_lock_bh(&bat_priv->mcast.want_lists_lock);
  485 + hlist_del_rcu(&orig->mcast_want_all_unsnoopables_node);
  486 + spin_unlock_bh(&bat_priv->mcast.want_lists_lock);
  487 + }
  488 +}
  489 +
  490 +/**
376 491 * batadv_mcast_tvlv_ogm_handler_v1 - process incoming multicast tvlv container
377 492 * @bat_priv: the bat priv with all the soft interface information
378 493 * @orig: the orig_node of the ogm
... ... @@ -416,6 +531,8 @@
416 531 (tvlv_value_len >= sizeof(mcast_flags)))
417 532 mcast_flags = *(uint8_t *)tvlv_value;
418 533  
  534 + batadv_mcast_want_unsnoop_update(bat_priv, orig, mcast_flags);
  535 +
419 536 orig->mcast_flags = mcast_flags;
420 537 }
421 538  
... ... @@ -452,5 +569,7 @@
452 569  
453 570 if (!(orig->capabilities & BATADV_ORIG_CAPA_HAS_MCAST))
454 571 atomic_dec(&bat_priv->mcast.num_disabled);
  572 +
  573 + batadv_mcast_want_unsnoop_update(bat_priv, orig, BATADV_NO_FLAGS);
455 574 }
net/batman-adv/packet.h
... ... @@ -89,6 +89,15 @@
89 89 BATADV_PARAMETER_PROBLEM = 12,
90 90 };
91 91  
  92 +/**
  93 + * enum batadv_mcast_flags - flags for multicast capabilities and settings
  94 + * @BATADV_MCAST_WANT_ALL_UNSNOOPABLES: we want all packets destined for
  95 + * 224.0.0.0/24 or ff02::1
  96 + */
  97 +enum batadv_mcast_flags {
  98 + BATADV_MCAST_WANT_ALL_UNSNOOPABLES = BIT(0),
  99 +};
  100 +
92 101 /* tt data subtypes */
93 102 #define BATADV_TT_DATA_TYPE_MASK 0x0F
94 103  
net/batman-adv/soft-interface.c
... ... @@ -710,6 +710,7 @@
710 710 bat_priv->mcast.flags = BATADV_NO_FLAGS;
711 711 atomic_set(&bat_priv->multicast_mode, 1);
712 712 atomic_set(&bat_priv->mcast.num_disabled, 0);
  713 + atomic_set(&bat_priv->mcast.num_want_all_unsnoopables, 0);
713 714 #endif
714 715 atomic_set(&bat_priv->gw_mode, BATADV_GW_MODE_OFF);
715 716 atomic_set(&bat_priv->gw_sel_class, 20);
net/batman-adv/types.h
... ... @@ -205,6 +205,8 @@
205 205 * @last_seen: time when last packet from this node was received
206 206 * @bcast_seqno_reset: time when the broadcast seqno window was reset
207 207 * @mcast_flags: multicast flags announced by the orig node
  208 + * @mcast_want_all_unsnoop_node: a list node for the
  209 + * mcast.want_all_unsnoopables list
208 210 * @capabilities: announced capabilities of this originator
209 211 * @capa_initialized: bitfield to remember whether a capability was initialized
210 212 * @last_ttvn: last seen translation table version number
... ... @@ -249,6 +251,7 @@
249 251 unsigned long bcast_seqno_reset;
250 252 #ifdef CONFIG_BATMAN_ADV_MCAST
251 253 uint8_t mcast_flags;
  254 + struct hlist_node mcast_want_all_unsnoopables_node;
252 255 #endif
253 256 uint8_t capabilities;
254 257 uint8_t capa_initialized;
255 258  
256 259  
257 260  
... ... @@ -619,15 +622,24 @@
619 622 /**
620 623 * struct batadv_priv_mcast - per mesh interface mcast data
621 624 * @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
  626 + * multicast traffic
622 627 * @flags: the flags we have last sent in our mcast tvlv
623 628 * @enabled: whether the multicast tvlv is currently enabled
624 629 * @num_disabled: number of nodes that have no mcast tvlv
  630 + * @num_want_all_unsnoopables: number of nodes wanting unsnoopable IP traffic
  631 + * @want_lists_lock: lock for protecting modifications to mcast want lists
  632 + * (traversals are rcu-locked)
625 633 */
626 634 struct batadv_priv_mcast {
627 635 struct hlist_head mla_list;
  636 + struct hlist_head want_all_unsnoopables_list;
628 637 uint8_t flags;
629 638 bool enabled;
630 639 atomic_t num_disabled;
  640 + atomic_t num_want_all_unsnoopables;
  641 + /* protects want_all_unsnoopables_list */
  642 + spinlock_t want_lists_lock;
631 643 };
632 644 #endif
633 645