diff options
author | Linus Lüssing <linus.luessing@web.de> | 2014-02-15 11:47:53 -0500 |
---|---|---|
committer | Antonio Quartulli <antonio@meshcoding.com> | 2014-03-22 04:18:58 -0400 |
commit | ab49886e3da73b6b35ece21006e191910427bb30 (patch) | |
tree | d4ae8a3192413628fd8301044fcd039c57ba7066 /net/batman-adv/multicast.c | |
parent | 1d8ab8d3c176d31530b3ffd4547cf731018e2a45 (diff) |
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>
Diffstat (limited to 'net/batman-adv/multicast.c')
-rw-r--r-- | net/batman-adv/multicast.c | 135 |
1 files changed, 127 insertions, 8 deletions
diff --git a/net/batman-adv/multicast.c b/net/batman-adv/multicast.c index 1d1627fe0de0..a4804fa1ad11 100644 --- a/net/batman-adv/multicast.c +++ b/net/batman-adv/multicast.c | |||
@@ -248,9 +248,48 @@ out: | |||
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 | * batadv_mcast_forw_mode_check_ipv6 - check for optimized forwarding potential | 289 | * batadv_mcast_forw_mode_check_ipv6 - check for optimized forwarding potential |
252 | * @bat_priv: the bat priv with all the soft interface information | 290 | * @bat_priv: the bat priv with all the soft interface information |
253 | * @skb: the IPv6 packet to check | 291 | * @skb: the IPv6 packet to check |
292 | * @is_unsnoopable: stores whether the destination is snoopable | ||
254 | * | 293 | * |
255 | * Checks whether the given IPv6 packet has the potential to be forwarded with a | 294 | * Checks whether the given IPv6 packet has the potential to be forwarded with a |
256 | * mode more optimal than classic flooding. | 295 | * mode more optimal than classic flooding. |
@@ -259,7 +298,8 @@ out: | |||
259 | * of memory. | 298 | * of memory. |
260 | */ | 299 | */ |
261 | static int batadv_mcast_forw_mode_check_ipv6(struct batadv_priv *bat_priv, | 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 | struct ipv6hdr *ip6hdr; | 304 | struct ipv6hdr *ip6hdr; |
265 | 305 | ||
@@ -279,7 +319,7 @@ static int batadv_mcast_forw_mode_check_ipv6(struct batadv_priv *bat_priv, | |||
279 | * not snoopable (see RFC4541, section 3, paragraph 3) | 319 | * not snoopable (see RFC4541, section 3, paragraph 3) |
280 | */ | 320 | */ |
281 | if (ipv6_addr_is_ll_all_nodes(&ip6hdr->daddr)) | 321 | if (ipv6_addr_is_ll_all_nodes(&ip6hdr->daddr)) |
282 | return -EINVAL; | 322 | *is_unsnoopable = true; |
283 | 323 | ||
284 | return 0; | 324 | return 0; |
285 | } | 325 | } |
@@ -288,6 +328,7 @@ static int batadv_mcast_forw_mode_check_ipv6(struct batadv_priv *bat_priv, | |||
288 | * batadv_mcast_forw_mode_check - check for optimized forwarding potential | 328 | * batadv_mcast_forw_mode_check - check for optimized forwarding potential |
289 | * @bat_priv: the bat priv with all the soft interface information | 329 | * @bat_priv: the bat priv with all the soft interface information |
290 | * @skb: the multicast frame to check | 330 | * @skb: the multicast frame to check |
331 | * @is_unsnoopable: stores whether the destination is snoopable | ||
291 | * | 332 | * |
292 | * Checks whether the given multicast ethernet frame has the potential to be | 333 | * Checks whether the given multicast ethernet frame has the potential to be |
293 | * forwarded with a mode more optimal than classic flooding. | 334 | * forwarded with a mode more optimal than classic flooding. |
@@ -296,7 +337,8 @@ static int batadv_mcast_forw_mode_check_ipv6(struct batadv_priv *bat_priv, | |||
296 | * of memory. | 337 | * of memory. |
297 | */ | 338 | */ |
298 | static int batadv_mcast_forw_mode_check(struct batadv_priv *bat_priv, | 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 | struct ethhdr *ethhdr = eth_hdr(skb); | 343 | struct ethhdr *ethhdr = eth_hdr(skb); |
302 | 344 | ||
@@ -307,8 +349,12 @@ static int batadv_mcast_forw_mode_check(struct batadv_priv *bat_priv, | |||
307 | return -EINVAL; | 349 | return -EINVAL; |
308 | 350 | ||
309 | switch (ntohs(ethhdr->h_proto)) { | 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 | case ETH_P_IPV6: | 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 | default: | 358 | default: |
313 | return -EINVAL; | 359 | return -EINVAL; |
314 | } | 360 | } |
@@ -331,6 +377,33 @@ batadv_mcast_forw_tt_node_get(struct batadv_priv *bat_priv, | |||
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 | * batadv_mcast_forw_mode - check on how to forward a multicast packet | 407 | * batadv_mcast_forw_mode - check on how to forward a multicast packet |
335 | * @bat_priv: the bat priv with all the soft interface information | 408 | * @bat_priv: the bat priv with all the soft interface information |
336 | * @skb: The multicast packet to check | 409 | * @skb: The multicast packet to check |
@@ -344,10 +417,11 @@ enum batadv_forw_mode | |||
344 | batadv_mcast_forw_mode(struct batadv_priv *bat_priv, struct sk_buff *skb, | 417 | batadv_mcast_forw_mode(struct batadv_priv *bat_priv, struct sk_buff *skb, |
345 | struct batadv_orig_node **orig) | 418 | struct batadv_orig_node **orig) |
346 | { | 419 | { |
420 | int ret, tt_count, unsnoop_count, total_count; | ||
421 | bool is_unsnoopable = false; | ||
347 | struct ethhdr *ethhdr; | 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 | if (ret == -ENOMEM) | 425 | if (ret == -ENOMEM) |
352 | return BATADV_FORW_NONE; | 426 | return BATADV_FORW_NONE; |
353 | else if (ret < 0) | 427 | else if (ret < 0) |
@@ -357,10 +431,18 @@ batadv_mcast_forw_mode(struct batadv_priv *bat_priv, struct sk_buff *skb, | |||
357 | 431 | ||
358 | tt_count = batadv_tt_global_hash_count(bat_priv, ethhdr->h_dest, | 432 | tt_count = batadv_tt_global_hash_count(bat_priv, ethhdr->h_dest, |
359 | BATADV_NO_FLAGS); | 433 | BATADV_NO_FLAGS); |
434 | unsnoop_count = !is_unsnoopable ? 0 : | ||
435 | atomic_read(&bat_priv->mcast.num_want_all_unsnoopables); | ||
436 | |||
437 | total_count = tt_count + unsnoop_count; | ||
360 | 438 | ||
361 | switch (tt_count) { | 439 | switch (total_count) { |
362 | case 1: | 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 | if (*orig) | 446 | if (*orig) |
365 | return BATADV_FORW_SINGLE; | 447 | return BATADV_FORW_SINGLE; |
366 | 448 | ||
@@ -373,6 +455,39 @@ batadv_mcast_forw_mode(struct batadv_priv *bat_priv, struct sk_buff *skb, | |||
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 | * batadv_mcast_tvlv_ogm_handler_v1 - process incoming multicast tvlv container | 491 | * batadv_mcast_tvlv_ogm_handler_v1 - process incoming multicast tvlv container |
377 | * @bat_priv: the bat priv with all the soft interface information | 492 | * @bat_priv: the bat priv with all the soft interface information |
378 | * @orig: the orig_node of the ogm | 493 | * @orig: the orig_node of the ogm |
@@ -416,6 +531,8 @@ static void batadv_mcast_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv, | |||
416 | (tvlv_value_len >= sizeof(mcast_flags))) | 531 | (tvlv_value_len >= sizeof(mcast_flags))) |
417 | mcast_flags = *(uint8_t *)tvlv_value; | 532 | mcast_flags = *(uint8_t *)tvlv_value; |
418 | 533 | ||
534 | batadv_mcast_want_unsnoop_update(bat_priv, orig, mcast_flags); | ||
535 | |||
419 | orig->mcast_flags = mcast_flags; | 536 | orig->mcast_flags = mcast_flags; |
420 | } | 537 | } |
421 | 538 | ||
@@ -452,4 +569,6 @@ void batadv_mcast_purge_orig(struct batadv_orig_node *orig) | |||
452 | 569 | ||
453 | if (!(orig->capabilities & BATADV_ORIG_CAPA_HAS_MCAST)) | 570 | if (!(orig->capabilities & BATADV_ORIG_CAPA_HAS_MCAST)) |
454 | atomic_dec(&bat_priv->mcast.num_disabled); | 571 | atomic_dec(&bat_priv->mcast.num_disabled); |
572 | |||
573 | batadv_mcast_want_unsnoop_update(bat_priv, orig, BATADV_NO_FLAGS); | ||
455 | } | 574 | } |