diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-09 13:16:35 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-09 13:16:35 +0000 |
commit | e2bbf175a2184bd76f6c54ccf8456babeb1a46fc (patch) | |
tree | f0b76550d6e6f500ada964a3a4ee933a45e5a6f1 /bgpd/bgp_mplsvpn_snmp.c | |
parent | Initial commit. (diff) | |
download | frr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.tar.xz frr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.zip |
Adding upstream version 9.1.upstream/9.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'bgpd/bgp_mplsvpn_snmp.c')
-rw-r--r-- | bgpd/bgp_mplsvpn_snmp.c | 1686 |
1 files changed, 1686 insertions, 0 deletions
diff --git a/bgpd/bgp_mplsvpn_snmp.c b/bgpd/bgp_mplsvpn_snmp.c new file mode 100644 index 0000000..0208a6f --- /dev/null +++ b/bgpd/bgp_mplsvpn_snmp.c @@ -0,0 +1,1686 @@ +// 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 "frrevent.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, + 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, + 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 = NULL; + static char rt_b_str[BUFSIZ] = {}; + + 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: + break; + } + if (rt_b) { + *var_len = strnlen(rt_b, ECOMMUNITY_STRLEN); + strlcpy(rt_b_str, rt_b, sizeof(rt_b_str)); + XFREE(MTYPE_ECOMMUNITY_STR, rt_b); + } else { + *var_len = 0; + } + return (uint8_t *)rt_b_str; + 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); + break; + 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); +} |