bgpd, lib: add mplsL3VpnVrf table

Add SNMP support for L3vpn Vrf table as defined in [RFC4382]
Keep track of vrf status for the table and for future traps.

Signed-off-by: Pat Ruddy <pat@voltanet.io>
This commit is contained in:
Pat Ruddy 2020-09-28 16:35:35 +01:00
parent aa53f69348
commit 0d020cd6d9
8 changed files with 407 additions and 23 deletions

View file

@ -41,8 +41,6 @@
#define BGP_mplsvpn_notif_enable_true 1
#define BGP_mplsvpn_notif_enable_false 2
static uint8_t bgp_mplsvpn_notif_enable = SNMP_FALSE;
/* MPLSL3VPN MIB described in RFC4382 */
#define MPLSL3VPNMIB 1, 3, 6, 1, 2, 1, 10, 166, 11
@ -55,6 +53,22 @@ static uint8_t bgp_mplsvpn_notif_enable = SNMP_FALSE;
#define MPLSL3VPNVRFCONFRTEMXTHRSHTIME 6
#define MPLSL3VPNILLLBLRCVTHRSH 7
/* MPLSL3VPN VRF Table */
#define MPLSL3VPNVRFVPNID 1
#define MPLSL3VPNVRFDESC 2
#define MPLSL3VPNVRFRD 3
#define MPLSL3VPNVRFCREATIONTIME 4
#define MPLSL3VPNVRFOPERSTATUS 5
#define MPLSL3VPNVRFACTIVEINTERFACES 6
#define MPLSL3VPNVRFASSOCIATEDINTERFACES 7
#define MPLSL3VPNVRFCONFMIDRTETHRESH 8
#define MPLSL3VPNVRFCONFHIGHRTETHRSH 9
#define MPLSL3VPNVRFCONFMAXROUTES 10
#define MPLSL3VPNVRFCONFLASTCHANGED 11
#define MPLSL3VPNVRFCONFROWSTATUS 12
#define MPLSL3VPNVRFCONFADMINSTATUS 13
#define MPLSL3VPNVRFCONFSTORAGETYPE 14
/* SNMP value hack. */
#define INTEGER ASN_INTEGER
#define INTEGER32 ASN_INTEGER
@ -62,12 +76,15 @@ static uint8_t bgp_mplsvpn_notif_enable = SNMP_FALSE;
#define OCTET_STRING ASN_OCTET_STR
#define IPADDRESS ASN_IPADDRESS
#define GAUGE32 ASN_UNSIGNED
#define TIMETICKS ASN_TIMETICKS
/* Declare static local variables for convenience. */
SNMP_LOCAL_VARIABLES
/* BGP-MPLS-MIB innstances */
static oid mpls_l3vpn_oid[] = {MPLSL3VPNMIB};
static char rd_buf[RD_ADDRSTRLEN];
static uint8_t bgp_mplsvpn_notif_enable = SNMP_FALSE;
static uint8_t *mplsL3vpnConfiguredVrfs(struct variable *, oid[], size_t *, int,
size_t *, WriteMethod **);
@ -91,6 +108,10 @@ static uint8_t *mplsL3vpnVrfConfRteMxThrshTime(struct variable *, oid[],
static uint8_t *mplsL3vpnIllLblRcvThrsh(struct variable *, oid[], size_t *, int,
size_t *, WriteMethod **);
static uint8_t *mplsL3vpnVrfTable(struct variable *, oid[], size_t *, int,
size_t *, WriteMethod **);
static struct variable mpls_l3vpn_variables[] = {
/* BGP version. */
{MPLSL3VPNCONFIGUREDVRFS,
@ -136,8 +157,168 @@ static struct variable mpls_l3vpn_variables[] = {
3,
{1, 1, 7} },
/* Vrf Table */
{MPLSL3VPNVRFVPNID,
OCTET_STRING,
RONLY,
mplsL3vpnVrfTable,
5,
{1, 2, 2, 1, 2} },
{MPLSL3VPNVRFDESC,
OCTET_STRING,
RONLY,
mplsL3vpnVrfTable,
5,
{1, 2, 2, 1, 3} },
{MPLSL3VPNVRFRD,
OCTET_STRING,
RONLY,
mplsL3vpnVrfTable,
5,
{1, 2, 2, 1, 4} },
{MPLSL3VPNVRFCREATIONTIME,
TIMETICKS,
RONLY,
mplsL3vpnVrfTable,
5,
{1, 2, 2, 1, 5} },
{MPLSL3VPNVRFOPERSTATUS,
INTEGER,
RONLY,
mplsL3vpnVrfTable,
5,
{1, 2, 2, 1, 6} },
{MPLSL3VPNVRFACTIVEINTERFACES,
GAUGE32,
RONLY,
mplsL3vpnVrfTable,
5,
{1, 2, 2, 1, 7} },
{MPLSL3VPNVRFASSOCIATEDINTERFACES,
GAUGE32,
RONLY,
mplsL3vpnVrfTable,
5,
{1, 2, 2, 1, 8} },
{MPLSL3VPNVRFCONFMIDRTETHRESH,
GAUGE32,
RONLY,
mplsL3vpnVrfTable,
5,
{1, 2, 2, 1, 9} },
{MPLSL3VPNVRFCONFHIGHRTETHRSH,
GAUGE32,
RONLY,
mplsL3vpnVrfTable,
5,
{1, 2, 2, 1, 10} },
{MPLSL3VPNVRFCONFMAXROUTES,
GAUGE32,
RONLY,
mplsL3vpnVrfTable,
5,
{1, 2, 2, 1, 11} },
{MPLSL3VPNVRFCONFLASTCHANGED,
TIMETICKS,
RONLY,
mplsL3vpnVrfTable,
5,
{1, 2, 2, 1, 12} },
{MPLSL3VPNVRFCONFROWSTATUS,
INTEGER,
RONLY,
mplsL3vpnVrfTable,
5,
{1, 2, 2, 1, 13} },
{MPLSL3VPNVRFCONFADMINSTATUS,
INTEGER,
RONLY,
mplsL3vpnVrfTable,
5,
{1, 2, 2, 1, 14} },
{MPLSL3VPNVRFCONFSTORAGETYPE,
INTEGER,
RONLY,
mplsL3vpnVrfTable,
5,
{1, 2, 2, 1, 15} },
};
/* timeticks are in hundredths of a second */
static void bgp_mpls_l3vpn_update_timeticks(time_t *counter)
{
struct timeval tv;
monotime(&tv);
*counter = (tv.tv_sec * 100) + (tv.tv_usec / 10000);
}
static int bgp_mpls_l3vpn_update_last_changed(struct bgp *bgp)
{
if (bgp->snmp_stats)
bgp_mpls_l3vpn_update_timeticks(
&(bgp->snmp_stats->modify_time));
return 0;
}
static int bgp_init_snmp_stats(struct bgp *bgp)
{
if (is_bgp_vrf_mplsvpn(bgp)) {
if (bgp->snmp_stats == NULL) {
bgp->snmp_stats = XCALLOC(
MTYPE_BGP, sizeof(struct bgp_snmp_stats));
/* fix up added routes */
if (bgp->snmp_stats)
bgp_mpls_l3vpn_update_timeticks(
&(bgp->snmp_stats->creation_time));
}
} else {
if (bgp->snmp_stats) {
XFREE(MTYPE_BGP, bgp->snmp_stats);
bgp->snmp_stats = NULL;
}
}
/* Something changed - update the timestamp */
bgp_mpls_l3vpn_update_last_changed(bgp);
return 0;
}
static bool is_bgp_vrf_active(struct bgp *bgp)
{
struct vrf *vrf;
struct interface *ifp;
/* if there is one interface in the vrf which is up then it is deemed
* active
*/
vrf = vrf_lookup_by_id(bgp->vrf_id);
if (vrf == NULL)
return false;
RB_FOREACH (ifp, if_name_head, &vrf->ifaces_by_name) {
/* if we are in a vrf skip the l3mdev */
if (bgp->name && strncmp(ifp->name, bgp->name, VRF_NAMSIZ) == 0)
continue;
if (if_is_up(ifp))
return true;
}
return false;
}
static int bgp_vrf_check_update_active(struct bgp *bgp, struct interface *ifp)
{
bool new_active = false;
if (!is_bgp_vrf_mplsvpn(bgp) || bgp->snmp_stats == NULL)
return 0;
new_active = is_bgp_vrf_active(bgp);
if (bgp->snmp_stats->active != new_active) {
/* add trap in here */
bgp->snmp_stats->active = new_active;
}
return 0;
}
static uint8_t *mplsL3vpnConfiguredVrfs(struct variable *v, oid name[],
size_t *length, int exact,
size_t *var_len,
@ -211,7 +392,6 @@ static int write_mplsL3vpnNotificationEnable(int action, uint8_t *var_val,
{
uint32_t intval;
zlog_debug("PJDR: %s", __func__);
if (var_val_type != ASN_INTEGER) {
return SNMP_ERR_WRONGTYPE;
}
@ -234,7 +414,6 @@ static uint8_t *mplsL3vpnNotificationEnable(struct variable *v, oid name[],
== MATCH_FAILED)
return NULL;
zlog_debug("PJDR: %s", __func__);
*write_method = write_mplsL3vpnNotificationEnable;
return SNMP_INTEGER(bgp_mplsvpn_notif_enable);
}
@ -276,8 +455,149 @@ static uint8_t *mplsL3vpnIllLblRcvThrsh(struct variable *v, oid name[],
}
/* 1.3.6.1.2.1.10.166.11.1.2.2.1.x = 14*/
#define VRFTAB_NAMELEN 14
static struct bgp *bgp_lookup_by_name_next(const char *vrf_name)
{
struct bgp *bgp, *bgp_next = NULL;
struct listnode *node, *nnode;
bool first = false;
/*
* the vrfs are not stored alphabetically but since we are using the
* vrf name as an index we need the getnext function to return them
* in a atrict order. Thus run through and find the best next one.
*/
for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) {
if (!is_bgp_vrf_mplsvpn(bgp))
continue;
if (strnlen(vrf_name, VRF_NAMSIZ) == 0 && bgp_next == NULL) {
first = true;
bgp_next = bgp;
continue;
}
if (first || strncmp(bgp->name, vrf_name, VRF_NAMSIZ) > 0) {
if (bgp_next == NULL)
bgp_next = bgp;
else if (strncmp(bgp->name, bgp_next->name, VRF_NAMSIZ)
< 0)
bgp_next = bgp;
}
}
return bgp_next;
}
static struct bgp *bgpL3vpnTable_lookup(struct variable *v, oid name[],
size_t *length, char *vrf_name,
int exact)
{
struct bgp *bgp = NULL;
size_t namelen = v ? v->namelen : VRFTAB_NAMELEN;
int len;
if (*length - namelen > VRF_NAMSIZ)
return NULL;
oid2string(name + namelen, *length - namelen, vrf_name);
if (exact) {
/* Check the length. */
bgp = bgp_lookup_by_name(vrf_name);
if (bgp && !is_bgp_vrf_mplsvpn(bgp))
return NULL;
} else {
bgp = bgp_lookup_by_name_next(vrf_name);
if (bgp == NULL)
return NULL;
len = strnlen(bgp->name, VRF_NAMSIZ);
oid_copy_str(name + namelen, bgp->name, len);
*length = len + namelen;
}
return bgp;
}
static uint8_t *mplsL3vpnVrfTable(struct variable *v, oid name[],
size_t *length, int exact, size_t *var_len,
WriteMethod **write_method)
{
char vrf_name[VRF_NAMSIZ];
struct bgp *l3vpn_bgp;
if (smux_header_table(v, name, length, exact, var_len, write_method)
== MATCH_FAILED)
return NULL;
memset(vrf_name, 0, VRF_NAMSIZ);
l3vpn_bgp = bgpL3vpnTable_lookup(v, name, length, vrf_name, exact);
if (!l3vpn_bgp)
return NULL;
switch (v->magic) {
case MPLSL3VPNVRFVPNID:
*var_len = 0;
return NULL;
case MPLSL3VPNVRFDESC:
*var_len = strnlen(l3vpn_bgp->name, VRF_NAMSIZ);
return (uint8_t *)l3vpn_bgp->name;
case MPLSL3VPNVRFRD:
/*
* this is a horror show but the MIB dicates one RD per vrf
* and not one RD per AFI as we (FRR) have. So this little gem
* returns the V4 one if it's set OR the v6 one if it's set or
* zero-length string id neither are set
*/
memset(rd_buf, 0, RD_ADDRSTRLEN);
if (CHECK_FLAG(l3vpn_bgp->vpn_policy[AFI_IP].flags,
BGP_VPN_POLICY_TOVPN_RD_SET))
prefix_rd2str(&l3vpn_bgp->vpn_policy[AFI_IP].tovpn_rd,
rd_buf, sizeof(rd_buf));
else if (CHECK_FLAG(l3vpn_bgp->vpn_policy[AFI_IP6].flags,
BGP_VPN_POLICY_TOVPN_RD_SET))
prefix_rd2str(&l3vpn_bgp->vpn_policy[AFI_IP6].tovpn_rd,
rd_buf, sizeof(rd_buf));
*var_len = strnlen(rd_buf, RD_ADDRSTRLEN);
return (uint8_t *)rd_buf;
case MPLSL3VPNVRFCREATIONTIME:
return SNMP_INTEGER(
(uint32_t)l3vpn_bgp->snmp_stats->creation_time);
case MPLSL3VPNVRFOPERSTATUS:
if (l3vpn_bgp->snmp_stats->active)
return SNMP_INTEGER(1);
else
return SNMP_INTEGER(2);
case MPLSL3VPNVRFACTIVEINTERFACES:
return SNMP_INTEGER(bgp_vrf_interfaces(l3vpn_bgp, true));
case MPLSL3VPNVRFASSOCIATEDINTERFACES:
return SNMP_INTEGER(bgp_vrf_interfaces(l3vpn_bgp, false));
case MPLSL3VPNVRFCONFMIDRTETHRESH:
return SNMP_INTEGER(0);
case MPLSL3VPNVRFCONFHIGHRTETHRSH:
return SNMP_INTEGER(0);
case MPLSL3VPNVRFCONFMAXROUTES:
return SNMP_INTEGER(0);
case MPLSL3VPNVRFCONFLASTCHANGED:
return SNMP_INTEGER(
(uint32_t)l3vpn_bgp->snmp_stats->modify_time);
case MPLSL3VPNVRFCONFROWSTATUS:
return SNMP_INTEGER(1);
case MPLSL3VPNVRFCONFADMINSTATUS:
return SNMP_INTEGER(1);
case MPLSL3VPNVRFCONFSTORAGETYPE:
return SNMP_INTEGER(2);
return NULL;
}
return NULL;
}
void bgp_mpls_l3vpn_module_init(void)
{
hook_register(bgp_vrf_status_changed, bgp_vrf_check_update_active);
hook_register(bgp_snmp_init_stats, bgp_init_snmp_stats);
hook_register(bgp_snmp_update_last_changed,
bgp_mpls_l3vpn_update_last_changed);
REGISTER_MIB("mplsL3VpnMIB", mpls_l3vpn_variables, variable,
mpls_l3vpn_oid);
}

View file

@ -32,6 +32,8 @@
#include "bgpd/bgp_io.h"
#include "bgpd/bgp_damp.h"
DEFINE_HOOK(bgp_snmp_init_stats, (struct bgp *bgp), (bgp))
FRR_CFG_DEFAULT_ULONG(BGP_CONNECT_RETRY,
{ .val_ulong = 10, .match_profile = "datacenter", },
{ .val_ulong = 120 },
@ -9862,6 +9864,7 @@ static int bgp_global_afi_safi_ip_unicast_vpn_config_import_export_vpn_modify(
UNSET_FLAG(bgp->af_flags[afi][safi], flag);
}
hook_call(bgp_snmp_init_stats, bgp);
return NB_OK;
}

View file

@ -127,6 +127,7 @@ FRR_CFG_DEFAULT_BOOL(BGP_SUPPRESS_DUPLICATES,
DEFINE_HOOK(bgp_inst_config_write,
(struct bgp *bgp, struct vty *vty),
(bgp, vty))
DEFINE_HOOK(bgp_snmp_update_last_changed, (struct bgp *bgp), (bgp))
#define GR_NO_OPER \
"The Graceful Restart No Operation was executed as cmd same as previous one."
@ -9141,6 +9142,7 @@ DEFPY (af_label_vpn_export,
vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, afi,
bgp_get_default(), bgp);
hook_call(bgp_snmp_update_last_changed, bgp);
return CMD_SUCCESS;
}

View file

@ -67,6 +67,10 @@
/* All information about zebra. */
struct zclient *zclient = NULL;
/* hook to indicate vrf status change for SNMP */
DEFINE_HOOK(bgp_vrf_status_changed, (struct bgp *bgp, struct interface *ifp),
(bgp, ifp))
/* Can we install into zebra? */
static inline bool bgp_install_info_to_zebra(struct bgp *bgp)
{
@ -212,8 +216,10 @@ static int bgp_ifp_destroy(struct interface *ifp)
if (BGP_DEBUG(zebra, ZEBRA))
zlog_debug("Rx Intf del VRF %u IF %s", ifp->vrf_id, ifp->name);
if (bgp)
if (bgp) {
bgp_update_interface_nbrs(bgp, ifp, NULL);
hook_call(bgp_vrf_status_changed, bgp, ifp);
}
bgp_mac_del_mac_entry(ifp);
@ -243,6 +249,7 @@ static int bgp_ifp_up(struct interface *ifp)
for (ALL_LIST_ELEMENTS(ifp->nbr_connected, node, nnode, nc))
bgp_nbr_connected_add(bgp, nc);
hook_call(bgp_vrf_status_changed, bgp, ifp);
return 0;
}
@ -297,6 +304,7 @@ static int bgp_ifp_down(struct interface *ifp)
}
}
hook_call(bgp_vrf_status_changed, bgp, ifp);
return 0;
}
@ -461,6 +469,8 @@ static int bgp_interface_vrf_update(ZAPI_CALLBACK_ARGS)
for (ALL_LIST_ELEMENTS(ifp->nbr_connected, node, nnode, nc))
bgp_nbr_connected_add(bgp, nc);
hook_call(bgp_vrf_status_changed, bgp, ifp);
return 0;
}
@ -2963,6 +2973,7 @@ static int bgp_ifp_create(struct interface *ifp)
bgp_mac_add_mac_entry(ifp);
bgp_update_interface_nbrs(bgp, ifp, ifp);
hook_call(bgp_vrf_status_changed, bgp, ifp);
return 0;
}

View file

@ -3704,6 +3704,7 @@ void bgp_free(struct bgp *bgp)
XFREE(MTYPE_BGP, bgp->name);
XFREE(MTYPE_BGP, bgp->name_pretty);
XFREE(MTYPE_BGP, bgp->snmp_stats);
XFREE(MTYPE_BGP, bgp);
}

View file

@ -310,6 +310,13 @@ enum bgp_link_bw_handling {
RB_HEAD(bgp_es_vrf_rb_head, bgp_evpn_es_vrf);
RB_PROTOTYPE(bgp_es_vrf_rb_head, bgp_evpn_es_vrf, rb_node, bgp_es_vrf_rb_cmp);
struct bgp_snmp_stats {
/* SNMP variables for mplsL3Vpn*/
time_t creation_time;
time_t modify_time;
bool active;
};
/* BGP instance structure. */
struct bgp {
/* AS number of this BGP instance. */
@ -363,6 +370,8 @@ struct bgp {
uint32_t subgrps_deleted;
} update_group_stats;
struct bgp_snmp_stats *snmp_stats;
/* BGP configuration. */
uint16_t config;
#define BGP_CONFIG_CLUSTER_ID (1 << 0)
@ -2165,23 +2174,6 @@ static inline int afindex(afi_t afi, safi_t safi)
}
}
static inline bool is_bgp_vrf_active(struct bgp *bgp)
{
struct vrf *vrf;
struct interface *ifp;
/* if there is one interface in the vrf which is up then it is deemed
* active
*/
vrf = vrf_lookup_by_name(bgp->name);
if (vrf == NULL)
return false;
RB_FOREACH (ifp, if_name_head, &vrf->ifaces_by_name)
if (if_is_up(ifp))
return true;
return false;
}
/* If the peer is not a peer-group but is bound to a peer-group return 1 */
static inline int peer_group_active(struct peer *peer)
{
@ -2263,6 +2255,27 @@ static inline struct vrf *bgp_vrf_lookup_by_instance_type(struct bgp *bgp)
return vrf;
}
static inline uint32_t bgp_vrf_interfaces(struct bgp *bgp, bool active)
{
struct vrf *vrf;
struct interface *ifp;
uint32_t count = 0;
/* if there is one interface in the vrf which is up then it is deemed
* active
*/
vrf = bgp_vrf_lookup_by_instance_type(bgp);
if (vrf == NULL)
return 0;
RB_FOREACH (ifp, if_name_head, &vrf->ifaces_by_name) {
if (strncmp(ifp->name, bgp->name, VRF_NAMSIZ) == 0)
continue;
if (!active || if_is_up(ifp))
count++;
}
return count;
}
/* Link BGP instance to VRF. */
static inline void bgp_vrf_link(struct bgp *bgp, struct vrf *vrf)
{
@ -2300,7 +2313,11 @@ extern int bgp_lookup_by_as_name_type(struct bgp **bgp_val, as_t *as,
enum bgp_instance_type inst_type);
/* Hooks */
DECLARE_HOOK(peer_status_changed, (struct peer * peer), (peer))
DECLARE_HOOK(bgp_vrf_status_changed, (struct bgp *bgp, struct interface *ifp),
(bgp, ifp))
DECLARE_HOOK(peer_status_changed, (struct peer *peer), (peer))
DECLARE_HOOK(bgp_snmp_init_stats, (struct bgp *bgp), (bgp))
DECLARE_HOOK(bgp_snmp_update_last_changed, (struct bgp *bgp), (bgp))
void peer_nsf_stop(struct peer *peer);
#endif /* _QUAGGA_BGPD_H */

View file

@ -106,6 +106,8 @@ extern int oid_compare(const oid *, int, const oid *, int);
extern void oid2in_addr(oid[], int, struct in_addr *);
extern void *oid_copy(void *, const void *, size_t);
extern void oid_copy_addr(oid[], const struct in_addr *, int);
extern void oid2string(oid oid[], int len, char *string);
extern void oid_copy_str(oid oid[], const char *string, int len);
#ifdef __cplusplus
}

View file

@ -78,6 +78,34 @@ void oid_copy_addr(oid oid[], const struct in_addr *addr, int len)
oid[i] = *pnt++;
}
void oid2string(oid oid[], int len, char *string)
{
int i;
uint8_t *pnt;
if (len == 0)
return;
pnt = (uint8_t *)string;
for (i = 0; i < len; i++)
*pnt++ = oid[i];
}
void oid_copy_str(oid oid[], const char *string, int len)
{
int i;
const uint8_t *pnt;
if (len == 0)
return;
pnt = (uint8_t *)string;
for (i = 0; i < len; i++)
oid[i] = *pnt++;
}
int smux_header_generic(struct variable *v, oid *name, size_t *length,
int exact, size_t *var_len, WriteMethod **write_method)
{