frr/bgpd/bgp_mplsvpn_snmp.c

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1683 lines
41 KiB
C
Raw Normal View History

// SPDX-License-Identifier: GPL-2.0-or-later
/* MPLS/BGP L3VPN MIB
* Copyright (C) 2020 Volta Networks Inc
*/
#include <zebra.h>
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include "if.h"
#include "log.h"
#include "prefix.h"
#include "command.h"
#include "thread.h"
#include "smux.h"
#include "filter.h"
#include "hook.h"
#include "libfrr.h"
#include "lib/version.h"
#include "bgpd/bgpd.h"
#include "bgpd/bgp_route.h"
#include "bgpd/bgp_attr.h"
#include "bgpd/bgp_ecommunity.h"
#include "bgpd/bgp_mplsvpn.h"
#include "bgpd/bgp_mplsvpn_snmp.h"
#define BGP_mplsvpn_notif_enable_true 1
#define BGP_mplsvpn_notif_enable_false 2
/* MPLSL3VPN MIB described in RFC4382 */
#define MPLSL3VPNMIB 1, 3, 6, 1, 2, 1, 10, 166, 11
/* MPLSL3VPN Scalars */
#define MPLSL3VPNCONFIGUREDVRFS 1
#define MPLSL3VPNACTIVEVRFS 2
#define MPLSL3VPNCONNECTEDINTERFACES 3
#define MPLSL3VPNNOTIFICATIONENABLE 4
#define MPLSL3VPNCONFMAXPOSSRTS 5
#define MPLSL3VPNVRFCONFRTEMXTHRSHTIME 6
#define MPLSL3VPNILLLBLRCVTHRSH 7
/* MPLSL3VPN IFConf Table */
#define MPLSL3VPNIFVPNCLASSIFICATION 1
#define MPLSL3VPNIFCONFSTORAGETYPE 2
#define MPLSL3VPNIFCONFROWSTATUS 3
/* 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
/* MPLSL3VPN RT Table */
#define MPLSL3VPNVRFRT 1
#define MPLSL3VPNVRFRTDESCR 2
#define MPLSL3VPNVRFRTROWSTATUS 3
#define MPLSL3VPNVRFRTSTORAGETYPE 4
/* MPLSL3VPN PERF Table */
#define MPLSL3VPNVRFPERFROUTESADDED 1
#define MPLSL3VPNVRFPERFROUTESDELETED 2
#define MPLSL3VPNVRFPERFCURRNUMROUTES 3
/* MPLSL3VPN RTE Table */
#define MPLSL3VPNVRFRTEINETCIDRDESTTYPE 1
#define MPLSL3VPNVRFRTEINETCIDRDEST 2
#define MPLSL3VPNVRFRTEINETCIDRPFXLEN 3
#define MPLSL3VPNVRFRTEINETCIDRPOLICY 4
#define MPLSL3VPNVRFRTEINETCIDRNHOPTYPE 5
#define MPLSL3VPNVRFRTEINETCIDRNEXTHOP 6
#define MPLSL3VPNVRFRTEINETCIDRIFINDEX 7
#define MPLSL3VPNVRFRTEINETCIDRTYPE 8
#define MPLSL3VPNVRFRTEINETCIDRPROTO 9
#define MPLSL3VPNVRFRTEINETCIDRAGE 10
#define MPLSL3VPNVRFRTEINETCIDRNEXTHOPAS 11
#define MPLSL3VPNVRFRTEINETCIDRMETRIC1 12
#define MPLSL3VPNVRFRTEINETCIDRMETRIC2 13
#define MPLSL3VPNVRFRTEINETCIDRMETRIC3 14
#define MPLSL3VPNVRFRTEINETCIDRMETRIC4 15
#define MPLSL3VPNVRFRTEINETCIDRMETRIC5 16
#define MPLSL3VPNVRFRTEINETCIDRXCPOINTER 17
#define MPLSL3VPNVRFRTEINETCIDRSTATUS 18
/* BGP Trap */
#define MPLSL3VPNVRFUP 1
#define MPLSL3VPNDOWN 2
/* SNMP value hack. */
#define INTEGER ASN_INTEGER
#define INTEGER32 ASN_INTEGER
#define COUNTER32 ASN_COUNTER
#define OCTET_STRING ASN_OCTET_STR
#define IPADDRESS ASN_IPADDRESS
#define GAUGE32 ASN_UNSIGNED
#define TIMETICKS ASN_TIMETICKS
#define OID ASN_OBJECT_ID
/* Declare static local variables for convenience. */
SNMP_LOCAL_VARIABLES
#define RT_PREAMBLE_SIZE 20
/* BGP-MPLS-MIB instances */
static oid mpls_l3vpn_oid[] = {MPLSL3VPNMIB};
static oid mpls_l3vpn_trap_oid[] = {MPLSL3VPNMIB, 0};
static char rd_buf[RD_ADDRSTRLEN];
/* Notifications enabled by default */
static uint8_t bgp_mplsvpn_notif_enable = SNMP_TRUE;
static oid mpls_l3vpn_policy_oid[2] = {0, 0};
static const char *empty_nhop = "";
char rt_description[VRF_NAMSIZ + RT_PREAMBLE_SIZE];
static uint8_t *mplsL3vpnConfiguredVrfs(struct variable *, oid[], size_t *, int,
size_t *, WriteMethod **);
static uint8_t *mplsL3vpnActiveVrfs(struct variable *, oid[], size_t *, int,
size_t *, WriteMethod **);
static uint8_t *mplsL3vpnConnectedInterfaces(struct variable *, oid[], size_t *,
int, size_t *, WriteMethod **);
static uint8_t *mplsL3vpnNotificationEnable(struct variable *, oid[], size_t *,
int, size_t *, WriteMethod **);
static uint8_t *mplsL3vpnVrfConfMaxPossRts(struct variable *, oid[], size_t *,
int, size_t *, WriteMethod **);
static uint8_t *mplsL3vpnVrfConfRteMxThrshTime(struct variable *, oid[],
size_t *, int, size_t *,
WriteMethod **);
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 uint8_t *mplsL3vpnVrfRtTable(struct variable *, oid[], size_t *, int,
size_t *, WriteMethod **);
static uint8_t *mplsL3vpnIfConfTable(struct variable *, oid[], size_t *, int,
size_t *, WriteMethod **);
static uint8_t *mplsL3vpnPerfTable(struct variable *, oid[], size_t *, int,
size_t *, WriteMethod **);
static uint8_t *mplsL3vpnRteTable(struct variable *, oid[], size_t *, int,
size_t *, WriteMethod **);
static struct variable mpls_l3vpn_variables[] = {
/* BGP version. */
{MPLSL3VPNCONFIGUREDVRFS,
GAUGE32,
RONLY,
mplsL3vpnConfiguredVrfs,
3,
{1, 1, 1} },
{MPLSL3VPNACTIVEVRFS,
GAUGE32,
RONLY,
mplsL3vpnActiveVrfs,
3,
{1, 1, 2} },
{MPLSL3VPNCONNECTEDINTERFACES,
GAUGE32,
RONLY,
mplsL3vpnConnectedInterfaces,
3,
{1, 1, 3} },
{MPLSL3VPNNOTIFICATIONENABLE,
INTEGER,
RWRITE,
mplsL3vpnNotificationEnable,
3,
{1, 1, 4} },
{MPLSL3VPNCONFMAXPOSSRTS,
GAUGE32,
RONLY,
mplsL3vpnVrfConfMaxPossRts,
3,
{1, 1, 5} },
{MPLSL3VPNVRFCONFRTEMXTHRSHTIME,
GAUGE32,
RONLY,
mplsL3vpnVrfConfRteMxThrshTime,
3,
{1, 1, 6} },
{MPLSL3VPNILLLBLRCVTHRSH,
GAUGE32,
RONLY,
mplsL3vpnIllLblRcvThrsh,
3,
{1, 1, 7} },
/* Ifconf Table */
{MPLSL3VPNIFVPNCLASSIFICATION,
INTEGER,
RONLY,
mplsL3vpnIfConfTable,
5,
{1, 2, 1, 1, 2} },
{MPLSL3VPNIFCONFSTORAGETYPE,
INTEGER,
RONLY,
mplsL3vpnIfConfTable,
5,
{1, 2, 1, 1, 4} },
{MPLSL3VPNIFCONFROWSTATUS,
INTEGER,
RONLY,
mplsL3vpnIfConfTable,
5,
{1, 2, 1, 1, 5} },
/* mplsL3VpnVrf 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} },
/* mplsL3vpnVrfRt Table */
{MPLSL3VPNVRFRT,
OCTET_STRING,
RONLY,
mplsL3vpnVrfRtTable,
5,
{1, 2, 3, 1, 4} },
{MPLSL3VPNVRFRTDESCR,
OCTET_STRING,
RONLY,
mplsL3vpnVrfRtTable,
5,
{1, 2, 3, 1, 5} },
{MPLSL3VPNVRFRTROWSTATUS,
INTEGER,
RONLY,
mplsL3vpnVrfRtTable,
5,
{1, 2, 3, 1, 6} },
{MPLSL3VPNVRFRTSTORAGETYPE,
INTEGER,
RONLY,
mplsL3vpnVrfRtTable,
5,
{1, 2, 3, 1, 7} },
/* mplsL3VpnPerfTable */
{MPLSL3VPNVRFPERFROUTESADDED,
COUNTER32,
RONLY,
mplsL3vpnPerfTable,
5,
{1, 3, 1, 1, 1} },
{MPLSL3VPNVRFPERFROUTESDELETED,
COUNTER32,
RONLY,
mplsL3vpnPerfTable,
5,
{1, 3, 1, 1, 2} },
{MPLSL3VPNVRFPERFCURRNUMROUTES,
GAUGE32,
RONLY,
mplsL3vpnPerfTable,
5,
{1, 3, 1, 1, 3} },
/* mplsVpnRteTable */
{MPLSL3VPNVRFRTEINETCIDRDESTTYPE,
INTEGER,
RONLY,
mplsL3vpnRteTable,
5,
{1, 4, 1, 1, 1} },
{MPLSL3VPNVRFRTEINETCIDRDEST,
OCTET_STRING,
RONLY,
mplsL3vpnRteTable,
5,
{1, 4, 1, 1, 2} },
{MPLSL3VPNVRFRTEINETCIDRPFXLEN,
GAUGE32,
RONLY,
mplsL3vpnRteTable,
5,
{1, 4, 1, 1, 3} },
{MPLSL3VPNVRFRTEINETCIDRPOLICY,
OID,
RONLY,
mplsL3vpnRteTable,
5,
{1, 4, 1, 1, 4} },
{MPLSL3VPNVRFRTEINETCIDRNHOPTYPE,
INTEGER,
RONLY,
mplsL3vpnRteTable,
5,
{1, 4, 1, 1, 5} },
{MPLSL3VPNVRFRTEINETCIDRNEXTHOP,
OCTET_STRING,
RONLY,
mplsL3vpnRteTable,
5,
{1, 4, 1, 1, 6} },
{MPLSL3VPNVRFRTEINETCIDRIFINDEX,
INTEGER,
RONLY,
mplsL3vpnRteTable,
5,
{1, 4, 1, 1, 7} },
{MPLSL3VPNVRFRTEINETCIDRTYPE,
INTEGER,
RONLY,
mplsL3vpnRteTable,
5,
{1, 4, 1, 1, 8} },
{MPLSL3VPNVRFRTEINETCIDRPROTO,
INTEGER,
RONLY,
mplsL3vpnRteTable,
5,
{1, 4, 1, 1, 9} },
{MPLSL3VPNVRFRTEINETCIDRAGE,
GAUGE32,
RONLY,
mplsL3vpnRteTable,
5,
{1, 4, 1, 1, 10} },
{MPLSL3VPNVRFRTEINETCIDRNEXTHOPAS,
GAUGE32,
RONLY,
mplsL3vpnRteTable,
5,
{1, 4, 1, 1, 11} },
{MPLSL3VPNVRFRTEINETCIDRMETRIC1,
INTEGER,
RONLY,
mplsL3vpnRteTable,
5,
{1, 4, 1, 1, 12} },
{MPLSL3VPNVRFRTEINETCIDRMETRIC2,
INTEGER,
RONLY,
mplsL3vpnRteTable,
5,
{1, 4, 1, 1, 13} },
{MPLSL3VPNVRFRTEINETCIDRMETRIC3,
INTEGER,
RONLY,
mplsL3vpnRteTable,
5,
{1, 4, 1, 1, 14} },
{MPLSL3VPNVRFRTEINETCIDRMETRIC4,
INTEGER,
RONLY,
mplsL3vpnRteTable,
5,
{1, 4, 1, 1, 15} },
{MPLSL3VPNVRFRTEINETCIDRMETRIC5,
INTEGER,
RONLY,
mplsL3vpnRteTable,
5,
{1, 4, 1, 1, 16} },
{MPLSL3VPNVRFRTEINETCIDRXCPOINTER,
OCTET_STRING,
RONLY,
mplsL3vpnRteTable,
5,
{1, 4, 1, 1, 17} },
{MPLSL3VPNVRFRTEINETCIDRSTATUS,
INTEGER,
RONLY,
mplsL3vpnRteTable,
5,
{1, 4, 1, 1, 18} },
};
/* 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 uint32_t bgp_mpls_l3vpn_current_routes(struct bgp *l3vpn_bgp)
{
uint32_t count = 0;
struct bgp_table *table;
struct bgp_dest *dest;
struct bgp_path_info *pi;
table = l3vpn_bgp->rib[AFI_IP][SAFI_UNICAST];
for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) {
pi = bgp_dest_get_bgp_path_info(dest);
for (; pi; pi = pi->next)
count++;
}
table = l3vpn_bgp->rib[AFI_IP6][SAFI_UNICAST];
for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) {
pi = bgp_dest_get_bgp_path_info(dest);
for (; pi; pi = pi->next)
count++;
}
return count;
}
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->snmp_stats->routes_added =
bgp_mpls_l3vpn_current_routes(bgp);
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 int bgp_snmp_update_route_stats(struct bgp_dest *dest,
struct bgp_path_info *pi, bool added)
{
struct bgp_table *table;
if (dest) {
table = bgp_dest_table(dest);
/* only update if we have a stats block - MPLSVPN vrfs for now*/
if (table && table->bgp && table->bgp->snmp_stats) {
if (added)
table->bgp->snmp_stats->routes_added++;
else
table->bgp->snmp_stats->routes_deleted++;
}
}
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;
}
/* BGP Traps. */
static struct trap_object l3vpn_trap_list[] = {{5, {1, 2, 1, 1, 5} },
{5, {1, 2, 2, 1, 6} } };
static int bgp_vrf_check_update_active(struct bgp *bgp, struct interface *ifp)
{
bool new_active = false;
oid trap;
struct index_oid trap_index[2];
if (!is_bgp_vrf_mplsvpn(bgp) || bgp->snmp_stats == NULL
|| !bgp_mplsvpn_notif_enable)
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;
/* send relevent trap */
if (bgp->snmp_stats->active)
trap = MPLSL3VPNVRFUP;
else
trap = MPLSL3VPNDOWN;
/*
* first index vrf_name + ifindex
* second index vrf_name
*/
trap_index[1].indexlen = strnlen(bgp->name, VRF_NAMSIZ);
oid_copy_str(trap_index[0].indexname, bgp->name,
trap_index[1].indexlen);
oid_copy_str(trap_index[1].indexname, bgp->name,
trap_index[1].indexlen);
trap_index[0].indexlen =
trap_index[1].indexlen + sizeof(ifindex_t);
oid_copy_int(trap_index[0].indexname + trap_index[1].indexlen,
(int *)&(ifp->ifindex));
smux_trap_multi_index(
mpls_l3vpn_variables, array_size(mpls_l3vpn_variables),
mpls_l3vpn_trap_oid, array_size(mpls_l3vpn_trap_oid),
mpls_l3vpn_oid, sizeof(mpls_l3vpn_oid) / sizeof(oid),
trap_index, array_size(trap_index), l3vpn_trap_list,
array_size(l3vpn_trap_list), trap);
}
bgp_mpls_l3vpn_update_last_changed(bgp);
return 0;
}
static uint8_t *mplsL3vpnConfiguredVrfs(struct variable *v, oid name[],
size_t *length, int exact,
size_t *var_len,
WriteMethod **write_method)
{
struct listnode *node, *nnode;
struct bgp *bgp;
uint32_t count = 0;
if (smux_header_generic(v, name, length, exact, var_len, write_method)
== MATCH_FAILED)
return NULL;
for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) {
if (is_bgp_vrf_mplsvpn(bgp))
count++;
}
return SNMP_INTEGER(count);
}
static uint8_t *mplsL3vpnActiveVrfs(struct variable *v, oid name[],
size_t *length, int exact, size_t *var_len,
WriteMethod **write_method)
{
struct listnode *node, *nnode;
struct bgp *bgp;
uint32_t count = 0;
if (smux_header_generic(v, name, length, exact, var_len, write_method)
== MATCH_FAILED)
return NULL;
for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) {
if (is_bgp_vrf_mplsvpn(bgp) && is_bgp_vrf_active(bgp))
count++;
}
return SNMP_INTEGER(count);
}
static uint8_t *mplsL3vpnConnectedInterfaces(struct variable *v, oid name[],
size_t *length, int exact,
size_t *var_len,
WriteMethod **write_method)
{
struct listnode *node, *nnode;
struct bgp *bgp;
uint32_t count = 0;
struct vrf *vrf;
if (smux_header_generic(v, name, length, exact, var_len, write_method)
== MATCH_FAILED)
return NULL;
for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) {
if (is_bgp_vrf_mplsvpn(bgp)) {
vrf = vrf_lookup_by_name(bgp->name);
if (vrf == NULL)
continue;
count += vrf_interface_count(vrf);
}
}
return SNMP_INTEGER(count);
}
static int write_mplsL3vpnNotificationEnable(int action, uint8_t *var_val,
uint8_t var_val_type,
size_t var_val_len, uint8_t *statP,
oid *name, size_t length)
{
uint32_t intval;
if (var_val_type != ASN_INTEGER)
return SNMP_ERR_WRONGTYPE;
if (var_val_len != sizeof(long))
return SNMP_ERR_WRONGLENGTH;
intval = *(long *)var_val;
bgp_mplsvpn_notif_enable = intval;
return SNMP_ERR_NOERROR;
}
static uint8_t *mplsL3vpnNotificationEnable(struct variable *v, oid name[],
size_t *length, int exact,
size_t *var_len,
WriteMethod **write_method)
{
if (smux_header_generic(v, name, length, exact, var_len, write_method)
== MATCH_FAILED)
return NULL;
*write_method = write_mplsL3vpnNotificationEnable;
return SNMP_INTEGER(bgp_mplsvpn_notif_enable);
}
static uint8_t *mplsL3vpnVrfConfMaxPossRts(struct variable *v, oid name[],
size_t *length, int exact,
size_t *var_len,
WriteMethod **write_method)
{
if (smux_header_generic(v, name, length, exact, var_len, write_method)
== MATCH_FAILED)
return NULL;
return SNMP_INTEGER(0);
}
static uint8_t *mplsL3vpnVrfConfRteMxThrshTime(struct variable *v, oid name[],
size_t *length, int exact,
size_t *var_len,
WriteMethod **write_method)
{
if (smux_header_generic(v, name, length, exact, var_len, write_method)
== MATCH_FAILED)
return NULL;
return SNMP_INTEGER(0);
}
static uint8_t *mplsL3vpnIllLblRcvThrsh(struct variable *v, oid name[],
size_t *length, int exact,
size_t *var_len,
WriteMethod **write_method)
{
if (smux_header_generic(v, name, length, exact, var_len, write_method)
== MATCH_FAILED)
return NULL;
return SNMP_INTEGER(0);
}
static struct bgp *bgp_lookup_by_name_next(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;
}
/* 1.3.6.1.2.1.10.166.11.1.2.1.1.x = 14*/
#define IFCONFTAB_NAMELEN 14
static struct bgp *bgpL3vpnIfConf_lookup(struct variable *v, oid name[],
size_t *length, char *vrf_name,
ifindex_t *ifindex, int exact)
{
struct bgp *bgp = NULL;
size_t namelen = v ? v->namelen : IFCONFTAB_NAMELEN;
struct interface *ifp;
int vrf_name_len, len;
/* too long ? */
if (*length - namelen > (VRF_NAMSIZ + sizeof(uint32_t)))
return NULL;
/* do we have index info in the oid ? */
if (*length - namelen != 0 && *length - namelen >= sizeof(uint32_t)) {
/* copy the info from the oid */
vrf_name_len = *length - (namelen + sizeof(ifindex_t));
oid2string(name + namelen, vrf_name_len, vrf_name);
oid2int(name + namelen + vrf_name_len, ifindex);
}
if (exact) {
/* Check the length. */
bgp = bgp_lookup_by_name(vrf_name);
if (bgp && !is_bgp_vrf_mplsvpn(bgp))
return NULL;
if (!bgp)
return NULL;
ifp = if_lookup_by_index(*ifindex, bgp->vrf_id);
if (!ifp)
return NULL;
} else {
if (strnlen(vrf_name, VRF_NAMSIZ) == 0)
bgp = bgp_lookup_by_name_next(vrf_name);
else
bgp = bgp_lookup_by_name(vrf_name);
while (bgp) {
ifp = if_vrf_lookup_by_index_next(*ifindex,
bgp->vrf_id);
if (ifp) {
vrf_name_len = strnlen(bgp->name, VRF_NAMSIZ);
*ifindex = ifp->ifindex;
len = vrf_name_len + sizeof(ifindex_t);
oid_copy_str(name + namelen, bgp->name,
vrf_name_len);
oid_copy_int(name + namelen + vrf_name_len,
ifindex);
*length = len + namelen;
return bgp;
}
*ifindex = 0;
bgp = bgp_lookup_by_name_next(bgp->name);
}
return NULL;
}
return bgp;
}
static uint8_t *mplsL3vpnIfConfTable(struct variable *v, oid name[],
size_t *length, int exact, size_t *var_len,
WriteMethod **write_method)
{
char vrf_name[VRF_NAMSIZ];
ifindex_t ifindex = 0;
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 = bgpL3vpnIfConf_lookup(v, name, length, vrf_name, &ifindex,
exact);
if (!l3vpn_bgp)
return NULL;
switch (v->magic) {
case MPLSL3VPNIFVPNCLASSIFICATION:
return SNMP_INTEGER(2);
case MPLSL3VPNIFCONFSTORAGETYPE:
return SNMP_INTEGER(2);
case MPLSL3VPNIFCONFROWSTATUS:
return SNMP_INTEGER(1);
}
return NULL;
}
/* 1.3.6.1.2.1.10.166.11.1.2.2.1.x = 14*/
#define VRFTAB_NAMELEN 14
static struct bgp *bgpL3vpnVrf_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 = bgpL3vpnVrf_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,
bgpd: support for as notation format for route distinguisher RD may be built based on an AS number. Like for the AS, the RD may use the AS notation. The two below examples can illustrate: RD 1.1:20 stands for an AS4B:NN RD with AS4B=65536 in dot format. RD 0.1:20 stands for an AS2B:NNNN RD with AS2B=0.1 in dot+ format. This commit adds the asnotation mode to prefix_rd2str() API so as to pick up the relevant display. Two new printfrr extensions are available to display the RD with the two above display methods. - The pRDD extension stands for dot asnotation format - The pRDE extension stands for dot+ asnotation format. - The pRD extension has been renamed to pRDP extension The code is changed each time '%pRD' printf extension is called. Possibly, the asnotation may change the output, then a macro defines the asnotation mode to use. A side effect of forging the mode to use is that the string could not be concatenated with other strings in vty_out and snprintfrr. Those functions have been called multiple times. When zlog_debug needs to display the RD with some other string, the prefix_rd2str() old API is used instead of the printf extension. Some code has been kept untouched: - code related to running-config. Actually, wherever an RD is displayed, its configured name should be dumped. - bgp rfapi code - bgp evpn multihoming code (partially done), since the logic is missing to get the asnotation of 'struct bgp_evpn_es'. Signed-off-by: Philippe Guibert <philippe.guibert@6wind.com>
2022-11-22 09:57:10 +01:00
rd_buf, sizeof(rd_buf),
bgp_get_asnotation(l3vpn_bgp));
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,
bgpd: support for as notation format for route distinguisher RD may be built based on an AS number. Like for the AS, the RD may use the AS notation. The two below examples can illustrate: RD 1.1:20 stands for an AS4B:NN RD with AS4B=65536 in dot format. RD 0.1:20 stands for an AS2B:NNNN RD with AS2B=0.1 in dot+ format. This commit adds the asnotation mode to prefix_rd2str() API so as to pick up the relevant display. Two new printfrr extensions are available to display the RD with the two above display methods. - The pRDD extension stands for dot asnotation format - The pRDE extension stands for dot+ asnotation format. - The pRD extension has been renamed to pRDP extension The code is changed each time '%pRD' printf extension is called. Possibly, the asnotation may change the output, then a macro defines the asnotation mode to use. A side effect of forging the mode to use is that the string could not be concatenated with other strings in vty_out and snprintfrr. Those functions have been called multiple times. When zlog_debug needs to display the RD with some other string, the prefix_rd2str() old API is used instead of the printf extension. Some code has been kept untouched: - code related to running-config. Actually, wherever an RD is displayed, its configured name should be dumped. - bgp rfapi code - bgp evpn multihoming code (partially done), since the logic is missing to get the asnotation of 'struct bgp_evpn_es'. Signed-off-by: Philippe Guibert <philippe.guibert@6wind.com>
2022-11-22 09:57:10 +01:00
rd_buf, sizeof(rd_buf),
bgp_get_asnotation(l3vpn_bgp));
*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;
}
/* 1.3.6.1.2.1.10.166.11.1.2.3.1.x = 14*/
#define VRFRTTAB_NAMELEN 14
static struct bgp *bgpL3vpnVrfRt_lookup(struct variable *v, oid name[],
size_t *length, char *vrf_name,
uint32_t *rt_index, uint8_t *rt_type,
int exact)
{
uint32_t type_index_size;
struct bgp *l3vpn_bgp;
size_t namelen = v ? v->namelen : VRFRTTAB_NAMELEN;
int vrf_name_len, len;
/* too long ? */
if (*length - namelen
> (VRF_NAMSIZ + sizeof(uint32_t)) + sizeof(uint8_t))
return NULL;
type_index_size = sizeof(uint32_t) + sizeof(uint8_t);
/* do we have index info in the oid ? */
if (*length - namelen != 0 && *length - namelen >= type_index_size) {
/* copy the info from the oid */
vrf_name_len = *length - (namelen + type_index_size);
oid2string(name + namelen, vrf_name_len, vrf_name);
oid2int(name + namelen + vrf_name_len, (int *)rt_index);
*rt_type = name[namelen + vrf_name_len + sizeof(uint32_t)];
}
/* validate the RT index is in range */
if (*rt_index > AFI_IP6)
return NULL;
if (exact) {
l3vpn_bgp = bgp_lookup_by_name(vrf_name);
if (l3vpn_bgp && !is_bgp_vrf_mplsvpn(l3vpn_bgp))
return NULL;
if (!l3vpn_bgp)
return NULL;
if ((*rt_index != AFI_IP) && (*rt_index != AFI_IP6))
return NULL;
/* do we have RT config */
if (!(l3vpn_bgp->vpn_policy[*rt_index]
.rtlist[BGP_VPN_POLICY_DIR_FROMVPN]
|| l3vpn_bgp->vpn_policy[*rt_index]
.rtlist[BGP_VPN_POLICY_DIR_TOVPN]))
return NULL;
return l3vpn_bgp;
}
if (strnlen(vrf_name, VRF_NAMSIZ) == 0)
l3vpn_bgp = bgp_lookup_by_name_next(vrf_name);
else
l3vpn_bgp = bgp_lookup_by_name(vrf_name);
while (l3vpn_bgp) {
switch (*rt_index) {
case 0:
*rt_index = AFI_IP;
break;
case AFI_IP:
*rt_index = AFI_IP6;
break;
case AFI_IP6:
*rt_index = 0;
continue;
}
if (*rt_index) {
switch (*rt_type) {
case 0:
*rt_type = MPLSVPNVRFRTTYPEIMPORT;
break;
case MPLSVPNVRFRTTYPEIMPORT:
*rt_type = MPLSVPNVRFRTTYPEEXPORT;
break;
case MPLSVPNVRFRTTYPEEXPORT:
case MPLSVPNVRFRTTYPEBOTH:
*rt_type = 0;
break;
}
if (*rt_type) {
bool import, export;
import =
(!!l3vpn_bgp->vpn_policy[*rt_index].rtlist
[BGP_VPN_POLICY_DIR_FROMVPN]);
export =
(!!l3vpn_bgp->vpn_policy[*rt_index].rtlist
[BGP_VPN_POLICY_DIR_TOVPN]);
if (*rt_type == MPLSVPNVRFRTTYPEIMPORT
&& !import)
continue;
if (*rt_type == MPLSVPNVRFRTTYPEEXPORT
&& !export)
continue;
/* ckeck for both */
if (*rt_type == MPLSVPNVRFRTTYPEIMPORT && import
&& export
&& ecommunity_cmp(
l3vpn_bgp->vpn_policy[*rt_index].rtlist
[BGP_VPN_POLICY_DIR_FROMVPN],
l3vpn_bgp->vpn_policy[*rt_index].rtlist
[BGP_VPN_POLICY_DIR_TOVPN]))
*rt_type = MPLSVPNVRFRTTYPEBOTH;
/* we have a match copy the oid info */
vrf_name_len =
strnlen(l3vpn_bgp->name, VRF_NAMSIZ);
len = vrf_name_len + sizeof(uint32_t)
+ sizeof(uint8_t);
oid_copy_str(name + namelen, l3vpn_bgp->name,
vrf_name_len);
oid_copy_int(name + namelen + vrf_name_len,
(int *)rt_index);
name[(namelen + len) - 1] = *rt_type;
*length = len + namelen;
return l3vpn_bgp;
}
l3vpn_bgp = bgp_lookup_by_name_next(l3vpn_bgp->name);
}
}
return NULL;
}
static const char *rt_type2str(uint8_t rt_type)
{
switch (rt_type) {
case MPLSVPNVRFRTTYPEIMPORT:
return "import";
case MPLSVPNVRFRTTYPEEXPORT:
return "export";
case MPLSVPNVRFRTTYPEBOTH:
return "both";
default:
return "unknown";
}
}
static uint8_t *mplsL3vpnVrfRtTable(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;
uint32_t rt_index = 0;
uint8_t rt_type = 0;
char *rt_b;
if (smux_header_table(v, name, length, exact, var_len, write_method)
== MATCH_FAILED)
return NULL;
memset(vrf_name, 0, VRF_NAMSIZ);
l3vpn_bgp = bgpL3vpnVrfRt_lookup(v, name, length, vrf_name, &rt_index,
&rt_type, exact);
if (!l3vpn_bgp)
return NULL;
switch (v->magic) {
case MPLSL3VPNVRFRT:
switch (rt_type) {
case MPLSVPNVRFRTTYPEIMPORT:
rt_b = ecommunity_ecom2str(
l3vpn_bgp->vpn_policy[rt_index]
.rtlist[BGP_VPN_POLICY_DIR_FROMVPN],
ECOMMUNITY_FORMAT_ROUTE_MAP,
ECOMMUNITY_ROUTE_TARGET);
break;
case MPLSVPNVRFRTTYPEEXPORT:
case MPLSVPNVRFRTTYPEBOTH:
rt_b = ecommunity_ecom2str(
l3vpn_bgp->vpn_policy[rt_index]
.rtlist[BGP_VPN_POLICY_DIR_TOVPN],
ECOMMUNITY_FORMAT_ROUTE_MAP,
ECOMMUNITY_ROUTE_TARGET);
break;
default:
rt_b = NULL;
break;
}
if (rt_b)
*var_len = strnlen(rt_b, ECOMMUNITY_STRLEN);
else
*var_len = 0;
return (uint8_t *)rt_b;
case MPLSL3VPNVRFRTDESCR:
/* since we dont have a description generate one */
memset(rt_description, 0, VRF_NAMSIZ + RT_PREAMBLE_SIZE);
snprintf(rt_description, VRF_NAMSIZ + RT_PREAMBLE_SIZE,
"RT %s for VRF %s", rt_type2str(rt_type),
l3vpn_bgp->name);
*var_len =
strnlen(rt_description, VRF_NAMSIZ + RT_PREAMBLE_SIZE);
return (uint8_t *)rt_description;
case MPLSL3VPNVRFRTROWSTATUS:
return SNMP_INTEGER(1);
case MPLSL3VPNVRFRTSTORAGETYPE:
return SNMP_INTEGER(2);
}
return NULL;
}
/* 1.3.6.1.2.1.10.166.11.1.3.1.1.x = 14*/
#define PERFTAB_NAMELEN 14
static uint8_t *mplsL3vpnPerfTable(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 = bgpL3vpnVrf_lookup(v, name, length, vrf_name, exact);
if (!l3vpn_bgp)
return NULL;
switch (v->magic) {
case MPLSL3VPNVRFPERFROUTESADDED:
return SNMP_INTEGER(l3vpn_bgp->snmp_stats->routes_added);
case MPLSL3VPNVRFPERFROUTESDELETED:
return SNMP_INTEGER(l3vpn_bgp->snmp_stats->routes_deleted);
case MPLSL3VPNVRFPERFCURRNUMROUTES:
return SNMP_INTEGER(bgp_mpls_l3vpn_current_routes(l3vpn_bgp));
}
return NULL;
}
static struct bgp_path_info *
bgp_lookup_route(struct bgp *l3vpn_bgp, struct bgp_dest **dest,
struct prefix *prefix, uint16_t policy, struct ipaddr *nexthop)
{
struct bgp_path_info *pi = NULL;
struct bgp_table *table;
switch (prefix->family) {
case AF_INET:
table = l3vpn_bgp->rib[AFI_IP][SAFI_UNICAST];
break;
case AF_INET6:
table = l3vpn_bgp->rib[AFI_IP6][SAFI_UNICAST];
break;
default:
return NULL;
}
/*get the prefix */
*dest = bgp_node_lookup(table, prefix);
if (*dest == NULL)
return NULL;
/* now find the right path */
pi = bgp_dest_get_bgp_path_info(*dest);
for (; pi; pi = pi->next) {
switch (nexthop->ipa_type) {
case IPADDR_V4:
if (nexthop->ip._v4_addr.s_addr
== pi->attr->nexthop.s_addr)
return pi;
break;
case IPADDR_V6:
if (memcmp(&nexthop->ip._v6_addr,
&pi->attr->mp_nexthop_global,
sizeof(struct in6_addr))
== 0)
return pi;
break;
case IPADDR_NONE:
return pi;
}
}
return NULL;
}
static struct bgp_path_info *bgp_lookup_route_next(struct bgp **l3vpn_bgp,
struct bgp_dest **dest,
struct prefix *prefix,
uint16_t *policy,
struct ipaddr *nexthop)
{
struct bgp_path_info *pi = NULL;
struct bgp_table *table;
const struct prefix *p;
uint8_t family;
/* First route?*/
if (prefix->prefixlen == 0) {
/* try V4 table */
table = (*l3vpn_bgp)->rib[AFI_IP][SAFI_UNICAST];
for (*dest = bgp_table_top(table); *dest;
*dest = bgp_route_next(*dest)) {
pi = bgp_dest_get_bgp_path_info(*dest);
if (pi)
break;
}
if (!pi) {
/* try V6 table */
table = (*l3vpn_bgp)->rib[AFI_IP6][SAFI_UNICAST];
for (*dest = bgp_table_top(table); *dest;
*dest = bgp_route_next(*dest)) {
pi = bgp_dest_get_bgp_path_info(*dest);
if (pi)
break;
}
}
return pi;
}
/* real next search for the entry first use exact lookup */
pi = bgp_lookup_route(*l3vpn_bgp, dest, prefix, *policy, nexthop);
if (pi == NULL)
return NULL;
p = bgp_dest_get_prefix(*dest);
family = p->family;
/* We have found the input path let's find the next one in the list */
if (pi->next) {
/* ensure OID is always higher for multipath routes by
* incrementing opaque policy oid
*/
*policy += 1;
return pi->next;
}
/* No more paths in the input route so find the next route */
for (; *l3vpn_bgp;
*l3vpn_bgp = bgp_lookup_by_name_next((*l3vpn_bgp)->name)) {
*policy = 0;
if (!*dest) {
table = (*l3vpn_bgp)->rib[AFI_IP][SAFI_UNICAST];
*dest = bgp_table_top(table);
family = AF_INET;
} else
*dest = bgp_route_next(*dest);
while (true) {
for (; *dest; *dest = bgp_route_next(*dest)) {
pi = bgp_dest_get_bgp_path_info(*dest);
if (pi)
return pi;
}
if (family == AF_INET) {
table = (*l3vpn_bgp)
->rib[AFI_IP6][SAFI_UNICAST];
*dest = bgp_table_top(table);
family = AF_INET6;
continue;
}
break;
}
}
return NULL;
}
static bool is_addr_type(oid id)
{
switch (id) {
case INETADDRESSTYPEUNKNOWN:
case INETADDRESSTYPEIPV4:
case INETADDRESSTYPEIPV6:
return true;
}
return false;
}
/* 1.3.6.1.2.1.10.166.11.1.4.1.1.x = 14*/
#define PERFTAB_NAMELEN 14
static struct bgp_path_info *bgpL3vpnRte_lookup(struct variable *v, oid name[],
size_t *length, char *vrf_name,
struct bgp **l3vpn_bgp,
struct bgp_dest **dest,
uint16_t *policy, int exact)
{
uint8_t i;
uint8_t vrf_name_len = 0;
struct bgp_path_info *pi = NULL;
size_t namelen = v ? v->namelen : IFCONFTAB_NAMELEN;
struct prefix prefix = {0};
struct ipaddr nexthop = {0};
uint8_t prefix_type;
uint8_t nexthop_type;
if ((uint32_t)(*length - namelen) > (VRF_NAMSIZ + 37))
return NULL;
if (*length - namelen != 0) {
/* parse incoming OID */
for (i = namelen; i < (*length); i++) {
if (is_addr_type(name[i]))
break;
vrf_name_len++;
}
if (vrf_name_len > VRF_NAMSIZ)
return NULL;
oid2string(name + namelen, vrf_name_len, vrf_name);
prefix_type = name[i++];
switch (prefix_type) {
case INETADDRESSTYPEUNKNOWN:
prefix.family = AF_UNSPEC;
break;
case INETADDRESSTYPEIPV4:
prefix.family = AF_INET;
oid2in_addr(&name[i], sizeof(struct in_addr),
&prefix.u.prefix4);
i += sizeof(struct in_addr);
break;
case INETADDRESSTYPEIPV6:
prefix.family = AF_INET6;
oid2in6_addr(&name[i], &prefix.u.prefix6);
i += sizeof(struct in6_addr);
break;
}
prefix.prefixlen = (uint8_t)name[i++];
*policy |= name[i++] << 8;
*policy |= name[i++];
nexthop_type = name[i++];
switch (nexthop_type) {
case INETADDRESSTYPEUNKNOWN:
nexthop.ipa_type = (prefix.family == AF_INET)
? IPADDR_V4
: IPADDR_V6;
break;
case INETADDRESSTYPEIPV4:
nexthop.ipa_type = IPADDR_V4;
oid2in_addr(&name[i], sizeof(struct in_addr),
&nexthop.ip._v4_addr);
/* i += sizeof(struct in_addr); */
break;
case INETADDRESSTYPEIPV6:
nexthop.ipa_type = IPADDR_V6;
oid2in6_addr(&name[i], &nexthop.ip._v6_addr);
/* i += sizeof(struct in6_addr); */
break;
}
}
if (exact) {
*l3vpn_bgp = bgp_lookup_by_name(vrf_name);
if (*l3vpn_bgp && !is_bgp_vrf_mplsvpn(*l3vpn_bgp))
return NULL;
if (*l3vpn_bgp == NULL)
return NULL;
/* now lookup the route in this bgp table */
pi = bgp_lookup_route(*l3vpn_bgp, dest, &prefix, *policy,
&nexthop);
} else {
int str_len;
str_len = strnlen(vrf_name, VRF_NAMSIZ);
if (str_len == 0) {
*l3vpn_bgp = bgp_lookup_by_name_next(vrf_name);
} else
/* otherwise lookup the one we have */
*l3vpn_bgp = bgp_lookup_by_name(vrf_name);
if (*l3vpn_bgp == NULL)
return NULL;
pi = bgp_lookup_route_next(l3vpn_bgp, dest, &prefix, policy,
&nexthop);
if (pi) {
uint8_t vrf_name_len =
strnlen((*l3vpn_bgp)->name, VRF_NAMSIZ);
const struct prefix *p = bgp_dest_get_prefix(*dest);
uint8_t oid_index;
bool v4 = (p->family == AF_INET);
uint8_t addr_len = v4 ? sizeof(struct in_addr)
: sizeof(struct in6_addr);
struct attr *attr = pi->attr;
/* copy the index parameters */
oid_copy_str(&name[namelen], (*l3vpn_bgp)->name,
vrf_name_len);
oid_index = namelen + vrf_name_len;
if (v4) {
name[oid_index++] = INETADDRESSTYPEIPV4;
oid_copy_in_addr(&name[oid_index],
&p->u.prefix4);
} else {
name[oid_index++] = INETADDRESSTYPEIPV6;
oid_copy_in6_addr(&name[oid_index],
&p->u.prefix6);
}
oid_index += addr_len;
name[oid_index++] = p->prefixlen;
name[oid_index++] = *policy >> 8;
name[oid_index++] = *policy & 0xff;
if (!BGP_ATTR_NEXTHOP_AFI_IP6(attr)) {
if (attr->nexthop.s_addr == INADDR_ANY)
name[oid_index++] =
INETADDRESSTYPEUNKNOWN;
else {
name[oid_index++] = INETADDRESSTYPEIPV4;
oid_copy_in_addr(&name[oid_index],
&attr->nexthop);
oid_index += sizeof(struct in_addr);
}
} else {
if (IN6_IS_ADDR_UNSPECIFIED(
&attr->mp_nexthop_global))
name[oid_index++] =
INETADDRESSTYPEUNKNOWN;
else {
name[oid_index++] = INETADDRESSTYPEIPV6;
oid_copy_in6_addr(
&name[oid_index],
&attr->mp_nexthop_global);
oid_index += sizeof(struct in6_addr);
}
}
*length = oid_index;
}
}
return pi;
}
static uint8_t *mplsL3vpnRteTable(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;
struct bgp_dest *dest;
struct bgp_path_info *pi, *bpi_ultimate;
const struct prefix *p;
uint16_t policy = 0;
if (smux_header_table(v, name, length, exact, var_len, write_method)
== MATCH_FAILED)
return NULL;
memset(vrf_name, 0, VRF_NAMSIZ);
pi = bgpL3vpnRte_lookup(v, name, length, vrf_name, &l3vpn_bgp, &dest,
&policy, exact);
if (!pi)
return NULL;
bpi_ultimate = bgp_get_imported_bpi_ultimate(pi);
p = bgp_dest_get_prefix(dest);
if (!p)
return NULL;
switch (v->magic) {
case MPLSL3VPNVRFRTEINETCIDRDESTTYPE:
switch (p->family) {
case AF_INET:
return SNMP_INTEGER(INETADDRESSTYPEIPV4);
case AF_INET6:
return SNMP_INTEGER(INETADDRESSTYPEIPV6);
default:
return SNMP_INTEGER(INETADDRESSTYPEUNKNOWN);
}
case MPLSL3VPNVRFRTEINETCIDRDEST:
switch (p->family) {
case AF_INET:
return SNMP_IPADDRESS(p->u.prefix4);
case AF_INET6:
return SNMP_IP6ADDRESS(p->u.prefix6);
default:
*var_len = 0;
return NULL;
}
case MPLSL3VPNVRFRTEINETCIDRPFXLEN:
return SNMP_INTEGER(p->prefixlen);
case MPLSL3VPNVRFRTEINETCIDRPOLICY:
*var_len = sizeof(mpls_l3vpn_policy_oid);
mpls_l3vpn_policy_oid[0] = policy >> 8;
mpls_l3vpn_policy_oid[1] = policy & 0xff;
return (uint8_t *)mpls_l3vpn_policy_oid;
case MPLSL3VPNVRFRTEINETCIDRNHOPTYPE:
if (!BGP_ATTR_NEXTHOP_AFI_IP6(pi->attr)) {
if (pi->attr->nexthop.s_addr == INADDR_ANY)
return SNMP_INTEGER(INETADDRESSTYPEUNKNOWN);
else
return SNMP_INTEGER(INETADDRESSTYPEIPV4);
} else if (IN6_IS_ADDR_UNSPECIFIED(
&pi->attr->mp_nexthop_global))
return SNMP_INTEGER(INETADDRESSTYPEUNKNOWN);
else
return SNMP_INTEGER(INETADDRESSTYPEIPV6);
case MPLSL3VPNVRFRTEINETCIDRNEXTHOP:
if (!BGP_ATTR_NEXTHOP_AFI_IP6(pi->attr))
if (pi->attr->nexthop.s_addr == INADDR_ANY) {
*var_len = 0;
return (uint8_t *)empty_nhop;
} else
return SNMP_IPADDRESS(pi->attr->nexthop);
else if (IN6_IS_ADDR_UNSPECIFIED(
&pi->attr->mp_nexthop_global)) {
*var_len = 0;
return (uint8_t *)empty_nhop;
} else
return SNMP_IP6ADDRESS(pi->attr->mp_nexthop_global);
case MPLSL3VPNVRFRTEINETCIDRIFINDEX:
if (pi->nexthop && pi->nexthop->nexthop)
return SNMP_INTEGER(pi->nexthop->nexthop->ifindex);
else
return SNMP_INTEGER(0);
case MPLSL3VPNVRFRTEINETCIDRTYPE:
if (pi->nexthop && pi->nexthop->nexthop) {
switch (pi->nexthop->nexthop->type) {
case NEXTHOP_TYPE_IFINDEX:
return SNMP_INTEGER(
MPLSL3VPNVRFRTECIDRTYPELOCAL);
case NEXTHOP_TYPE_IPV4:
case NEXTHOP_TYPE_IPV4_IFINDEX:
case NEXTHOP_TYPE_IPV6:
case NEXTHOP_TYPE_IPV6_IFINDEX:
return SNMP_INTEGER(
MPLSL3VPNVRFRTECIDRTYPEREMOTE);
case NEXTHOP_TYPE_BLACKHOLE:
switch (pi->nexthop->nexthop->bh_type) {
case BLACKHOLE_REJECT:
return SNMP_INTEGER(
MPLSL3VPNVRFRTECIDRTYPEREJECT);
case BLACKHOLE_UNSPEC:
case BLACKHOLE_NULL:
case BLACKHOLE_ADMINPROHIB:
return SNMP_INTEGER(
MPLSL3VPNVRFRTECIDRTYPEBLACKHOLE);
}
break;
}
} else
return SNMP_INTEGER(MPLSL3VPNVRFRTECIDRTYPEOTHER);
case MPLSL3VPNVRFRTEINETCIDRPROTO:
switch (pi->type) {
case ZEBRA_ROUTE_CONNECT:
return SNMP_INTEGER(IANAIPROUTEPROTOCOLLOCAL);
case ZEBRA_ROUTE_STATIC:
return SNMP_INTEGER(IANAIPROUTEPROTOCOLNETMGMT);
case ZEBRA_ROUTE_RIP:
case ZEBRA_ROUTE_RIPNG:
return SNMP_INTEGER(IANAIPROUTEPROTOCOLRIP);
case ZEBRA_ROUTE_OSPF:
case ZEBRA_ROUTE_OSPF6:
return SNMP_INTEGER(IANAIPROUTEPROTOCOLOSPF);
case ZEBRA_ROUTE_ISIS:
return SNMP_INTEGER(IANAIPROUTEPROTOCOLISIS);
case ZEBRA_ROUTE_BGP:
return SNMP_INTEGER(IANAIPROUTEPROTOCOLBGP);
case ZEBRA_ROUTE_EIGRP:
return SNMP_INTEGER(IANAIPROUTEPROTOCOLCISCOEIGRP);
default:
return SNMP_INTEGER(IANAIPROUTEPROTOCOLOTHER);
}
case MPLSL3VPNVRFRTEINETCIDRAGE:
return SNMP_INTEGER(pi->uptime);
case MPLSL3VPNVRFRTEINETCIDRNEXTHOPAS:
return SNMP_INTEGER(pi->peer ? pi->peer->as : 0);
case MPLSL3VPNVRFRTEINETCIDRMETRIC1:
if (bpi_ultimate->extra)
return SNMP_INTEGER(bpi_ultimate->extra->igpmetric);
else
return SNMP_INTEGER(0);
case MPLSL3VPNVRFRTEINETCIDRMETRIC2:
return SNMP_INTEGER(-1);
case MPLSL3VPNVRFRTEINETCIDRMETRIC3:
return SNMP_INTEGER(-1);
case MPLSL3VPNVRFRTEINETCIDRMETRIC4:
return SNMP_INTEGER(-1);
case MPLSL3VPNVRFRTEINETCIDRMETRIC5:
return SNMP_INTEGER(-1);
case MPLSL3VPNVRFRTEINETCIDRXCPOINTER:
return SNMP_OCTET(0);
case MPLSL3VPNVRFRTEINETCIDRSTATUS:
return SNMP_INTEGER(1);
}
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);
hook_register(bgp_snmp_update_stats, bgp_snmp_update_route_stats);
REGISTER_MIB("mplsL3VpnMIB", mpls_l3vpn_variables, variable,
mpls_l3vpn_oid);
}