LCOV - code coverage report
Current view: top level - lib - mcast-snooping.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 16 388 4.1 %
Date: 2016-09-14 01:02:56 Functions: 13 62 21.0 %
Branches: 4 188 2.1 %

           Branch data     Line data    Source code
       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"
      29                 :            : #include "openvswitch/list.h"
      30                 :            : #include "poll-loop.h"
      31                 :            : #include "timeval.h"
      32                 :            : #include "entropy.h"
      33                 :            : #include "unaligned.h"
      34                 :            : #include "util.h"
      35                 :            : #include "vlan-bitmap.h"
      36                 :            : #include "openvswitch/vlog.h"
      37                 :            : 
      38                 :      71668 : COVERAGE_DEFINE(mcast_snooping_learned);
      39                 :      71668 : COVERAGE_DEFINE(mcast_snooping_expired);
      40                 :            : 
      41                 :            : static struct mcast_port_bundle *
      42                 :            : mcast_snooping_port_lookup(struct ovs_list *list, void *port);
      43                 :            : static struct mcast_mrouter_bundle *
      44                 :            : mcast_snooping_mrouter_lookup(struct mcast_snooping *ms, uint16_t vlan,
      45                 :            :                               void *port)
      46                 :            :     OVS_REQ_RDLOCK(ms->rwlock);
      47                 :            : 
      48                 :            : bool
      49                 :     376759 : mcast_snooping_enabled(const struct mcast_snooping *ms)
      50                 :            : {
      51                 :     376759 :     return !!ms;
      52                 :            : }
      53                 :            : 
      54                 :            : bool
      55                 :          0 : mcast_snooping_flood_unreg(const struct mcast_snooping *ms)
      56                 :            : {
      57                 :          0 :     return ms->flood_unreg;
      58                 :            : }
      59                 :            : 
      60                 :            : bool
      61                 :          0 : mcast_snooping_is_query(ovs_be16 igmp_type)
      62                 :            : {
      63                 :          0 :     return igmp_type == htons(IGMP_HOST_MEMBERSHIP_QUERY);
      64                 :            : }
      65                 :            : 
      66                 :            : bool
      67                 :          0 : mcast_snooping_is_membership(ovs_be16 igmp_type)
      68                 :            : {
      69         [ #  # ]:          0 :     switch (ntohs(igmp_type)) {
      70                 :            :     case IGMP_HOST_MEMBERSHIP_REPORT:
      71                 :            :     case IGMPV2_HOST_MEMBERSHIP_REPORT:
      72                 :            :     case IGMPV3_HOST_MEMBERSHIP_REPORT:
      73                 :            :     case IGMP_HOST_LEAVE_MESSAGE:
      74                 :          0 :         return true;
      75                 :            :     }
      76                 :          0 :     return false;
      77                 :            : }
      78                 :            : 
      79                 :            : /* Returns the number of seconds since multicast group 'b' was learned in a
      80                 :            :  * port on 'ms'. */
      81                 :            : int
      82                 :          0 : mcast_bundle_age(const struct mcast_snooping *ms,
      83                 :            :                  const struct mcast_group_bundle *b)
      84                 :            : {
      85                 :          0 :     time_t remaining = b->expires - time_now();
      86                 :          0 :     return ms->idle_time - remaining;
      87                 :            : }
      88                 :            : 
      89                 :            : static uint32_t
      90                 :          0 : mcast_table_hash(const struct mcast_snooping *ms,
      91                 :            :                  const struct in6_addr *grp_addr, uint16_t vlan)
      92                 :            : {
      93                 :          0 :     return hash_bytes(grp_addr->s6_addr, 16,
      94                 :            :                       hash_2words(ms->secret, vlan));
      95                 :            : }
      96                 :            : 
      97                 :            : static struct mcast_group_bundle *
      98                 :          0 : mcast_group_bundle_from_lru_node(struct ovs_list *list)
      99                 :            : {
     100                 :          0 :     return CONTAINER_OF(list, struct mcast_group_bundle, bundle_node);
     101                 :            : }
     102                 :            : 
     103                 :            : static struct mcast_group *
     104                 :          0 : mcast_group_from_lru_node(struct ovs_list *list)
     105                 :            : {
     106                 :          0 :     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'. */
     111                 :            : struct mcast_group *
     112                 :          0 : mcast_snooping_lookup(const struct mcast_snooping *ms,
     113                 :            :                       const struct in6_addr *dip, uint16_t vlan)
     114                 :            :     OVS_REQ_RDLOCK(ms->rwlock)
     115                 :            : {
     116                 :            :     struct mcast_group *grp;
     117                 :            :     uint32_t hash;
     118                 :            : 
     119                 :          0 :     hash = mcast_table_hash(ms, dip, vlan);
     120 [ #  # ][ #  # ]:          0 :     HMAP_FOR_EACH_WITH_HASH (grp, hmap_node, hash, &ms->table) {
     121 [ #  # ][ #  # ]:          0 :         if (grp->vlan == vlan && ipv6_addr_equals(&grp->addr, dip)) {
     122                 :          0 :            return grp;
     123                 :            :         }
     124                 :            :     }
     125                 :          0 :     return NULL;
     126                 :            : }
     127                 :            : 
     128                 :            : struct mcast_group *
     129                 :          0 : mcast_snooping_lookup4(const struct mcast_snooping *ms, ovs_be32 ip4,
     130                 :            :                       uint16_t vlan)
     131                 :            :     OVS_REQ_RDLOCK(ms->rwlock)
     132                 :            : {
     133                 :          0 :     struct in6_addr addr = in6_addr_mapped_ipv4(ip4);
     134                 :          0 :     return mcast_snooping_lookup(ms, &addr, vlan);
     135                 :            : }
     136                 :            : 
     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. */
     140                 :            : static bool
     141                 :          0 : group_get_lru(const struct mcast_snooping *ms, struct mcast_group **grp)
     142                 :            :     OVS_REQ_RDLOCK(ms->rwlock)
     143                 :            : {
     144         [ #  # ]:          0 :     if (!ovs_list_is_empty(&ms->group_lru)) {
     145                 :          0 :         *grp = mcast_group_from_lru_node(ms->group_lru.next);
     146                 :          0 :         return true;
     147                 :            :     } else {
     148                 :          0 :         *grp = NULL;
     149                 :          0 :         return false;
     150                 :            :     }
     151                 :            : }
     152                 :            : 
     153                 :            : static unsigned int
     154                 :          0 : normalize_idle_time(unsigned int idle_time)
     155                 :            : {
     156                 :          0 :     return (idle_time < 15 ? 15
     157         [ #  # ]:          0 :             : 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. */
     164                 :            : struct mcast_snooping *
     165                 :          0 : mcast_snooping_create(void)
     166                 :            : {
     167                 :            :     struct mcast_snooping *ms;
     168                 :            : 
     169                 :          0 :     ms = xmalloc(sizeof *ms);
     170                 :          0 :     hmap_init(&ms->table);
     171                 :          0 :     ovs_list_init(&ms->group_lru);
     172                 :          0 :     ovs_list_init(&ms->mrouter_lru);
     173                 :          0 :     ovs_list_init(&ms->fport_list);
     174                 :          0 :     ovs_list_init(&ms->rport_list);
     175                 :          0 :     ms->secret = random_uint32();
     176                 :          0 :     ms->idle_time = MCAST_ENTRY_DEFAULT_IDLE_TIME;
     177                 :          0 :     ms->max_entries = MCAST_DEFAULT_MAX_ENTRIES;
     178                 :          0 :     ms->need_revalidate = false;
     179                 :          0 :     ms->flood_unreg = true;
     180                 :          0 :     ovs_refcount_init(&ms->ref_cnt);
     181                 :          0 :     ovs_rwlock_init(&ms->rwlock);
     182                 :          0 :     return ms;
     183                 :            : }
     184                 :            : 
     185                 :            : struct mcast_snooping *
     186                 :          0 : mcast_snooping_ref(const struct mcast_snooping *ms_)
     187                 :            : {
     188                 :          0 :     struct mcast_snooping *ms = CONST_CAST(struct mcast_snooping *, ms_);
     189         [ #  # ]:          0 :     if (ms) {
     190                 :          0 :         ovs_refcount_ref(&ms->ref_cnt);
     191                 :            :     }
     192                 :          0 :     return ms;
     193                 :            : }
     194                 :            : 
     195                 :            : /* Unreferences (and possibly destroys) mcast snooping table 'ms'. */
     196                 :            : void
     197                 :      47223 : mcast_snooping_unref(struct mcast_snooping *ms)
     198                 :            : {
     199         [ +  - ]:      47223 :     if (!mcast_snooping_enabled(ms)) {
     200                 :      47223 :         return;
     201                 :            :     }
     202                 :            : 
     203         [ #  # ]:          0 :     if (ovs_refcount_unref_relaxed(&ms->ref_cnt) == 1) {
     204                 :          0 :         mcast_snooping_flush(ms);
     205                 :          0 :         hmap_destroy(&ms->table);
     206                 :          0 :         ovs_rwlock_destroy(&ms->rwlock);
     207                 :          0 :         free(ms);
     208                 :            :     }
     209                 :            : }
     210                 :            : 
     211                 :            : /* Changes the mcast aging timeout of 'ms' to 'idle_time' seconds. */
     212                 :            : void
     213                 :          0 : mcast_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                 :          0 :     idle_time = normalize_idle_time(idle_time);
     221         [ #  # ]:          0 :     if (idle_time != ms->idle_time) {
     222                 :          0 :         delta = (int) idle_time - (int) ms->idle_time;
     223         [ #  # ]:          0 :         LIST_FOR_EACH (grp, group_node, &ms->group_lru) {
     224         [ #  # ]:          0 :             LIST_FOR_EACH (b, bundle_node, &grp->bundle_lru) {
     225                 :          0 :                 b->expires += delta;
     226                 :            :             }
     227                 :            :         }
     228                 :          0 :         ms->idle_time = idle_time;
     229                 :            :     }
     230                 :          0 : }
     231                 :            : 
     232                 :            : /* Sets the maximum number of entries in 'ms' to 'max_entries', adjusting it
     233                 :            :  * to be within a reasonable range. */
     234                 :            : void
     235                 :          0 : mcast_snooping_set_max_entries(struct mcast_snooping *ms,
     236                 :            :                                size_t max_entries)
     237                 :            :     OVS_REQ_WRLOCK(ms->rwlock)
     238                 :            : {
     239                 :          0 :     ms->max_entries = (max_entries < 10 ? 10
     240         [ #  # ]:          0 :                        : max_entries > 1000 * 1000 ? 1000 * 1000
     241                 :            :                        : max_entries);
     242                 :          0 : }
     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. */
     249                 :            : bool
     250                 :          0 : mcast_snooping_set_flood_unreg(struct mcast_snooping *ms, bool enable)
     251                 :            :     OVS_REQ_WRLOCK(ms->rwlock)
     252                 :            : {
     253                 :          0 :     bool prev = ms->flood_unreg;
     254                 :          0 :     ms->flood_unreg = enable;
     255                 :          0 :     return prev != enable;
     256                 :            : }
     257                 :            : 
     258                 :            : static struct mcast_group_bundle *
     259                 :          0 : mcast_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         [ #  # ]:          0 :     LIST_FOR_EACH (b, bundle_node, &grp->bundle_lru) {
     266         [ #  # ]:          0 :         if (b->port == port) {
     267                 :          0 :             return b;
     268                 :            :         }
     269                 :            :     }
     270                 :          0 :     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. */
     275                 :            : static struct mcast_group_bundle *
     276                 :          0 : mcast_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                 :          0 :     b = mcast_group_bundle_lookup(ms, grp, port);
     283         [ #  # ]:          0 :     if (b) {
     284                 :          0 :         ovs_list_remove(&b->bundle_node);
     285                 :            :     } else {
     286                 :          0 :         b = xmalloc(sizeof *b);
     287                 :          0 :         ovs_list_init(&b->bundle_node);
     288                 :          0 :         b->port = port;
     289                 :          0 :         ms->need_revalidate = true;
     290                 :            :     }
     291                 :            : 
     292                 :          0 :     b->expires = time_now() + idle_time;
     293                 :          0 :     ovs_list_push_back(&grp->bundle_lru, &b->bundle_node);
     294                 :          0 :     return b;
     295                 :            : }
     296                 :            : 
     297                 :            : /* Return true if multicast still has bundles associated.
     298                 :            :  * Return false if there is no bundles. */
     299                 :            : static bool
     300                 :          0 : mcast_group_has_bundles(struct mcast_group *grp)
     301                 :            : {
     302                 :          0 :     return !ovs_list_is_empty(&grp->bundle_lru);
     303                 :            : }
     304                 :            : 
     305                 :            : /* Delete 'grp' from the 'ms' hash table.
     306                 :            :  * Caller is responsible to clean bundle lru first. */
     307                 :            : static void
     308                 :          0 : mcast_snooping_flush_group__(struct mcast_snooping *ms,
     309                 :            :                              struct mcast_group *grp)
     310                 :            : {
     311         [ #  # ]:          0 :     ovs_assert(ovs_list_is_empty(&grp->bundle_lru));
     312                 :          0 :     hmap_remove(&ms->table, &grp->hmap_node);
     313                 :          0 :     ovs_list_remove(&grp->group_node);
     314                 :          0 :     free(grp);
     315                 :          0 : }
     316                 :            : 
     317                 :            : /* Flush out mcast group and its bundles */
     318                 :            : static void
     319                 :          0 : mcast_snooping_flush_group(struct mcast_snooping *ms, struct mcast_group *grp)
     320                 :            :     OVS_REQ_WRLOCK(ms->rwlock)
     321                 :            : {
     322                 :            :     struct mcast_group_bundle *b;
     323                 :            : 
     324         [ #  # ]:          0 :     LIST_FOR_EACH_POP (b, bundle_node, &grp->bundle_lru) {
     325                 :          0 :         free(b);
     326                 :            :     }
     327                 :          0 :     mcast_snooping_flush_group__(ms, grp);
     328                 :          0 :     ms->need_revalidate = true;
     329                 :          0 : }
     330                 :            : 
     331                 :            : 
     332                 :            : /* Delete bundle returning true if it succeeds,
     333                 :            :  * false if it didn't find the group. */
     334                 :            : static bool
     335                 :          0 : mcast_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         [ #  # ]:          0 :     LIST_FOR_EACH (b, bundle_node, &grp->bundle_lru) {
     342         [ #  # ]:          0 :         if (b->port == port) {
     343                 :          0 :             ovs_list_remove(&b->bundle_node);
     344                 :          0 :             free(b);
     345                 :          0 :             return true;
     346                 :            :         }
     347                 :            :     }
     348                 :          0 :     return false;
     349                 :            : }
     350                 :            : 
     351                 :            : /* If any bundle has expired, delete it.  Returns the number of deleted
     352                 :            :  * bundles. */
     353                 :            : static int
     354                 :          0 : mcast_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                 :          0 :     time_t timenow = time_now();
     361                 :            : 
     362                 :          0 :     expired = 0;
     363 [ #  # ][ #  # ]:          0 :     LIST_FOR_EACH_SAFE (b, next_b, bundle_node, &grp->bundle_lru) {
     364                 :            :         /* This list is sorted on expiration time. */
     365         [ #  # ]:          0 :         if (b->expires > timenow) {
     366                 :          0 :             break;
     367                 :            :         }
     368                 :          0 :         ovs_list_remove(&b->bundle_node);
     369                 :          0 :         free(b);
     370                 :          0 :         expired++;
     371                 :            :     }
     372                 :            : 
     373         [ #  # ]:          0 :     if (!mcast_group_has_bundles(grp)) {
     374                 :          0 :         mcast_snooping_flush_group__(ms, grp);
     375                 :          0 :         expired++;
     376                 :            :     }
     377                 :            : 
     378         [ #  # ]:          0 :     if (expired) {
     379                 :          0 :         ms->need_revalidate = true;
     380                 :          0 :         COVERAGE_ADD(mcast_snooping_expired, expired);
     381                 :            :     }
     382                 :            : 
     383                 :          0 :     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                 :            :  */
     389                 :            : bool
     390                 :          0 : mcast_snooping_add_group(struct mcast_snooping *ms,
     391                 :            :                          const struct in6_addr *addr,
     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         [ #  # ]:          0 :     if (mcast_snooping_mrouter_lookup(ms, vlan, port)
     400         [ #  # ]:          0 :         || mcast_snooping_port_lookup(&ms->fport_list, port)) {
     401                 :          0 :         return false;
     402                 :            :     }
     403                 :            : 
     404                 :          0 :     learned = false;
     405                 :          0 :     grp = mcast_snooping_lookup(ms, addr, vlan);
     406         [ #  # ]:          0 :     if (!grp) {
     407                 :          0 :         uint32_t hash = mcast_table_hash(ms, addr, vlan);
     408                 :            : 
     409         [ #  # ]:          0 :         if (hmap_count(&ms->table) >= ms->max_entries) {
     410                 :          0 :             group_get_lru(ms, &grp);
     411                 :          0 :             mcast_snooping_flush_group(ms, grp);
     412                 :            :         }
     413                 :            : 
     414                 :          0 :         grp = xmalloc(sizeof *grp);
     415                 :          0 :         hmap_insert(&ms->table, &grp->hmap_node, hash);
     416                 :          0 :         grp->addr = *addr;
     417                 :          0 :         grp->vlan = vlan;
     418                 :          0 :         ovs_list_init(&grp->bundle_lru);
     419                 :          0 :         learned = true;
     420                 :          0 :         ms->need_revalidate = true;
     421                 :          0 :         COVERAGE_INC(mcast_snooping_learned);
     422                 :            :     } else {
     423                 :          0 :         ovs_list_remove(&grp->group_node);
     424                 :            :     }
     425                 :          0 :     mcast_group_insert_bundle(ms, grp, port, ms->idle_time);
     426                 :            : 
     427                 :            :     /* Mark 'grp' as recently used. */
     428                 :          0 :     ovs_list_push_back(&ms->group_lru, &grp->group_node);
     429                 :          0 :     return learned;
     430                 :            : }
     431                 :            : 
     432                 :            : bool
     433                 :          0 : mcast_snooping_add_group4(struct mcast_snooping *ms, ovs_be32 ip4,
     434                 :            :                          uint16_t vlan, void *port)
     435                 :            :     OVS_REQ_WRLOCK(ms->rwlock)
     436                 :            : {
     437                 :          0 :     struct in6_addr addr = in6_addr_mapped_ipv4(ip4);
     438                 :          0 :     return mcast_snooping_add_group(ms, &addr, vlan, port);
     439                 :            : }
     440                 :            : 
     441                 :            : int
     442                 :          0 : mcast_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                 :          0 :     int count = 0;
     451                 :            :     int ngrp;
     452                 :            : 
     453                 :          0 :     offset = (char *) dp_packet_l4(p) - (char *) dp_packet_data(p);
     454                 :          0 :     igmpv3 = dp_packet_at(p, offset, IGMPV3_HEADER_LEN);
     455         [ #  # ]:          0 :     if (!igmpv3) {
     456                 :          0 :         return 0;
     457                 :            :     }
     458                 :          0 :     ngrp = ntohs(igmpv3->ngrp);
     459                 :          0 :     offset += IGMPV3_HEADER_LEN;
     460         [ #  # ]:          0 :     while (ngrp--) {
     461                 :            :         bool ret;
     462                 :          0 :         record = dp_packet_at(p, offset, sizeof(struct igmpv3_record));
     463         [ #  # ]:          0 :         if (!record) {
     464                 :          0 :             break;
     465                 :            :         }
     466                 :            :         /* Only consider known record types. */
     467         [ #  # ]:          0 :         if (record->type < IGMPV3_MODE_IS_INCLUDE
     468         [ #  # ]:          0 :             || record->type > IGMPV3_BLOCK_OLD_SOURCES) {
     469                 :          0 :             continue;
     470                 :            :         }
     471                 :          0 :         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         [ #  # ]:          0 :         if (ntohs(record->nsrcs) == 0
     477         [ #  # ]:          0 :             && (record->type == IGMPV3_MODE_IS_INCLUDE
     478         [ #  # ]:          0 :                 || record->type == IGMPV3_CHANGE_TO_INCLUDE_MODE)) {
     479                 :          0 :             ret = mcast_snooping_leave_group4(ms, ip4, vlan, port);
     480                 :            :         } else {
     481                 :          0 :             ret = mcast_snooping_add_group4(ms, ip4, vlan, port);
     482                 :            :         }
     483         [ #  # ]:          0 :         if (ret) {
     484                 :          0 :             count++;
     485                 :            :         }
     486                 :          0 :         offset += sizeof(*record)
     487                 :          0 :                   + ntohs(record->nsrcs) * sizeof(ovs_be32) + record->aux_len;
     488                 :            :     }
     489                 :          0 :     return count;
     490                 :            : }
     491                 :            : 
     492                 :            : int
     493                 :          0 : mcast_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                 :          0 :     int count = 0;
     502                 :            :     int ngrp;
     503                 :            :     bool ret;
     504                 :            : 
     505                 :          0 :     offset = (char *) dp_packet_l4(p) - (char *) dp_packet_data(p);
     506                 :          0 :     mld = dp_packet_at(p, offset, MLD_HEADER_LEN);
     507         [ #  # ]:          0 :     if (!mld) {
     508                 :          0 :         return 0;
     509                 :            :     }
     510                 :          0 :     ngrp = ntohs(mld->ngrp);
     511                 :          0 :     offset += MLD_HEADER_LEN;
     512                 :          0 :     addr = dp_packet_at(p, offset, sizeof(struct in6_addr));
     513                 :            : 
     514   [ #  #  #  # ]:          0 :     switch (mld->type) {
     515                 :            :     case MLD_REPORT:
     516                 :          0 :         ret = mcast_snooping_add_group(ms, addr, vlan, port);
     517         [ #  # ]:          0 :         if (ret) {
     518                 :          0 :             count++;
     519                 :            :         }
     520                 :          0 :         break;
     521                 :            :     case MLD_DONE:
     522                 :          0 :         ret = mcast_snooping_leave_group(ms, addr, vlan, port);
     523         [ #  # ]:          0 :         if (ret) {
     524                 :          0 :             count++;
     525                 :            :         }
     526                 :          0 :         break;
     527                 :            :     case MLD2_REPORT:
     528         [ #  # ]:          0 :         while (ngrp--) {
     529                 :          0 :             record = dp_packet_at(p, offset, sizeof(struct mld2_record));
     530         [ #  # ]:          0 :             if (!record) {
     531                 :          0 :                 break;
     532                 :            :             }
     533                 :            :             /* Only consider known record types. */
     534         [ #  # ]:          0 :             if (record->type >= IGMPV3_MODE_IS_INCLUDE
     535         [ #  # ]:          0 :                 && record->type <= IGMPV3_BLOCK_OLD_SOURCES) {
     536                 :            :                 struct in6_addr maddr;
     537                 :          0 :                 memcpy(maddr.s6_addr, record->maddr.be16, 16);
     538                 :          0 :                 addr = &maddr;
     539                 :            :                 /*
     540                 :            :                  * If record is INCLUDE MODE and there are no sources, it's
     541                 :            :                  * equivalent to a LEAVE.
     542                 :            :                  */
     543         [ #  # ]:          0 :                 if (record->nsrcs == htons(0)
     544         [ #  # ]:          0 :                     && (record->type == IGMPV3_MODE_IS_INCLUDE
     545         [ #  # ]:          0 :                         || record->type == IGMPV3_CHANGE_TO_INCLUDE_MODE)) {
     546                 :          0 :                     ret = mcast_snooping_leave_group(ms, addr, vlan, port);
     547                 :            :                 } else {
     548                 :          0 :                     ret = mcast_snooping_add_group(ms, addr, vlan, port);
     549                 :            :                 }
     550         [ #  # ]:          0 :                 if (ret) {
     551                 :          0 :                     count++;
     552                 :            :                 }
     553                 :            :             }
     554                 :          0 :             offset += sizeof(*record)
     555                 :          0 :                       + ntohs(record->nsrcs) * sizeof(struct in6_addr)
     556                 :          0 :                       + record->aux_len;
     557                 :            :         }
     558                 :            :     }
     559                 :            : 
     560                 :          0 :     return count;
     561                 :            : }
     562                 :            : 
     563                 :            : bool
     564                 :          0 : mcast_snooping_leave_group(struct mcast_snooping *ms,
     565                 :            :                            const struct in6_addr *addr,
     566                 :            :                            uint16_t vlan, void *port)
     567                 :            :     OVS_REQ_WRLOCK(ms->rwlock)
     568                 :            : {
     569                 :            :     struct mcast_group *grp;
     570                 :            : 
     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         [ #  # ]:          0 :     if (mcast_snooping_port_lookup(&ms->rport_list, port)) {
     575                 :          0 :         return false;
     576                 :            :     }
     577                 :            : 
     578                 :          0 :     grp = mcast_snooping_lookup(ms, addr, vlan);
     579 [ #  # ][ #  # ]:          0 :     if (grp && mcast_group_delete_bundle(ms, grp, port)) {
     580                 :          0 :         ms->need_revalidate = true;
     581                 :          0 :         return true;
     582                 :            :     }
     583                 :          0 :     return false;
     584                 :            : }
     585                 :            : 
     586                 :            : bool
     587                 :          0 : mcast_snooping_leave_group4(struct mcast_snooping *ms, ovs_be32 ip4,
     588                 :            :                            uint16_t vlan, void *port)
     589                 :            : {
     590                 :          0 :     struct in6_addr addr = in6_addr_mapped_ipv4(ip4);
     591                 :          0 :     return mcast_snooping_leave_group(ms, &addr, vlan, port);
     592                 :            : }
     593                 :            : 
     594                 :            : 
     595                 :            : /* Router ports. */
     596                 :            : 
     597                 :            : /* Returns the number of seconds since the multicast router
     598                 :            :  * was learned in a port. */
     599                 :            : int
     600                 :          0 : mcast_mrouter_age(const struct mcast_snooping *ms OVS_UNUSED,
     601                 :            :                   const struct mcast_mrouter_bundle *mrouter)
     602                 :            : {
     603                 :          0 :     time_t remaining = mrouter->expires - time_now();
     604                 :          0 :     return MCAST_MROUTER_PORT_IDLE_TIME - remaining;
     605                 :            : }
     606                 :            : 
     607                 :            : static struct mcast_mrouter_bundle *
     608                 :          0 : mcast_mrouter_from_lru_node(struct ovs_list *list)
     609                 :            : {
     610                 :          0 :     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. */
     616                 :            : static bool
     617                 :          0 : mrouter_get_lru(const struct mcast_snooping *ms,
     618                 :            :                 struct mcast_mrouter_bundle **m)
     619                 :            :     OVS_REQ_RDLOCK(ms->rwlock)
     620                 :            : {
     621         [ #  # ]:          0 :     if (!ovs_list_is_empty(&ms->mrouter_lru)) {
     622                 :          0 :         *m = mcast_mrouter_from_lru_node(ms->mrouter_lru.next);
     623                 :          0 :         return true;
     624                 :            :     } else {
     625                 :          0 :         *m = NULL;
     626                 :          0 :         return false;
     627                 :            :     }
     628                 :            : }
     629                 :            : 
     630                 :            : static struct mcast_mrouter_bundle *
     631                 :          0 : mcast_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         [ #  # ]:          0 :     LIST_FOR_EACH (mrouter, mrouter_node, &ms->mrouter_lru) {
     638 [ #  # ][ #  # ]:          0 :         if (mrouter->vlan == vlan && mrouter->port == port) {
     639                 :          0 :             return mrouter;
     640                 :            :         }
     641                 :            :     }
     642                 :          0 :     return NULL;
     643                 :            : }
     644                 :            : 
     645                 :            : bool
     646                 :          0 : mcast_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. */
     653         [ #  # ]:          0 :     if (mcast_snooping_port_lookup(&ms->fport_list, port)) {
     654                 :          0 :         return false;
     655                 :            :     }
     656                 :            : 
     657                 :          0 :     mrouter = mcast_snooping_mrouter_lookup(ms, vlan, port);
     658         [ #  # ]:          0 :     if (mrouter) {
     659                 :          0 :         ovs_list_remove(&mrouter->mrouter_node);
     660                 :            :     } else {
     661                 :          0 :         mrouter = xmalloc(sizeof *mrouter);
     662                 :          0 :         mrouter->vlan = vlan;
     663                 :          0 :         mrouter->port = port;
     664                 :          0 :         COVERAGE_INC(mcast_snooping_learned);
     665                 :          0 :         ms->need_revalidate = true;
     666                 :            :     }
     667                 :            : 
     668                 :          0 :     mrouter->expires = time_now() + MCAST_MROUTER_PORT_IDLE_TIME;
     669                 :          0 :     ovs_list_push_back(&ms->mrouter_lru, &mrouter->mrouter_node);
     670                 :          0 :     return ms->need_revalidate;
     671                 :            : }
     672                 :            : 
     673                 :            : static void
     674                 :          0 : mcast_snooping_flush_mrouter(struct mcast_mrouter_bundle *mrouter)
     675                 :            : {
     676                 :          0 :     ovs_list_remove(&mrouter->mrouter_node);
     677                 :          0 :     free(mrouter);
     678                 :          0 : }
     679                 :            : 
     680                 :            : /* Ports */
     681                 :            : 
     682                 :            : static struct mcast_port_bundle *
     683                 :          0 : mcast_port_from_list_node(struct ovs_list *list)
     684                 :            : {
     685                 :          0 :     return CONTAINER_OF(list, struct mcast_port_bundle, node);
     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. */
     690                 :            : static bool
     691                 :          0 : mcast_snooping_port_get(const struct ovs_list *list,
     692                 :            :                         struct mcast_port_bundle **f)
     693                 :            : {
     694         [ #  # ]:          0 :     if (!ovs_list_is_empty(list)) {
     695                 :          0 :         *f = mcast_port_from_list_node(list->next);
     696                 :          0 :         return true;
     697                 :            :     } else {
     698                 :          0 :         *f = NULL;
     699                 :          0 :         return false;
     700                 :            :     }
     701                 :            : }
     702                 :            : 
     703                 :            : static struct mcast_port_bundle *
     704                 :          0 : mcast_snooping_port_lookup(struct ovs_list *list, void *port)
     705                 :            : {
     706                 :            :     struct mcast_port_bundle *pbundle;
     707                 :            : 
     708         [ #  # ]:          0 :     LIST_FOR_EACH (pbundle, node, list) {
     709         [ #  # ]:          0 :         if (pbundle->port == port) {
     710                 :          0 :             return pbundle;
     711                 :            :         }
     712                 :            :     }
     713                 :          0 :     return NULL;
     714                 :            : }
     715                 :            : 
     716                 :            : static void
     717                 :          0 : mcast_snooping_add_port(struct ovs_list *list, void *port)
     718                 :            : {
     719                 :            :     struct mcast_port_bundle *pbundle;
     720                 :            : 
     721                 :          0 :     pbundle = xmalloc(sizeof *pbundle);
     722                 :          0 :     pbundle->port = port;
     723                 :          0 :     ovs_list_insert(list, &pbundle->node);
     724                 :          0 : }
     725                 :            : 
     726                 :            : static void
     727                 :          0 : mcast_snooping_flush_port(struct mcast_port_bundle *pbundle)
     728                 :            : {
     729                 :          0 :     ovs_list_remove(&pbundle->node);
     730                 :          0 :     free(pbundle);
     731                 :          0 : }
     732                 :            : 
     733                 :            : 
     734                 :            : /* Flood ports. */
     735                 :            : void
     736                 :          0 : mcast_snooping_set_port_flood(struct mcast_snooping *ms, void *port,
     737                 :            :                               bool flood)
     738                 :            :     OVS_REQ_WRLOCK(ms->rwlock)
     739                 :            : {
     740                 :            :     struct mcast_port_bundle *fbundle;
     741                 :            : 
     742                 :          0 :     fbundle = mcast_snooping_port_lookup(&ms->fport_list, port);
     743 [ #  # ][ #  # ]:          0 :     if (flood && !fbundle) {
     744                 :          0 :         mcast_snooping_add_port(&ms->fport_list, port);
     745                 :          0 :         ms->need_revalidate = true;
     746 [ #  # ][ #  # ]:          0 :     } else if (!flood && fbundle) {
     747                 :          0 :         mcast_snooping_flush_port(fbundle);
     748                 :          0 :         ms->need_revalidate = true;
     749                 :            :     }
     750                 :          0 : }
     751                 :            : 
     752                 :            : /* Flood Reports ports. */
     753                 :            : 
     754                 :            : void
     755                 :          0 : mcast_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                 :          0 :     pbundle = mcast_snooping_port_lookup(&ms->rport_list, port);
     762 [ #  # ][ #  # ]:          0 :     if (flood && !pbundle) {
     763                 :          0 :         mcast_snooping_add_port(&ms->rport_list, port);
     764                 :          0 :         ms->need_revalidate = true;
     765 [ #  # ][ #  # ]:          0 :     } else if (!flood && pbundle) {
     766                 :          0 :         mcast_snooping_flush_port(pbundle);
     767                 :          0 :         ms->need_revalidate = true;
     768                 :            :     }
     769                 :          0 : }
     770                 :            : 
     771                 :            : /* Run and flush. */
     772                 :            : 
     773                 :            : static void
     774                 :          0 : mcast_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         [ #  # ]:          0 :     while (group_get_lru(ms, &grp)) {
     781                 :          0 :         mcast_snooping_flush_group(ms, grp);
     782                 :            :     }
     783                 :            : 
     784                 :          0 :     hmap_shrink(&ms->table);
     785                 :            : 
     786         [ #  # ]:          0 :     while (mrouter_get_lru(ms, &mrouter)) {
     787                 :          0 :         mcast_snooping_flush_mrouter(mrouter);
     788                 :            :     }
     789                 :          0 : }
     790                 :            : 
     791                 :            : void
     792                 :          7 : mcast_snooping_mdb_flush(struct mcast_snooping *ms)
     793                 :            : {
     794         [ +  - ]:          7 :     if (!mcast_snooping_enabled(ms)) {
     795                 :          7 :         return;
     796                 :            :     }
     797                 :            : 
     798                 :          0 :     ovs_rwlock_wrlock(&ms->rwlock);
     799                 :          0 :     mcast_snooping_mdb_flush__(ms);
     800                 :          0 :     ovs_rwlock_unlock(&ms->rwlock);
     801                 :            : }
     802                 :            : 
     803                 :            : /* Flushes mdb and flood ports. */
     804                 :            : static void
     805                 :          0 : mcast_snooping_flush__(struct mcast_snooping *ms)
     806                 :            :     OVS_REQ_WRLOCK(ms->rwlock)
     807                 :            : {
     808                 :            :     struct mcast_group *grp;
     809                 :            :     struct mcast_mrouter_bundle *mrouter;
     810                 :            :     struct mcast_port_bundle *pbundle;
     811                 :            : 
     812         [ #  # ]:          0 :     while (group_get_lru(ms, &grp)) {
     813                 :          0 :         mcast_snooping_flush_group(ms, grp);
     814                 :            :     }
     815                 :            : 
     816                 :          0 :     hmap_shrink(&ms->table);
     817                 :            : 
     818                 :            :     /* flush multicast routers */
     819         [ #  # ]:          0 :     while (mrouter_get_lru(ms, &mrouter)) {
     820                 :          0 :         mcast_snooping_flush_mrouter(mrouter);
     821                 :            :     }
     822                 :            : 
     823                 :            :     /* flush flood ports */
     824         [ #  # ]:          0 :     while (mcast_snooping_port_get(&ms->fport_list, &pbundle)) {
     825                 :          0 :         mcast_snooping_flush_port(pbundle);
     826                 :            :     }
     827                 :            : 
     828                 :            :     /* flush flood report ports */
     829         [ #  # ]:          0 :     while (mcast_snooping_port_get(&ms->rport_list, &pbundle)) {
     830                 :          0 :         mcast_snooping_flush_port(pbundle);
     831                 :            :     }
     832                 :          0 : }
     833                 :            : 
     834                 :            : void
     835                 :          0 : mcast_snooping_flush(struct mcast_snooping *ms)
     836                 :            : {
     837         [ #  # ]:          0 :     if (!mcast_snooping_enabled(ms)) {
     838                 :          0 :         return;
     839                 :            :     }
     840                 :            : 
     841                 :          0 :     ovs_rwlock_wrlock(&ms->rwlock);
     842                 :          0 :     mcast_snooping_flush__(ms);
     843                 :          0 :     ovs_rwlock_unlock(&ms->rwlock);
     844                 :            : }
     845                 :            : 
     846                 :            : static bool
     847                 :          0 : mcast_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         [ #  # ]:          0 :     while (group_get_lru(ms, &grp)) {
     856         [ #  # ]:          0 :         if (hmap_count(&ms->table) > ms->max_entries) {
     857                 :          0 :             mcast_snooping_flush_group(ms, grp);
     858                 :            :         } else {
     859         [ #  # ]:          0 :             if (!mcast_snooping_prune_expired(ms, grp)) {
     860                 :          0 :                 break;
     861                 :            :             }
     862                 :            :         }
     863                 :            :     }
     864                 :            : 
     865                 :          0 :     hmap_shrink(&ms->table);
     866                 :            : 
     867                 :          0 :     mrouter_expired = 0;
     868         [ #  # ]:          0 :     while (mrouter_get_lru(ms, &mrouter)
     869         [ #  # ]:          0 :            && time_now() >= mrouter->expires) {
     870                 :          0 :         mcast_snooping_flush_mrouter(mrouter);
     871                 :          0 :         mrouter_expired++;
     872                 :            :     }
     873                 :            : 
     874         [ #  # ]:          0 :     if (mrouter_expired) {
     875                 :          0 :         ms->need_revalidate = true;
     876                 :          0 :         COVERAGE_ADD(mcast_snooping_expired, mrouter_expired);
     877                 :            :     }
     878                 :            : 
     879                 :          0 :     need_revalidate = ms->need_revalidate;
     880                 :          0 :     ms->need_revalidate = false;
     881                 :          0 :     return need_revalidate;
     882                 :            : }
     883                 :            : 
     884                 :            : /* Does periodic work required by 'ms'. Returns true if something changed
     885                 :            :  * that may require flow revalidation. */
     886                 :            : bool
     887                 :     157605 : mcast_snooping_run(struct mcast_snooping *ms)
     888                 :            : {
     889                 :            :     bool need_revalidate;
     890                 :            : 
     891         [ +  - ]:     157605 :     if (!mcast_snooping_enabled(ms)) {
     892                 :     157605 :         return false;
     893                 :            :     }
     894                 :            : 
     895                 :          0 :     ovs_rwlock_wrlock(&ms->rwlock);
     896                 :          0 :     need_revalidate = mcast_snooping_run__(ms);
     897                 :          0 :     ovs_rwlock_unlock(&ms->rwlock);
     898                 :            : 
     899                 :          0 :     return need_revalidate;
     900                 :            : }
     901                 :            : 
     902                 :            : static void
     903                 :          0 : mcast_snooping_wait__(struct mcast_snooping *ms)
     904                 :            :     OVS_REQ_RDLOCK(ms->rwlock)
     905                 :            : {
     906         [ #  # ]:          0 :     if (hmap_count(&ms->table) > ms->max_entries
     907         [ #  # ]:          0 :         || ms->need_revalidate) {
     908                 :          0 :         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                 :          0 :         long long int msec = 0;
     915                 :            : 
     916         [ #  # ]:          0 :         if (!ovs_list_is_empty(&ms->group_lru)) {
     917                 :          0 :             grp = mcast_group_from_lru_node(ms->group_lru.next);
     918                 :          0 :             bundle = mcast_group_bundle_from_lru_node(grp->bundle_lru.next);
     919                 :          0 :             msec = bundle->expires * 1000LL;
     920                 :            :         }
     921                 :            : 
     922         [ #  # ]:          0 :         if (!ovs_list_is_empty(&ms->mrouter_lru)) {
     923                 :          0 :             mrouter = mcast_mrouter_from_lru_node(ms->mrouter_lru.next);
     924                 :          0 :             mrouter_msec = mrouter->expires * 1000LL;
     925         [ #  # ]:          0 :             msec = msec ? MIN(msec, mrouter_msec) : mrouter_msec;
     926                 :            :         }
     927                 :            : 
     928         [ #  # ]:          0 :         if (msec) {
     929                 :          0 :             poll_timer_wait_until(msec);
     930                 :            :         }
     931                 :            :     }
     932                 :          0 : }
     933                 :            : 
     934                 :            : void
     935                 :     153567 : mcast_snooping_wait(struct mcast_snooping *ms)
     936                 :            : {
     937         [ +  - ]:     153567 :     if (!mcast_snooping_enabled(ms)) {
     938                 :     153567 :         return;
     939                 :            :     }
     940                 :            : 
     941                 :          0 :     ovs_rwlock_rdlock(&ms->rwlock);
     942                 :          0 :     mcast_snooping_wait__(ms);
     943                 :          0 :     ovs_rwlock_unlock(&ms->rwlock);
     944                 :            : }

Generated by: LCOV version 1.12