summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bgpd/bgp_ecommunity.c1
-rw-r--r--bgpd/bgp_ecommunity.h3
-rw-r--r--bgpd/bgp_mplsvpn.h15
-rw-r--r--bgpd/bgp_mplsvpn_snmp.c1689
-rw-r--r--bgpd/bgp_mplsvpn_snmp.h31
-rw-r--r--bgpd/bgp_nb_config.c3
-rw-r--r--bgpd/bgp_route.c6
-rw-r--r--bgpd/bgp_snmp.c15
-rw-r--r--bgpd/bgp_vty.c2
-rw-r--r--bgpd/bgp_zebra.c13
-rw-r--r--bgpd/bgpd.c1
-rw-r--r--bgpd/bgpd.h41
-rw-r--r--bgpd/subdir.am3
-rw-r--r--doc/user/bgp.rst3
-rw-r--r--lib/agentx.c31
-rw-r--r--lib/if.c34
-rw-r--r--lib/if.h2
-rw-r--r--lib/smux.h45
-rw-r--r--lib/snmp.c54
-rw-r--r--lib/vrf.c50
-rw-r--r--lib/vrf.h15
-rw-r--r--tests/topotests/bgp-snmp-mplsl3vpn/ce1/bgpd.conf12
-rw-r--r--tests/topotests/bgp-snmp-mplsl3vpn/ce1/snmpd.conf15
-rw-r--r--tests/topotests/bgp-snmp-mplsl3vpn/ce1/zebra.conf19
-rw-r--r--tests/topotests/bgp-snmp-mplsl3vpn/ce2/bgpd.conf12
-rw-r--r--tests/topotests/bgp-snmp-mplsl3vpn/ce2/snmpd.conf15
-rw-r--r--tests/topotests/bgp-snmp-mplsl3vpn/ce2/zebra.conf19
-rw-r--r--tests/topotests/bgp-snmp-mplsl3vpn/ce3/bgpd.conf12
-rw-r--r--tests/topotests/bgp-snmp-mplsl3vpn/ce3/snmpd.conf15
-rw-r--r--tests/topotests/bgp-snmp-mplsl3vpn/ce3/zebra.conf19
-rw-r--r--tests/topotests/bgp-snmp-mplsl3vpn/ce4/bgpd.conf12
-rw-r--r--tests/topotests/bgp-snmp-mplsl3vpn/ce4/snmpd.conf15
-rw-r--r--tests/topotests/bgp-snmp-mplsl3vpn/ce4/zebra.conf19
-rw-r--r--tests/topotests/bgp-snmp-mplsl3vpn/r1/bgpd.conf48
-rw-r--r--tests/topotests/bgp-snmp-mplsl3vpn/r1/isisd.conf46
-rw-r--r--tests/topotests/bgp-snmp-mplsl3vpn/r1/snmpd.conf17
-rw-r--r--tests/topotests/bgp-snmp-mplsl3vpn/r1/zebra.conf33
-rw-r--r--tests/topotests/bgp-snmp-mplsl3vpn/r2/isisd.conf37
-rw-r--r--tests/topotests/bgp-snmp-mplsl3vpn/r2/snmpd.conf15
-rw-r--r--tests/topotests/bgp-snmp-mplsl3vpn/r2/zebra.conf24
-rw-r--r--tests/topotests/bgp-snmp-mplsl3vpn/r3/isisd.conf45
-rw-r--r--tests/topotests/bgp-snmp-mplsl3vpn/r3/snmpd.conf15
-rw-r--r--tests/topotests/bgp-snmp-mplsl3vpn/r3/zebra.conf27
-rw-r--r--tests/topotests/bgp-snmp-mplsl3vpn/r4/bgpd.conf43
-rw-r--r--tests/topotests/bgp-snmp-mplsl3vpn/r4/isisd.conf36
-rw-r--r--tests/topotests/bgp-snmp-mplsl3vpn/r4/snmpd.conf15
-rw-r--r--tests/topotests/bgp-snmp-mplsl3vpn/r4/zebra.conf27
-rwxr-xr-xtests/topotests/bgp-snmp-mplsl3vpn/test_bgp_snmp_mplsvpn.py738
-rw-r--r--tests/topotests/lib/snmptest.py21
-rw-r--r--vtysh/vtysh.c6
-rw-r--r--zebra/if_netlink.c8
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);
diff --git a/lib/if.c b/lib/if.c
index fa0b1d0195..7ec53d356d 100644
--- a/lib/if.c
+++ b/lib/if.c
@@ -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;
diff --git a/lib/if.h b/lib/if.h
index a2a40d0957..5bf52936ae 100644
--- a/lib/if.h
+++ b/lib/if.h
@@ -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)
{
diff --git a/lib/vrf.c b/lib/vrf.c
index 1a9cd7e451..33e9084f73 100644
--- a/lib/vrf.c
+++ b/lib/vrf.c
@@ -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;
}
diff --git a/lib/vrf.h b/lib/vrf.h
index c636b9ea7e..32e6fb4289 100644
--- a/lib/vrf.h
+++ b/lib/vrf.h
@@ -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);