Merge pull request #16634 from nabahr/autorp

PIM: Implement AutoRP functionality
This commit is contained in:
Donald Sharp 2024-09-24 15:36:09 -04:00 committed by GitHub
commit c0ccf381d4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
31 changed files with 2579 additions and 17 deletions

View file

@ -71,6 +71,31 @@ PIM Routers
prefix of group ranges covered. This command is vrf aware, to configure for
a vrf, specify the vrf in the router pim block.
.. clicmd:: no autorp discovery
In order to use pim, it is necessary to configure a RP for join messages to
be sent to. FRR supports learning RP information dynamically via the AutoRP
protocol and performs discovery by default. This command will disable the
AutoRP discovery protocol.
All routers in the pim network must agree on the network RP information, so
all routers in the network should have AutoRP either enabled or disabled.
This command is vrf aware, to configure for a vrf, specify the vrf in the
router pim block.
.. clicmd:: autorp announce A.B.C.D [A.B.C.D/M | group-list PREFIX_LIST]
Configure the router to advertise itself as a candidate PIM-SM RP via AutoRP.
The supported groups can be defined as a single group range, or multiple
group ranges can be defined via a prefix list.
.. clicmd:: autorp announce {scope (1-255) | interval (1-65535) | holdtime (0-65535)}
Configure the AutoRP advertise messages. The scope defines the TTL value in the
messages to limit the scope, defaults to 31. Interval defines the number of
seconds elapsed between advertise messages sent, defaults to 60. Hold time defines
how long the AutoRP mapping agent will consider the information valid, setting to
0 will disable expiration of the candidate RP information, defaults to 3 * interval.
.. clicmd:: rp keep-alive-timer (1-65535)
Modify the time out value for a S,G flow from 1-65535 seconds at RP.
@ -616,6 +641,11 @@ cause great confusion.
192.168.10.123 239.0.0.0/8 eth2 yes Static ASM
192.168.10.123 239.4.0.0/24 eth2 yes Static SSM
.. clicmd:: show ip pim [vrf NAME] autorp [json]
Display information about AutoRP. Including state of AutoRP Discovery parsing
and configured AutoRP candidate RP information.
.. clicmd:: show ip pim rpf
Display information about currently being used S,G's and their RPF lookup
@ -761,6 +791,10 @@ the config was written out.
This gathers data about events from zebra that come up through the ZAPI.
.. clicmd:: debug pim autorp
This turns on debugging for PIM AutoRP protocol events.
PIM Clear Commands
==================
Clear commands reset various variables.

1147
pimd/pim_autorp.c Normal file

File diff suppressed because it is too large Load diff

158
pimd/pim_autorp.h Normal file
View file

@ -0,0 +1,158 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* pim_autorp.h: PIM Auto RP handling related
*
* Copyright (C) 20224 ATCorp.
* Nathan Bahr
*/
#ifndef __PIM_AUTORP_H__
#define __PIM_AUTORP_H__
#include <typesafe.h>
#define AUTORP_VERSION 1
#define AUTORP_ANNOUNCEMENT_TYPE 1
#define AUTORP_DISCOVERY_TYPE 2
#define PIM_VUNKNOWN 0
#define PIM_V1 1
#define PIM_V2 2
#define PIM_V1_2 3
#define DEFAULT_ANNOUNCE_INTERVAL 60
#define DEFAULT_ANNOUNCE_SCOPE 31
#define DEFAULT_ANNOUNCE_HOLDTIME -1
PREDECL_SORTLIST_UNIQ(pim_autorp_rp);
struct autorp_pkt_grp {
#if __BYTE_ORDER == __LITTLE_ENDIAN
uint8_t negprefix : 1;
uint8_t reserved : 7;
#elif __BYTE_ORDER == __BIG_ENDIAN
uint8_t reserved : 7;
uint8_t negprefix : 1;
#else
#error "Please fix <bits/endian.h>"
#endif
uint8_t masklen;
uint32_t addr;
} __attribute__((__packed__));
struct autorp_pkt_rp {
uint32_t addr;
#if __BYTE_ORDER == __LITTLE_ENDIAN
uint8_t pimver : 2;
uint8_t reserved : 6;
#elif __BYTE_ORDER == __BIG_ENDIAN
uint8_t reserved : 6;
uint8_t pimver : 2;
#else
#error "Please fix <bits/endian.h>"
#endif
uint8_t grpcnt;
} __attribute__((__packed__));
struct autorp_pkt_hdr {
#if __BYTE_ORDER == __LITTLE_ENDIAN
uint8_t type : 4;
uint8_t version : 4;
#elif __BYTE_ORDER == __BIG_ENDIAN
uint8_t version : 4;
uint8_t type : 4;
#else
#error "Please fix <bits/endian.h>"
#endif
uint8_t rpcnt;
uint16_t holdtime;
uint32_t reserved;
} __attribute__((__packed__));
#define MIN_AUTORP_PKT_SZ \
(sizeof(struct autorp_pkt_hdr) + sizeof(struct autorp_pkt_rp) + \
sizeof(struct autorp_pkt_grp))
struct pim_autorp_rp {
struct pim_autorp *autorp;
struct in_addr addr;
uint16_t holdtime;
struct event *hold_timer;
struct prefix grp;
char grplist[32];
struct pim_autorp_rp_item list;
};
struct pim_autorp {
/* backpointer to pim instance */
struct pim_instance *pim;
/* UDP socket bound to AutoRP port, used for sending and receiving all AutoRP packets */
int sock;
/* Event for reading AutoRP packets */
struct event *read_event;
/* Event for sending announcement packets */
struct event *announce_timer;
/* Event for sending discovery packets*/
/* struct event *discovery_timer; */
/* Flag enabling reading discovery packets */
bool do_discovery;
/* Flag enabling mapping agent (reading announcements and sending discovery)*/
/* bool do_mapping; */
/* List of RP's in received discovery packets */
struct pim_autorp_rp_head discovery_rp_list;
/* List of configured candidate RP's to send in announcement packets */
struct pim_autorp_rp_head candidate_rp_list;
/* List of announced RP's to send in discovery packets */
/* struct pim_autorp_rp_head mapping_rp_list; */
/* Packet parameters for sending announcement packets */
uint8_t announce_scope;
uint16_t announce_interval;
int32_t announce_holdtime;
/* Pre-built announcement packet, only changes when configured RP's or packet parameters change */
uint8_t *annouce_pkt;
uint16_t annouce_pkt_sz;
/* TODO: Packet parameters for sending discovery packets
* int discovery_scope;
* int discovery_interval;
* int discovery_holdtime;
*/
};
#define AUTORP_GRPLEN 6
#define AUTORP_RPLEN 6
#define AUTORP_HDRLEN 8
bool pim_autorp_rm_candidate_rp(struct pim_instance *pim, pim_addr rpaddr);
void pim_autorp_add_candidate_rp_group(struct pim_instance *pim,
pim_addr rpaddr, struct prefix group);
bool pim_autorp_rm_candidate_rp_group(struct pim_instance *pim, pim_addr rpaddr,
struct prefix group);
void pim_autorp_add_candidate_rp_plist(struct pim_instance *pim,
pim_addr rpaddr, const char *plist);
bool pim_autorp_rm_candidate_rp_plist(struct pim_instance *pim, pim_addr rpaddr,
const char *plist);
void pim_autorp_announce_scope(struct pim_instance *pim, uint8_t scope);
void pim_autorp_announce_interval(struct pim_instance *pim, uint16_t interval);
void pim_autorp_announce_holdtime(struct pim_instance *pim, int32_t holdtime);
void pim_autorp_add_ifp(struct interface *ifp);
void pim_autorp_rm_ifp(struct interface *ifp);
void pim_autorp_start_discovery(struct pim_instance *pim);
void pim_autorp_stop_discovery(struct pim_instance *pim);
void pim_autorp_init(struct pim_instance *pim);
void pim_autorp_finish(struct pim_instance *pim);
int pim_autorp_config_write(struct pim_instance *pim, struct vty *vty);
void pim_autorp_show_autorp(struct vty *vty, struct pim_instance *pim,
json_object *json);
#endif

View file

@ -2820,6 +2820,75 @@ DEFPY (show_ip_pim_rp_vrf_all,
(struct prefix *)group, !!json);
}
DEFPY (show_ip_pim_autorp,
show_ip_pim_autorp_cmd,
"show ip pim [vrf NAME] autorp [json$json]",
SHOW_STR
IP_STR
PIM_STR
VRF_CMD_HELP_STR
"PIM AutoRP information\n"
JSON_STR)
{
struct vrf *v;
json_object *json_parent = NULL;
v = vrf_lookup_by_name(vrf ? vrf : VRF_DEFAULT_NAME);
if (!v || !v->info) {
if (!json)
vty_out(vty, "%% Unable to find pim instance\n");
return CMD_WARNING;
}
if (json)
json_parent = json_object_new_object();
pim_autorp_show_autorp(vty, v->info, json_parent);
if (json)
vty_json(vty, json_parent);
return CMD_SUCCESS;
}
DEFPY (show_ip_pim_autorp_vrf_all,
show_ip_pim_autorp_vrf_all_cmd,
"show ip pim vrf all autorp [json$json]",
SHOW_STR
IP_STR
PIM_STR
VRF_CMD_HELP_STR
"PIM AutoRP information\n"
JSON_STR)
{
struct vrf *vrf;
json_object *json_parent = NULL;
json_object *json_vrf = NULL;
if (json)
json_parent = json_object_new_object();
RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
if (vrf->info) {
if (!json)
vty_out(vty, "VRF: %s\n", vrf->name);
else
json_vrf = json_object_new_object();
pim_autorp_show_autorp(vty, vrf->info, json_vrf);
if (json)
json_object_object_add(json_parent, vrf->name,
json_vrf);
}
}
if (json)
vty_json(vty, json_parent);
return CMD_SUCCESS;
}
DEFPY (show_ip_pim_rpf,
show_ip_pim_rpf_cmd,
"show ip pim [vrf NAME] rpf [json$json]",
@ -4516,6 +4585,52 @@ DEFPY_ATTR(no_ip_pim_rp_prefix_list,
return ret;
}
DEFPY (pim_autorp_discovery,
pim_autorp_discovery_cmd,
"[no] autorp discovery",
NO_STR
"AutoRP\n"
"Enable AutoRP discovery\n")
{
if (no)
return pim_process_no_autorp_cmd(vty);
else
return pim_process_autorp_cmd(vty);
}
DEFPY (pim_autorp_announce_rp,
pim_autorp_announce_rp_cmd,
"[no] autorp announce A.B.C.D$rpaddr ![A.B.C.D/M$grp|group-list PREFIX_LIST$plist]",
NO_STR
"AutoRP\n"
"AutoRP Candidate RP announcement\n"
"AutoRP Candidate RP address\n"
"Group prefix\n"
"Prefix list\n"
"List name\n")
{
return pim_process_autorp_candidate_rp_cmd(vty, no, rpaddr_str, grp,
plist);
}
DEFPY (pim_autorp_announce_scope_int,
pim_autorp_announce_scope_int_cmd,
"[no] autorp announce ![{scope (1-255) | interval (1-65535) | holdtime (0-65535)}]",
NO_STR
"AutoRP\n"
"AutoRP Candidate RP announcement\n"
"Packet scope (TTL)\n"
"TTL value\n"
"Announcement interval\n"
"Time in seconds\n"
"Announcement holdtime\n"
"Time in seconds\n")
{
return pim_process_autorp_announce_scope_int_cmd(vty, no, scope_str,
interval_str,
holdtime_str);
}
DEFPY (pim_bsr_candidate_bsr,
pim_bsr_candidate_bsr_cmd,
"[no] bsr candidate-bsr [{priority (0-255)|source <address A.B.C.D|interface IFNAME|loopback$loopback|any$any>}]",
@ -6377,6 +6492,29 @@ DEFUN (no_debug_bsm,
return CMD_SUCCESS;
}
DEFUN (debug_autorp,
debug_autorp_cmd,
"debug pim autorp",
DEBUG_STR
DEBUG_PIM_STR
DEBUG_PIM_AUTORP_STR)
{
PIM_DO_DEBUG_AUTORP;
return CMD_SUCCESS;
}
DEFUN (no_debug_autorp,
no_debug_autorp_cmd,
"no debug pim autorp",
NO_STR
DEBUG_STR
DEBUG_PIM_STR
DEBUG_PIM_AUTORP_STR)
{
PIM_DONT_DEBUG_AUTORP;
return CMD_SUCCESS;
}
DEFUN_NOSH (show_debugging_pim,
show_debugging_pim_cmd,
@ -8714,6 +8852,9 @@ void pim_cmd_init(void)
install_element(PIM_NODE, &no_pim_rp_cmd);
install_element(PIM_NODE, &pim_rp_prefix_list_cmd);
install_element(PIM_NODE, &no_pim_rp_prefix_list_cmd);
install_element(PIM_NODE, &pim_autorp_discovery_cmd);
install_element(PIM_NODE, &pim_autorp_announce_rp_cmd);
install_element(PIM_NODE, &pim_autorp_announce_scope_int_cmd);
install_element(PIM_NODE, &no_pim_ssm_prefix_list_cmd);
install_element(PIM_NODE, &no_pim_ssm_prefix_list_name_cmd);
install_element(PIM_NODE, &pim_ssm_prefix_list_cmd);
@ -8868,6 +9009,8 @@ void pim_cmd_init(void)
install_element(VIEW_NODE, &show_ip_pim_upstream_rpf_cmd);
install_element(VIEW_NODE, &show_ip_pim_rp_cmd);
install_element(VIEW_NODE, &show_ip_pim_rp_vrf_all_cmd);
install_element(VIEW_NODE, &show_ip_pim_autorp_cmd);
install_element(VIEW_NODE, &show_ip_pim_autorp_vrf_all_cmd);
install_element(VIEW_NODE, &show_ip_pim_bsr_cmd);
install_element(VIEW_NODE, &show_ip_multicast_cmd);
install_element(VIEW_NODE, &show_ip_multicast_vrf_all_cmd);
@ -8975,6 +9118,8 @@ void pim_cmd_init(void)
install_element(CONFIG_NODE, &debug_pim_trace_detail_cmd);
install_element(ENABLE_NODE, &debug_ssmpingd_cmd);
install_element(CONFIG_NODE, &debug_ssmpingd_cmd);
install_element(ENABLE_NODE, &debug_autorp_cmd);
install_element(ENABLE_NODE, &no_debug_autorp_cmd);
install_element(ENABLE_NODE, &no_debug_ssmpingd_cmd);
install_element(CONFIG_NODE, &no_debug_ssmpingd_cmd);
install_element(ENABLE_NODE, &debug_pim_zebra_cmd);
@ -9007,6 +9152,8 @@ void pim_cmd_init(void)
install_element(CONFIG_NODE, &debug_bsm_cmd);
install_element(ENABLE_NODE, &no_debug_bsm_cmd);
install_element(CONFIG_NODE, &no_debug_bsm_cmd);
install_element(CONFIG_NODE, &debug_autorp_cmd);
install_element(CONFIG_NODE, &no_debug_autorp_cmd);
install_element(CONFIG_NODE, &ip_igmp_group_watermark_cmd);
install_element(VRF_NODE, &ip_igmp_group_watermark_cmd);

View file

@ -56,6 +56,7 @@
#define DEBUG_MSDP_PACKETS_STR "MSDP protocol packets\n"
#define DEBUG_MTRACE_STR "Mtrace protocol activity\n"
#define DEBUG_PIM_BSM_STR "BSR message processing activity\n"
#define DEBUG_PIM_AUTORP_STR "AutoRP message processing activity\n"
void pim_cmd_init(void);

View file

@ -606,6 +606,165 @@ int pim_process_no_rp_plist_cmd(struct vty *vty, const char *rp_str,
return nb_cli_apply_changes(vty, NULL);
}
int pim_process_autorp_cmd(struct vty *vty)
{
char xpath[XPATH_MAXLEN];
snprintf(xpath, sizeof(xpath), "%s/%s", FRR_PIM_AUTORP_XPATH,
"discovery-enabled");
nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, "true");
return nb_cli_apply_changes(vty, NULL);
}
int pim_process_no_autorp_cmd(struct vty *vty)
{
char xpath[XPATH_MAXLEN];
snprintf(xpath, sizeof(xpath), "%s/%s", FRR_PIM_AUTORP_XPATH,
"discovery-enabled");
nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
return nb_cli_apply_changes(vty, NULL);
}
int pim_process_autorp_candidate_rp_cmd(struct vty *vty, bool no,
const char *rpaddr_str,
const struct prefix_ipv4 *grp,
const char *plist)
{
char xpath[XPATH_MAXLEN];
char grpstr[64];
if (no) {
if (!is_default_prefix((const struct prefix *)grp) || plist) {
/* If any single values are set, only destroy those */
if (!is_default_prefix((const struct prefix *)grp)) {
snprintfrr(xpath, sizeof(xpath),
"%s/candidate-rp-list[rp-address='%s']/group",
FRR_PIM_AUTORP_XPATH, rpaddr_str);
nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY,
NULL);
}
if (plist) {
snprintfrr(xpath, sizeof(xpath),
"%s/candidate-rp-list[rp-address='%s']/prefix-list",
FRR_PIM_AUTORP_XPATH, rpaddr_str);
nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY,
NULL);
}
} else {
/* No values set, remove the entire RP */
snprintfrr(xpath, sizeof(xpath),
"%s/candidate-rp-list[rp-address='%s']",
FRR_PIM_AUTORP_XPATH, rpaddr_str);
nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
}
} else {
if (!is_default_prefix((const struct prefix *)grp) || plist) {
snprintfrr(xpath, sizeof(xpath),
"%s/candidate-rp-list[rp-address='%s']",
FRR_PIM_AUTORP_XPATH, rpaddr_str);
nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
if (!is_default_prefix((const struct prefix *)grp)) {
snprintfrr(xpath, sizeof(xpath),
"%s/candidate-rp-list[rp-address='%s']/group",
FRR_PIM_AUTORP_XPATH, rpaddr_str);
nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY,
prefix2str(grp, grpstr,
sizeof(grpstr)));
}
if (plist) {
snprintfrr(xpath, sizeof(xpath),
"%s/candidate-rp-list[rp-address='%s']/prefix-list",
FRR_PIM_AUTORP_XPATH, rpaddr_str);
nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY,
plist);
}
} else {
return CMD_WARNING_CONFIG_FAILED;
}
}
return nb_cli_apply_changes(vty, NULL);
}
int pim_process_autorp_announce_scope_int_cmd(struct vty *vty, bool no,
const char *scope,
const char *interval,
const char *holdtime)
{
char xpath[XPATH_MAXLEN];
if (no) {
if (scope || interval || holdtime) {
/* If any single values are set, only destroy those */
if (scope) {
snprintfrr(xpath, sizeof(xpath), "%s/%s",
FRR_PIM_AUTORP_XPATH,
"announce-scope");
nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY,
NULL);
}
if (interval) {
snprintfrr(xpath, sizeof(xpath), "%s/%s",
FRR_PIM_AUTORP_XPATH,
"announce-interval");
nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY,
NULL);
}
if (holdtime) {
snprintfrr(xpath, sizeof(xpath), "%s/%s",
FRR_PIM_AUTORP_XPATH,
"announce-holdtime");
nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY,
NULL);
}
} else {
/* No values set, remove all */
snprintfrr(xpath, sizeof(xpath), "%s/%s",
FRR_PIM_AUTORP_XPATH, "announce-scope");
nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
snprintfrr(xpath, sizeof(xpath), "%s/%s",
FRR_PIM_AUTORP_XPATH, "announce-interval");
nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
snprintfrr(xpath, sizeof(xpath), "%s/%s",
FRR_PIM_AUTORP_XPATH, "announce-holdtime");
nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
}
} else {
if (scope || interval || holdtime) {
if (scope) {
snprintfrr(xpath, sizeof(xpath), "%s/%s",
FRR_PIM_AUTORP_XPATH,
"announce-scope");
nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY,
scope);
}
if (interval) {
snprintfrr(xpath, sizeof(xpath), "%s/%s",
FRR_PIM_AUTORP_XPATH,
"announce-interval");
nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY,
interval);
}
if (holdtime) {
snprintfrr(xpath, sizeof(xpath), "%s/%s",
FRR_PIM_AUTORP_XPATH,
"announce-holdtime");
nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY,
holdtime);
}
} else {
return CMD_WARNING_CONFIG_FAILED;
}
}
return nb_cli_apply_changes(vty, NULL);
}
bool pim_sgaddr_match(pim_sgaddr item, pim_sgaddr match)
{
return (pim_addr_is_any(match.grp) ||

View file

@ -35,7 +35,16 @@ int pim_process_rp_plist_cmd(struct vty *vty, const char *rp_str,
const char *prefix_list);
int pim_process_no_rp_plist_cmd(struct vty *vty, const char *rp_str,
const char *prefix_list);
int pim_process_autorp_cmd(struct vty *vty);
int pim_process_no_autorp_cmd(struct vty *vty);
int pim_process_autorp_candidate_rp_cmd(struct vty *vty, bool no,
const char *rpaddr_str,
const struct prefix_ipv4 *grp,
const char *plist);
int pim_process_autorp_announce_scope_int_cmd(struct vty *vty, bool no,
const char *scope,
const char *interval,
const char *holdtime);
int pim_process_ip_pim_cmd(struct vty *vty);
int pim_process_no_ip_pim_cmd(struct vty *vty);
int pim_process_ip_pim_passive_cmd(struct vty *vty, bool enable);

View file

@ -1914,6 +1914,12 @@ static int pim_ifp_up(struct interface *ifp)
}
}
#if PIM_IPV == 4
if (pim->autorp && pim->autorp->do_discovery && pim_ifp &&
pim_ifp->pim_enable)
pim_autorp_add_ifp(ifp);
#endif
pim_cand_addrs_changed();
return 0;
}
@ -1951,6 +1957,10 @@ static int pim_ifp_down(struct interface *ifp)
pim_ifstat_reset(ifp);
}
#if PIM_IPV == 4
pim_autorp_rm_ifp(ifp);
#endif
pim_cand_addrs_changed();
return 0;
}
@ -2023,6 +2033,11 @@ void pim_pim_interface_delete(struct interface *ifp)
if (!pim_ifp)
return;
#if PIM_IPV == 4
if (pim_ifp->pim_enable)
pim_autorp_rm_ifp(ifp);
#endif
pim_ifp->pim_enable = false;
pim_if_membership_clear(ifp);

View file

@ -57,6 +57,10 @@ static void pim_instance_terminate(struct pim_instance *pim)
pim_mroute_socket_disable(pim);
#if PIM_IPV == 4
pim_autorp_finish(pim);
#endif
XFREE(MTYPE_PIM_PLIST_NAME, pim->spt.plist);
XFREE(MTYPE_PIM_PLIST_NAME, pim->register_plist);
@ -125,6 +129,10 @@ static struct pim_instance *pim_instance_init(struct vrf *vrf)
pim->msdp.keep_alive = PIM_MSDP_PEER_KA_TIME;
pim->msdp.connection_retry = PIM_MSDP_PEER_CONNECT_RETRY_TIME;
#if PIM_IPV == 4
pim_autorp_init(pim);
#endif
return pim;
}

View file

@ -17,6 +17,7 @@
#include "pim_oil.h"
#include "pim_upstream.h"
#include "pim_mroute.h"
#include "pim_autorp.h"
enum pim_spt_switchover {
PIM_SPT_IMMEDIATE,
@ -152,6 +153,8 @@ struct pim_instance {
struct pim_msdp msdp;
struct pim_vxlan_instance vxlan;
struct pim_autorp *autorp;
struct list *ssmpingd_list;
pim_addr ssmpingd_group_addr;

View file

@ -379,6 +379,55 @@ const struct frr_yang_module_info frr_pim_rp_info = {
.destroy = routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_static_rp_rp_list_prefix_list_destroy,
}
},
{
.xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-rp:rp/auto-rp/discovery-enabled",
.cbs = {
.modify = routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_discovery_enabled_modify,
.destroy = routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_discovery_enabled_destroy,
}
},
{
.xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-rp:rp/auto-rp/announce-scope",
.cbs = {
.modify = routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_announce_scope_modify,
.destroy = routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_announce_scope_destroy,
}
},
{
.xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-rp:rp/auto-rp/announce-interval",
.cbs = {
.modify = routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_announce_interval_modify,
.destroy = routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_announce_interval_destroy,
}
},
{
.xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-rp:rp/auto-rp/announce-holdtime",
.cbs = {
.modify = routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_announce_holdtime_modify,
.destroy = routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_announce_holdtime_destroy,
}
},
{
.xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-rp:rp/auto-rp/candidate-rp-list",
.cbs = {
.create = routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_candidate_rp_list_create,
.destroy = routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_candidate_rp_list_destroy,
}
},
{
.xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-rp:rp/auto-rp/candidate-rp-list/group",
.cbs = {
.modify = routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_candidate_rp_list_group_modify,
.destroy = routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_candidate_rp_list_group_destroy,
}
},
{
.xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-rp:rp/auto-rp/candidate-rp-list/prefix-list",
.cbs = {
.modify = routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_candidate_rp_list_prefix_list_modify,
.destroy = routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_candidate_rp_list_prefix_list_destroy,
}
},
{
.xpath = NULL,
},

View file

@ -159,6 +159,34 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp
struct nb_cb_modify_args *args);
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_static_rp_rp_list_prefix_list_destroy(
struct nb_cb_destroy_args *args);
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_discovery_enabled_modify(
struct nb_cb_modify_args *args);
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_discovery_enabled_destroy(
struct nb_cb_destroy_args *args);
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_announce_scope_modify(
struct nb_cb_modify_args *args);
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_announce_scope_destroy(
struct nb_cb_destroy_args *args);
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_announce_interval_modify(
struct nb_cb_modify_args *args);
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_announce_interval_destroy(
struct nb_cb_destroy_args *args);
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_announce_holdtime_modify(
struct nb_cb_modify_args *args);
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_announce_holdtime_destroy(
struct nb_cb_destroy_args *args);
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_candidate_rp_list_create(
struct nb_cb_create_args *args);
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_candidate_rp_list_destroy(
struct nb_cb_destroy_args *args);
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_candidate_rp_list_group_modify(
struct nb_cb_modify_args *args);
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_candidate_rp_list_group_destroy(
struct nb_cb_destroy_args *args);
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_candidate_rp_list_prefix_list_modify(
struct nb_cb_modify_args *args);
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_candidate_rp_list_prefix_list_destroy(
struct nb_cb_destroy_args *args);
/* frr-cand-bsr */
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_bsr_create(
@ -258,6 +286,7 @@ int routing_control_plane_protocols_name_validate(
"mroute[source-addr='%s'][group-addr='%s']"
#define FRR_PIM_STATIC_RP_XPATH \
"frr-pim-rp:rp/static-rp/rp-list[rp-address='%s']"
#define FRR_PIM_AUTORP_XPATH "./frr-pim-rp:rp/auto-rp"
#define FRR_GMP_INTERFACE_XPATH \
"./frr-gmp:gmp/address-family[address-family='%s']"
#define FRR_GMP_ENABLE_XPATH \

View file

@ -26,6 +26,7 @@
#include "lib_errors.h"
#include "pim_util.h"
#include "pim6_mld.h"
#include "pim_autorp.h"
#include "pim_igmp.h"
#if PIM_IPV == 6
@ -147,6 +148,11 @@ static int pim_cmd_interface_add(struct interface *ifp)
pim_if_membership_refresh(ifp);
pim_if_create_pimreg(pim_ifp->pim);
#if PIM_IPV == 4
pim_autorp_add_ifp(ifp);
#endif
return 1;
}
@ -2680,6 +2686,365 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp
return NB_OK;
}
/*
* XPath: /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-rp:rp/auto-rp/discovery-enabled
*/
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_discovery_enabled_modify(
struct nb_cb_modify_args *args)
{
#if PIM_IPV == 4
struct vrf *vrf;
struct pim_instance *pim;
bool enabled;
switch (args->event) {
case NB_EV_VALIDATE:
case NB_EV_PREPARE:
case NB_EV_ABORT:
break;
case NB_EV_APPLY:
vrf = nb_running_get_entry(args->dnode, NULL, true);
pim = vrf->info;
enabled = yang_dnode_get_bool(args->dnode, NULL);
if (enabled)
pim_autorp_start_discovery(pim);
else
pim_autorp_stop_discovery(pim);
break;
}
#endif
return NB_OK;
}
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_discovery_enabled_destroy(
struct nb_cb_destroy_args *args)
{
#if PIM_IPV == 4
struct vrf *vrf;
struct pim_instance *pim;
bool enabled;
switch (args->event) {
case NB_EV_VALIDATE:
case NB_EV_PREPARE:
case NB_EV_ABORT:
break;
case NB_EV_APPLY:
vrf = nb_running_get_entry(args->dnode, NULL, true);
pim = vrf->info;
enabled = yang_dnode_get_bool(args->dnode, NULL);
/* Run AutoRP discovery by default */
if (!enabled)
pim_autorp_start_discovery(pim);
break;
}
#endif
return NB_OK;
}
/*
* XPath: /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-rp:rp/auto-rp/announce-scope
*/
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_announce_scope_modify(
struct nb_cb_modify_args *args)
{
#if PIM_IPV == 4
struct vrf *vrf;
struct pim_instance *pim;
uint8_t scope;
switch (args->event) {
case NB_EV_VALIDATE:
case NB_EV_PREPARE:
case NB_EV_ABORT:
break;
case NB_EV_APPLY:
vrf = nb_running_get_entry(args->dnode, NULL, true);
pim = vrf->info;
scope = yang_dnode_get_uint8(args->dnode, NULL);
pim_autorp_announce_scope(pim, scope);
}
#endif
return NB_OK;
}
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_announce_scope_destroy(
struct nb_cb_destroy_args *args)
{
#if PIM_IPV == 4
struct vrf *vrf;
struct pim_instance *pim;
switch (args->event) {
case NB_EV_VALIDATE:
case NB_EV_PREPARE:
case NB_EV_ABORT:
break;
case NB_EV_APPLY:
vrf = nb_running_get_entry(args->dnode, NULL, true);
pim = vrf->info;
pim_autorp_announce_scope(pim, 0);
}
#endif
return NB_OK;
}
/*
* XPath: /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-rp:rp/auto-rp/announce-interval
*/
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_announce_interval_modify(
struct nb_cb_modify_args *args)
{
#if PIM_IPV == 4
struct vrf *vrf;
struct pim_instance *pim;
uint16_t interval;
switch (args->event) {
case NB_EV_VALIDATE:
case NB_EV_PREPARE:
case NB_EV_ABORT:
break;
case NB_EV_APPLY:
vrf = nb_running_get_entry(args->dnode, NULL, true);
pim = vrf->info;
interval = yang_dnode_get_uint16(args->dnode, NULL);
pim_autorp_announce_interval(pim, interval);
}
#endif
return NB_OK;
}
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_announce_interval_destroy(
struct nb_cb_destroy_args *args)
{
#if PIM_IPV == 4
struct vrf *vrf;
struct pim_instance *pim;
switch (args->event) {
case NB_EV_VALIDATE:
case NB_EV_PREPARE:
case NB_EV_ABORT:
break;
case NB_EV_APPLY:
vrf = nb_running_get_entry(args->dnode, NULL, true);
pim = vrf->info;
pim_autorp_announce_interval(pim, 0);
}
#endif
return NB_OK;
}
/*
* XPath: /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-rp:rp/auto-rp/announce-holdtime
*/
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_announce_holdtime_modify(
struct nb_cb_modify_args *args)
{
#if PIM_IPV == 4
struct vrf *vrf;
struct pim_instance *pim;
uint16_t holdtime;
switch (args->event) {
case NB_EV_VALIDATE:
case NB_EV_PREPARE:
case NB_EV_ABORT:
break;
case NB_EV_APPLY:
vrf = nb_running_get_entry(args->dnode, NULL, true);
pim = vrf->info;
holdtime = yang_dnode_get_uint16(args->dnode, NULL);
pim_autorp_announce_holdtime(pim, holdtime);
}
#endif
return NB_OK;
}
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_announce_holdtime_destroy(
struct nb_cb_destroy_args *args)
{
#if PIM_IPV == 4
struct vrf *vrf;
struct pim_instance *pim;
switch (args->event) {
case NB_EV_VALIDATE:
case NB_EV_PREPARE:
case NB_EV_ABORT:
break;
case NB_EV_APPLY:
vrf = nb_running_get_entry(args->dnode, NULL, true);
pim = vrf->info;
/* 0 is a valid value, so -1 indicates deleting (go back to default) */
pim_autorp_announce_holdtime(pim, -1);
}
#endif
return NB_OK;
}
/*
* XPath: /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-rp:rp/auto-rp/candidate-rp-list
*/
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_candidate_rp_list_create(
struct nb_cb_create_args *args)
{
#if PIM_IPV == 4
switch (args->event) {
case NB_EV_VALIDATE:
case NB_EV_PREPARE:
case NB_EV_ABORT:
case NB_EV_APPLY:
break;
}
#endif
return NB_OK;
}
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_candidate_rp_list_destroy(
struct nb_cb_destroy_args *args)
{
#if PIM_IPV == 4
struct vrf *vrf;
struct pim_instance *pim;
pim_addr rp_addr;
switch (args->event) {
case NB_EV_VALIDATE:
case NB_EV_PREPARE:
case NB_EV_ABORT:
break;
case NB_EV_APPLY:
vrf = nb_running_get_entry(args->dnode, NULL, true);
pim = vrf->info;
yang_dnode_get_pimaddr(&rp_addr, args->dnode, "rp-address");
if (!pim_autorp_rm_candidate_rp(pim, rp_addr))
return NB_ERR_INCONSISTENCY;
break;
}
#endif
return NB_OK;
}
/*
* XPath: /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-rp:rp/auto-rp/candidate-rp-list/group
*/
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_candidate_rp_list_group_modify(
struct nb_cb_modify_args *args)
{
#if PIM_IPV == 4
struct vrf *vrf;
struct pim_instance *pim;
struct prefix group;
pim_addr rp_addr;
switch (args->event) {
case NB_EV_VALIDATE:
case NB_EV_PREPARE:
case NB_EV_ABORT:
break;
case NB_EV_APPLY:
vrf = nb_running_get_entry(args->dnode, NULL, true);
pim = vrf->info;
yang_dnode_get_pimaddr(&rp_addr, args->dnode, "../rp-address");
yang_dnode_get_prefix(&group, args->dnode, NULL);
apply_mask(&group);
pim_autorp_add_candidate_rp_group(pim, rp_addr, group);
}
#endif
return NB_OK;
}
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_candidate_rp_list_group_destroy(
struct nb_cb_destroy_args *args)
{
#if PIM_IPV == 4
struct vrf *vrf;
struct pim_instance *pim;
struct prefix group;
pim_addr rp_addr;
switch (args->event) {
case NB_EV_VALIDATE:
case NB_EV_PREPARE:
case NB_EV_ABORT:
break;
case NB_EV_APPLY:
vrf = nb_running_get_entry(args->dnode, NULL, true);
pim = vrf->info;
yang_dnode_get_pimaddr(&rp_addr, args->dnode, "../rp-address");
yang_dnode_get_prefix(&group, args->dnode, NULL);
apply_mask(&group);
if (!pim_autorp_rm_candidate_rp_group(pim, rp_addr, group))
return NB_ERR_INCONSISTENCY;
}
#endif
return NB_OK;
}
/*
* XPath: /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-rp:rp/auto-rp/candidate-rp-list/prefix-list
*/
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_candidate_rp_list_prefix_list_modify(
struct nb_cb_modify_args *args)
{
#if PIM_IPV == 4
struct vrf *vrf;
struct pim_instance *pim;
pim_addr rp_addr;
const char *plist;
switch (args->event) {
case NB_EV_VALIDATE:
case NB_EV_PREPARE:
case NB_EV_ABORT:
break;
case NB_EV_APPLY:
vrf = nb_running_get_entry(args->dnode, NULL, true);
pim = vrf->info;
plist = yang_dnode_get_string(args->dnode, NULL);
yang_dnode_get_pimaddr(&rp_addr, args->dnode, "../rp-address");
pim_autorp_add_candidate_rp_plist(pim, rp_addr, plist);
}
#endif
return NB_OK;
}
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_candidate_rp_list_prefix_list_destroy(
struct nb_cb_destroy_args *args)
{
#if PIM_IPV == 4
struct vrf *vrf;
struct pim_instance *pim;
pim_addr rp_addr;
const char *plist;
switch (args->event) {
case NB_EV_VALIDATE:
case NB_EV_PREPARE:
case NB_EV_ABORT:
break;
case NB_EV_APPLY:
vrf = nb_running_get_entry(args->dnode, NULL, true);
pim = vrf->info;
yang_dnode_get_pimaddr(&rp_addr, args->dnode, "../rp-address");
plist = yang_dnode_get_string(args->dnode, NULL);
if (!pim_autorp_rm_candidate_rp_plist(pim, rp_addr, plist))
return NB_ERR_INCONSISTENCY;
break;
}
#endif
return NB_OK;
}
static void yang_addrsel(struct cand_addrsel *addrsel,
const struct lyd_node *node)
{

View file

@ -1140,7 +1140,8 @@ int pim_rp_config_write(struct pim_instance *pim, struct vty *vty)
if (pim_rpf_addr_is_inaddr_any(&rp_info->rp))
continue;
if (rp_info->rp_src == RP_SRC_BSR)
if (rp_info->rp_src != RP_SRC_NONE &&
rp_info->rp_src != RP_SRC_STATIC)
continue;
rp_addr = rp_info->rp.rpf_addr;
@ -1200,6 +1201,8 @@ void pim_rp_show_information(struct pim_instance *pim, struct prefix *range,
strlcpy(source, "Static", sizeof(source));
else if (rp_info->rp_src == RP_SRC_BSR)
strlcpy(source, "BSR", sizeof(source));
else if (rp_info->rp_src == RP_SRC_AUTORP)
strlcpy(source, "AutoRP", sizeof(source));
else
strlcpy(source, "None", sizeof(source));
if (json) {

View file

@ -16,11 +16,7 @@
struct pim_interface;
enum rp_source {
RP_SRC_NONE = 0,
RP_SRC_STATIC,
RP_SRC_BSR
};
enum rp_source { RP_SRC_NONE = 0, RP_SRC_STATIC, RP_SRC_BSR, RP_SRC_AUTORP };
struct rp_info {
struct prefix group;

View file

@ -292,6 +292,36 @@ int pim_socket_join(int fd, pim_addr group, pim_addr ifaddr, ifindex_t ifindex,
return ret;
}
int pim_socket_leave(int fd, pim_addr group, pim_addr ifaddr, ifindex_t ifindex,
struct pim_interface *pim_ifp)
{
int ret;
#if PIM_IPV == 4
ret = setsockopt_ipv4_multicast(fd, IP_DROP_MEMBERSHIP, ifaddr,
group.s_addr, ifindex);
#else
struct ipv6_mreq opt;
memcpy(&opt.ipv6mr_multiaddr, &group, 16);
opt.ipv6mr_interface = ifindex;
ret = setsockopt(fd, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &opt, sizeof(opt));
#endif
if (ret) {
flog_err(EC_LIB_SOCKET,
"Failure socket leaving fd=%d group %pPAs on interface address %pPAs: %m",
fd, &group, &ifaddr);
pim_ifp->igmp_ifstat_joins_failed++;
return ret;
}
if (PIM_DEBUG_TRACE)
zlog_debug("Socket fd=%d left group %pPAs on interface address %pPAs",
fd, &group, &ifaddr);
return ret;
}
#if PIM_IPV == 4
static void cmsg_getdstaddr(struct msghdr *mh, struct sockaddr_storage *dst,
ifindex_t *ifindex)

View file

@ -32,6 +32,8 @@ int pim_socket_mcast(int protocol, pim_addr ifaddr, struct interface *ifp,
uint8_t loop);
int pim_socket_join(int fd, pim_addr group, pim_addr ifaddr, ifindex_t ifindex,
struct pim_interface *pim_ifp);
int pim_socket_leave(int fd, pim_addr group, pim_addr ifaddr, ifindex_t ifindex,
struct pim_interface *pim_ifp);
int pim_socket_recvfromto(int fd, uint8_t *buf, size_t len,
struct sockaddr_storage *from, socklen_t *fromlen,
struct sockaddr_storage *to, socklen_t *tolen,

View file

@ -165,6 +165,11 @@ int pim_debug_config_write(struct vty *vty)
++writes;
}
if (PIM_DEBUG_AUTORP) {
vty_out(vty, "debug pim autorp\n");
++writes;
}
return writes;
}
@ -182,6 +187,9 @@ int pim_global_config_write_worker(struct pim_instance *pim, struct vty *vty)
}
writes += pim_rp_config_write(pim, vty);
#if PIM_IPV == 4
writes += pim_autorp_config_write(pim, vty);
#endif
writes += pim_cand_config_write(pim, vty);
if (pim->vrf->vrf_id == VRF_DEFAULT) {

View file

@ -95,6 +95,7 @@
#define PIM_MASK_VXLAN (1 << 26)
#define PIM_MASK_BSM_PROC (1 << 27)
#define PIM_MASK_MLAG (1 << 28)
#define PIM_MASK_AUTORP (1 << 29)
/* Remember 32 bits!!! */
/* PIM error codes */
@ -167,6 +168,7 @@ extern uint8_t qpim_ecmp_rebalance_enable;
#define PIM_DEBUG_MTRACE (router->debugs & PIM_MASK_MTRACE)
#define PIM_DEBUG_VXLAN (router->debugs & PIM_MASK_VXLAN)
#define PIM_DEBUG_BSM (router->debugs & PIM_MASK_BSM_PROC)
#define PIM_DEBUG_AUTORP (router->debugs & PIM_MASK_AUTORP)
#define PIM_DEBUG_EVENTS \
(router->debugs & (PIM_MASK_PIM_EVENTS | PIM_MASK_GM_EVENTS | \
@ -209,6 +211,7 @@ extern uint8_t qpim_ecmp_rebalance_enable;
#define PIM_DO_DEBUG_PIM_NHT_RP (router->debugs |= PIM_MASK_PIM_NHT_RP)
#define PIM_DO_DEBUG_MTRACE (router->debugs |= PIM_MASK_MTRACE)
#define PIM_DO_DEBUG_VXLAN (router->debugs |= PIM_MASK_VXLAN)
#define PIM_DO_DEBUG_AUTORP (router->debugs |= PIM_MASK_AUTORP)
#define PIM_DONT_DEBUG_PIM_EVENTS (router->debugs &= ~PIM_MASK_PIM_EVENTS)
#define PIM_DONT_DEBUG_PIM_PACKETS (router->debugs &= ~PIM_MASK_PIM_PACKETS)
@ -243,6 +246,7 @@ extern uint8_t qpim_ecmp_rebalance_enable;
#define PIM_DONT_DEBUG_MTRACE (router->debugs &= ~PIM_MASK_MTRACE)
#define PIM_DONT_DEBUG_VXLAN (router->debugs &= ~PIM_MASK_VXLAN)
#define PIM_DONT_DEBUG_BSM (router->debugs &= ~PIM_MASK_BSM_PROC)
#define PIM_DONT_DEBUG_AUTORP (router->debugs &= ~PIM_MASK_AUTORP)
/* RFC 3376: 8.1. Robustness Variable - Default: 2 for IGMP */
/* RFC 2710: 7.1. Robustness Variable - Default: 2 for MLD */

View file

@ -59,6 +59,7 @@ pim_common = \
pimd_pimd_SOURCES = \
$(pim_common) \
pimd/pim_autorp.c \
pimd/pim_cmd.c \
pimd/pim_igmp.c \
pimd/pim_igmp_mtrace.c \
@ -98,6 +99,7 @@ nodist_pimd_pim6d_SOURCES = \
noinst_HEADERS += \
pimd/pim_addr.h \
pimd/pim_assert.h \
pimd/pim_autorp.h \
pimd/pim_bfd.h \
pimd/pim_bsm.h \
pimd/pim_cmd.h \

View file

@ -1745,6 +1745,49 @@ def verify_pim_rp_info(
return True
@retry(retry_timeout=60, diag_pct=0)
def verify_pim_rp_info_is_empty(tgen, dut, af="ipv4"):
"""
Verify pim rp info by running "show ip pim rp-info" cli
Parameters
----------
* `tgen`: topogen object
* `dut`: device under test
Usage
-----
dut = "r1"
result = verify_pim_rp_info_is_empty(tgen, dut)
Returns
-------
errormsg(str) or True
"""
logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
if dut not in tgen.routers():
return False
rnode = tgen.routers()[dut]
ip_cmd = "ip"
if af == "ipv6":
ip_cmd = "ipv6"
logger.info("[DUT: %s]: Verifying %s rp info", dut, ip_cmd)
cmd = "show {} pim rp-info json".format(ip_cmd)
show_ip_rp_info_json = run_frr_cmd(rnode, cmd, isjson=True)
if show_ip_rp_info_json:
errormsg = "[DUT %s]: Verifying empty rp-info [FAILED]!!" % (dut)
return errormsg
logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
return True
@retry(retry_timeout=60, diag_pct=0)
def verify_pim_state(
tgen,
@ -2411,10 +2454,11 @@ def clear_igmp_interfaces(tgen, dut):
# Verify uptime for groups
for group in group_before_clear.keys():
d1 = datetime.datetime.strptime(group_before_clear[group], "%H:%M:%S")
d2 = datetime.datetime.strptime(group_after_clear[group], "%H:%M:%S")
if d2 >= d1:
errormsg = ("[DUT: %s]: IGMP group is not cleared", " [FAILED!!]", dut)
if group in group_after_clear:
d1 = datetime.datetime.strptime(group_before_clear[group], "%H:%M:%S")
d2 = datetime.datetime.strptime(group_after_clear[group], "%H:%M:%S")
if d2 >= d1:
errormsg = ("[DUT: %s]: IGMP group is not cleared", " [FAILED!!]", dut)
logger.info("[DUT: %s]: IGMP group is cleared [PASSED!!]")
@ -2751,6 +2795,48 @@ def scapy_send_bsr_raw_packet(tgen, topo, senderRouter, receiverRouter, packet=N
return True
def scapy_send_autorp_raw_packet(tgen, senderRouter, senderInterface, packet=None):
"""
Using scapy Raw() method to send AutoRP raw packet from one FRR
to other
Parameters:
-----------
* `tgen` : Topogen object
* `senderRouter` : Sender router
* `senderInterface` : SenderInterface
* `packet` : AutoRP packet in raw format
returns:
--------
errormsg or True
"""
global CWD
result = ""
logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
python3_path = tgen.net.get_exec_path(["python3", "python"])
# send_bsr_packet.py has no direct ties to bsr, just sends a raw packet out
# a given interface, so just reuse it
script_path = os.path.join(CWD, "send_bsr_packet.py")
node = tgen.net[senderRouter]
cmd = [
python3_path,
script_path,
packet,
senderInterface,
"--interval=1",
"--count=1",
]
logger.info("Scapy cmd: \n %s", cmd)
node.cmd_raises(cmd)
logger.debug("Exiting lib API: scapy_send_autorp_raw_packet")
return True
def find_rp_from_bsrp_info(tgen, dut, bsr, grp=None):
"""
Find which RP is having lowest prioriy and returns rp IP

View file

@ -1 +1 @@
{"totalGroups":5,"watermarkLimit":0,"l1-i1-eth1":{"name":"l1-i1-eth1","state":"up","address":"10.0.8.2","index":"*","flagMulticast":true,"flagBroadcast":true,"lanDelayEnabled":true,"groups":[{"group":"225.1.1.2","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.1","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.3","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.5","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.4","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]}]}}
{"watermarkLimit":0,"l1-i1-eth1":{"name":"l1-i1-eth1","state":"up","address":"10.0.8.2","index":"*","flagMulticast":true,"flagBroadcast":true,"lanDelayEnabled":true,"groups":[{"group":"225.1.1.2","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.1","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.3","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.5","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.4","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]}]}}

View file

@ -1,5 +1,4 @@
{
"totalGroups":5,
"watermarkLimit":0,
"l1-i1-eth1":{
"name":"l1-i1-eth1",
@ -48,4 +47,3 @@
]
}
}

View file

@ -1 +1 @@
{"totalGroups":5,"watermarkLimit":0,"l1-i1-eth1":{"name":"l1-i1-eth1","state":"up","address":"10.0.8.2","index":"*","flagMulticast":true,"flagBroadcast":true,"lanDelayEnabled":true,"groups":[{"group":"225.1.1.1","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.2","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.3","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.4","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.5","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]}]}}
{"watermarkLimit":0,"l1-i1-eth1":{"name":"l1-i1-eth1","state":"up","address":"10.0.8.2","index":"*","flagMulticast":true,"flagBroadcast":true,"lanDelayEnabled":true,"groups":[{"group":"225.1.1.1","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.2","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.3","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.4","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.5","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]}]}}

View file

@ -1,5 +1,4 @@
{
"totalGroups":5,
"watermarkLimit":0,
"l1-i1-eth1":{
"name":"l1-i1-eth1",

View file

@ -1 +1 @@
{"totalGroups":5,"watermarkLimit":0,"l1-i1-eth1":{"name":"l1-i1-eth1","state":"up","address":"10.0.8.2","index":"*","flagMulticast":true,"flagBroadcast":true,"lanDelayEnabled":true,"groups":[{"group":"225.1.1.5","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]}]}}
{"watermarkLimit":0,"l1-i1-eth1":{"name":"l1-i1-eth1","state":"up","address":"10.0.8.2","index":"*","flagMulticast":true,"flagBroadcast":true,"lanDelayEnabled":true,"groups":[{"group":"225.1.1.5","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]}]}}

View file

View file

@ -0,0 +1,16 @@
!
hostname r1
password zebra
log file /tmp/r1-frr.log
debug pim autorp
!
interface r1-eth0
ip address 10.10.76.1/24
ip igmp
ip pim
!
ip forwarding
!
router pim
autorp discovery
!

View file

@ -0,0 +1,16 @@
!
hostname r2
password zebra
log file /tmp/r2-frr.log
debug pim autorp
!
interface r2-eth0
ip address 10.10.76.2/24
ip igmp
ip pim
!
ip forwarding
!
router pim
autorp discovery
!

View file

@ -0,0 +1,207 @@
#!/usr/bin/env python
# SPDX-License-Identifier: ISC
#
# test_pim_autorp.py
#
# Copyright (c) 2024 ATCorp
# Nathan Bahr
#
import os
import sys
import pytest
# pylint: disable=C0413
# Import topogen and topotest helpers
from lib.topogen import Topogen, get_topogen
from lib.topolog import logger
from lib.pim import scapy_send_autorp_raw_packet, verify_pim_rp_info, verify_pim_rp_info_is_empty
from lib.common_config import step, write_test_header
from time import sleep
"""
test_pim_autorp.py: Test general PIM AutoRP functionality
"""
TOPOLOGY = """
Basic AutoRP functionality
+---+---+ +---+---+
| | 10.10.76.0/24 | |
+ R1 + <------------------> + R2 |
| | .1 .2 | |
+---+---+ +---+---+
"""
# Save the Current Working Directory to find configuration files.
CWD = os.path.dirname(os.path.realpath(__file__))
sys.path.append(os.path.join(CWD, "../"))
# Required to instantiate the topology builder class.
pytestmark = [pytest.mark.pimd]
def build_topo(tgen):
"Build function"
# Create routers
tgen.add_router("r1")
tgen.add_router("r2")
# Create link between router 1 and 2
switch = tgen.add_switch("s1-2")
switch.add_link(tgen.gears["r1"])
switch.add_link(tgen.gears["r2"])
def setup_module(mod):
logger.info("PIM AutoRP basic functionality:\n {}".format(TOPOLOGY))
tgen = Topogen(build_topo, mod.__name__)
tgen.start_topology()
# Router 1 will be the router configured with "fake" autorp configuration, so give it a default route
# to router 2 so that routing to the RP address is not an issue
# r1_defrt_setup_cmds = [
# "ip route add default via 10.10.76.1 dev r1-eth0",
# ]
# for cmd in r1_defrt_setup_cmds:
# tgen.net["r1"].cmd(cmd)
logger.info("Testing PIM AutoRP support")
router_list = tgen.routers()
for rname, router in router_list.items():
logger.info("Loading router %s" % rname)
router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname)))
# Initialize all routers.
tgen.start_router()
for router in router_list.values():
if router.has_version("<", "4.0"):
tgen.set_error("unsupported version")
def teardown_module(mod):
"Teardown the pytest environment"
tgen = get_topogen()
tgen.stop_topology()
def test_pim_autorp_discovery_single_rp(request):
"Test PIM AutoRP Discovery with single RP"
tgen = get_topogen()
tc_name = request.node.name
write_test_header(tc_name)
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
step("Start with no RP configuration")
result = verify_pim_rp_info_is_empty(tgen, "r1")
assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
step("Send AutoRP packet from r1 to r2")
# 1 RP(s), hold time 5 secs, 10.10.76.1, group(s) 224.0.0.0/4
data = "01005e00012800127f55cfb1080045c00030700c000008110abe0a0a4c01e000012801f001f0001c798b12010005000000000a0a4c0103010004e0000000"
scapy_send_autorp_raw_packet(tgen, "r1", "r1-eth0", data)
step("Verify rp-info from AutoRP packet")
result = verify_pim_rp_info(tgen, None, "r2", "224.0.0.0/4", "r2-eth0", "10.10.76.1", "AutoRP", False, "ipv4", True)
assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
step("Verify AutoRP configuration times out")
result = verify_pim_rp_info_is_empty(tgen, "r2")
assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
def test_pim_autorp_discovery_multiple_rp(request):
"Test PIM AutoRP Discovery with multiple RP's"
tgen = get_topogen()
tc_name = request.node.name
write_test_header(tc_name)
if tgen.routers_have_failure():
pytest.skip("skipped because of router(s) failure")
step("Start with no RP configuration")
result = verify_pim_rp_info_is_empty(tgen, "r2")
assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
step("Send AutoRP packet from r1 to r2")
# 2 RP(s), hold time 5 secs, 10.10.76.1, group(s) 224.0.0.0/8, 10.10.76.3, group(s) 225.0.0.0/8
data = "01005e00012800127f55cfb1080045c0003c700c000008110ab20a0a4c01e000012801f001f000283f5712020005000000000a0a4c0103010008e00000000a0a4c0303010008e1000000"
scapy_send_autorp_raw_packet(tgen, "r1", "r1-eth0", data)
step("Verify rp-info from AutoRP packet")
result = verify_pim_rp_info(tgen, None, "r2", "224.0.0.0/8", "r2-eth0", "10.10.76.1", "AutoRP", False, "ipv4", True)
assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
result = verify_pim_rp_info(tgen, None, "r2", "225.0.0.0/8", "r2-eth0", "10.10.76.3", "AutoRP", False, "ipv4", True)
assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
def test_pim_autorp_discovery_static(request):
"Test PIM AutoRP Discovery with Static RP"
tgen = get_topogen()
tc_name = request.node.name
write_test_header(tc_name)
if tgen.routers_have_failure():
pytest.skip("skipped because of router(s) failure")
step("Start with no RP configuration")
result = verify_pim_rp_info_is_empty(tgen, "r2")
assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
step("Add static RP configuration to r2")
rnode = tgen.routers()["r2"]
rnode.cmd("vtysh -c 'conf t' -c 'router pim' -c 'rp 10.10.76.3 224.0.0.0/4'")
step("Verify static rp-info from r2")
result = verify_pim_rp_info(tgen, None, "r2", "224.0.0.0/4", "r2-eth0", "10.10.76.3", "Static", False, "ipv4", True)
assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
step("Send AutoRP packet from r1 to r2")
# 1 RP(s), hold time 5 secs, 10.10.76.1, group(s) 224.0.0.0/4
data = "01005e00012800127f55cfb1080045c00030700c000008110abe0a0a4c01e000012801f001f0001c798b12010005000000000a0a4c0103010004e0000000"
scapy_send_autorp_raw_packet(tgen, "r1", "r1-eth0", data)
step("Verify rp-info from AutoRP packet")
result = verify_pim_rp_info(tgen, None, "r2", "224.0.0.0/4", "r2-eth0", "10.10.76.1", "AutoRP", False, "ipv4", True)
assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
def test_pim_autorp_announce_group(request):
"Test PIM AutoRP Announcement with a single group"
tgen = get_topogen()
tc_name = request.node.name
write_test_header(tc_name)
if tgen.routers_have_failure():
pytest.skip("skipped because of router(s) failure")
step("Add candidate RP configuration to r1")
rnode = tgen.routers()["r1"]
rnode.cmd("vtysh -c 'conf t' -c 'router pim' -c 'send-rp-announce 10.10.76.1 224.0.0.0/4'")
step("Verify Announcement sent data")
# TODO: Verify AutoRP mapping agent receives candidate RP announcement
# Mapping agent is not yet implemented
#sleep(10)
step("Change AutoRP Announcement packet parameters")
rnode.cmd("vtysh -c 'conf t' -c 'router pim' -c 'send-rp-announce scope 8 interval 10 holdtime 60'")
step("Verify Announcement sent data")
# TODO: Verify AutoRP mapping agent receives updated candidate RP announcement
# Mapping agent is not yet implemented
#sleep(10)
def test_memory_leak():
"Run the memory leak test and report results."
tgen = get_topogen()
if not tgen.is_memleak_enabled():
pytest.skip("Memory leak test/report is disabled")
tgen.report_memory_leaks()
if __name__ == "__main__":
args = ["-s"] + sys.argv[1:]
sys.exit(pytest.main(args))

View file

@ -111,6 +111,70 @@ module frr-pim-rp {
} // static-rp
} // static-rp-container
grouping auto-rp-container {
description
"Grouping of AutoRP container.";
container auto-rp {
description
"Containing AutoRP attributes.";
leaf discovery-enabled {
type boolean;
description
"Flag indicating if Auto RP discovery is enabled.";
}
leaf announce-scope {
type uint8;
description
"The TTL of the C-RP Announcement packet.";
}
leaf announce-interval {
type uint16;
description
"The time between sending C-RP announcement packets.";
}
leaf announce-holdtime {
type uint16;
description
"The hold time in seconds advertised in the announcement packet.";
}
list candidate-rp-list {
key "rp-address";
description
"A list of Candidate RP addresses.";
leaf rp-address {
type inet:ip-address;
description
"Specifies a candidate RP address.";
}
choice group-or-prefix-list {
description "Use group or prefix-list";
case group {
leaf group {
type frr-route-types:ip-multicast-group-prefix;
description
"Multicast group prefix.";
}
}
case prefix-list {
leaf prefix-list {
type plist-ref;
description
"Group prefix-list filter";
}
}
}
} // candidate-rp-list
} // auto-rp
} // auto-rp-container
/*
* Configuration data nodes
*/
@ -123,6 +187,13 @@ module frr-pim-rp {
description
"PIM RP configuration data.";
uses static-rp-container;
uses auto-rp-container {
when "../frr-pim:address-family = 'frr-rt:ipv4'" {
description
"Only applicable to IPv4 address family.";
}
}
} // rp
} // augment
}