Merge pull request #10775 from opensourcerouting/pim6-mld-pr

pim6d: MLD code
This commit is contained in:
Donald Sharp 2022-05-17 07:20:08 -04:00 committed by GitHub
commit 8a8ad459af
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 3632 additions and 12 deletions

View file

@ -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.

View file

@ -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);
}

View file

@ -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

File diff suppressed because it is too large Load diff

367
pimd/pim6_mld.h Normal file
View 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
View 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 */

View file

@ -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;
}

View file

@ -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 */

View file

@ -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);

View file

@ -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);

View file

@ -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;
}

View file

@ -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)) {

View file

@ -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

View file

@ -96,6 +96,7 @@ module frr-gmp {
type uint8 {
range "1..2";
}
default "2";
description
"MLD version.";
}