diff options
51 files changed, 3422 insertions, 20 deletions
diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c index c358d4203e..7f6f61e141 100644 --- a/bgpd/bgp_ecommunity.c +++ b/bgpd/bgp_ecommunity.c @@ -899,7 +899,6 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter) uint8_t *pnt; uint8_t type = 0; uint8_t sub_type = 0; -#define ECOMMUNITY_STRLEN 64 int str_size; char *str_buf; diff --git a/bgpd/bgp_ecommunity.h b/bgpd/bgp_ecommunity.h index 6d0275a0c3..03b23fcd37 100644 --- a/bgpd/bgp_ecommunity.h +++ b/bgpd/bgp_ecommunity.h @@ -103,6 +103,9 @@ /* Extended Communities type flag. */ #define ECOMMUNITY_FLAG_NON_TRANSITIVE 0x40 +/* Extended Community readable string length */ +#define ECOMMUNITY_STRLEN 64 + /* Extended Communities attribute. */ struct ecommunity { /* Reference counter. */ diff --git a/bgpd/bgp_mplsvpn.h b/bgpd/bgp_mplsvpn.h index df2544d608..91a073d5d7 100644 --- a/bgpd/bgp_mplsvpn.h +++ b/bgpd/bgp_mplsvpn.h @@ -83,6 +83,21 @@ extern void vrf_import_from_vrf(struct bgp *to_bgp, struct bgp *from_bgp, void vrf_unimport_from_vrf(struct bgp *to_bgp, struct bgp *from_bgp, afi_t afi, safi_t safi); +static inline bool is_bgp_vrf_mplsvpn(struct bgp *bgp) +{ + afi_t afi; + + if (bgp->inst_type == BGP_INSTANCE_TYPE_VRF) + for (afi = 0; afi < AFI_MAX; ++afi) { + if (CHECK_FLAG(bgp->af_flags[afi][SAFI_UNICAST], + BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT) + || CHECK_FLAG(bgp->af_flags[afi][SAFI_UNICAST], + BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT)) + return true; + } + return false; +} + static inline int vpn_leak_to_vpn_active(struct bgp *bgp_vrf, afi_t afi, const char **pmsg) { diff --git a/bgpd/bgp_mplsvpn_snmp.c b/bgpd/bgp_mplsvpn_snmp.c new file mode 100644 index 0000000000..ef5556be17 --- /dev/null +++ b/bgpd/bgp_mplsvpn_snmp.c @@ -0,0 +1,1689 @@ +/* MPLS/BGP L3VPN MIB + * Copyright (C) 2020 Volta Networks Inc + * + * This file is part of FRR. + * + * FRRouting is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRRouting is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#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 "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)); + 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; +} + +/* 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)]; + } + + 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; + /* check the index and type match up */ + 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; + default: + 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; + oid2in_addr(&name[i], sizeof(struct in6_addr), + &prefix.u.prefix4); /* sic */ + 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; + oid2in_addr(&name[i], sizeof(struct in6_addr), + &nexthop.ip._v4_addr); /* sic */ + 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; + name[oid_index++] = + v4 ? INETADDRESSTYPEIPV4 : INETADDRESSTYPEIPV6; + oid_copy_addr(&name[oid_index], &p->u.prefix4, + addr_len); + 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_addr(&name[oid_index], + &attr->nexthop, + sizeof(struct in_addr)); + 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_addr( + &name[oid_index], + (struct in_addr *)&attr + ->mp_nexthop_global, + sizeof(struct in6_addr)); + 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; + 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; + + 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); + default: + return SNMP_INTEGER( + MPLSL3VPNVRFRTECIDRTYPEBLACKHOLE); + } + default: + return SNMP_INTEGER( + MPLSL3VPNVRFRTECIDRTYPEOTHER); + } + } 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 (pi->extra) + return SNMP_INTEGER(pi->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); +} diff --git a/bgpd/bgp_mplsvpn_snmp.h b/bgpd/bgp_mplsvpn_snmp.h new file mode 100644 index 0000000000..781d5e98f6 --- /dev/null +++ b/bgpd/bgp_mplsvpn_snmp.h @@ -0,0 +1,31 @@ +/* MPLS/BGP L3VPN MIB + * Copyright (C) 2020 Volta Networks Inc + * + * This file is part of FRR. + * + * FRRouting is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRRouting is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +void bgp_mpls_l3vpn_module_init(void); + +#define MPLSL3VPNVRFRTECIDRTYPEOTHER 1 +#define MPLSL3VPNVRFRTECIDRTYPEREJECT 2 +#define MPLSL3VPNVRFRTECIDRTYPELOCAL 3 +#define MPLSL3VPNVRFRTECIDRTYPEREMOTE 4 +#define MPLSL3VPNVRFRTECIDRTYPEBLACKHOLE 5 + +#define MPLSVPNVRFRTTYPEIMPORT 1 +#define MPLSVPNVRFRTTYPEEXPORT 2 +#define MPLSVPNVRFRTTYPEBOTH 3 diff --git a/bgpd/bgp_nb_config.c b/bgpd/bgp_nb_config.c index 531ff4a60a..f2443bd164 100644 --- a/bgpd/bgp_nb_config.c +++ b/bgpd/bgp_nb_config.c @@ -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; } diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 4f902ec1e7..19761d701a 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -93,6 +93,10 @@ #include "bgpd/bgp_route_clippy.c" #endif +DEFINE_HOOK(bgp_snmp_update_stats, + (struct bgp_node *rn, struct bgp_path_info *pi, bool added), + (rn, pi, added)) + /* Extern from bgp_dump.c */ extern const char *bgp_origin_str[]; extern const char *bgp_origin_long_str[]; @@ -402,6 +406,7 @@ void bgp_path_info_add(struct bgp_dest *dest, struct bgp_path_info *pi) bgp_dest_lock_node(dest); peer_lock(pi->peer); /* bgp_path_info peer reference */ bgp_dest_set_defer_flag(dest, false); + hook_call(bgp_snmp_update_stats, dest, pi, true); } /* Do the actual removal of info from RIB, for use by bgp_process @@ -417,6 +422,7 @@ void bgp_path_info_reap(struct bgp_dest *dest, struct bgp_path_info *pi) bgp_path_info_mpath_dequeue(pi); bgp_path_info_unlock(pi); + hook_call(bgp_snmp_update_stats, dest, pi, false); bgp_dest_unlock_node(dest); } diff --git a/bgpd/bgp_snmp.c b/bgpd/bgp_snmp.c index 303f4ca56e..00d225bf65 100644 --- a/bgpd/bgp_snmp.c +++ b/bgpd/bgp_snmp.c @@ -40,6 +40,7 @@ #include "bgpd/bgp_attr.h" #include "bgpd/bgp_route.h" #include "bgpd/bgp_fsm.h" +#include "bgpd/bgp_mplsvpn_snmp.h" /* BGP4-MIB described in RFC1657. */ #define BGP4MIB 1,3,6,1,2,1,15 @@ -849,8 +850,9 @@ static uint8_t *bgp4PathAttrTable(struct variable *v, oid name[], } /* BGP Traps. */ -static struct trap_object bgpTrapList[] = {{3, {3, 1, BGPPEERLASTERROR}}, - {3, {3, 1, BGPPEERSTATE}}}; +static struct trap_object bgpTrapList[] = {{3, {3, 1, BGPPEERREMOTEADDR} }, + {3, {3, 1, BGPPEERLASTERROR} }, + {3, {3, 1, BGPPEERSTATE} } }; static int bgpTrapEstablished(struct peer *peer) { @@ -868,10 +870,10 @@ static int bgpTrapEstablished(struct peer *peer) oid_copy_addr(index, &addr, IN_ADDR_SIZE); - smux_trap(bgp_variables, array_size(bgp_variables), bgp_trap_oid, - array_size(bgp_trap_oid), bgp_oid, - sizeof(bgp_oid) / sizeof(oid), index, IN_ADDR_SIZE, - bgpTrapList, array_size(bgpTrapList), BGPESTABLISHED); + ret = smux_trap(bgp_variables, array_size(bgp_variables), bgp_trap_oid, + array_size(bgp_trap_oid), bgp_oid, + sizeof(bgp_oid) / sizeof(oid), index, IN_ADDR_SIZE, + bgpTrapList, array_size(bgpTrapList), BGPESTABLISHED); return 0; } @@ -898,6 +900,7 @@ static int bgp_snmp_init(struct thread_master *tm) { smux_init(tm); REGISTER_MIB("mibII/bgp", bgp_variables, variable, bgp_oid); + bgp_mpls_l3vpn_module_init(); return 0; } diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index a89c245e2a..8d06a4b374 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -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; } diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index f7c4b04adf..ca7da8070c 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -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; } diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 9073e5a32e..b11fd5288a 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -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); } diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 9089608062..9f453bf1e6 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -310,6 +310,15 @@ 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; + uint32_t routes_added; + uint32_t routes_deleted; +}; + /* BGP instance structure. */ struct bgp { /* AS number of this BGP instance. */ @@ -363,6 +372,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) @@ -2246,6 +2257,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) { @@ -2283,7 +2315,14 @@ 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)) +DECLARE_HOOK(bgp_snmp_update_stats, + (struct bgp_node *rn, struct bgp_path_info *pi, bool added), + (rn, pi, added)) void peer_nsf_stop(struct peer *peer); #endif /* _QUAGGA_BGPD_H */ diff --git a/bgpd/subdir.am b/bgpd/subdir.am index df1555c32a..4614363bf0 100644 --- a/bgpd/subdir.am +++ b/bgpd/subdir.am @@ -167,6 +167,7 @@ noinst_HEADERS += \ bgpd/bgp_memory.h \ bgpd/bgp_mpath.h \ bgpd/bgp_mplsvpn.h \ + bgpd/bgp_mplsvpn_snmp.h \ bgpd/bgp_network.h \ bgpd/bgp_nexthop.h \ bgpd/bgp_nht.h \ @@ -218,7 +219,7 @@ bgpd_bgp_btoa_CFLAGS = $(AM_CFLAGS) bgpd_bgpd_LDADD = bgpd/libbgp.a $(RFPLDADD) lib/libfrr.la $(LIBCAP) $(LIBM) $(UST_LIBS) bgpd_bgp_btoa_LDADD = bgpd/libbgp.a $(RFPLDADD) lib/libfrr.la $(LIBCAP) $(LIBM) $(UST_LIBS) -bgpd_bgpd_snmp_la_SOURCES = bgpd/bgp_snmp.c +bgpd_bgpd_snmp_la_SOURCES = bgpd/bgp_snmp.c bgpd/bgp_mplsvpn_snmp.c bgpd_bgpd_snmp_la_CFLAGS = $(WERROR) $(SNMP_CFLAGS) -std=gnu99 bgpd_bgpd_snmp_la_LDFLAGS = -avoid-version -module -shared -export-dynamic bgpd_bgpd_snmp_la_LIBADD = lib/libfrrsnmp.la diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index cb2444da54..cb97ee22df 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -1488,6 +1488,9 @@ Configuring Peers directly connected and this knob is not enabled, the session will not establish. + If the peer's IP address is not in the RIB and is reachable via the + default route, then you have to enable ``ip nht resolve-via-default``. + .. index:: neighbor PEER description ... .. clicmd:: [no] neighbor PEER description ... diff --git a/lib/agentx.c b/lib/agentx.c index f049d699a8..dfe5d93754 100644 --- a/lib/agentx.c +++ b/lib/agentx.c @@ -271,6 +271,23 @@ int smux_trap(struct variable *vp, size_t vp_len, const oid *ename, const struct trap_object *trapobj, size_t trapobjlen, uint8_t sptrap) { + struct index_oid trap_index[1]; + + /* copy the single index into the multi-index format */ + oid_copy(trap_index[0].indexname, iname, inamelen); + trap_index[0].indexlen = inamelen; + + return (smux_trap_multi_index( + vp, vp_len, ename, enamelen, name, namelen, trap_index, + array_size(trap_index), trapobj, trapobjlen, sptrap)); +} + +int smux_trap_multi_index(struct variable *vp, size_t vp_len, const oid *ename, + size_t enamelen, const oid *name, size_t namelen, + struct index_oid *iname, size_t index_len, + const struct trap_object *trapobj, size_t trapobjlen, + uint8_t sptrap) +{ oid objid_snmptrap[] = {1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0}; size_t objid_snmptrap_len = sizeof(objid_snmptrap) / sizeof(oid); oid notification_oid[MAX_OID_LEN]; @@ -299,6 +316,13 @@ int smux_trap(struct variable *vp, size_t vp_len, const oid *ename, size_t val_len; WriteMethod *wm = NULL; struct variable cvp; + unsigned int iindex; + /* + * this allows the behaviour of smux_trap with a singe index + * for all objects to be maintained whilst allowing traps which + * have different indices per object to be supported + */ + iindex = (index_len == 1) ? 0 : i; /* Make OID. */ if (trapobj[i].namelen > 0) { @@ -306,8 +330,10 @@ int smux_trap(struct variable *vp, size_t vp_len, const oid *ename, onamelen = trapobj[i].namelen; oid_copy(oid, name, namelen); oid_copy(oid + namelen, trapobj[i].name, onamelen); - oid_copy(oid + namelen + onamelen, iname, inamelen); - oid_len = namelen + onamelen + inamelen; + oid_copy(oid + namelen + onamelen, + iname[iindex].indexname, + iname[iindex].indexlen); + oid_len = namelen + onamelen + iname[iindex].indexlen; } else { /* Scalar object */ onamelen = trapobj[i].namelen * (-1); @@ -333,6 +359,7 @@ int smux_trap(struct variable *vp, size_t vp_len, const oid *ename, cvp.magic = vp[j].magic; cvp.acl = vp[j].acl; cvp.findVar = vp[j].findVar; + /* Grab the result. */ val = cvp.findVar(&cvp, oid, &oid_len, 1, &val_len, &wm); @@ -351,6 +351,40 @@ struct interface *if_lookup_by_index(ifindex_t ifindex, vrf_id_t vrf_id) return NULL; } +/* Interface existance check by index. */ +struct interface *if_vrf_lookup_by_index_next(ifindex_t ifindex, + vrf_id_t vrf_id) +{ + struct vrf *vrf = vrf_lookup_by_id(vrf_id); + struct interface *tmp_ifp; + bool found = false; + + if (!vrf) + return NULL; + + if (ifindex == 0) { + tmp_ifp = RB_MIN(if_index_head, &vrf->ifaces_by_index); + /* skip the vrf interface */ + if (tmp_ifp && if_is_vrf(tmp_ifp)) + ifindex = tmp_ifp->ifindex; + else + return tmp_ifp; + } + + RB_FOREACH (tmp_ifp, if_index_head, &vrf->ifaces_by_index) { + if (found) { + /* skip the vrf interface */ + if (tmp_ifp && if_is_vrf(tmp_ifp)) + continue; + else + return tmp_ifp; + } + if (tmp_ifp->ifindex == ifindex) + found = true; + } + return NULL; +} + const char *ifindex2ifname(ifindex_t ifindex, vrf_id_t vrf_id) { struct interface *ifp; @@ -513,6 +513,8 @@ extern struct interface *if_create_name(const char *name, vrf_id_t vrf_id); /* Create new interface, adds to index list only */ extern struct interface *if_create_ifindex(ifindex_t ifindex, vrf_id_t vrf_id); extern struct interface *if_lookup_by_index(ifindex_t, vrf_id_t vrf_id); +extern struct interface *if_vrf_lookup_by_index_next(ifindex_t ifindex, + vrf_id_t vrf_id); extern struct interface *if_lookup_by_index_all_vrf(ifindex_t); extern struct interface *if_lookup_exact_address(const void *matchaddr, int family, vrf_id_t vrf_id); diff --git a/lib/smux.h b/lib/smux.h index 6896f02354..ff97c8ab51 100644 --- a/lib/smux.h +++ b/lib/smux.h @@ -44,6 +44,29 @@ extern "C" { #define IN_ADDR_SIZE sizeof(struct in_addr) +/* IANAipRouteProtocol */ +#define IANAIPROUTEPROTOCOLOTHER 1 +#define IANAIPROUTEPROTOCOLLOCAL 2 +#define IANAIPROUTEPROTOCOLNETMGMT 3 +#define IANAIPROUTEPROTOCOLICMP 4 +#define IANAIPROUTEPROTOCOLEGP 5 +#define IANAIPROUTEPROTOCOLGGP 6 +#define IANAIPROUTEPROTOCOLHELLO 7 +#define IANAIPROUTEPROTOCOLRIP 8 +#define IANAIPROUTEPROTOCOLISIS 9 +#define IANAIPROUTEPROTOCOLESIS 10 +#define IANAIPROUTEPROTOCOLCISCOIGRP 11 +#define IANAIPROUTEPROTOCOLBBNSPFIGP 12 +#define IANAIPROUTEPROTOCOLOSPF 13 +#define IANAIPROUTEPROTOCOLBGP 14 +#define IANAIPROUTEPROTOCOLIDPR 15 +#define IANAIPROUTEPROTOCOLCISCOEIGRP 16 +#define IANAIPROUTEPROTOCOLDVMRP 17 + +#define INETADDRESSTYPEUNKNOWN 0 +#define INETADDRESSTYPEIPV4 1 +#define INETADDRESSTYPEIPV6 2 + #undef REGISTER_MIB #define REGISTER_MIB(descr, var, vartype, theoid) \ smux_register_mib(descr, (struct variable *)var, \ @@ -56,19 +79,29 @@ struct trap_object { oid name[MAX_OID_LEN]; }; +struct index_oid { + int indexlen; + oid indexname[MAX_OID_LEN]; +}; /* Declare SMUX return value. */ #define SNMP_LOCAL_VARIABLES \ static long snmp_int_val __attribute__((unused)); \ static struct in_addr snmp_in_addr_val __attribute__((unused)); - + static uint8_t snmp_octet_val __attribute__((unused)); #define SNMP_INTEGER(V) \ (*var_len = sizeof(snmp_int_val), snmp_int_val = V, \ (uint8_t *)&snmp_int_val) +#define SNMP_OCTET(V) \ + (*var_len = sizeof(snmp_octet_val), snmp_octet_val = V, \ + (uint8_t *)&snmp_octet_val) + #define SNMP_IPADDRESS(V) \ (*var_len = sizeof(struct in_addr), snmp_in_addr_val = V, \ (uint8_t *)&snmp_in_addr_val) +#define SNMP_IP6ADDRESS(V) (*var_len = sizeof(struct in6_addr), (uint8_t *)&V) + extern void smux_init(struct thread_master *tm); extern void smux_register_mib(const char *, struct variable *, size_t, int, oid[], size_t); @@ -102,10 +135,20 @@ extern int smux_trap(struct variable *, size_t, const oid *, size_t, const oid *, size_t, const oid *, size_t, const struct trap_object *, size_t, uint8_t); +extern int smux_trap_multi_index(struct variable *vp, size_t vp_len, + const oid *ename, size_t enamelen, + const oid *name, size_t namelen, + struct index_oid *iname, size_t index_len, + const struct trap_object *trapobj, + size_t trapobjlen, uint8_t sptrap); extern int oid_compare(const oid *, int, const oid *, int); extern void oid2in_addr(oid[], int, struct in_addr *); +extern void oid2int(oid oid[], int *dest); extern void *oid_copy(void *, const void *, size_t); extern void oid_copy_addr(oid[], const struct in_addr *, int); +extern void oid_copy_int(oid oid[], int *val); +extern void oid2string(oid oid[], int len, char *string); +extern void oid_copy_str(oid oid[], const char *string, int len); #ifdef __cplusplus } diff --git a/lib/snmp.c b/lib/snmp.c index 736a3c62b8..e92f622bb9 100644 --- a/lib/snmp.c +++ b/lib/snmp.c @@ -64,6 +64,19 @@ void oid2in_addr(oid oid[], int len, struct in_addr *addr) *pnt++ = oid[i]; } +void oid2int(oid oid[], int *dest) +{ + uint8_t i; + uint8_t *pnt; + int network_dest; + + pnt = (uint8_t *)&network_dest; + + for (i = 0; i < sizeof(int); i++) + *pnt++ = oid[i]; + *dest = ntohl(network_dest); +} + void oid_copy_addr(oid oid[], const struct in_addr *addr, int len) { int i; @@ -78,6 +91,47 @@ void oid_copy_addr(oid oid[], const struct in_addr *addr, int len) oid[i] = *pnt++; } +void oid_copy_int(oid oid[], int *val) +{ + uint8_t i; + const uint8_t *pnt; + int network_val; + + network_val = htonl(*val); + pnt = (uint8_t *)&network_val; + + for (i = 0; i < sizeof(int); i++) + 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++ = (uint8_t)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) { @@ -214,6 +214,53 @@ struct vrf *vrf_get(vrf_id_t vrf_id, const char *name) return vrf; } +/* Update a VRF. If not found, create one. + * Arg: + * name - The name of the vrf. + * vrf_id - The vrf_id of the vrf. + * Description: This function first finds the vrf using its name. If the vrf is + * found and the vrf-id of the existing vrf does not match the new vrf id, it + * will disable the existing vrf and update it with new vrf-id. If the vrf is + * not found, it will create the vrf with given name and the new vrf id. + */ +struct vrf *vrf_update(vrf_id_t new_vrf_id, const char *name) +{ + struct vrf *vrf = NULL; + + /*Treat VRF add for existing vrf as update + * Update VRF ID and also update in VRF ID table + */ + if (name) + vrf = vrf_lookup_by_name(name); + if (vrf && new_vrf_id != VRF_UNKNOWN && vrf->vrf_id != VRF_UNKNOWN + && vrf->vrf_id != new_vrf_id) { + if (debug_vrf) { + zlog_debug( + "Vrf Update event: %s old id: %u, new id: %u", + name, vrf->vrf_id, new_vrf_id); + } + + /*Disable the vrf to simulate implicit delete + * so that all stale routes are deleted + * This vrf will be enabled down the line + */ + vrf_disable(vrf); + + + RB_REMOVE(vrf_id_head, &vrfs_by_id, vrf); + vrf->vrf_id = new_vrf_id; + RB_INSERT(vrf_id_head, &vrfs_by_id, vrf); + + } else { + + /* + * vrf_get is implied creation if it does not exist + */ + vrf = vrf_get(new_vrf_id, name); + } + return vrf; +} + /* Delete a VRF. This is called when the underlying VRF goes away, a * pre-configured VRF is deleted or when shutting down (vrf_terminate()). */ @@ -1064,6 +1111,7 @@ static int lib_vrf_create(struct nb_cb_create_args *args) vrfp = vrf_get(VRF_UNKNOWN, vrfname); + vrf_set_user_cfged(vrfp); nb_running_set_entry(args->dnode, vrfp); return NB_OK; @@ -1089,7 +1137,7 @@ static int lib_vrf_destroy(struct nb_cb_destroy_args *args) vrfp = nb_running_unset_entry(args->dnode); /* Clear configured flag and invoke delete. */ - UNSET_FLAG(vrfp->status, VRF_CONFIGURED); + vrf_reset_user_cfged(vrfp); vrf_delete(vrfp); break; } @@ -114,6 +114,7 @@ extern struct vrf_name_head vrfs_by_name; extern struct vrf *vrf_lookup_by_id(vrf_id_t); extern struct vrf *vrf_lookup_by_name(const char *); extern struct vrf *vrf_get(vrf_id_t, const char *); +extern struct vrf *vrf_update(vrf_id_t new_vrf_id, const char *name); extern const char *vrf_id_to_name(vrf_id_t vrf_id); extern vrf_id_t vrf_name_to_id(const char *); @@ -167,6 +168,20 @@ static inline void vrf_reset_user_cfged(struct vrf *vrf) UNSET_FLAG(vrf->status, VRF_CONFIGURED); } +static inline uint32_t vrf_interface_count(struct vrf *vrf) +{ + uint32_t count = 0; + struct interface *ifp; + + RB_FOREACH (ifp, if_name_head, &vrf->ifaces_by_name) { + /* skip the l3mdev */ + if (strncmp(ifp->name, vrf->name, VRF_NAMSIZ) == 0) + continue; + count++; + } + return count; +} + /* * Utilities to obtain the user data */ diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/ce1/bgpd.conf b/tests/topotests/bgp-snmp-mplsl3vpn/ce1/bgpd.conf new file mode 100644 index 0000000000..b598666dfb --- /dev/null +++ b/tests/topotests/bgp-snmp-mplsl3vpn/ce1/bgpd.conf @@ -0,0 +1,12 @@ +log file /tmp/bgpd.log debugging +! +router bgp 65001 + timers bgp 3 9 + bgp router-id 192.168.100.10 + neighbor 192.168.100.20 remote-as 65001 + neighbor 192.168.100.20 update-source 192.168.100.10 + ! + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/ce1/snmpd.conf b/tests/topotests/bgp-snmp-mplsl3vpn/ce1/snmpd.conf new file mode 100644 index 0000000000..36218d3538 --- /dev/null +++ b/tests/topotests/bgp-snmp-mplsl3vpn/ce1/snmpd.conf @@ -0,0 +1,15 @@ +agentAddress udp:10.5.5.5:161 + +com2sec public localhost public + +group public_group v1 public +group public_group v2c public + +access public_group "" any noauth prefix all all none + +view all included .1 + +iquerySecName frr +rouser frr + +master agentx diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/ce1/zebra.conf b/tests/topotests/bgp-snmp-mplsl3vpn/ce1/zebra.conf new file mode 100644 index 0000000000..8ad2ddc48c --- /dev/null +++ b/tests/topotests/bgp-snmp-mplsl3vpn/ce1/zebra.conf @@ -0,0 +1,19 @@ +log file /tmp/zebra.log +log stdout +! +debug zebra events +debug zebra dplane +! +! +interface ce1-eth0 + ip address 192.168.100.10/24 + ipv6 address 2000:1:1:100::10/64 +! +! +interface lo + ip address 10.5.5.5/32 + ipv6 address 2000:5:5:5::5/128 +! +! +! +line vty diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/ce2/bgpd.conf b/tests/topotests/bgp-snmp-mplsl3vpn/ce2/bgpd.conf new file mode 100644 index 0000000000..e388ccba8a --- /dev/null +++ b/tests/topotests/bgp-snmp-mplsl3vpn/ce2/bgpd.conf @@ -0,0 +1,12 @@ +log file /tmp/bgpd.log debugging +! +router bgp 65001 + bgp router-id 192.168.200.10 + timers bgp 3 9 + neighbor 192.168.200.20 remote-as 65001 + neighbor 192.168.200.20 update-source 192.168.200.10 + ! + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/ce2/snmpd.conf b/tests/topotests/bgp-snmp-mplsl3vpn/ce2/snmpd.conf new file mode 100644 index 0000000000..714585cb9b --- /dev/null +++ b/tests/topotests/bgp-snmp-mplsl3vpn/ce2/snmpd.conf @@ -0,0 +1,15 @@ +agentAddress udp:10.6.6.6:161 + +com2sec public localhost public + +group public_group v1 public +group public_group v2c public + +access public_group "" any noauth prefix all all none + +view all included .1 + +iquerySecName frr +rouser frr + +master agentx diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/ce2/zebra.conf b/tests/topotests/bgp-snmp-mplsl3vpn/ce2/zebra.conf new file mode 100644 index 0000000000..fa2e968e55 --- /dev/null +++ b/tests/topotests/bgp-snmp-mplsl3vpn/ce2/zebra.conf @@ -0,0 +1,19 @@ +log file /tmp/zebra.log +log stdout +! +debug zebra events +debug zebra dplane +! +! +interface ce2-eth0 + ip address 192.168.200.10/24 + ipv6 address 2000:1:1:200::10/64 +! +! +interface lo + ip address 10.6.6.6/32 + ipv6 address 2000:6:6:6::6/128 +! +! +! +line vty diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/ce3/bgpd.conf b/tests/topotests/bgp-snmp-mplsl3vpn/ce3/bgpd.conf new file mode 100644 index 0000000000..e388ccba8a --- /dev/null +++ b/tests/topotests/bgp-snmp-mplsl3vpn/ce3/bgpd.conf @@ -0,0 +1,12 @@ +log file /tmp/bgpd.log debugging +! +router bgp 65001 + bgp router-id 192.168.200.10 + timers bgp 3 9 + neighbor 192.168.200.20 remote-as 65001 + neighbor 192.168.200.20 update-source 192.168.200.10 + ! + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/ce3/snmpd.conf b/tests/topotests/bgp-snmp-mplsl3vpn/ce3/snmpd.conf new file mode 100644 index 0000000000..36218d3538 --- /dev/null +++ b/tests/topotests/bgp-snmp-mplsl3vpn/ce3/snmpd.conf @@ -0,0 +1,15 @@ +agentAddress udp:10.5.5.5:161 + +com2sec public localhost public + +group public_group v1 public +group public_group v2c public + +access public_group "" any noauth prefix all all none + +view all included .1 + +iquerySecName frr +rouser frr + +master agentx diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/ce3/zebra.conf b/tests/topotests/bgp-snmp-mplsl3vpn/ce3/zebra.conf new file mode 100644 index 0000000000..ea91e21bad --- /dev/null +++ b/tests/topotests/bgp-snmp-mplsl3vpn/ce3/zebra.conf @@ -0,0 +1,19 @@ +log file /tmp/zebra.log +log stdout +! +debug zebra events +debug zebra dplane +! +! +interface ce3-eth0 + ip address 192.168.200.10/24 + ipv6 address 2000:1:1:200::10/64 +! +! +interface lo + ip address 10.7.7.7/32 + ipv6 address 2000:7:7:7::7/128 +! +! +! +line vty diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/ce4/bgpd.conf b/tests/topotests/bgp-snmp-mplsl3vpn/ce4/bgpd.conf new file mode 100644 index 0000000000..e388ccba8a --- /dev/null +++ b/tests/topotests/bgp-snmp-mplsl3vpn/ce4/bgpd.conf @@ -0,0 +1,12 @@ +log file /tmp/bgpd.log debugging +! +router bgp 65001 + bgp router-id 192.168.200.10 + timers bgp 3 9 + neighbor 192.168.200.20 remote-as 65001 + neighbor 192.168.200.20 update-source 192.168.200.10 + ! + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/ce4/snmpd.conf b/tests/topotests/bgp-snmp-mplsl3vpn/ce4/snmpd.conf new file mode 100644 index 0000000000..36218d3538 --- /dev/null +++ b/tests/topotests/bgp-snmp-mplsl3vpn/ce4/snmpd.conf @@ -0,0 +1,15 @@ +agentAddress udp:10.5.5.5:161 + +com2sec public localhost public + +group public_group v1 public +group public_group v2c public + +access public_group "" any noauth prefix all all none + +view all included .1 + +iquerySecName frr +rouser frr + +master agentx diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/ce4/zebra.conf b/tests/topotests/bgp-snmp-mplsl3vpn/ce4/zebra.conf new file mode 100644 index 0000000000..0866fa9759 --- /dev/null +++ b/tests/topotests/bgp-snmp-mplsl3vpn/ce4/zebra.conf @@ -0,0 +1,19 @@ +log file /tmp/zebra.log +log stdout +! +debug zebra events +debug zebra dplane +! +! +interface ce4-eth0 + ip address 192.168.34.10/24 + ipv6 address 2000:1:1:300::10/64 +! +! +interface lo + ip address 10.8.8.8/32 + ipv6 address 2000:8:8:8::8/128 +! +! +! +line vty diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/r1/bgpd.conf b/tests/topotests/bgp-snmp-mplsl3vpn/r1/bgpd.conf new file mode 100644 index 0000000000..098e55d0ed --- /dev/null +++ b/tests/topotests/bgp-snmp-mplsl3vpn/r1/bgpd.conf @@ -0,0 +1,48 @@ +log file /tmp/bgpd.log debugging +! +router bgp 65000 + bgp router-id 10.1.1.1 + neighbor 10.4.4.4 remote-as 65000 + neighbor 10.4.4.4 update-source 10.1.1.1 + neighbor 10.4.4.4 timers connect 10 + ! + address-family ipv4 vpn + neighbor 10.4.4.4 activate + exit-address-family + +! +router bgp 65001 vrf VRF-a + bgp router-id 192.168.100.20 + timers bgp 3 9 + neighbor 192.168.100.10 remote-as 65001 + neighbor 192.168.100.10 update-source 192.168.100.20 + neighbor 192.168.200.10 remote-as 65001 + neighbor 192.168.200.10 update-source 192.168.200.20 + ! + address-family ipv4 unicast + redistribute connected + redistribute isis + label vpn export 1111 + rd vpn export 10:1 + rt vpn both 1:1 + export vpn + import vpn + exit-address-family + +router bgp 65002 vrf VRF-b + bgp router-id 192.168.10.20 + timers bgp 3 9 + neighbor 192.168.10.10 remote-as 65003 + neighbor 192.168.10.10 update-source 192.168.10.20 +! + address-family ipv4 unicast + redistribute connected + redistribute isis + label vpn export 6666 + rd vpn export 10:2 + rt vpn both 1:2 + export vpn + import vpn + exit-address-family + +agentx diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/r1/isisd.conf b/tests/topotests/bgp-snmp-mplsl3vpn/r1/isisd.conf new file mode 100644 index 0000000000..b5ca993da3 --- /dev/null +++ b/tests/topotests/bgp-snmp-mplsl3vpn/r1/isisd.conf @@ -0,0 +1,46 @@ +log stdout debugging +! +debug isis route-events +debug isis events +! +interface r1-eth0 + ip router isis ISIS1 + ipv6 router isis ISIS1 + isis circuit-type level-1 + no isis hello padding + isis hello-interval 1 + isis hello-multiplier 3 + isis network point-to-point +! +interface r1-eth1 + ip router isis ISIS1 + ipv6 router isis ISIS1 + isis circuit-type level-1 + no isis hello padding + isis hello-interval 1 + isis hello-multiplier 3 + isis network point-to-point +! +interface r1-eth2 + ip router isis ISIS1 + ipv6 router isis ISIS1 + isis circuit-type level-1 + no isis hello padding + isis hello-interval 1 + isis hello-multiplier 3 + isis network point-to-point +! +interface lo + ip router isis ISIS1 + ipv6 router isis ISIS1 + isis circuit-type level-1 + isis passive + no isis hello padding +! +router isis ISIS1 + net 01.1111.0000.0000.0001.00 + is-type level-1 + topology ipv6-unicast +! +line vty +! diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/r1/snmpd.conf b/tests/topotests/bgp-snmp-mplsl3vpn/r1/snmpd.conf new file mode 100644 index 0000000000..c903c1ad2e --- /dev/null +++ b/tests/topotests/bgp-snmp-mplsl3vpn/r1/snmpd.conf @@ -0,0 +1,17 @@ +agentAddress udp:10.1.1.1:161 + +com2sec public 10.1.1.1 public + +group public_group v1 public +group public_group v2c public + +access public_group "" any noauth prefix all all none + +view all included .1 + +iquerySecName frr +rouser frr + +master agentx + +noRangeCheck yes
\ No newline at end of file diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/r1/zebra.conf b/tests/topotests/bgp-snmp-mplsl3vpn/r1/zebra.conf new file mode 100644 index 0000000000..7228ae6bd2 --- /dev/null +++ b/tests/topotests/bgp-snmp-mplsl3vpn/r1/zebra.conf @@ -0,0 +1,33 @@ +log file zebra.log +! +interface r1-eth0 + ip address 192.168.12.12/24 + ipv6 address 2000:1:1:12::12/64 +! +interface r1-eth1 + ip address 192.168.13.13/24 + ipv6 address 2000:1:1:13::13/64 +! +interface r1-eth2 + ip address 192.168.14.14/24 + ipv6 address 2000:1:1:14::14/64 +! +interface r1-eth3 vrf VRF-a + ip address 192.168.100.20/24 + ipv6 address 2000:1:1:100::20/64 +! +interface r1-eth4 vrf VRF-a + ip address 192.168.200.20/24 + ipv6 address 2000:1:1:200::20/64 +! +interface r1-eth5 vrf VRF-b + ip address 192.168.300.20/24 + ipv6 address 2000:1:1:300::20/64 + +interface lo + ip address 10.1.1.1/32 + ipv6 address 2000:1:1:1::1/128 +! +! +! +line vty diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/r2/isisd.conf b/tests/topotests/bgp-snmp-mplsl3vpn/r2/isisd.conf new file mode 100644 index 0000000000..3dfa43831a --- /dev/null +++ b/tests/topotests/bgp-snmp-mplsl3vpn/r2/isisd.conf @@ -0,0 +1,37 @@ +log stdout debugging +! +debug isis route-events +debug isis events +! +interface r2-eth0 + ip router isis ISIS1 + ipv6 router isis ISIS1 + isis circuit-type level-1 + no isis hello padding + isis hello-interval 1 + isis hello-multiplier 3 + isis network point-to-point +! +interface r2-eth1 + ip router isis ISIS1 + ipv6 router isis ISIS1 + isis circuit-type level-1 + no isis hello padding + isis hello-interval 1 + isis hello-multiplier 3 + isis network point-to-point +! +interface lo + ip router isis ISIS1 + ipv6 router isis ISIS1 + isis circuit-type level-1 + isis passive + no isis hello padding +! +router isis ISIS1 + net 01.1111.0000.0000.0002.00 + is-type level-1 + topology ipv6-unicast +! +line vty +! diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/r2/snmpd.conf b/tests/topotests/bgp-snmp-mplsl3vpn/r2/snmpd.conf new file mode 100644 index 0000000000..0cfebc7238 --- /dev/null +++ b/tests/topotests/bgp-snmp-mplsl3vpn/r2/snmpd.conf @@ -0,0 +1,15 @@ +agentAddress udp:10.2.2.2:161 + +com2sec public localhost public + +group public_group v1 public +group public_group v2c public + +access public_group "" any noauth prefix all all none + +view all included .1 + +iquerySecName frr +rouser frr + +master agentx diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/r2/zebra.conf b/tests/topotests/bgp-snmp-mplsl3vpn/r2/zebra.conf new file mode 100644 index 0000000000..9bc4331bae --- /dev/null +++ b/tests/topotests/bgp-snmp-mplsl3vpn/r2/zebra.conf @@ -0,0 +1,24 @@ +log file /tmp/zebra.log +log stdout +! +debug zebra events +debug zebra dplane +! +! +interface r2-eth0 + ip address 192.168.12.21/24 + ipv6 address 2000:1:1:12::21/64 +! +interface r2-eth1 + ip address 192.168.23.23/24 + ipv6 address 2000:1:1:23::23/64 +! +! +interface lo + ip address 10.2.2.2/32 + ipv6 address 2000:2:2:2::2/128 +! +! +! +line vty +! diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/r3/isisd.conf b/tests/topotests/bgp-snmp-mplsl3vpn/r3/isisd.conf new file mode 100644 index 0000000000..578ebafad6 --- /dev/null +++ b/tests/topotests/bgp-snmp-mplsl3vpn/r3/isisd.conf @@ -0,0 +1,45 @@ +log stdout debugging +! +debug isis route-events +debug isis events +! +interface r3-eth0 + ip router isis ISIS1 + ipv6 router isis ISIS1 + isis circuit-type level-1 + no isis hello padding + isis hello-interval 1 + isis hello-multiplier 3 + isis network point-to-point +! +interface r3-eth1 + ip router isis ISIS1 + ipv6 router isis ISIS1 + isis circuit-type level-1 + no isis hello padding + isis hello-interval 1 + isis hello-multiplier 3 + isis network point-to-point +! +interface r3-eth2 + ip router isis ISIS1 + ipv6 router isis ISIS1 + isis circuit-type level-1 + no isis hello padding + isis hello-interval 1 + isis hello-multiplier 3 + isis network point-to-point +! +interface lo + ip router isis ISIS1 + ipv6 router isis ISIS1 + isis circuit-type level-1 + isis passive + no isis hello padding +! +router isis ISIS1 + net 01.1111.0000.0000.0003.00 + is-type level-1 + topology ipv6-unicast +! +line vty diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/r3/snmpd.conf b/tests/topotests/bgp-snmp-mplsl3vpn/r3/snmpd.conf new file mode 100644 index 0000000000..b9eb00ea52 --- /dev/null +++ b/tests/topotests/bgp-snmp-mplsl3vpn/r3/snmpd.conf @@ -0,0 +1,15 @@ +agentAddress udp:10.3.3.3:161 + +com2sec public localhost public + +group public_group v1 public +group public_group v2c public + +access public_group "" any noauth prefix all all none + +view all included .1 + +iquerySecName frr +rouser frr + +master agentx diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/r3/zebra.conf b/tests/topotests/bgp-snmp-mplsl3vpn/r3/zebra.conf new file mode 100644 index 0000000000..4d2007e787 --- /dev/null +++ b/tests/topotests/bgp-snmp-mplsl3vpn/r3/zebra.conf @@ -0,0 +1,27 @@ +log file /tmp/zebra.log +log stdout +! +debug zebra events +debug zebra dplane +! +! +interface r3-eth0 + ip address 192.168.13.31/24 + ipv6 address 2000:1:1:13::31/64 +! +interface r3-eth1 + ip address 192.168.23.32/24 + ipv6 address 2000:1:1:23::32/64 +! +interface r3-eth2 + ip address 192.168.34.34/24 + ipv6 address 2000:1:1:34::34/64 +! +! +interface lo + ip address 10.3.3.3/32 + ipv6 address 2000:3:3:3::3/128 +! +! +! +line vty diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/r4/bgpd.conf b/tests/topotests/bgp-snmp-mplsl3vpn/r4/bgpd.conf new file mode 100644 index 0000000000..2a834c799e --- /dev/null +++ b/tests/topotests/bgp-snmp-mplsl3vpn/r4/bgpd.conf @@ -0,0 +1,43 @@ +log file /tmp/bgpd.log debugging +! +router bgp 65000 + bgp router-id 10.4.4.4 + timers bgp 3 9 + neighbor 10.1.1.1 remote-as 65000 + neighbor 10.1.1.1 update-source 10.4.4.4 + neighbor 10.1.1.1 timers connect 10 + ! + address-family ipv4 vpn + neighbor 10.1.1.1 activate + exit-address-family +! + + address-family ipv6 vpn + neighbor 10.1.1.1 activate + exit-address-family +! +router bgp 65001 vrf VRF-a + bgp router-id 192.168.200.20 + timers bgp 3 9 + neighbor 192.168.200.10 remote-as 65001 + neighbor 192.168.200.10 update-source 192.168.200.20 + ! + address-family ipv4 unicast + redistribute connected + redistribute isis + label vpn export 1111 + rd vpn export 10:3 + rt vpn both 1:1 + export vpn + import vpn + exit-address-family + + address-family ipv6 unicast + redistribute connected + redistribute isis + label vpn export 1111 + rd vpn export 10:3 + rt vpn both 1:2 + export vpn + import vpn + exit-address-family diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/r4/isisd.conf b/tests/topotests/bgp-snmp-mplsl3vpn/r4/isisd.conf new file mode 100644 index 0000000000..3e9e9af45f --- /dev/null +++ b/tests/topotests/bgp-snmp-mplsl3vpn/r4/isisd.conf @@ -0,0 +1,36 @@ +log stdout debugging +! +debug isis route-events +debug isis events +! +interface r4-eth0 + ip router isis ISIS1 + ipv6 router isis ISIS1 + isis circuit-type level-1 + no isis hello padding + isis hello-interval 1 + isis hello-multiplier 3 + isis network point-to-point +! +interface r4-eth1 + ip router isis ISIS1 + ipv6 router isis ISIS1 + isis circuit-type level-1 + no isis hello padding + isis hello-interval 1 + isis hello-multiplier 3 + isis network point-to-point +! +interface lo + ip router isis ISIS1 + ipv6 router isis ISIS1 + isis circuit-type level-1 + isis passive + no isis hello padding +! +router isis ISIS1 + net 01.1111.0000.0000.0004.00 + is-type level-1 + topology ipv6-unicast +! +line vty diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/r4/snmpd.conf b/tests/topotests/bgp-snmp-mplsl3vpn/r4/snmpd.conf new file mode 100644 index 0000000000..ec35f9f9c9 --- /dev/null +++ b/tests/topotests/bgp-snmp-mplsl3vpn/r4/snmpd.conf @@ -0,0 +1,15 @@ +agentAddress udp:10.4.4.4:161 + +com2sec public localhost public + +group public_group v1 public +group public_group v2c public + +access public_group "" any noauth prefix all all none + +view all included .1 + +iquerySecName frr +rouser frr + +master agentx diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/r4/zebra.conf b/tests/topotests/bgp-snmp-mplsl3vpn/r4/zebra.conf new file mode 100644 index 0000000000..c48407c108 --- /dev/null +++ b/tests/topotests/bgp-snmp-mplsl3vpn/r4/zebra.conf @@ -0,0 +1,27 @@ +log file /tmp/zebra.log +log stdout +! +debug zebra events +debug zebra dplane +! +! +interface r4-eth0 + ip address 192.168.14.41/24 + ipv6 address 2000:1:1:14::41/64 +! +interface r4-eth1 + ip address 192.168.34.43/24 + ipv6 address 2000:1:1:34::43/64 +! +interface r4-eth2 vrf aaa + ip address 192.168.200.20/24 + ipv6 address 2000:1:1:200::20/64 +! +! +interface lo + ip address 10.4.4.4/32 + ipv6 address 2000:4:4:4::4/128 +! +! +line vty +! diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/test_bgp_snmp_mplsvpn.py b/tests/topotests/bgp-snmp-mplsl3vpn/test_bgp_snmp_mplsvpn.py new file mode 100755 index 0000000000..5eb1738632 --- /dev/null +++ b/tests/topotests/bgp-snmp-mplsl3vpn/test_bgp_snmp_mplsvpn.py @@ -0,0 +1,738 @@ +#!/usr/bin/env python + +# +# test_bgp_snmp_mplsl3vpn.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 by Volta Networks +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_bgp_snmp_mplsl3vpn.py: Test mplsL3Vpn MIB [RFC4382]. +""" + +import os +import sys +import json +from functools import partial +from time import sleep +import pytest +import re + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from lib.snmptest import SnmpTester + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + + +class TemplateTopo(Topo): + "Test topology builder" + + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # This function only purpose is to define allocation and relationship + # between routers, switches and hosts. + # + # + # Create routers + tgen.add_router("r1") + tgen.add_router("r2") + tgen.add_router("r3") + tgen.add_router("r4") + tgen.add_router("ce1") + tgen.add_router("ce2") + tgen.add_router("ce3") + tgen.add_router("ce4") + + # r1-r2 + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + # r1-r3 + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r3"]) + + # r1-r4 + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r4"]) + + # r1-ce1 + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["ce1"]) + + # r1-ce3 + switch = tgen.add_switch("s5") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["ce3"]) + + # r1-ce4 + switch = tgen.add_switch("s6") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["ce4"]) + + # r1-dangling + switch = tgen.add_switch("s7") + switch.add_link(tgen.gears["r1"]) + + # r2-r3 + switch = tgen.add_switch("s8") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + # r3-r4 + switch = tgen.add_switch("s9") + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["r4"]) + + # r4-ce2 + switch = tgen.add_switch("s10") + switch.add_link(tgen.gears["r4"]) + switch.add_link(tgen.gears["ce2"]) + + +def setup_module(mod): + "Sets up the pytest environment" + + # skip tests is SNMP not installed + snmpd = os.system("which snmpd") + if snmpd: + error_msg = "SNMP not installed - skipping" + pytest.skip(error_msg) + + # This function initiates the topology build with Topogen... + tgen = Topogen(TemplateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + tgen.start_topology() + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + r3 = tgen.gears["r3"] + r4 = tgen.gears["r4"] + + # setup VRF-a in r1 + r1.run("ip link add VRF-a type vrf table 1001") + r1.run("ip link set up dev VRF-a") + r1.run("ip link add VRF-b type vrf table 1002") + r1.run("ip link set up dev VRF-b") + r4.run("ip link add VRF-a type vrf table 1001") + r4.run("ip link set up dev VRF-a") + + # enslave vrf interfaces + r1.run("ip link set r1-eth3 master VRF-a") + r1.run("ip link set r1-eth4 master VRF-a") + r1.run("ip link set r1-eth5 master VRF-b") + r4.run("ip link set r4-eth1 master VRF-a") + + r1.run("sysctl -w net.ipv4.ip_forward=1") + r2.run("sysctl -w net.ipv4.ip_forward=1") + r3.run("sysctl -w net.ipv4.ip_forward=1") + r4.run("sysctl -w net.ipv4.ip_forward=1") + r1.run("sysctl -w net.mpls.conf.r1-eth0.input=1") + r1.run("sysctl -w net.mpls.conf.r1-eth1.input=1") + r1.run("sysctl -w net.mpls.conf.r1-eth2.input=1") + r2.run("sysctl -w net.mpls.conf.r1-eth0.input=1") + r2.run("sysctl -w net.mpls.conf.r1-eth1.input=1") + r3.run("sysctl -w net.mpls.conf.r1-eth0.input=1") + r3.run("sysctl -w net.mpls.conf.r1-eth1.input=1") + r3.run("sysctl -w net.mpls.conf.r1-eth2.input=1") + r4.run("sysctl -w net.mpls.conf.r1-eth0.input=1") + r4.run("sysctl -w net.mpls.conf.r1-eth1.input=1") + + router_list = tgen.routers() + + # For all registred routers, load the zebra configuration file + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_ISIS, os.path.join(CWD, "{}/isisd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, + os.path.join(CWD, "{}/bgpd.conf".format(rname)), + "-M snmp", + ) + router.load_config( + TopoRouter.RD_SNMP, + os.path.join(CWD, "{}/snmpd.conf".format(rname)), + "-Le -Ivacm_conf,usmConf,iquery -V -DAgentX,trap", + ) + + # After loading the configurations, this function loads configured daemons. + tgen.start_router() + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + + +# SNMP utilities - maybe move to lib +def snmp_uint32_to_oid(val): + oid1 = int(val / 16777216) % 256 + oid2 = int(val / 65536) % 256 + oid3 = int(val / 256) % 256 + oid4 = int(val) % 256 + return "%(oid1)s.%(oid2)s.%(oid3)s.%(oid4)s" % locals() + + +def snmp_oid_to_uint32(oid): + values = oid.split(".") + return ( + (int(values[0]) * 16777216) + + (int(values[1]) * 65536) + + (int(values[2]) * 256) + + int(values[3]) + ) + + +def snmp_str_to_oid(str): + out_oid = "" + for char in str: + out_oid += "{}.".format(ord(char)) + return out_oid.rstrip(".") + + +def snmp_oid_to_str(oid): + out_str = "" + oids = oid.split(".") + for char in oids: + out_str += "{}".format(chr(int(char))) + return out_str + + +def snmp_rte_oid(vrf, dtype, dest, plen, policy, ntype, nhop=0): + oid_1 = snmp_str_to_oid(vrf) + oid_2 = dtype + oid_3 = dest + oid_4 = plen + oid_5 = "0.{}".format(policy) + oid_6 = ntype + if ntype == 0: + oid_7 = "" + else: + oid_7 = ".{}".format(nhop) + + return "{}.{}.{}.{}.{}.{}{}".format(oid_1, oid_2, oid_3, oid_4, oid_5, oid_6, oid_7) + + +def test_pe1_converge_evpn(): + "Wait for protocol convergence" + tgen = get_topogen() + + r1 = tgen.net.get("r1") + r1_snmp = SnmpTester(r1, "10.1.1.1", "public", "2c") + + assertmsg = "BGP SNMP does not seem to be running" + assert r1_snmp.test_oid("bgpVersion", "10"), assertmsg + count = 0 + passed = False + while count < 125: + if r1_snmp.test_oid_walk("bgpPeerLocalAddr.10.4.4.4", ["10.1.1.1"]): + passed = True + break + count += 1 + sleep(1) + #tgen.mininet_cli() + assertmsg = "BGP Peer 10.4.4.4 did not connect" + assert passed, assertmsg + + +interfaces_up_test = { + "mplsL3VpnConfiguredVrfs": "2", + "mplsL3VpnActiveVrfs": "2", + "mplsL3VpnConnectedInterfaces": "3", + "mplsL3VpnNotificationEnable": "true(1)", + "mplsL3VpnVrfConfMaxPossRts": "0", + "mplsL3VpnVrfConfRteMxThrshTime": "0 seconds", + "mplsL3VpnIlllblRcvThrsh": "0", +} + +interfaces_down_test = { + "mplsL3VpnConfiguredVrfs": "2", + "mplsL3VpnActiveVrfs": "1", + "mplsL3VpnConnectedInterfaces": "3", + "mplsL3VpnNotificationEnable": "true(1)", + "mplsL3VpnVrfConfMaxPossRts": "0", + "mplsL3VpnVrfConfRteMxThrshTime": "0 seconds", + "mplsL3VpnIlllblRcvThrsh": "0", +} + + +def test_r1_mplsvpn_scalars(): + "check scalar values" + tgen = get_topogen() + r1 = tgen.net.get("r1") + r1_snmp = SnmpTester(r1, "10.1.1.1", "public", "2c") + + for item in interfaces_up_test.keys(): + assertmsg = "{} should be {}: value {}".format( + item, interfaces_up_test[item], r1_snmp.get_next(item) + ) + assert r1_snmp.test_oid(item, interfaces_up_test[item]), assertmsg + + +def test_r1_mplsvpn_scalars_interface(): + "check scalar interface changing values" + tgen = get_topogen() + r1 = tgen.net.get("r1") + r1_cmd = tgen.gears["r1"] + r1_snmp = SnmpTester(r1, "10.1.1.1", "public", "2c") + + r1_cmd.vtysh_cmd("conf t\ninterface r1-eth3\nshutdown") + r1_cmd.vtysh_cmd("conf t\ninterface r1-eth4\nshutdown") + + for item in interfaces_up_test.keys(): + assertmsg = "{} should be {}: value {}".format( + item, interfaces_down_test[item], r1_snmp.get_next(item) + ) + assert r1_snmp.test_oid(item, interfaces_down_test[item]), assertmsg + + r1_cmd.vtysh_cmd("conf t\ninterface r1-eth3\nno shutdown") + r1_cmd.vtysh_cmd("conf t\ninterface r1-eth4\nno shutdown") + + for item in interfaces_up_test.keys(): + assertmsg = "{} should be {}: value {}".format( + item, interfaces_up_test[item], r1_snmp.get_next(item) + ) + assert r1_snmp.test_oid(item, interfaces_up_test[item]), assertmsg + + +def router_interface_get_ifindex(router, interface): + ifindex = 0 + r_int_output = router.vtysh_cmd( + "show interface {}-{}".format(router.name, interface) + ) + int_lines = r_int_output.splitlines() + for line in int_lines: + line_items = line.lstrip().split(" ") + if "index" in line_items[0]: + ifindex = line_items[1] + return ifindex + + +def generate_vrf_ifindex_oid(vrf, ifindex): + + intoid = snmp_uint32_to_oid(int(ifindex)) + vrfoid = snmp_str_to_oid(vrf) + oid = "{}.{}".format(vrfoid, intoid) + + return oid + + +def generate_vrf_index_type_oid(vrf, index, type): + vrfoid = snmp_str_to_oid(vrf) + intoid = snmp_uint32_to_oid(int(index)) + oid = "{}.{}.{}".format(vrfoid, intoid, type) + + return oid + + +iftable_up_test = { + "mplsL3VpnIfVpnClassification": ["enterprise(2)", "enterprise(2)", "enterprise(2)"], + "mplsL3VpnIfConfStorageType": ["volatile(2)", "volatile(2)", "volatile(2)"], + "mplsL3VpnIfConfRowStatus": ["active(1)", "active(1)", "active(1)"], +} + + +def get_timetick_val(time): + return int(time.split(" ")[0].lstrip("(").rstrip(")")) + + +def test_r1_mplsvpn_IfTable(): + "mplsL3VpnIf table values" + + tgen = get_topogen() + r1 = tgen.net.get("r1") + r1r = tgen.gears["r1"] + + r1_snmp = SnmpTester(r1, "10.1.1.1", "public", "2c") + + # tgen.mininet_cli() + eth3_ifindex = router_interface_get_ifindex(r1r, "eth3") + eth4_ifindex = router_interface_get_ifindex(r1r, "eth4") + eth5_ifindex = router_interface_get_ifindex(r1r, "eth5") + + # get ifindex and make sure the oid is correct + + oids = [] + # generate oid + oids.append(generate_vrf_ifindex_oid("VRF-a", eth3_ifindex)) + oids.append(generate_vrf_ifindex_oid("VRF-a", eth4_ifindex)) + oids.append(generate_vrf_ifindex_oid("VRF-b", eth5_ifindex)) + + for item in iftable_up_test.keys(): + assertmsg = "{} should be {} oids {} full dict {}:".format( + item, iftable_up_test[item], oids, r1_snmp.walk(item) + ) + assert r1_snmp.test_oid_walk(item, iftable_up_test[item], oids), assertmsg + + # an inactive vrf should not affect these values + r1.cmd("ip link set r1-eth5 down") + + for item in iftable_up_test.keys(): + assertmsg = "{} should be {} oids {} full dict {}:".format( + item, iftable_up_test[item], oids, r1_snmp.walk(item) + ) + assert r1_snmp.test_oid_walk(item, iftable_up_test[item], oids), assertmsg + + r1.cmd("ip link set r1-eth5 up") + + +vrftable_test = { + "mplsL3VpnVrfDescription": ["VRF-a", "VRF-b"], + "mplsL3VpnVrfRD": ['"10:1"', '"10:2"'], + "mplsL3VpnVrfOperStatus": ["up(1)", "up(1)"], + "mplsL3VpnVrfActiveInterfaces": ["2", "1"], + "mplsL3VpnVrfAssociatedInterfaces": ["2", "1"], + "mplsL3VpnVrfConfMidRteThresh": ["0", "0"], + "mplsL3VpnVrfConfHighRteThresh": ["0", "0"], + "mplsL3VpnVrfConfMaxRoutes": ["0", "0"], + "mplsL3VpnVrfConfRowStatus": ["active(1)", "active(1)"], + "mplsL3VpnVrfConfAdminStatus": ["up(1)", "up(1)"], + "mplsL3VpnVrfConfStorageType": ["volatile(2)", "volatile(2)"], +} + + +def test_r1_mplsvpn_VrfTable(): + tgen = get_topogen() + + r1 = tgen.net.get("r1") + r1r = tgen.gears["r1"] + + r1_snmp = SnmpTester(r1, "10.1.1.1", "public", "2c") + + # tgen.mininet_cli() + + oids = [] + + oids.append(snmp_str_to_oid("VRF-a")) + oids.append(snmp_str_to_oid("VRF-b")) + + # check items + for item in vrftable_test.keys(): + assertmsg = "{} should be {} oids {} full dict {}:".format( + item, vrftable_test[item], oids, r1_snmp.walk(item) + ) + assert r1_snmp.test_oid_walk(item, vrftable_test[item], oids), assertmsg + + # check timetick set and stable + ts_a = r1_snmp.get("mplsL3VpnVrfCreationTime.{}".format(snmp_str_to_oid("VRF-a"))) + ts_b = r1_snmp.get("mplsL3VpnVrfCreationTime.{}".format(snmp_str_to_oid("VRF-b"))) + ts_val_a1 = get_timetick_val(ts_a) + ts_val_b1 = get_timetick_val(ts_b) + ts_a = r1_snmp.get("mplsL3VpnVrfCreationTime.{}".format(snmp_str_to_oid("VRF-a"))) + ts_b = r1_snmp.get("mplsL3VpnVrfCreationTime.{}".format(snmp_str_to_oid("VRF-b"))) + ts_val_a2 = get_timetick_val(ts_a) + ts_val_b2 = get_timetick_val(ts_b) + + assertmsg = "timestamp values for VRF-a do not match {} {}".format( + ts_val_a1, ts_val_a2 + ) + assert ts_val_a1 == ts_val_a2, assertmsg + assertmsg = "timestamp values for VRF-b do not match {} {}".format( + ts_val_b1, ts_val_b2 + ) + assert ts_val_b1 == ts_val_b2, assertmsg + + # take Last changed time, fiddle with active interfaces, ensure + # time changes and active interfaces change + ts_last = r1_snmp.get( + "mplsL3VpnVrfConfLastChanged.{}".format(snmp_str_to_oid("VRF-a")) + ) + ts_val_last_1 = get_timetick_val(ts_last) + r1r.vtysh_cmd("conf t\ninterface r1-eth3\nshutdown") + active_int = r1_snmp.get( + "mplsL3VpnVrfActiveInterfaces.{}".format(snmp_str_to_oid("VRF-a")) + ) + assertmsg = "mplsL3VpnVrfActiveInterfaces incorrect should be 1 value {}".format( + active_int + ) + assert active_int == "1", assertmsg + + ts_last = r1_snmp.get( + "mplsL3VpnVrfConfLastChanged.{}".format(snmp_str_to_oid("VRF-a")) + ) + ts_val_last_2 = get_timetick_val(ts_last) + assertmsg = "mplsL3VpnVrfConfLastChanged does not update on interface change" + assert ts_val_last_2 > ts_val_last_1, assertmsg + r1r.vtysh_cmd("conf t\ninterface r1-eth3\nno shutdown") + + # take Last changed time, fiddle with associated interfaces, ensure + # time changes and active interfaces change + ts_last = r1_snmp.get( + "mplsL3VpnVrfConfLastChanged.{}".format(snmp_str_to_oid("VRF-a")) + ) + ts_val_last_1 = get_timetick_val(ts_last) + r1.cmd("ip link set r1-eth6 master VRF-a") + r1.cmd("ip link set r1-eth6 up") + + associated_int = r1_snmp.get( + "mplsL3VpnVrfAssociatedInterfaces.{}".format(snmp_str_to_oid("VRF-a")) + ) + assertmsg = "mplsL3VpnVrfAssociatedInterfaces incorrect should be 3 value {}".format( + associated_int + ) + + assert associated_int == "3", assertmsg + ts_last = r1_snmp.get( + "mplsL3VpnVrfConfLastChanged.{}".format(snmp_str_to_oid("VRF-a")) + ) + ts_val_last_2 = get_timetick_val(ts_last) + assertmsg = "mplsL3VpnVrfConfLastChanged does not update on interface change" + assert ts_val_last_2 > ts_val_last_1, assertmsg + r1.cmd("ip link del r1-eth6 master VRF-a") + r1.cmd("ip link set r1-eth6 down") + + +rt_table_test = { + "mplsL3VpnVrfRT": ['"1:1"', '"1:2"'], + "mplsL3VpnVrfRTDescr": ["RT both for VRF VRF-a", "RT both for VRF VRF-b"], + "mplsL3VpnVrfRTRowStatus": ["active(1)", "active(1)"], + "mplsL3VpnVrfRTStorageType": ["volatile(2)", "volatile(2)"], +} + + +def test_r1_mplsvpn_VrfRT_table(): + tgen = get_topogen() + + r1 = tgen.net.get("r1") + r1r = tgen.gears["r1"] + + r1_snmp = SnmpTester(r1, "10.1.1.1", "public", "2c") + + oids = [] + oids.append(generate_vrf_index_type_oid("VRF-a", 1, 3)) + oids.append(generate_vrf_index_type_oid("VRF-b", 1, 3)) + + # check items + for item in rt_table_test.keys(): + print(item) + assertmsg = "{} should be {} oids {} full dict {}:".format( + item, rt_table_test[item], oids, r1_snmp.walk(item) + ) + assert r1_snmp.test_oid_walk(item, rt_table_test[item], oids), assertmsg + + +def test_r1_mplsvpn_perf_table(): + tgen = get_topogen() + + r1 = tgen.net.get("r1") + r1r = tgen.gears["r1"] + + r1_snmp = SnmpTester(r1, "10.1.1.1", "public", "2c") + + # tgen.mininet_cli() + oid_a = snmp_str_to_oid("VRF-a") + oid_b = snmp_str_to_oid("VRF-b") + + # poll for 10 seconds for routes to appear + count = 0 + passed = False + while count < 60: + if r1_snmp.test_oid_walk( + "mplsL3VpnVrfPerfCurrNumRoutes.{}".format(oid_a), ["7"] + ): + passed = True + break + count += 1 + sleep(1) + # tgen.mininet_cli() + assertmsg = "mplsL3VpnVrfPerfCurrNumRoutes shouold be 7 got {}".format( + r1_snmp.get("mplsL3VpnVrfPerfCurrNumRoutes.{}".format(oid_a)) + ) + assert passed, assertmsg + curr_a = int(r1_snmp.get("mplsL3VpnVrfPerfCurrNumRoutes.{}".format(oid_a))) + del_a = int(r1_snmp.get("mplsL3VpnVrfPerfRoutesDeleted.{}".format(oid_a))) + add_a = int(r1_snmp.get("mplsL3VpnVrfPerfRoutesAdded.{}".format(oid_a))) + + assertmsg = "FAIL curr{} does not equal added{} - deleted {}".format( + curr_a, add_a, del_a + ) + assert curr_a == (add_a - del_a), assertmsg + curr_b = int(r1_snmp.get("mplsL3VpnVrfPerfCurrNumRoutes.{}".format(oid_b))) + del_b = int(r1_snmp.get("mplsL3VpnVrfPerfRoutesDeleted.{}".format(oid_b))) + add_b = int(r1_snmp.get("mplsL3VpnVrfPerfRoutesAdded.{}".format(oid_b))) + assertmsg = "FAIL curr{} does not equal added{} - deleted {}".format( + curr_b, add_b, del_b + ) + assert curr_b == (add_b - del_b), assertmsg + + +rte_table_test = { + "mplsL3VpnVrfRteInetCidrDestType": [ + "ipv4(1)", + "ipv4(1)", + "ipv4(1)", + "ipv4(1)", + "ipv4(1)", + "ipv4(1)", + "ipv4(1)", + ], + "mplsL3VpnVrfRteInetCidrDest": [ + "0A 05 05 05", + "0A 07 07 07", + "C0 A8 22 00", + "C0 A8 64 00", + "C0 A8 64 00", + "C0 A8 C8 00", + "C0 A8 C8 00", + ], + "mplsL3VpnVrfRteInetCidrPfxLen": ["32", "32", "24", "24", "24", "24", "24"], + "mplsL3VpnVrfRteInetCidrNHopType": [ + "ipv4(1)", + "ipv4(1)", + "ipv4(1)", + "ipv4(1)", + "unknown(0)", + "ipv4(1)", + "unknown(0)", + ], + "mplsL3VpnVrfRteInetCidrNextHop": [ + "C0 A8 64 0A", + "C0 A8 C8 0A", + "0A 04 04 04", + "C0 A8 64 0A", + '""', + "C0 A8 C8 0A", + '""', + ], + "mplsL3VpnVrfRteInetCidrIfIndex": ["5", "6", "4", "5", "0", "6", "0"], + "mplsL3VpnVrfRteInetCidrType": [ + "local(3)", + "local(3)", + "remote(4)", + "local(3)", + "other(1)", + "local(3)", + "other(1)", + ], + "mplsL3VpnVrfRteInetCidrProto": [ + "bgp(14)", + "bgp(14)", + "bgp(14)", + "bgp(14)", + "local(2)", + "bgp(14)", + "local(2)", + ], + "mplsL3VpnVrfRteInetCidrNextHopAS": ["65001", "65001", "0", "65001", "0", "65001", "0"], + "mplsL3VpnVrfRteInetCidrMetric1": ["0", "0", "20", "0", "0", "0", "0"], + "mplsL3VpnVrfRteInetCidrMetric2": ["-1", "-1", "-1", "-1", "-1", "-1", "-1"], + "mplsL3VpnVrfRteInetCidrMetric3": ["-1", "-1", "-1", "-1", "-1", "-1", "-1"], + "mplsL3VpnVrfRteInetCidrMetric4": ["-1", "-1", "-1", "-1", "-1", "-1", "-1"], + "mplsL3VpnVrfRteInetCidrMetric5": ["-1", "-1", "-1", "-1", "-1", "-1", "-1"], + "mplsL3VpnVrfRteXCPointer": ["00", "00", "00", "00", "00", "00", "00"], + "mplsL3VpnVrfRteInetCidrStatus": [ + "active(1)", + "active(1)", + "active(1)", + "active(1)", + "active(1)", + "active(1)", + "active(1)", + ], +} + + +def test_r1_mplsvpn_rte_table(): + tgen = get_topogen() + + r1 = tgen.net.get("r1") + r1r = tgen.gears["r1"] + + r1_snmp = SnmpTester(r1, "10.1.1.1", "public", "2c") + + # tgen.mininet_cli() + oid_1 = snmp_rte_oid("VRF-a", 1, "10.5.5.5", 32, 0, 1, "192.168.100.10") + oid_2 = snmp_rte_oid("VRF-a", 1, "10.7.7.7", 32, 0, 1, "192.168.200.10") + oid_3 = snmp_rte_oid("VRF-a", 1, "192.168.34.0", 24, 0, 1, "10.4.4.4") + oid_4 = snmp_rte_oid("VRF-a", 1, "192.168.100.0", 24, 1, 1, "192.168.100.10") + oid_4_a = snmp_rte_oid("VRF-a", 1, "192.168.100.0", 24, 0, 1, "192.168.100.10") + oid_5 = snmp_rte_oid("VRF-a", 1, "192.168.100.0", 24, 0, 0) + oid_5_a = snmp_rte_oid("VRF-a", 1, "192.168.100.0", 24, 1, 0) + oid_6 = snmp_rte_oid("VRF-a", 1, "192.168.200.0", 24, 1, 1, "192.168.200.10") + oid_6_a = snmp_rte_oid("VRF-a", 1, "192.168.200.0", 24, 0, 1, "192.168.200.10") + oid_7 = snmp_rte_oid("VRF-a", 1, "192.168.200.0", 24, 0, 0) + oid_7_a = snmp_rte_oid("VRF-a", 1, "192.168.200.0", 24, 1, 0) + + oid_lists = [ + [oid_1, oid_2, oid_3, oid_4, oid_5, oid_6, oid_7], + [oid_1, oid_2, oid_3, oid_4_a, oid_5_a, oid_6, oid_7], + [oid_1, oid_2, oid_3, oid_4, oid_5, oid_6_a, oid_7_a], + [oid_1, oid_2, oid_3, oid_4_a, oid_5_a, oid_6_a, oid_7_a], + [oid_1, oid_2, oid_3, oid_4, oid_5, oid_6, oid_7], + [oid_1, oid_2, oid_3, oid_4_a, oid_5_a, oid_6, oid_7], + [oid_1, oid_2, oid_3, oid_4, oid_5, oid_6_a, oid_7_a], + [oid_1, oid_2, oid_3, oid_4_a, oid_5_a, oid_6_a, oid_7_a], + ] + + # check items + + passed = False + for oid_list in oid_lists: + passed = True + for item in rte_table_test.keys(): + print(item) + assertmsg = "{} should be {} oids {} full dict {}:".format( + item, rte_table_test[item], oid_list, r1_snmp.walk(item) + ) + if not r1_snmp.test_oid_walk(item, rte_table_test[item], oid_list): + passed = False + break + print( + "{} should be {} oids {} full dict {}:".format( + item, rte_table_test[item], oid_list, r1_snmp.walk(item) + ) + ) + if passed: + break + print("passed {}".format(passed)) + assert passed, assertmsg + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/lib/snmptest.py b/tests/topotests/lib/snmptest.py index 1bf83c2aea..5112500e0b 100644 --- a/tests/topotests/lib/snmptest.py +++ b/tests/topotests/lib/snmptest.py @@ -60,6 +60,10 @@ class SnmpTester(object): num_value_tokens = len(tokens) - 3 + # this copes with the emptys string return + if num_value_tokens == 0: + return tokens[2] + if num_value_tokens > 1: output = "" index = 3 @@ -78,6 +82,17 @@ class SnmpTester(object): # third token onwards is the value of the object return tokens[0].split(".", 1)[1] + @staticmethod + def _get_snmp_oid(snmp_output): + tokens = snmp_output.strip().split() + +# if len(tokens) > 5: +# return None + + + # third token is the value of the object + return tokens[0].split('.',1)[1] + def _parse_multiline(self, snmp_output): results = snmp_output.strip().split("\r\n") @@ -122,10 +137,16 @@ class SnmpTester(object): if oids is not None: index = 0 for oid in oids: + # avoid key error for missing keys + if not oid in results_dict.keys(): + print("FAIL: missing oid key {}".format(oid)) + return False if results_dict[oid] != values[index]: + print("FAIL{} {} |{}| == |{}|".format(oid, index, results_dict[oid], values[index])) return False index += 1 return True # Return true if 'values' is a subset of 'results_list' + print("test {} == {}".format(results_list[: len(values)], values)) return results_list[: len(values)] == values diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index efb6b28acc..e026a28628 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -4001,10 +4001,16 @@ static char *vtysh_completion_entry_function(const char *ignore, void vtysh_readline_init(void) { /* readline related settings. */ + char *disable_bracketed_paste = + XSTRDUP(MTYPE_TMP, "set enable-bracketed-paste off"); + rl_initialize(); + rl_parse_and_bind(disable_bracketed_paste); rl_bind_key('?', (rl_command_func_t *)vtysh_rl_describe); rl_completion_entry_function = vtysh_completion_entry_function; rl_attempted_completion_function = new_completion; + + XFREE(MTYPE_TMP, disable_bracketed_paste); } char *vtysh_prompt(void) diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index e4dd745f42..00471b9645 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -301,7 +301,7 @@ static void netlink_vrf_change(struct nlmsghdr *h, struct rtattr *tb, struct ifinfomsg *ifi; struct rtattr *linkinfo[IFLA_INFO_MAX + 1]; struct rtattr *attr[IFLA_VRF_MAX + 1]; - struct vrf *vrf; + struct vrf *vrf = NULL; struct zebra_vrf *zvrf; uint32_t nl_table_id; @@ -350,11 +350,7 @@ static void netlink_vrf_change(struct nlmsghdr *h, struct rtattr *tb, } } - /* - * vrf_get is implied creation if it does not exist - */ - vrf = vrf_get((vrf_id_t)ifi->ifi_index, - name); // It would create vrf + vrf = vrf_update((vrf_id_t)ifi->ifi_index, name); if (!vrf) { flog_err(EC_LIB_INTERFACE, "VRF %s id %u not created", name, ifi->ifi_index); |
