forked from Mirror/frr
Merge pull request #10775 from opensourcerouting/pim6-mld-pr
pim6d: MLD code
This commit is contained in:
commit
8a8ad459af
|
@ -218,6 +218,9 @@ vrf is specified then the default vrf is assumed. Finally the special keyword
|
|||
'all' allows you to look at all vrfs for the command. Naming a vrf 'all' will
|
||||
cause great confusion.
|
||||
|
||||
PIM protocol state
|
||||
------------------
|
||||
|
||||
.. clicmd:: show ipv6 pim [vrf NAME] group-type [json]
|
||||
|
||||
Display SSM group ranges.
|
||||
|
@ -289,6 +292,39 @@ cause great confusion.
|
|||
|
||||
Display upstream information for S,G's and the RPF data associated with them.
|
||||
|
||||
|
||||
MLD state
|
||||
---------
|
||||
|
||||
.. clicmd:: show ipv6 mld [vrf NAME] interface [IFNAME] [detail|json]
|
||||
|
||||
Display per-interface MLD state, elected querier and related timers. Use
|
||||
the ``detail`` or ``json`` options for further information (the JSON output
|
||||
always contains all details.)
|
||||
|
||||
.. clicmd:: show ipv6 mld [vrf NAME] statistics [interface IFNAME] [json]
|
||||
|
||||
Display packet and error counters for MLD interfaces. All counters are
|
||||
packet counters (not bytes) and wrap at 64 bit. In some rare cases,
|
||||
malformed received MLD reports may be partially processed and counted on
|
||||
multiple counters.
|
||||
|
||||
.. clicmd:: show ipv6 mld [vrf NAME] joins [{interface IFNAME|groups X:X::X:X/M|sources X:X::X:X/M|detail}] [json]
|
||||
|
||||
Display joined groups tracked by MLD. ``interface``, ``groups`` and
|
||||
``sources`` options may be used to limit output to a subset (note ``sources``
|
||||
refers to the multicast traffic sender, not the host that joined to receive
|
||||
the traffic.)
|
||||
|
||||
The ``detail`` option also reports which hosts have joined (subscribed) to
|
||||
particular ``S,G``. This information is only available for MLDv2 hosts with
|
||||
a MLDv2 querier. MLDv1 joins are recorded as "untracked" and shown in the
|
||||
``NonTrkSeen`` output column.
|
||||
|
||||
|
||||
General multicast routing state
|
||||
-------------------------------
|
||||
|
||||
.. clicmd:: show ipv6 multicast
|
||||
|
||||
Display various information about the interfaces used in this pim instance.
|
||||
|
|
|
@ -707,7 +707,7 @@ DEFPY (interface_ipv6_mld_query_max_response_time,
|
|||
IPV6_STR
|
||||
IFACE_MLD_STR
|
||||
IFACE_MLD_QUERY_MAX_RESPONSE_TIME_STR
|
||||
"Query response value in deci-seconds\n")
|
||||
"Query response value in milliseconds\n")
|
||||
{
|
||||
return gm_process_query_max_response_time_cmd(vty, qmrt_str);
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
#include "pim_zebra.h"
|
||||
#include "pim_nb.h"
|
||||
#include "pim6_cmd.h"
|
||||
#include "pim6_mld.h"
|
||||
|
||||
zebra_capabilities_t _caps_p[] = {
|
||||
ZCAP_SYS_ADMIN,
|
||||
|
@ -133,7 +134,6 @@ FRR_DAEMON_INFO(pim6d, PIM6,
|
|||
);
|
||||
/* clang-format on */
|
||||
|
||||
|
||||
int main(int argc, char **argv, char **envp)
|
||||
{
|
||||
static struct option longopts[] = {
|
||||
|
@ -184,6 +184,8 @@ int main(int argc, char **argv, char **envp)
|
|||
*/
|
||||
pim_iface_init();
|
||||
|
||||
gm_cli_init();
|
||||
|
||||
pim_zebra_init();
|
||||
#if 0
|
||||
pim_bfd_init();
|
||||
|
|
3011
pimd/pim6_mld.c
Normal file
3011
pimd/pim6_mld.c
Normal file
File diff suppressed because it is too large
Load diff
367
pimd/pim6_mld.h
Normal file
367
pimd/pim6_mld.h
Normal file
|
@ -0,0 +1,367 @@
|
|||
/*
|
||||
* PIMv6 MLD querier
|
||||
* Copyright (C) 2021-2022 David Lamparter for NetDEF, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; see the file COPYING; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef PIM6_MLD_H
|
||||
#define PIM6_MLD_H
|
||||
|
||||
#include "typesafe.h"
|
||||
#include "pim_addr.h"
|
||||
|
||||
struct thread;
|
||||
struct pim_instance;
|
||||
struct gm_packet_sg;
|
||||
struct gm_if;
|
||||
struct channel_oil;
|
||||
|
||||
#define MLD_DEFAULT_VERSION 2
|
||||
|
||||
/* see comment below on subs_negative/subs_positive */
|
||||
enum gm_sub_sense {
|
||||
/* negative/pruning: S,G in EXCLUDE */
|
||||
GM_SUB_NEG = 0,
|
||||
/* positive/joining: *,G in EXCLUDE and S,G in INCLUDE */
|
||||
GM_SUB_POS = 1,
|
||||
};
|
||||
|
||||
enum gm_sg_state {
|
||||
GM_SG_NOINFO = 0,
|
||||
GM_SG_JOIN,
|
||||
GM_SG_JOIN_EXPIRING,
|
||||
/* remaining 3 only valid for S,G when *,G in EXCLUDE */
|
||||
GM_SG_PRUNE,
|
||||
GM_SG_NOPRUNE,
|
||||
GM_SG_NOPRUNE_EXPIRING,
|
||||
};
|
||||
|
||||
static inline bool gm_sg_state_want_join(enum gm_sg_state state)
|
||||
{
|
||||
return state != GM_SG_NOINFO && state != GM_SG_PRUNE;
|
||||
}
|
||||
|
||||
/* MLD (S,G) state (on an interface)
|
||||
*
|
||||
* group is always != ::, src is :: for (*,G) joins. sort order in RB tree is
|
||||
* such that sources for a particular group can be iterated by starting at the
|
||||
* group. For INCLUDE, no (*,G) entry exists, only (S,G).
|
||||
*/
|
||||
|
||||
PREDECL_RBTREE_UNIQ(gm_packet_sg_subs);
|
||||
PREDECL_RBTREE_UNIQ(gm_sgs);
|
||||
struct gm_sg {
|
||||
pim_sgaddr sgaddr;
|
||||
struct gm_if *iface;
|
||||
struct gm_sgs_item itm;
|
||||
|
||||
enum gm_sg_state state;
|
||||
struct channel_oil *oil;
|
||||
bool tib_joined;
|
||||
|
||||
struct timeval created;
|
||||
|
||||
/* if a group- or group-and-source specific query is running
|
||||
* (implies we haven't received any report yet, since it's cancelled
|
||||
* by that)
|
||||
*/
|
||||
struct thread *t_sg_expire;
|
||||
|
||||
/* last-member-left triggered queries (group/group-source specific)
|
||||
*
|
||||
* this timer will be running even if we aren't the elected querier,
|
||||
* in case the election result changes midway through.
|
||||
*/
|
||||
struct thread *t_sg_query;
|
||||
|
||||
/* we must keep sending (QRV) queries even if we get a positive
|
||||
* response, to make sure other routers are updated. query_sbit
|
||||
* will be set in that case, since other routers need the *response*,
|
||||
* not the *query*
|
||||
*/
|
||||
uint8_t n_query;
|
||||
bool query_sbit;
|
||||
|
||||
/* subs_positive tracks gm_packet_sg resulting in a JOIN, i.e. for
|
||||
* (*,G) it has *EXCLUDE* items, for (S,G) it has *INCLUDE* items.
|
||||
*
|
||||
* subs_negative is always empty for (*,G) and tracks EXCLUDE items
|
||||
* for (S,G). This means that an (S,G) entry is active as a PRUNE if
|
||||
* len(src->subs_negative) == len(grp->subs_positive)
|
||||
* && len(src->subs_positive) == 0
|
||||
* (i.e. all receivers for the group opted to exclude this S,G and
|
||||
* noone did an SSM join for the S,G)
|
||||
*/
|
||||
union {
|
||||
struct {
|
||||
struct gm_packet_sg_subs_head subs_negative[1];
|
||||
struct gm_packet_sg_subs_head subs_positive[1];
|
||||
};
|
||||
struct gm_packet_sg_subs_head subs[2];
|
||||
};
|
||||
|
||||
/* If the elected querier is not ourselves, queries and reports might
|
||||
* get reordered in rare circumstances, i.e. the report could arrive
|
||||
* just a microsecond before the query kicks off the timer. This can
|
||||
* then result in us thinking there are no more receivers since no
|
||||
* report might be received during the query period.
|
||||
*
|
||||
* To avoid this, keep track of the most recent report for this (S,G)
|
||||
* so we can do a quick check to add just a little bit of slack.
|
||||
*
|
||||
* EXCLUDE S,Gs are never in most_recent.
|
||||
*/
|
||||
struct gm_packet_sg *most_recent;
|
||||
};
|
||||
|
||||
/* host tracking entry. addr will be one of:
|
||||
*
|
||||
* :: - used by hosts during address acquisition
|
||||
* ::1 - may show up on some OS for joins by the router itself
|
||||
* link-local - regular operation by MLDv2 hosts
|
||||
* ffff:..:ffff - MLDv1 entry (cannot be tracked due to report suppression)
|
||||
*
|
||||
* global scope IPv6 addresses can never show up here
|
||||
*/
|
||||
PREDECL_HASH(gm_subscribers);
|
||||
PREDECL_DLIST(gm_packets);
|
||||
struct gm_subscriber {
|
||||
pim_addr addr;
|
||||
struct gm_subscribers_item itm;
|
||||
|
||||
struct gm_if *iface;
|
||||
size_t refcount;
|
||||
|
||||
struct gm_packets_head packets[1];
|
||||
|
||||
struct timeval created;
|
||||
};
|
||||
|
||||
/*
|
||||
* MLD join state is kept batched by packet. Since the timers for all items
|
||||
* in a packet are the same, this reduces the number of timers we're keeping
|
||||
* track of. It also eases tracking for EXCLUDE state groups because the
|
||||
* excluded sources are in the same packet. (MLD does not support splitting
|
||||
* that if it exceeds MTU, it's always a full replace for exclude.)
|
||||
*
|
||||
* Since packets may be partially superseded by newer packets, the "active"
|
||||
* field is used to track this.
|
||||
*/
|
||||
|
||||
/* gm_packet_sg is allocated as part of gm_packet_state, note the items[0]
|
||||
* array at the end of that. gm_packet_sg is NEVER directly allocated with
|
||||
* XMALLOC/XFREE.
|
||||
*/
|
||||
struct gm_packet_sg {
|
||||
/* non-NULL as long as this gm_packet_sg is the most recent entry
|
||||
* for (subscriber,S,G). Cleared to NULL when a newer packet by the
|
||||
* subscriber replaces this item.
|
||||
*
|
||||
* (Old items are kept around so we don't need to realloc/resize
|
||||
* gm_packet_state, which would mess up a whole lot of pointers)
|
||||
*/
|
||||
struct gm_sg *sg;
|
||||
|
||||
/* gm_sg -> (subscriber, gm_packet_sg)
|
||||
* only on RB-tree while sg != NULL, i.e. not superseded by newer.
|
||||
*/
|
||||
struct gm_packet_sg_subs_item subs_itm;
|
||||
|
||||
bool is_src : 1; /* := (src != ::) */
|
||||
bool is_excl : 1;
|
||||
|
||||
/* for getting back to struct gm_packet_state, cf.
|
||||
* gm_packet_sg2state() below
|
||||
*/
|
||||
uint16_t offset;
|
||||
|
||||
/* if this is a group entry in EXCLUDE state, n_exclude counts how
|
||||
* many sources are on the exclude list here. They follow immediately
|
||||
* after.
|
||||
*/
|
||||
uint16_t n_exclude;
|
||||
};
|
||||
|
||||
#define gm_packet_sg2state(sg) \
|
||||
container_of(sg, struct gm_packet_state, items[sg->offset])
|
||||
|
||||
PREDECL_DLIST(gm_packet_expires);
|
||||
struct gm_packet_state {
|
||||
struct gm_if *iface;
|
||||
struct gm_subscriber *subscriber;
|
||||
struct gm_packets_item pkt_itm;
|
||||
|
||||
struct timeval received;
|
||||
struct gm_packet_expires_item exp_itm;
|
||||
|
||||
/* n_active starts equal to n_sg; whenever active is set to false on
|
||||
* an item it is decremented. When n_active == 0, the packet can be
|
||||
* freed.
|
||||
*/
|
||||
uint16_t n_sg, n_active;
|
||||
struct gm_packet_sg items[0];
|
||||
};
|
||||
|
||||
/* general queries are rather different from group/S,G specific queries; it's
|
||||
* not particularly efficient or useful to try to shoehorn them into the S,G
|
||||
* timers. Instead, we keep a history of recent queries and their implied
|
||||
* expiries.
|
||||
*/
|
||||
struct gm_general_pending {
|
||||
struct timeval query, expiry;
|
||||
};
|
||||
|
||||
/* similarly, group queries also age out S,G entries for the group, but in
|
||||
* this case we only keep one query for each group
|
||||
*
|
||||
* why is this not in the *,G gm_sg? There may not be one (for INCLUDE mode
|
||||
* groups, or groups we don't know about.) Also, malicious clients could spam
|
||||
* random group-specific queries to trigger resource exhaustion, so it makes
|
||||
* sense to limit these.
|
||||
*/
|
||||
PREDECL_RBTREE_UNIQ(gm_grp_pends);
|
||||
struct gm_grp_pending {
|
||||
struct gm_grp_pends_item itm;
|
||||
struct gm_if *iface;
|
||||
pim_addr grp;
|
||||
|
||||
struct timeval query;
|
||||
struct thread *t_expire;
|
||||
};
|
||||
|
||||
/* guaranteed MTU for IPv6 is 1280 bytes. IPv6 header is 40 bytes, MLDv2
|
||||
* query header is 24 bytes, RA option is 8 bytes - leaves 1208 bytes for the
|
||||
* source list, which is 151 IPv6 addresses. But we may have some more IPv6
|
||||
* extension headers (e.g. IPsec AH), so just cap to 128
|
||||
*/
|
||||
#define MLD_V2Q_MTU_MAX_SOURCES 128
|
||||
|
||||
/* group-and-source-specific queries are bundled together, if some host joins
|
||||
* multiple sources it's likely to drop all at the same time.
|
||||
*
|
||||
* Unlike gm_grp_pending, this is only used for aggregation since the S,G
|
||||
* state is kept directly in the gm_sg structure.
|
||||
*/
|
||||
PREDECL_HASH(gm_gsq_pends);
|
||||
struct gm_gsq_pending {
|
||||
struct gm_gsq_pends_item itm;
|
||||
|
||||
struct gm_if *iface;
|
||||
struct thread *t_send;
|
||||
|
||||
pim_addr grp;
|
||||
bool s_bit;
|
||||
|
||||
size_t n_src;
|
||||
pim_addr srcs[MLD_V2Q_MTU_MAX_SOURCES];
|
||||
};
|
||||
|
||||
|
||||
/* The size of this history is limited by QRV, i.e. there can't be more than
|
||||
* 8 items here.
|
||||
*/
|
||||
#define GM_MAX_PENDING 8
|
||||
|
||||
enum gm_version {
|
||||
GM_NONE,
|
||||
GM_MLDV1,
|
||||
GM_MLDV2,
|
||||
};
|
||||
|
||||
struct gm_if_stats {
|
||||
uint64_t rx_drop_csum;
|
||||
uint64_t rx_drop_srcaddr;
|
||||
uint64_t rx_drop_dstaddr;
|
||||
uint64_t rx_drop_ra;
|
||||
uint64_t rx_drop_malformed;
|
||||
uint64_t rx_trunc_report;
|
||||
|
||||
/* since the types are different, this is rx_old_* not of rx_*_old */
|
||||
uint64_t rx_old_report;
|
||||
uint64_t rx_old_leave;
|
||||
uint64_t rx_new_report;
|
||||
|
||||
uint64_t rx_query_new_general;
|
||||
uint64_t rx_query_new_group;
|
||||
uint64_t rx_query_new_groupsrc;
|
||||
uint64_t rx_query_new_sbit;
|
||||
uint64_t rx_query_old_general;
|
||||
uint64_t rx_query_old_group;
|
||||
|
||||
uint64_t tx_query_new_general;
|
||||
uint64_t tx_query_new_group;
|
||||
uint64_t tx_query_new_groupsrc;
|
||||
uint64_t tx_query_old_general;
|
||||
uint64_t tx_query_old_group;
|
||||
|
||||
uint64_t tx_query_fail;
|
||||
};
|
||||
|
||||
struct gm_if {
|
||||
struct interface *ifp;
|
||||
struct pim_instance *pim;
|
||||
struct thread *t_query, *t_other_querier, *t_expire;
|
||||
|
||||
bool stopping;
|
||||
|
||||
uint8_t n_startup;
|
||||
|
||||
uint8_t cur_qrv;
|
||||
unsigned int cur_query_intv; /* ms */
|
||||
unsigned int cur_query_intv_trig; /* ms */
|
||||
unsigned int cur_max_resp; /* ms */
|
||||
enum gm_version cur_version;
|
||||
|
||||
/* this value (positive, default 10ms) defines our "timing tolerance":
|
||||
* - added to deadlines for expiring joins
|
||||
* - used to look backwards in time for queries, in case a report was
|
||||
* reordered before the query
|
||||
*/
|
||||
struct timeval cfg_timing_fuzz;
|
||||
|
||||
/* items in pending[] are sorted by expiry, pending[0] is earliest */
|
||||
struct gm_general_pending pending[GM_MAX_PENDING];
|
||||
uint8_t n_pending;
|
||||
struct gm_grp_pends_head grp_pends[1];
|
||||
struct gm_gsq_pends_head gsq_pends[1];
|
||||
|
||||
pim_addr querier;
|
||||
pim_addr cur_ll_lowest;
|
||||
|
||||
struct gm_sgs_head sgs[1];
|
||||
struct gm_subscribers_head subscribers[1];
|
||||
struct gm_packet_expires_head expires[1];
|
||||
|
||||
struct timeval started;
|
||||
struct gm_if_stats stats;
|
||||
};
|
||||
|
||||
#if PIM_IPV == 6
|
||||
extern void gm_ifp_update(struct interface *ifp);
|
||||
extern void gm_ifp_teardown(struct interface *ifp);
|
||||
#else
|
||||
static inline void gm_ifp_update(struct interface *ifp)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void gm_ifp_teardown(struct interface *ifp)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
extern void gm_cli_init(void);
|
||||
|
||||
#endif /* PIM6_MLD_H */
|
125
pimd/pim6_mld_protocol.h
Normal file
125
pimd/pim6_mld_protocol.h
Normal file
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
* MLD protocol definitions
|
||||
* Copyright (C) 2022 David Lamparter for NetDEF, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; see the file COPYING; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef _PIM6_MLD_PROTOCOL_H
|
||||
#define _PIM6_MLD_PROTOCOL_H
|
||||
|
||||
#include <stdalign.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* There is a struct icmp6_hdr provided by OS, but it includes 4 bytes of data.
|
||||
* Not helpful for us if we want to put the MLD struct after it.
|
||||
*/
|
||||
|
||||
struct icmp6_plain_hdr {
|
||||
uint8_t icmp6_type;
|
||||
uint8_t icmp6_code;
|
||||
uint16_t icmp6_cksum;
|
||||
};
|
||||
static_assert(sizeof(struct icmp6_plain_hdr) == 4, "struct mismatch");
|
||||
static_assert(alignof(struct icmp6_plain_hdr) <= 4, "struct mismatch");
|
||||
|
||||
/* for MLDv1 query, report and leave all use the same packet format */
|
||||
struct mld_v1_pkt {
|
||||
uint16_t max_resp_code;
|
||||
uint16_t rsvd0;
|
||||
struct in6_addr grp;
|
||||
};
|
||||
static_assert(sizeof(struct mld_v1_pkt) == 20, "struct mismatch");
|
||||
static_assert(alignof(struct mld_v1_pkt) <= 4, "struct mismatch");
|
||||
|
||||
|
||||
struct mld_v2_query_hdr {
|
||||
uint16_t max_resp_code;
|
||||
uint16_t rsvd0;
|
||||
struct in6_addr grp;
|
||||
uint8_t flags;
|
||||
uint8_t qqic;
|
||||
uint16_t n_src;
|
||||
struct in6_addr srcs[0];
|
||||
};
|
||||
static_assert(sizeof(struct mld_v2_query_hdr) == 24, "struct mismatch");
|
||||
static_assert(alignof(struct mld_v2_query_hdr) <= 4, "struct mismatch");
|
||||
|
||||
|
||||
struct mld_v2_report_hdr {
|
||||
uint16_t rsvd;
|
||||
uint16_t n_records;
|
||||
};
|
||||
static_assert(sizeof(struct mld_v2_report_hdr) == 4, "struct mismatch");
|
||||
static_assert(alignof(struct mld_v2_report_hdr) <= 4, "struct mismatch");
|
||||
|
||||
|
||||
struct mld_v2_rec_hdr {
|
||||
uint8_t type;
|
||||
uint8_t aux_len;
|
||||
uint16_t n_src;
|
||||
struct in6_addr grp;
|
||||
struct in6_addr srcs[0];
|
||||
};
|
||||
static_assert(sizeof(struct mld_v2_rec_hdr) == 20, "struct mismatch");
|
||||
static_assert(alignof(struct mld_v2_rec_hdr) <= 4, "struct mismatch");
|
||||
|
||||
/* clang-format off */
|
||||
enum icmp6_mld_type {
|
||||
ICMP6_MLD_QUERY = 130,
|
||||
ICMP6_MLD_V1_REPORT = 131,
|
||||
ICMP6_MLD_V1_DONE = 132,
|
||||
ICMP6_MLD_V2_REPORT = 143,
|
||||
};
|
||||
|
||||
enum mld_v2_rec_type {
|
||||
MLD_RECTYPE_IS_INCLUDE = 1,
|
||||
MLD_RECTYPE_IS_EXCLUDE = 2,
|
||||
MLD_RECTYPE_CHANGE_TO_INCLUDE = 3,
|
||||
MLD_RECTYPE_CHANGE_TO_EXCLUDE = 4,
|
||||
MLD_RECTYPE_ALLOW_NEW_SOURCES = 5,
|
||||
MLD_RECTYPE_BLOCK_OLD_SOURCES = 6,
|
||||
};
|
||||
/* clang-format on */
|
||||
|
||||
/* helper functions */
|
||||
|
||||
static inline unsigned int mld_max_resp_decode(uint16_t wire)
|
||||
{
|
||||
uint16_t code = ntohs(wire);
|
||||
uint8_t exp;
|
||||
|
||||
if (code < 0x8000)
|
||||
return code;
|
||||
exp = (code >> 12) & 0x7;
|
||||
return ((code & 0xfff) | 0x1000) << (exp + 3);
|
||||
}
|
||||
|
||||
static inline uint16_t mld_max_resp_encode(uint32_t value)
|
||||
{
|
||||
uint16_t code;
|
||||
uint8_t exp;
|
||||
|
||||
if (value < 0x8000)
|
||||
code = value;
|
||||
else {
|
||||
exp = 16 - __builtin_clz(value);
|
||||
code = (value >> (exp + 3)) & 0xfff;
|
||||
code |= 0x8000 | (exp << 12);
|
||||
}
|
||||
return htons(code);
|
||||
}
|
||||
|
||||
#endif /* _PIM6_MLD_PROTOCOL_H */
|
|
@ -50,6 +50,8 @@
|
|||
#include "pim_igmp_join.h"
|
||||
#include "pim_vxlan.h"
|
||||
|
||||
#include "pim6_mld.h"
|
||||
|
||||
#if PIM_IPV == 4
|
||||
static void pim_if_igmp_join_del_all(struct interface *ifp);
|
||||
static int igmp_join_sock(const char *ifname, ifindex_t ifindex,
|
||||
|
@ -127,6 +129,7 @@ struct pim_interface *pim_if_new(struct interface *ifp, bool igmp, bool pim,
|
|||
pim_ifp->mroute_vif_index = -1;
|
||||
|
||||
pim_ifp->igmp_version = IGMP_DEFAULT_VERSION;
|
||||
pim_ifp->mld_version = MLD_DEFAULT_VERSION;
|
||||
pim_ifp->gm_default_robustness_variable =
|
||||
IGMP_DEFAULT_ROBUSTNESS_VARIABLE;
|
||||
pim_ifp->gm_default_query_interval = IGMP_GENERAL_QUERY_INTERVAL;
|
||||
|
@ -651,6 +654,7 @@ void pim_if_addr_add(struct connected *ifc)
|
|||
vxlan_term = pim_vxlan_is_term_dev_cfg(pim_ifp->pim, ifp);
|
||||
pim_if_add_vif(ifp, false, vxlan_term);
|
||||
}
|
||||
gm_ifp_update(ifp);
|
||||
pim_ifchannel_scan_forward_start(ifp);
|
||||
}
|
||||
|
||||
|
@ -763,6 +767,8 @@ void pim_if_addr_del(struct connected *ifc, int force_prim_as_any)
|
|||
"%s: removed link-local %pI6, lowest now %pI6, highest %pI6",
|
||||
ifc->ifp->name, &ifc->address->u.prefix6,
|
||||
&pim_ifp->ll_lowest, &pim_ifp->ll_highest);
|
||||
|
||||
gm_ifp_update(ifp);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -822,6 +828,7 @@ void pim_if_addr_add_all(struct interface *ifp)
|
|||
vxlan_term = pim_vxlan_is_term_dev_cfg(pim_ifp->pim, ifp);
|
||||
pim_if_add_vif(ifp, false, vxlan_term);
|
||||
}
|
||||
gm_ifp_update(ifp);
|
||||
pim_ifchannel_scan_forward_start(ifp);
|
||||
|
||||
pim_rp_setup(pim_ifp->pim);
|
||||
|
@ -1000,12 +1007,15 @@ int pim_if_add_vif(struct interface *ifp, bool ispimreg, bool is_vxlan_term)
|
|||
}
|
||||
|
||||
ifaddr = pim_ifp->primary_address;
|
||||
#if PIM_IPV != 6
|
||||
/* IPv6 API is always by interface index */
|
||||
if (!ispimreg && !is_vxlan_term && pim_addr_is_any(ifaddr)) {
|
||||
zlog_warn(
|
||||
"%s: could not get address for interface %s ifindex=%d",
|
||||
__func__, ifp->name, ifp->ifindex);
|
||||
return -4;
|
||||
}
|
||||
#endif
|
||||
|
||||
pim_ifp->mroute_vif_index = pim_iface_next_vif_index(ifp);
|
||||
|
||||
|
@ -1030,9 +1040,10 @@ int pim_if_add_vif(struct interface *ifp, bool ispimreg, bool is_vxlan_term)
|
|||
|
||||
pim_ifp->pim->iface_vif_index[pim_ifp->mroute_vif_index] = 1;
|
||||
|
||||
gm_ifp_update(ifp);
|
||||
|
||||
/* if the device qualifies as pim_vxlan iif/oif update vxlan entries */
|
||||
pim_vxlan_add_vif(ifp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1050,6 +1061,8 @@ int pim_if_del_vif(struct interface *ifp)
|
|||
/* if the device was a pim_vxlan iif/oif update vxlan mroute entries */
|
||||
pim_vxlan_del_vif(ifp);
|
||||
|
||||
gm_ifp_teardown(ifp);
|
||||
|
||||
pim_mroute_del_vif(ifp);
|
||||
|
||||
/*
|
||||
|
@ -1058,7 +1071,6 @@ int pim_if_del_vif(struct interface *ifp)
|
|||
pim_ifp->pim->iface_vif_index[pim_ifp->mroute_vif_index] = 0;
|
||||
|
||||
pim_ifp->mroute_vif_index = -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -68,6 +68,8 @@ struct pim_secondary_addr {
|
|||
enum pim_secondary_addr_flags flags;
|
||||
};
|
||||
|
||||
struct gm_if;
|
||||
|
||||
struct pim_interface {
|
||||
bool pim_enable : 1;
|
||||
bool pim_can_disable_join_suppression : 1;
|
||||
|
@ -90,6 +92,7 @@ struct pim_interface {
|
|||
* address of the interface */
|
||||
|
||||
int igmp_version; /* IGMP version */
|
||||
int mld_version;
|
||||
int gm_default_robustness_variable; /* IGMP or MLD QRV */
|
||||
int gm_default_query_interval; /* IGMP or MLD secs between general
|
||||
queries */
|
||||
|
@ -111,6 +114,8 @@ struct pim_interface {
|
|||
struct list *gm_group_list; /* list of struct IGMP or MLD group */
|
||||
struct hash *gm_group_hash;
|
||||
|
||||
struct gm_if *mld;
|
||||
|
||||
int pim_sock_fd; /* PIM socket file descriptor */
|
||||
struct thread *t_pim_sock_read; /* thread for reading PIM socket */
|
||||
int64_t pim_sock_creation; /* timestamp of PIM socket creation */
|
||||
|
|
|
@ -115,6 +115,8 @@ static struct pim_instance *pim_instance_init(struct vrf *vrf)
|
|||
|
||||
pim->send_v6_secondary = 1;
|
||||
|
||||
pim->gm_socket = -1;
|
||||
|
||||
pim_rp_init(pim);
|
||||
|
||||
pim_bsm_proc_init(pim);
|
||||
|
|
|
@ -167,6 +167,10 @@ struct pim_instance {
|
|||
struct list *ssmpingd_list;
|
||||
pim_addr ssmpingd_group_addr;
|
||||
|
||||
unsigned int gm_socket_if_count;
|
||||
int gm_socket;
|
||||
struct thread *t_gm_recv;
|
||||
|
||||
unsigned int igmp_group_count;
|
||||
unsigned int igmp_watermark_limit;
|
||||
unsigned int keep_alive_time;
|
||||
|
@ -194,6 +198,8 @@ struct pim_instance {
|
|||
int64_t nexthop_lookups;
|
||||
int64_t nexthop_lookups_avoided;
|
||||
int64_t last_route_change_time;
|
||||
|
||||
uint64_t gm_rx_drop_sys;
|
||||
};
|
||||
|
||||
void pim_vrf_init(void);
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include "log.h"
|
||||
#include "lib_errors.h"
|
||||
#include "pim_util.h"
|
||||
#include "pim6_mld.h"
|
||||
|
||||
#if PIM_IPV == 6
|
||||
#define pim6_msdp_err(funcname, argtype) \
|
||||
|
@ -2702,12 +2703,22 @@ int lib_interface_gmp_address_family_igmp_version_destroy(
|
|||
int lib_interface_gmp_address_family_mld_version_modify(
|
||||
struct nb_cb_modify_args *args)
|
||||
{
|
||||
struct interface *ifp;
|
||||
struct pim_interface *pim_ifp;
|
||||
|
||||
switch (args->event) {
|
||||
case NB_EV_VALIDATE:
|
||||
case NB_EV_PREPARE:
|
||||
case NB_EV_ABORT:
|
||||
break;
|
||||
case NB_EV_APPLY:
|
||||
/* TBD depends on MLD data structure changes */
|
||||
ifp = nb_running_get_entry(args->dnode, NULL, true);
|
||||
pim_ifp = ifp->info;
|
||||
if (!pim_ifp)
|
||||
return NB_ERR_INCONSISTENCY;
|
||||
|
||||
pim_ifp->mld_version = yang_dnode_get_uint8(args->dnode, NULL);
|
||||
gm_ifp_update(ifp);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -2717,11 +2728,22 @@ int lib_interface_gmp_address_family_mld_version_modify(
|
|||
int lib_interface_gmp_address_family_mld_version_destroy(
|
||||
struct nb_cb_destroy_args *args)
|
||||
{
|
||||
struct interface *ifp;
|
||||
struct pim_interface *pim_ifp;
|
||||
|
||||
switch (args->event) {
|
||||
case NB_EV_VALIDATE:
|
||||
case NB_EV_PREPARE:
|
||||
case NB_EV_ABORT:
|
||||
break;
|
||||
case NB_EV_APPLY:
|
||||
ifp = nb_running_get_entry(args->dnode, NULL, true);
|
||||
pim_ifp = ifp->info;
|
||||
if (!pim_ifp)
|
||||
return NB_ERR_INCONSISTENCY;
|
||||
|
||||
pim_ifp->mld_version = 2;
|
||||
gm_ifp_update(ifp);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -2734,10 +2756,10 @@ int lib_interface_gmp_address_family_mld_version_destroy(
|
|||
int lib_interface_gmp_address_family_query_interval_modify(
|
||||
struct nb_cb_modify_args *args)
|
||||
{
|
||||
#if PIM_IPV == 4
|
||||
struct interface *ifp;
|
||||
int query_interval;
|
||||
|
||||
#if PIM_IPV == 4
|
||||
switch (args->event) {
|
||||
case NB_EV_VALIDATE:
|
||||
case NB_EV_PREPARE:
|
||||
|
@ -2749,7 +2771,23 @@ int lib_interface_gmp_address_family_query_interval_modify(
|
|||
change_query_interval(ifp->info, query_interval);
|
||||
}
|
||||
#else
|
||||
/* TBD Depends on MLD data structure changes */
|
||||
struct pim_interface *pim_ifp;
|
||||
|
||||
switch (args->event) {
|
||||
case NB_EV_VALIDATE:
|
||||
case NB_EV_PREPARE:
|
||||
case NB_EV_ABORT:
|
||||
break;
|
||||
case NB_EV_APPLY:
|
||||
ifp = nb_running_get_entry(args->dnode, NULL, true);
|
||||
pim_ifp = ifp->info;
|
||||
if (!pim_ifp)
|
||||
return NB_ERR_INCONSISTENCY;
|
||||
|
||||
query_interval = yang_dnode_get_uint16(args->dnode, NULL);
|
||||
pim_ifp->gm_default_query_interval = query_interval;
|
||||
gm_ifp_update(ifp);
|
||||
}
|
||||
#endif
|
||||
return NB_OK;
|
||||
}
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
#include "pim_bfd.h"
|
||||
#include "pim_bsm.h"
|
||||
#include "pim_vxlan.h"
|
||||
#include "pim6_mld.h"
|
||||
|
||||
int pim_debug_config_write(struct vty *vty)
|
||||
{
|
||||
|
@ -291,8 +292,8 @@ int pim_global_config_write_worker(struct pim_instance *pim, struct vty *vty)
|
|||
}
|
||||
|
||||
#if PIM_IPV == 4
|
||||
static int pim_igmp_config_write(struct vty *vty, int writes,
|
||||
struct pim_interface *pim_ifp)
|
||||
static int gm_config_write(struct vty *vty, int writes,
|
||||
struct pim_interface *pim_ifp)
|
||||
{
|
||||
/* IF ip igmp */
|
||||
if (pim_ifp->igmp_enable) {
|
||||
|
@ -360,6 +361,17 @@ static int pim_igmp_config_write(struct vty *vty, int writes,
|
|||
|
||||
return writes;
|
||||
}
|
||||
#else
|
||||
static int gm_config_write(struct vty *vty, int writes,
|
||||
struct pim_interface *pim_ifp)
|
||||
{
|
||||
if (pim_ifp->mld_version != MLD_DEFAULT_VERSION)
|
||||
vty_out(vty, " ipv6 mld version %d\n", pim_ifp->mld_version);
|
||||
if (pim_ifp->gm_default_query_interval != IGMP_GENERAL_QUERY_INTERVAL)
|
||||
vty_out(vty, " ipv6 mld query-interval %d\n",
|
||||
pim_ifp->gm_default_query_interval);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int pim_config_write(struct vty *vty, int writes, struct interface *ifp,
|
||||
|
@ -388,9 +400,7 @@ int pim_config_write(struct vty *vty, int writes, struct interface *ifp,
|
|||
++writes;
|
||||
}
|
||||
|
||||
#if PIM_IPV == 4
|
||||
writes += pim_igmp_config_write(vty, writes, pim_ifp);
|
||||
#endif
|
||||
writes += gm_config_write(vty, writes, pim_ifp);
|
||||
|
||||
/* update source */
|
||||
if (!pim_addr_is_any(pim_ifp->update_source)) {
|
||||
|
|
|
@ -9,6 +9,7 @@ noinst_PROGRAMS += pimd/test_igmpv3_join
|
|||
vtysh_scan += \
|
||||
pimd/pim_cmd.c \
|
||||
pimd/pim6_cmd.c \
|
||||
pimd/pim6_mld.c \
|
||||
#end
|
||||
vtysh_daemons += pimd
|
||||
vtysh_daemons += pim6d
|
||||
|
@ -89,6 +90,7 @@ nodist_pimd_pimd_SOURCES = \
|
|||
pimd_pim6d_SOURCES = \
|
||||
$(pim_common) \
|
||||
pimd/pim6_main.c \
|
||||
pimd/pim6_mld.c \
|
||||
pimd/pim6_stubs.c \
|
||||
pimd/pim6_cmd.c \
|
||||
pimd/pim6_mroute_msg.c \
|
||||
|
@ -155,6 +157,8 @@ noinst_HEADERS += \
|
|||
pimd/pim_vxlan.h \
|
||||
pimd/pim_vxlan_instance.h \
|
||||
pimd/pimd.h \
|
||||
pimd/pim6_mld.h \
|
||||
pimd/pim6_mld_protocol.h \
|
||||
pimd/mtracebis_netlink.h \
|
||||
pimd/mtracebis_routeget.h \
|
||||
pimd/pim6_cmd.h \
|
||||
|
@ -163,6 +167,7 @@ noinst_HEADERS += \
|
|||
clippy_scan += \
|
||||
pimd/pim_cmd.c \
|
||||
pimd/pim6_cmd.c \
|
||||
pimd/pim6_mld.c \
|
||||
# end
|
||||
|
||||
pimd_pimd_CFLAGS = $(AM_CFLAGS) -DPIM_IPV=4
|
||||
|
|
|
@ -96,6 +96,7 @@ module frr-gmp {
|
|||
type uint8 {
|
||||
range "1..2";
|
||||
}
|
||||
default "2";
|
||||
description
|
||||
"MLD version.";
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue