Commit ab49886e3da73b6b35ece21006e191910427bb30
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
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 |