]> git.proxmox.com Git - mirror_ovs.git/blame - lib/mcast-snooping.c
python: Update build system to ensure dirs.py is created.
[mirror_ovs.git] / lib / mcast-snooping.c
CommitLineData
4a95091d
FL
1/*
2 * Copyright (c) 2014 Red Hat, Inc.
3 *
4 * Based on mac-learning implementation.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at:
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19#include <config.h>
20#include "mcast-snooping.h"
21
22#include <inttypes.h>
23#include <stdlib.h>
24
25#include "bitmap.h"
26#include "byte-order.h"
27#include "coverage.h"
28#include "hash.h"
b19bab5b 29#include "openvswitch/list.h"
fd016ae3 30#include "openvswitch/poll-loop.h"
4a95091d
FL
31#include "timeval.h"
32#include "entropy.h"
33#include "unaligned.h"
34#include "util.h"
35#include "vlan-bitmap.h"
e6211adc 36#include "openvswitch/vlog.h"
4a95091d
FL
37
38COVERAGE_DEFINE(mcast_snooping_learned);
39COVERAGE_DEFINE(mcast_snooping_expired);
40
f4ae6e23
FL
41static struct mcast_port_bundle *
42mcast_snooping_port_lookup(struct ovs_list *list, void *port);
4a95091d
FL
43static struct mcast_mrouter_bundle *
44mcast_snooping_mrouter_lookup(struct mcast_snooping *ms, uint16_t vlan,
45 void *port)
46 OVS_REQ_RDLOCK(ms->rwlock);
47
48bool
49mcast_snooping_enabled(const struct mcast_snooping *ms)
50{
51 return !!ms;
52}
53
54bool
55mcast_snooping_flood_unreg(const struct mcast_snooping *ms)
56{
57 return ms->flood_unreg;
58}
59
60bool
61mcast_snooping_is_query(ovs_be16 igmp_type)
62{
63 return igmp_type == htons(IGMP_HOST_MEMBERSHIP_QUERY);
64}
65
66bool
67mcast_snooping_is_membership(ovs_be16 igmp_type)
68{
69 switch (ntohs(igmp_type)) {
70 case IGMP_HOST_MEMBERSHIP_REPORT:
71 case IGMPV2_HOST_MEMBERSHIP_REPORT:
e3102e42 72 case IGMPV3_HOST_MEMBERSHIP_REPORT:
4a95091d
FL
73 case IGMP_HOST_LEAVE_MESSAGE:
74 return true;
75 }
76 return false;
77}
78
79/* Returns the number of seconds since multicast group 'b' was learned in a
80 * port on 'ms'. */
81int
82mcast_bundle_age(const struct mcast_snooping *ms,
83 const struct mcast_group_bundle *b)
84{
85 time_t remaining = b->expires - time_now();
86 return ms->idle_time - remaining;
87}
88
89static uint32_t
964a4d5f
TLSC
90mcast_table_hash(const struct mcast_snooping *ms,
91 const struct in6_addr *grp_addr, uint16_t vlan)
4a95091d 92{
964a4d5f
TLSC
93 return hash_bytes(grp_addr->s6_addr, 16,
94 hash_2words(ms->secret, vlan));
4a95091d
FL
95}
96
97static struct mcast_group_bundle *
ca6ba700 98mcast_group_bundle_from_lru_node(struct ovs_list *list)
4a95091d
FL
99{
100 return CONTAINER_OF(list, struct mcast_group_bundle, bundle_node);
101}
102
103static struct mcast_group *
ca6ba700 104mcast_group_from_lru_node(struct ovs_list *list)
4a95091d
FL
105{
106 return CONTAINER_OF(list, struct mcast_group, group_node);
107}
108
109/* Searches 'ms' for and returns an mcast group for destination address
110 * 'dip' in 'vlan'. */
111struct mcast_group *
964a4d5f
TLSC
112mcast_snooping_lookup(const struct mcast_snooping *ms,
113 const struct in6_addr *dip, uint16_t vlan)
4a95091d
FL
114 OVS_REQ_RDLOCK(ms->rwlock)
115{
116 struct mcast_group *grp;
117 uint32_t hash;
118
119 hash = mcast_table_hash(ms, dip, vlan);
120 HMAP_FOR_EACH_WITH_HASH (grp, hmap_node, hash, &ms->table) {
964a4d5f 121 if (grp->vlan == vlan && ipv6_addr_equals(&grp->addr, dip)) {
4a95091d
FL
122 return grp;
123 }
124 }
125 return NULL;
126}
127
964a4d5f
TLSC
128struct mcast_group *
129mcast_snooping_lookup4(const struct mcast_snooping *ms, ovs_be32 ip4,
130 uint16_t vlan)
131 OVS_REQ_RDLOCK(ms->rwlock)
132{
12d0ee08 133 struct in6_addr addr = in6_addr_mapped_ipv4(ip4);
964a4d5f
TLSC
134 return mcast_snooping_lookup(ms, &addr, vlan);
135}
136
4a95091d
FL
137/* If the LRU list is not empty, stores the least-recently-used entry
138 * in '*e' and returns true. Otherwise, if the LRU list is empty,
139 * stores NULL in '*e' and return false. */
140static bool
141group_get_lru(const struct mcast_snooping *ms, struct mcast_group **grp)
142 OVS_REQ_RDLOCK(ms->rwlock)
143{
417e7e66 144 if (!ovs_list_is_empty(&ms->group_lru)) {
4a95091d
FL
145 *grp = mcast_group_from_lru_node(ms->group_lru.next);
146 return true;
147 } else {
148 *grp = NULL;
149 return false;
150 }
151}
152
153static unsigned int
154normalize_idle_time(unsigned int idle_time)
155{
156 return (idle_time < 15 ? 15
157 : idle_time > 3600 ? 3600
158 : idle_time);
159}
160
161/* Creates and returns a new mcast table with an initial mcast aging
162 * timeout of MCAST_ENTRY_DEFAULT_IDLE_TIME seconds and an initial maximum of
163 * MCAST_DEFAULT_MAX entries. */
164struct mcast_snooping *
165mcast_snooping_create(void)
166{
167 struct mcast_snooping *ms;
168
169 ms = xmalloc(sizeof *ms);
170 hmap_init(&ms->table);
417e7e66
BW
171 ovs_list_init(&ms->group_lru);
172 ovs_list_init(&ms->mrouter_lru);
173 ovs_list_init(&ms->fport_list);
174 ovs_list_init(&ms->rport_list);
4a95091d
FL
175 ms->secret = random_uint32();
176 ms->idle_time = MCAST_ENTRY_DEFAULT_IDLE_TIME;
177 ms->max_entries = MCAST_DEFAULT_MAX_ENTRIES;
178 ms->need_revalidate = false;
179 ms->flood_unreg = true;
180 ovs_refcount_init(&ms->ref_cnt);
181 ovs_rwlock_init(&ms->rwlock);
182 return ms;
183}
184
185struct mcast_snooping *
186mcast_snooping_ref(const struct mcast_snooping *ms_)
187{
188 struct mcast_snooping *ms = CONST_CAST(struct mcast_snooping *, ms_);
189 if (ms) {
190 ovs_refcount_ref(&ms->ref_cnt);
191 }
192 return ms;
193}
194
195/* Unreferences (and possibly destroys) mcast snooping table 'ms'. */
196void
197mcast_snooping_unref(struct mcast_snooping *ms)
198{
199 if (!mcast_snooping_enabled(ms)) {
200 return;
201 }
202
24f83812 203 if (ovs_refcount_unref_relaxed(&ms->ref_cnt) == 1) {
4a95091d
FL
204 mcast_snooping_flush(ms);
205 hmap_destroy(&ms->table);
206 ovs_rwlock_destroy(&ms->rwlock);
207 free(ms);
208 }
209}
210
211/* Changes the mcast aging timeout of 'ms' to 'idle_time' seconds. */
212void
213mcast_snooping_set_idle_time(struct mcast_snooping *ms, unsigned int idle_time)
214 OVS_REQ_WRLOCK(ms->rwlock)
215{
216 struct mcast_group *grp;
217 struct mcast_group_bundle *b;
218 int delta;
219
220 idle_time = normalize_idle_time(idle_time);
221 if (idle_time != ms->idle_time) {
222 delta = (int) idle_time - (int) ms->idle_time;
223 LIST_FOR_EACH (grp, group_node, &ms->group_lru) {
224 LIST_FOR_EACH (b, bundle_node, &grp->bundle_lru) {
225 b->expires += delta;
226 }
227 }
228 ms->idle_time = idle_time;
229 }
230}
231
232/* Sets the maximum number of entries in 'ms' to 'max_entries', adjusting it
233 * to be within a reasonable range. */
234void
235mcast_snooping_set_max_entries(struct mcast_snooping *ms,
236 size_t max_entries)
237 OVS_REQ_WRLOCK(ms->rwlock)
238{
239 ms->max_entries = (max_entries < 10 ? 10
240 : max_entries > 1000 * 1000 ? 1000 * 1000
241 : max_entries);
242}
243
244/* Sets if unregistered multicast packets should be flooded to
245 * all ports or only to ports connected to multicast routers
246 *
247 * Returns true if previous state differs from current state,
248 * false otherwise. */
249bool
250mcast_snooping_set_flood_unreg(struct mcast_snooping *ms, bool enable)
251 OVS_REQ_WRLOCK(ms->rwlock)
252{
253 bool prev = ms->flood_unreg;
254 ms->flood_unreg = enable;
255 return prev != enable;
256}
257
258static struct mcast_group_bundle *
259mcast_group_bundle_lookup(struct mcast_snooping *ms OVS_UNUSED,
260 struct mcast_group *grp, void *port)
261 OVS_REQ_RDLOCK(ms->rwlock)
262{
263 struct mcast_group_bundle *b;
264
265 LIST_FOR_EACH (b, bundle_node, &grp->bundle_lru) {
266 if (b->port == port) {
267 return b;
268 }
269 }
270 return NULL;
271}
272
273/* Insert a new bundle to the mcast group or update its
274 * position and expiration if it is already there. */
275static struct mcast_group_bundle *
276mcast_group_insert_bundle(struct mcast_snooping *ms OVS_UNUSED,
277 struct mcast_group *grp, void *port, int idle_time)
278 OVS_REQ_WRLOCK(ms->rwlock)
279{
280 struct mcast_group_bundle *b;
281
282 b = mcast_group_bundle_lookup(ms, grp, port);
283 if (b) {
417e7e66 284 ovs_list_remove(&b->bundle_node);
4a95091d
FL
285 } else {
286 b = xmalloc(sizeof *b);
417e7e66 287 ovs_list_init(&b->bundle_node);
4a95091d 288 b->port = port;
04418350 289 ms->need_revalidate = true;
4a95091d
FL
290 }
291
292 b->expires = time_now() + idle_time;
417e7e66 293 ovs_list_push_back(&grp->bundle_lru, &b->bundle_node);
4a95091d
FL
294 return b;
295}
296
297/* Return true if multicast still has bundles associated.
298 * Return false if there is no bundles. */
299static bool
300mcast_group_has_bundles(struct mcast_group *grp)
301{
417e7e66 302 return !ovs_list_is_empty(&grp->bundle_lru);
4a95091d
FL
303}
304
305/* Delete 'grp' from the 'ms' hash table.
306 * Caller is responsible to clean bundle lru first. */
307static void
308mcast_snooping_flush_group__(struct mcast_snooping *ms,
309 struct mcast_group *grp)
310{
417e7e66 311 ovs_assert(ovs_list_is_empty(&grp->bundle_lru));
4a95091d 312 hmap_remove(&ms->table, &grp->hmap_node);
417e7e66 313 ovs_list_remove(&grp->group_node);
4a95091d
FL
314 free(grp);
315}
316
317/* Flush out mcast group and its bundles */
318static void
319mcast_snooping_flush_group(struct mcast_snooping *ms, struct mcast_group *grp)
320 OVS_REQ_WRLOCK(ms->rwlock)
321{
5f03c983 322 struct mcast_group_bundle *b;
4a95091d 323
5f03c983 324 LIST_FOR_EACH_POP (b, bundle_node, &grp->bundle_lru) {
4a95091d
FL
325 free(b);
326 }
327 mcast_snooping_flush_group__(ms, grp);
328 ms->need_revalidate = true;
329}
330
331
332/* Delete bundle returning true if it succeeds,
333 * false if it didn't find the group. */
334static bool
335mcast_group_delete_bundle(struct mcast_snooping *ms OVS_UNUSED,
336 struct mcast_group *grp, void *port)
337 OVS_REQ_WRLOCK(ms->rwlock)
338{
339 struct mcast_group_bundle *b;
340
341 LIST_FOR_EACH (b, bundle_node, &grp->bundle_lru) {
342 if (b->port == port) {
417e7e66 343 ovs_list_remove(&b->bundle_node);
4a95091d
FL
344 free(b);
345 return true;
346 }
347 }
348 return false;
349}
350
351/* If any bundle has expired, delete it. Returns the number of deleted
352 * bundles. */
353static int
354mcast_snooping_prune_expired(struct mcast_snooping *ms,
355 struct mcast_group *grp)
356 OVS_REQ_WRLOCK(ms->rwlock)
357{
358 int expired;
359 struct mcast_group_bundle *b, *next_b;
360 time_t timenow = time_now();
361
362 expired = 0;
363 LIST_FOR_EACH_SAFE (b, next_b, bundle_node, &grp->bundle_lru) {
364 /* This list is sorted on expiration time. */
365 if (b->expires > timenow) {
366 break;
367 }
417e7e66 368 ovs_list_remove(&b->bundle_node);
4a95091d
FL
369 free(b);
370 expired++;
371 }
372
373 if (!mcast_group_has_bundles(grp)) {
374 mcast_snooping_flush_group__(ms, grp);
375 expired++;
376 }
377
378 if (expired) {
379 ms->need_revalidate = true;
380 COVERAGE_ADD(mcast_snooping_expired, expired);
381 }
382
383 return expired;
384}
385
386/* Add a multicast group to the mdb. If it exists, then
387 * move to the last position in the LRU list.
388 */
389bool
964a4d5f
TLSC
390mcast_snooping_add_group(struct mcast_snooping *ms,
391 const struct in6_addr *addr,
4a95091d
FL
392 uint16_t vlan, void *port)
393 OVS_REQ_WRLOCK(ms->rwlock)
394{
395 bool learned;
396 struct mcast_group *grp;
397
398 /* Avoid duplicate packets. */
399 if (mcast_snooping_mrouter_lookup(ms, vlan, port)
f4ae6e23 400 || mcast_snooping_port_lookup(&ms->fport_list, port)) {
4a95091d
FL
401 return false;
402 }
403
404 learned = false;
964a4d5f 405 grp = mcast_snooping_lookup(ms, addr, vlan);
4a95091d 406 if (!grp) {
964a4d5f 407 uint32_t hash = mcast_table_hash(ms, addr, vlan);
4a95091d
FL
408
409 if (hmap_count(&ms->table) >= ms->max_entries) {
410 group_get_lru(ms, &grp);
411 mcast_snooping_flush_group(ms, grp);
412 }
413
414 grp = xmalloc(sizeof *grp);
415 hmap_insert(&ms->table, &grp->hmap_node, hash);
964a4d5f 416 grp->addr = *addr;
4a95091d 417 grp->vlan = vlan;
417e7e66 418 ovs_list_init(&grp->bundle_lru);
4a95091d
FL
419 learned = true;
420 ms->need_revalidate = true;
421 COVERAGE_INC(mcast_snooping_learned);
422 } else {
417e7e66 423 ovs_list_remove(&grp->group_node);
4a95091d
FL
424 }
425 mcast_group_insert_bundle(ms, grp, port, ms->idle_time);
426
427 /* Mark 'grp' as recently used. */
417e7e66 428 ovs_list_push_back(&ms->group_lru, &grp->group_node);
4a95091d
FL
429 return learned;
430}
431
964a4d5f
TLSC
432bool
433mcast_snooping_add_group4(struct mcast_snooping *ms, ovs_be32 ip4,
434 uint16_t vlan, void *port)
435 OVS_REQ_WRLOCK(ms->rwlock)
436{
12d0ee08 437 struct in6_addr addr = in6_addr_mapped_ipv4(ip4);
964a4d5f
TLSC
438 return mcast_snooping_add_group(ms, &addr, vlan, port);
439}
440
e3102e42
TLSC
441int
442mcast_snooping_add_report(struct mcast_snooping *ms,
443 const struct dp_packet *p,
444 uint16_t vlan, void *port)
445{
446 ovs_be32 ip4;
447 size_t offset;
448 const struct igmpv3_header *igmpv3;
449 const struct igmpv3_record *record;
450 int count = 0;
451 int ngrp;
452
453 offset = (char *) dp_packet_l4(p) - (char *) dp_packet_data(p);
454 igmpv3 = dp_packet_at(p, offset, IGMPV3_HEADER_LEN);
455 if (!igmpv3) {
456 return 0;
457 }
458 ngrp = ntohs(igmpv3->ngrp);
459 offset += IGMPV3_HEADER_LEN;
460 while (ngrp--) {
461 bool ret;
462 record = dp_packet_at(p, offset, sizeof(struct igmpv3_record));
463 if (!record) {
464 break;
465 }
466 /* Only consider known record types. */
467 if (record->type < IGMPV3_MODE_IS_INCLUDE
468 || record->type > IGMPV3_BLOCK_OLD_SOURCES) {
469 continue;
470 }
471 ip4 = get_16aligned_be32(&record->maddr);
472 /*
473 * If record is INCLUDE MODE and there are no sources, it's equivalent
474 * to a LEAVE.
475 */
476 if (ntohs(record->nsrcs) == 0
477 && (record->type == IGMPV3_MODE_IS_INCLUDE
478 || record->type == IGMPV3_CHANGE_TO_INCLUDE_MODE)) {
964a4d5f 479 ret = mcast_snooping_leave_group4(ms, ip4, vlan, port);
e3102e42 480 } else {
964a4d5f 481 ret = mcast_snooping_add_group4(ms, ip4, vlan, port);
e3102e42
TLSC
482 }
483 if (ret) {
484 count++;
485 }
486 offset += sizeof(*record)
487 + ntohs(record->nsrcs) * sizeof(ovs_be32) + record->aux_len;
488 }
489 return count;
490}
491
06994f87
TLSC
492int
493mcast_snooping_add_mld(struct mcast_snooping *ms,
494 const struct dp_packet *p,
495 uint16_t vlan, void *port)
496{
497 const struct in6_addr *addr;
498 size_t offset;
499 const struct mld_header *mld;
500 const struct mld2_record *record;
501 int count = 0;
502 int ngrp;
503 bool ret;
504
505 offset = (char *) dp_packet_l4(p) - (char *) dp_packet_data(p);
506 mld = dp_packet_at(p, offset, MLD_HEADER_LEN);
507 if (!mld) {
508 return 0;
509 }
510 ngrp = ntohs(mld->ngrp);
511 offset += MLD_HEADER_LEN;
512 addr = dp_packet_at(p, offset, sizeof(struct in6_addr));
513
514 switch (mld->type) {
515 case MLD_REPORT:
516 ret = mcast_snooping_add_group(ms, addr, vlan, port);
517 if (ret) {
518 count++;
519 }
520 break;
521 case MLD_DONE:
522 ret = mcast_snooping_leave_group(ms, addr, vlan, port);
523 if (ret) {
524 count++;
525 }
526 break;
527 case MLD2_REPORT:
528 while (ngrp--) {
529 record = dp_packet_at(p, offset, sizeof(struct mld2_record));
530 if (!record) {
531 break;
532 }
533 /* Only consider known record types. */
534 if (record->type >= IGMPV3_MODE_IS_INCLUDE
535 && record->type <= IGMPV3_BLOCK_OLD_SOURCES) {
536 struct in6_addr maddr;
537 memcpy(maddr.s6_addr, record->maddr.be16, 16);
538 addr = &maddr;
539 /*
540 * If record is INCLUDE MODE and there are no sources, it's
541 * equivalent to a LEAVE.
542 */
543 if (record->nsrcs == htons(0)
544 && (record->type == IGMPV3_MODE_IS_INCLUDE
545 || record->type == IGMPV3_CHANGE_TO_INCLUDE_MODE)) {
546 ret = mcast_snooping_leave_group(ms, addr, vlan, port);
547 } else {
548 ret = mcast_snooping_add_group(ms, addr, vlan, port);
549 }
550 if (ret) {
551 count++;
552 }
553 }
554 offset += sizeof(*record)
555 + ntohs(record->nsrcs) * sizeof(struct in6_addr)
556 + record->aux_len;
557 }
558 }
559
560 return count;
561}
562
4a95091d 563bool
964a4d5f
TLSC
564mcast_snooping_leave_group(struct mcast_snooping *ms,
565 const struct in6_addr *addr,
4a95091d
FL
566 uint16_t vlan, void *port)
567 OVS_REQ_WRLOCK(ms->rwlock)
568{
569 struct mcast_group *grp;
570
8e04a33f
FL
571 /* Ports flagged to forward Reports usually have more
572 * than one host behind it, so don't leave the group
573 * on the first message and just let it expire */
574 if (mcast_snooping_port_lookup(&ms->rport_list, port)) {
575 return false;
576 }
577
964a4d5f 578 grp = mcast_snooping_lookup(ms, addr, vlan);
4a95091d
FL
579 if (grp && mcast_group_delete_bundle(ms, grp, port)) {
580 ms->need_revalidate = true;
581 return true;
582 }
583 return false;
584}
585
964a4d5f
TLSC
586bool
587mcast_snooping_leave_group4(struct mcast_snooping *ms, ovs_be32 ip4,
588 uint16_t vlan, void *port)
589{
12d0ee08 590 struct in6_addr addr = in6_addr_mapped_ipv4(ip4);
964a4d5f
TLSC
591 return mcast_snooping_leave_group(ms, &addr, vlan, port);
592}
593
4a95091d
FL
594\f
595/* Router ports. */
596
597/* Returns the number of seconds since the multicast router
598 * was learned in a port. */
599int
600mcast_mrouter_age(const struct mcast_snooping *ms OVS_UNUSED,
601 const struct mcast_mrouter_bundle *mrouter)
602{
603 time_t remaining = mrouter->expires - time_now();
604 return MCAST_MROUTER_PORT_IDLE_TIME - remaining;
605}
606
607static struct mcast_mrouter_bundle *
ca6ba700 608mcast_mrouter_from_lru_node(struct ovs_list *list)
4a95091d
FL
609{
610 return CONTAINER_OF(list, struct mcast_mrouter_bundle, mrouter_node);
611}
612
613/* If the LRU list is not empty, stores the least-recently-used mrouter
614 * in '*m' and returns true. Otherwise, if the LRU list is empty,
615 * stores NULL in '*m' and return false. */
616static bool
617mrouter_get_lru(const struct mcast_snooping *ms,
618 struct mcast_mrouter_bundle **m)
619 OVS_REQ_RDLOCK(ms->rwlock)
620{
417e7e66 621 if (!ovs_list_is_empty(&ms->mrouter_lru)) {
4a95091d
FL
622 *m = mcast_mrouter_from_lru_node(ms->mrouter_lru.next);
623 return true;
624 } else {
625 *m = NULL;
626 return false;
627 }
628}
629
630static struct mcast_mrouter_bundle *
631mcast_snooping_mrouter_lookup(struct mcast_snooping *ms, uint16_t vlan,
632 void *port)
633 OVS_REQ_RDLOCK(ms->rwlock)
634{
635 struct mcast_mrouter_bundle *mrouter;
636
637 LIST_FOR_EACH (mrouter, mrouter_node, &ms->mrouter_lru) {
638 if (mrouter->vlan == vlan && mrouter->port == port) {
639 return mrouter;
640 }
641 }
642 return NULL;
643}
644
645bool
646mcast_snooping_add_mrouter(struct mcast_snooping *ms, uint16_t vlan,
647 void *port)
648 OVS_REQ_WRLOCK(ms->rwlock)
649{
650 struct mcast_mrouter_bundle *mrouter;
651
652 /* Avoid duplicate packets. */
f4ae6e23 653 if (mcast_snooping_port_lookup(&ms->fport_list, port)) {
4a95091d
FL
654 return false;
655 }
656
657 mrouter = mcast_snooping_mrouter_lookup(ms, vlan, port);
658 if (mrouter) {
417e7e66 659 ovs_list_remove(&mrouter->mrouter_node);
4a95091d
FL
660 } else {
661 mrouter = xmalloc(sizeof *mrouter);
662 mrouter->vlan = vlan;
663 mrouter->port = port;
664 COVERAGE_INC(mcast_snooping_learned);
665 ms->need_revalidate = true;
666 }
667
668 mrouter->expires = time_now() + MCAST_MROUTER_PORT_IDLE_TIME;
417e7e66 669 ovs_list_push_back(&ms->mrouter_lru, &mrouter->mrouter_node);
4a95091d
FL
670 return ms->need_revalidate;
671}
672
673static void
674mcast_snooping_flush_mrouter(struct mcast_mrouter_bundle *mrouter)
675{
417e7e66 676 ovs_list_remove(&mrouter->mrouter_node);
4a95091d
FL
677 free(mrouter);
678}
679\f
f4ae6e23 680/* Ports */
4a95091d 681
f4ae6e23
FL
682static struct mcast_port_bundle *
683mcast_port_from_list_node(struct ovs_list *list)
4a95091d 684{
f4ae6e23 685 return CONTAINER_OF(list, struct mcast_port_bundle, node);
4a95091d
FL
686}
687
688/* If the list is not empty, stores the fport in '*f' and returns true.
689 * Otherwise, if the list is empty, stores NULL in '*f' and return false. */
690static bool
f4ae6e23
FL
691mcast_snooping_port_get(const struct ovs_list *list,
692 struct mcast_port_bundle **f)
4a95091d 693{
417e7e66 694 if (!ovs_list_is_empty(list)) {
f4ae6e23 695 *f = mcast_port_from_list_node(list->next);
4a95091d
FL
696 return true;
697 } else {
698 *f = NULL;
699 return false;
700 }
701}
702
f4ae6e23
FL
703static struct mcast_port_bundle *
704mcast_snooping_port_lookup(struct ovs_list *list, void *port)
4a95091d 705{
f4ae6e23 706 struct mcast_port_bundle *pbundle;
4a95091d 707
f4ae6e23
FL
708 LIST_FOR_EACH (pbundle, node, list) {
709 if (pbundle->port == port) {
710 return pbundle;
4a95091d
FL
711 }
712 }
713 return NULL;
714}
715
716static void
f4ae6e23 717mcast_snooping_add_port(struct ovs_list *list, void *port)
4a95091d 718{
f4ae6e23 719 struct mcast_port_bundle *pbundle;
4a95091d 720
f4ae6e23
FL
721 pbundle = xmalloc(sizeof *pbundle);
722 pbundle->port = port;
417e7e66 723 ovs_list_insert(list, &pbundle->node);
4a95091d
FL
724}
725
726static void
f4ae6e23 727mcast_snooping_flush_port(struct mcast_port_bundle *pbundle)
4a95091d 728{
417e7e66 729 ovs_list_remove(&pbundle->node);
f4ae6e23 730 free(pbundle);
4a95091d
FL
731}
732
f4ae6e23
FL
733\f
734/* Flood ports. */
4a95091d 735void
f4ae6e23
FL
736mcast_snooping_set_port_flood(struct mcast_snooping *ms, void *port,
737 bool flood)
4a95091d
FL
738 OVS_REQ_WRLOCK(ms->rwlock)
739{
f4ae6e23 740 struct mcast_port_bundle *fbundle;
4a95091d 741
f4ae6e23
FL
742 fbundle = mcast_snooping_port_lookup(&ms->fport_list, port);
743 if (flood && !fbundle) {
744 mcast_snooping_add_port(&ms->fport_list, port);
4a95091d 745 ms->need_revalidate = true;
f4ae6e23
FL
746 } else if (!flood && fbundle) {
747 mcast_snooping_flush_port(fbundle);
4a95091d
FL
748 ms->need_revalidate = true;
749 }
750}
751\f
8e04a33f
FL
752/* Flood Reports ports. */
753
754void
755mcast_snooping_set_port_flood_reports(struct mcast_snooping *ms, void *port,
756 bool flood)
757 OVS_REQ_WRLOCK(ms->rwlock)
758{
759 struct mcast_port_bundle *pbundle;
760
761 pbundle = mcast_snooping_port_lookup(&ms->rport_list, port);
762 if (flood && !pbundle) {
763 mcast_snooping_add_port(&ms->rport_list, port);
764 ms->need_revalidate = true;
765 } else if (!flood && pbundle) {
766 mcast_snooping_flush_port(pbundle);
767 ms->need_revalidate = true;
768 }
769}
770\f
4a95091d
FL
771/* Run and flush. */
772
773static void
774mcast_snooping_mdb_flush__(struct mcast_snooping *ms)
775 OVS_REQ_WRLOCK(ms->rwlock)
776{
777 struct mcast_group *grp;
778 struct mcast_mrouter_bundle *mrouter;
779
780 while (group_get_lru(ms, &grp)) {
781 mcast_snooping_flush_group(ms, grp);
782 }
783
784 hmap_shrink(&ms->table);
785
786 while (mrouter_get_lru(ms, &mrouter)) {
787 mcast_snooping_flush_mrouter(mrouter);
788 }
789}
790
791void
792mcast_snooping_mdb_flush(struct mcast_snooping *ms)
793{
794 if (!mcast_snooping_enabled(ms)) {
795 return;
796 }
797
798 ovs_rwlock_wrlock(&ms->rwlock);
799 mcast_snooping_mdb_flush__(ms);
800 ovs_rwlock_unlock(&ms->rwlock);
801}
802
803/* Flushes mdb and flood ports. */
804static void
805mcast_snooping_flush__(struct mcast_snooping *ms)
806 OVS_REQ_WRLOCK(ms->rwlock)
807{
808 struct mcast_group *grp;
809 struct mcast_mrouter_bundle *mrouter;
f4ae6e23 810 struct mcast_port_bundle *pbundle;
4a95091d
FL
811
812 while (group_get_lru(ms, &grp)) {
813 mcast_snooping_flush_group(ms, grp);
814 }
815
816 hmap_shrink(&ms->table);
817
8e04a33f 818 /* flush multicast routers */
4a95091d
FL
819 while (mrouter_get_lru(ms, &mrouter)) {
820 mcast_snooping_flush_mrouter(mrouter);
821 }
822
8e04a33f 823 /* flush flood ports */
f4ae6e23
FL
824 while (mcast_snooping_port_get(&ms->fport_list, &pbundle)) {
825 mcast_snooping_flush_port(pbundle);
4a95091d 826 }
8e04a33f
FL
827
828 /* flush flood report ports */
829 while (mcast_snooping_port_get(&ms->rport_list, &pbundle)) {
830 mcast_snooping_flush_port(pbundle);
831 }
4a95091d
FL
832}
833
834void
835mcast_snooping_flush(struct mcast_snooping *ms)
836{
837 if (!mcast_snooping_enabled(ms)) {
838 return;
839 }
840
841 ovs_rwlock_wrlock(&ms->rwlock);
842 mcast_snooping_flush__(ms);
843 ovs_rwlock_unlock(&ms->rwlock);
844}
845
846static bool
847mcast_snooping_run__(struct mcast_snooping *ms)
848 OVS_REQ_WRLOCK(ms->rwlock)
849{
850 bool need_revalidate;
851 struct mcast_group *grp;
852 struct mcast_mrouter_bundle *mrouter;
853 int mrouter_expired;
854
855 while (group_get_lru(ms, &grp)) {
856 if (hmap_count(&ms->table) > ms->max_entries) {
857 mcast_snooping_flush_group(ms, grp);
858 } else {
859 if (!mcast_snooping_prune_expired(ms, grp)) {
860 break;
861 }
862 }
863 }
864
865 hmap_shrink(&ms->table);
866
867 mrouter_expired = 0;
868 while (mrouter_get_lru(ms, &mrouter)
869 && time_now() >= mrouter->expires) {
870 mcast_snooping_flush_mrouter(mrouter);
871 mrouter_expired++;
872 }
873
874 if (mrouter_expired) {
875 ms->need_revalidate = true;
876 COVERAGE_ADD(mcast_snooping_expired, mrouter_expired);
877 }
878
879 need_revalidate = ms->need_revalidate;
880 ms->need_revalidate = false;
881 return need_revalidate;
882}
883
884/* Does periodic work required by 'ms'. Returns true if something changed
885 * that may require flow revalidation. */
886bool
887mcast_snooping_run(struct mcast_snooping *ms)
888{
889 bool need_revalidate;
890
891 if (!mcast_snooping_enabled(ms)) {
892 return false;
893 }
894
895 ovs_rwlock_wrlock(&ms->rwlock);
896 need_revalidate = mcast_snooping_run__(ms);
897 ovs_rwlock_unlock(&ms->rwlock);
898
899 return need_revalidate;
900}
901
902static void
903mcast_snooping_wait__(struct mcast_snooping *ms)
904 OVS_REQ_RDLOCK(ms->rwlock)
905{
906 if (hmap_count(&ms->table) > ms->max_entries
907 || ms->need_revalidate) {
908 poll_immediate_wake();
909 } else {
910 struct mcast_group *grp;
911 struct mcast_group_bundle *bundle;
912 struct mcast_mrouter_bundle *mrouter;
913 long long int mrouter_msec;
914 long long int msec = 0;
915
417e7e66 916 if (!ovs_list_is_empty(&ms->group_lru)) {
4a95091d
FL
917 grp = mcast_group_from_lru_node(ms->group_lru.next);
918 bundle = mcast_group_bundle_from_lru_node(grp->bundle_lru.next);
919 msec = bundle->expires * 1000LL;
920 }
921
417e7e66 922 if (!ovs_list_is_empty(&ms->mrouter_lru)) {
4a95091d
FL
923 mrouter = mcast_mrouter_from_lru_node(ms->mrouter_lru.next);
924 mrouter_msec = mrouter->expires * 1000LL;
925 msec = msec ? MIN(msec, mrouter_msec) : mrouter_msec;
926 }
927
928 if (msec) {
929 poll_timer_wait_until(msec);
930 }
931 }
932}
933
934void
935mcast_snooping_wait(struct mcast_snooping *ms)
936{
937 if (!mcast_snooping_enabled(ms)) {
938 return;
939 }
940
941 ovs_rwlock_rdlock(&ms->rwlock);
942 mcast_snooping_wait__(ms);
943 ovs_rwlock_unlock(&ms->rwlock);
944}
4fbbf862 945
946void
947mcast_snooping_flush_bundle(struct mcast_snooping *ms, void *port)
948{
949 struct mcast_group *g, *next_g;
950 struct mcast_mrouter_bundle *m, *next_m;
951
952 if (!mcast_snooping_enabled(ms)) {
953 return;
954 }
955
956 ovs_rwlock_wrlock(&ms->rwlock);
957 LIST_FOR_EACH_SAFE (g, next_g, group_node, &ms->group_lru) {
958 if (mcast_group_delete_bundle(ms, g, port)) {
959 ms->need_revalidate = true;
960
961 if (!mcast_group_has_bundles(g)) {
962 mcast_snooping_flush_group__(ms, g);
963 }
964 }
965 }
966
967 LIST_FOR_EACH_SAFE (m, next_m, mrouter_node, &ms->mrouter_lru) {
968 if (m->port == port) {
969 mcast_snooping_flush_mrouter(m);
970 ms->need_revalidate = true;
971 }
972 }
973
974 ovs_rwlock_unlock(&ms->rwlock);
975}