back to topotato report
topotato coverage report
Current view: top level - pimd - pim6_mld.h (source / functions) Hit Total Coverage
Test: test_pim_basic2.py::PIMTopo2Test Lines: 0 2 0.0 %
Date: 2023-02-24 18:39:36 Functions: 0 0 -

          Line data    Source code
       1             : /*
       2             :  * PIMv6 MLD querier
       3             :  * Copyright (C) 2021-2022  David Lamparter for NetDEF, Inc.
       4             :  *
       5             :  * This program is free software; you can redistribute it and/or modify it
       6             :  * under the terms of the GNU General Public License as published by the Free
       7             :  * Software Foundation; either version 2 of the License, or (at your option)
       8             :  * any later version.
       9             :  *
      10             :  * This program is distributed in the hope that it will be useful, but WITHOUT
      11             :  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
      12             :  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
      13             :  * more details.
      14             :  *
      15             :  * You should have received a copy of the GNU General Public License along
      16             :  * with this program; see the file COPYING; if not, write to the Free Software
      17             :  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
      18             :  */
      19             : 
      20             : #ifndef PIM6_MLD_H
      21             : #define PIM6_MLD_H
      22             : 
      23             : #include "typesafe.h"
      24             : #include "pim_addr.h"
      25             : 
      26             : struct thread;
      27             : struct pim_instance;
      28             : struct gm_packet_sg;
      29             : struct gm_if;
      30             : struct channel_oil;
      31             : 
      32             : #define MLD_DEFAULT_VERSION 2
      33             : 
      34             : /* see comment below on subs_negative/subs_positive */
      35             : enum gm_sub_sense {
      36             :         /* negative/pruning: S,G in EXCLUDE */
      37             :         GM_SUB_NEG = 0,
      38             :         /* positive/joining: *,G in EXCLUDE and S,G in INCLUDE */
      39             :         GM_SUB_POS = 1,
      40             : };
      41             : 
      42             : enum gm_sg_state {
      43             :         GM_SG_NOINFO = 0,
      44             :         GM_SG_JOIN,
      45             :         GM_SG_JOIN_EXPIRING,
      46             :         /* remaining 3 only valid for S,G when *,G in EXCLUDE */
      47             :         GM_SG_PRUNE,
      48             :         GM_SG_NOPRUNE,
      49             :         GM_SG_NOPRUNE_EXPIRING,
      50             : };
      51             : 
      52             : static inline bool gm_sg_state_want_join(enum gm_sg_state state)
      53             : {
      54             :         return state != GM_SG_NOINFO && state != GM_SG_PRUNE;
      55             : }
      56             : 
      57             : /* MLD (S,G) state (on an interface)
      58             :  *
      59             :  * group is always != ::, src is :: for (*,G) joins.  sort order in RB tree is
      60             :  * such that sources for a particular group can be iterated by starting at the
      61             :  * group.  For INCLUDE, no (*,G) entry exists, only (S,G).
      62             :  */
      63             : 
      64             : PREDECL_RBTREE_UNIQ(gm_packet_sg_subs);
      65             : PREDECL_RBTREE_UNIQ(gm_sgs);
      66             : struct gm_sg {
      67             :         pim_sgaddr sgaddr;
      68             :         struct gm_if *iface;
      69             :         struct gm_sgs_item itm;
      70             : 
      71             :         enum gm_sg_state state;
      72             :         struct channel_oil *oil;
      73             :         bool tib_joined;
      74             : 
      75             :         struct timeval created;
      76             : 
      77             :         /* if a group- or group-and-source specific query is running
      78             :          * (implies we haven't received any report yet, since it's cancelled
      79             :          * by that)
      80             :          */
      81             :         struct thread *t_sg_expire;
      82             : 
      83             :         /* last-member-left triggered queries (group/group-source specific)
      84             :          *
      85             :          * this timer will be running even if we aren't the elected querier,
      86             :          * in case the election result changes midway through.
      87             :          */
      88             :         struct thread *t_sg_query;
      89             : 
      90             :         /* we must keep sending (QRV) queries even if we get a positive
      91             :          * response, to make sure other routers are updated.  query_sbit
      92             :          * will be set in that case, since other routers need the *response*,
      93             :          * not the *query*
      94             :          */
      95             :         uint8_t n_query;
      96             :         bool query_sbit;
      97             : 
      98             :         /* subs_positive tracks gm_packet_sg resulting in a JOIN, i.e. for
      99             :          * (*,G) it has *EXCLUDE* items, for (S,G) it has *INCLUDE* items.
     100             :          *
     101             :          * subs_negative is always empty for (*,G) and tracks EXCLUDE items
     102             :          * for (S,G).  This means that an (S,G) entry is active as a PRUNE if
     103             :          *   len(src->subs_negative) == len(grp->subs_positive)
     104             :          *   && len(src->subs_positive) == 0
     105             :          * (i.e. all receivers for the group opted to exclude this S,G and
     106             :          * noone did an SSM join for the S,G)
     107             :          */
     108             :         union {
     109             :                 struct {
     110             :                         struct gm_packet_sg_subs_head subs_negative[1];
     111             :                         struct gm_packet_sg_subs_head subs_positive[1];
     112             :                 };
     113             :                 struct gm_packet_sg_subs_head subs[2];
     114             :         };
     115             : 
     116             :         /* If the elected querier is not ourselves, queries and reports might
     117             :          * get reordered in rare circumstances, i.e. the report could arrive
     118             :          * just a microsecond before the query kicks off the timer.  This can
     119             :          * then result in us thinking there are no more receivers since no
     120             :          * report might be received during the query period.
     121             :          *
     122             :          * To avoid this, keep track of the most recent report for this (S,G)
     123             :          * so we can do a quick check to add just a little bit of slack.
     124             :          *
     125             :          * EXCLUDE S,Gs are never in most_recent.
     126             :          */
     127             :         struct gm_packet_sg *most_recent;
     128             : };
     129             : 
     130             : /* host tracking entry.  addr will be one of:
     131             :  *
     132             :  * ::           - used by hosts during address acquisition
     133             :  * ::1          - may show up on some OS for joins by the router itself
     134             :  * link-local   - regular operation by MLDv2 hosts
     135             :  * ffff:..:ffff - MLDv1 entry (cannot be tracked due to report suppression)
     136             :  *
     137             :  * global scope IPv6 addresses can never show up here
     138             :  */
     139             : PREDECL_HASH(gm_subscribers);
     140             : PREDECL_DLIST(gm_packets);
     141             : struct gm_subscriber {
     142             :         pim_addr addr;
     143             :         struct gm_subscribers_item itm;
     144             : 
     145             :         struct gm_if *iface;
     146             :         size_t refcount;
     147             : 
     148             :         struct gm_packets_head packets[1];
     149             : 
     150             :         struct timeval created;
     151             : };
     152             : 
     153             : /*
     154             :  * MLD join state is kept batched by packet.  Since the timers for all items
     155             :  * in a packet are the same, this reduces the number of timers we're keeping
     156             :  * track of.  It also eases tracking for EXCLUDE state groups because the
     157             :  * excluded sources are in the same packet.  (MLD does not support splitting
     158             :  * that if it exceeds MTU, it's always a full replace for exclude.)
     159             :  *
     160             :  * Since packets may be partially superseded by newer packets, the "active"
     161             :  * field is used to track this.
     162             :  */
     163             : 
     164             : /* gm_packet_sg is allocated as part of gm_packet_state, note the items[0]
     165             :  * array at the end of that.  gm_packet_sg is NEVER directly allocated with
     166             :  * XMALLOC/XFREE.
     167             :  */
     168             : struct gm_packet_sg {
     169             :         /* non-NULL as long as this gm_packet_sg is the most recent entry
     170             :          * for (subscriber,S,G).  Cleared to NULL when a newer packet by the
     171             :          * subscriber replaces this item.
     172             :          *
     173             :          * (Old items are kept around so we don't need to realloc/resize
     174             :          * gm_packet_state, which would mess up a whole lot of pointers)
     175             :          */
     176             :         struct gm_sg *sg;
     177             : 
     178             :         /* gm_sg -> (subscriber, gm_packet_sg)
     179             :          * only on RB-tree while sg != NULL, i.e. not superseded by newer.
     180             :          */
     181             :         struct gm_packet_sg_subs_item subs_itm;
     182             : 
     183             :         bool is_src : 1; /* := (src != ::) */
     184             :         bool is_excl : 1;
     185             : 
     186             :         /* for getting back to struct gm_packet_state, cf.
     187             :          * gm_packet_sg2state() below
     188             :          */
     189             :         uint16_t offset;
     190             : 
     191             :         /* if this is a group entry in EXCLUDE state, n_exclude counts how
     192             :          * many sources are on the exclude list here.  They follow immediately
     193             :          * after.
     194             :          */
     195             :         uint16_t n_exclude;
     196             : };
     197             : 
     198             : #define gm_packet_sg2state(sg)                                                 \
     199             :         container_of(sg, struct gm_packet_state, items[sg->offset])
     200             : 
     201             : PREDECL_DLIST(gm_packet_expires);
     202             : struct gm_packet_state {
     203             :         struct gm_if *iface;
     204             :         struct gm_subscriber *subscriber;
     205             :         struct gm_packets_item pkt_itm;
     206             : 
     207             :         struct timeval received;
     208             :         struct gm_packet_expires_item exp_itm;
     209             : 
     210             :         /* n_active starts equal to n_sg;  whenever active is set to false on
     211             :          * an item it is decremented.  When n_active == 0, the packet can be
     212             :          * freed.
     213             :          */
     214             :         uint16_t n_sg, n_active;
     215             :         struct gm_packet_sg items[0];
     216             : };
     217             : 
     218             : /* general queries are rather different from group/S,G specific queries;  it's
     219             :  * not particularly efficient or useful to try to shoehorn them into the S,G
     220             :  * timers.  Instead, we keep a history of recent queries and their implied
     221             :  * expiries.
     222             :  */
     223             : struct gm_general_pending {
     224             :         struct timeval query, expiry;
     225             : };
     226             : 
     227             : /* similarly, group queries also age out S,G entries for the group, but in
     228             :  * this case we only keep one query for each group
     229             :  *
     230             :  * why is this not in the *,G gm_sg?  There may not be one (for INCLUDE mode
     231             :  * groups, or groups we don't know about.)  Also, malicious clients could spam
     232             :  * random group-specific queries to trigger resource exhaustion, so it makes
     233             :  * sense to limit these.
     234             :  */
     235             : PREDECL_RBTREE_UNIQ(gm_grp_pends);
     236             : struct gm_grp_pending {
     237             :         struct gm_grp_pends_item itm;
     238             :         struct gm_if *iface;
     239             :         pim_addr grp;
     240             : 
     241             :         struct timeval query;
     242             :         struct thread *t_expire;
     243             : };
     244             : 
     245             : /* guaranteed MTU for IPv6 is 1280 bytes.  IPv6 header is 40 bytes, MLDv2
     246             :  * query header is 24 bytes, RA option is 8 bytes - leaves 1208 bytes for the
     247             :  * source list, which is 151 IPv6 addresses.  But we may have some more IPv6
     248             :  * extension headers (e.g. IPsec AH), so just cap to 128
     249             :  */
     250             : #define MLD_V2Q_MTU_MAX_SOURCES 128
     251             : 
     252             : /* group-and-source-specific queries are bundled together, if some host joins
     253             :  * multiple sources it's likely to drop all at the same time.
     254             :  *
     255             :  * Unlike gm_grp_pending, this is only used for aggregation since the S,G
     256             :  * state is kept directly in the gm_sg structure.
     257             :  */
     258             : PREDECL_HASH(gm_gsq_pends);
     259             : struct gm_gsq_pending {
     260             :         struct gm_gsq_pends_item itm;
     261             : 
     262             :         struct gm_if *iface;
     263             :         struct thread *t_send;
     264             : 
     265             :         pim_addr grp;
     266             :         bool s_bit;
     267             : 
     268             :         size_t n_src;
     269             :         pim_addr srcs[MLD_V2Q_MTU_MAX_SOURCES];
     270             : };
     271             : 
     272             : 
     273             : /* The size of this history is limited by QRV, i.e. there can't be more than
     274             :  * 8 items here.
     275             :  */
     276             : #define GM_MAX_PENDING 8
     277             : 
     278             : enum gm_version {
     279             :         GM_NONE,
     280             :         GM_MLDV1,
     281             :         GM_MLDV2,
     282             : };
     283             : 
     284             : struct gm_if_stats {
     285             :         uint64_t rx_drop_csum;
     286             :         uint64_t rx_drop_srcaddr;
     287             :         uint64_t rx_drop_dstaddr;
     288             :         uint64_t rx_drop_ra;
     289             :         uint64_t rx_drop_malformed;
     290             :         uint64_t rx_trunc_report;
     291             : 
     292             :         /* since the types are different, this is rx_old_* not of rx_*_old */
     293             :         uint64_t rx_old_report;
     294             :         uint64_t rx_old_leave;
     295             :         uint64_t rx_new_report;
     296             : 
     297             :         uint64_t rx_query_new_general;
     298             :         uint64_t rx_query_new_group;
     299             :         uint64_t rx_query_new_groupsrc;
     300             :         uint64_t rx_query_new_sbit;
     301             :         uint64_t rx_query_old_general;
     302             :         uint64_t rx_query_old_group;
     303             : 
     304             :         uint64_t tx_query_new_general;
     305             :         uint64_t tx_query_new_group;
     306             :         uint64_t tx_query_new_groupsrc;
     307             :         uint64_t tx_query_old_general;
     308             :         uint64_t tx_query_old_group;
     309             : 
     310             :         uint64_t tx_query_fail;
     311             : };
     312             : 
     313             : struct gm_if {
     314             :         struct interface *ifp;
     315             :         struct pim_instance *pim;
     316             :         struct thread *t_query, *t_other_querier, *t_expire;
     317             : 
     318             :         bool stopping;
     319             : 
     320             :         uint8_t n_startup;
     321             : 
     322             :         uint8_t cur_qrv;
     323             :         unsigned int cur_query_intv;      /* ms */
     324             :         unsigned int cur_query_intv_trig; /* ms */
     325             :         unsigned int cur_max_resp;        /* ms */
     326             :         enum gm_version cur_version;
     327             :         int cur_lmqc; /* last member query count in ds */
     328             : 
     329             :         /* this value (positive, default 10ms) defines our "timing tolerance":
     330             :          * - added to deadlines for expiring joins
     331             :          * - used to look backwards in time for queries, in case a report was
     332             :          *   reordered before the query
     333             :          */
     334             :         struct timeval cfg_timing_fuzz;
     335             : 
     336             :         /* items in pending[] are sorted by expiry, pending[0] is earliest */
     337             :         struct gm_general_pending pending[GM_MAX_PENDING];
     338             :         uint8_t n_pending;
     339             :         struct gm_grp_pends_head grp_pends[1];
     340             :         struct gm_gsq_pends_head gsq_pends[1];
     341             : 
     342             :         pim_addr querier;
     343             :         pim_addr cur_ll_lowest;
     344             : 
     345             :         struct gm_sgs_head sgs[1];
     346             :         struct gm_subscribers_head subscribers[1];
     347             :         struct gm_packet_expires_head expires[1];
     348             : 
     349             :         struct timeval started;
     350             :         struct gm_if_stats stats;
     351             : };
     352             : 
     353             : #if PIM_IPV == 6
     354             : extern void gm_ifp_update(struct interface *ifp);
     355             : extern void gm_ifp_teardown(struct interface *ifp);
     356             : extern void gm_group_delete(struct gm_if *gm_ifp);
     357             : #else
     358           0 : static inline void gm_ifp_update(struct interface *ifp)
     359             : {
     360           0 : }
     361             : 
     362             : static inline void gm_ifp_teardown(struct interface *ifp)
     363             : {
     364             : }
     365             : #endif
     366             : 
     367             : extern void gm_cli_init(void);
     368             : 
     369             : #endif /* PIM6_MLD_H */

Generated by: LCOV version v1.16-topotato