/* OSPFv3 SNMP support
 * Copyright (C) 2004 Yasuhiro Ohara
 *
 * This file is part of GNU Zebra.
 *
 * GNU Zebra 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.
 *
 * GNU Zebra 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 "log.h"
#include "vty.h"
#include "linklist.h"
#include "vector.h"
#include "vrf.h"
#include "smux.h"
#include "libfrr.h"
#include "lib/version.h"

#include "ospf6_proto.h"
#include "ospf6_lsa.h"
#include "ospf6_lsdb.h"
#include "ospf6_route.h"
#include "ospf6_top.h"
#include "ospf6_area.h"
#include "ospf6_interface.h"
#include "ospf6_message.h"
#include "ospf6_neighbor.h"
#include "ospf6_abr.h"
#include "ospf6_asbr.h"
#include "ospf6d.h"

/* OSPFv3-MIB */
#define OSPFv3MIB 1,3,6,1,2,1,191

/* OSPFv3 MIB General Group values. */
#define OSPFv3ROUTERID                   1
#define OSPFv3ADMINSTAT                  2
#define OSPFv3VERSIONNUMBER              3
#define OSPFv3AREABDRRTRSTATUS           4
#define OSPFv3ASBDRRTRSTATUS             5
#define OSPFv3ASSCOPELSACOUNT            6
#define OSPFv3ASSCOPELSACHECKSUMSUM      7
#define OSPFv3ORIGINATENEWLSAS           8
#define OSPFv3RXNEWLSAS                  9
#define OSPFv3EXTLSACOUNT               10
#define OSPFv3EXTAREALSDBLIMIT          11
#define OSPFv3EXITOVERFLOWINTERVAL      12
#define OSPFv3DEMANDEXTENSIONS          13
#define OSPFv3REFERENCEBANDWIDTH        14
#define OSPFv3RESTARTSUPPORT            15
#define OSPFv3RESTARTINTERVAL           16
#define OSPFv3RESTARTSTRICTLSACHECKING  17
#define OSPFv3RESTARTSTATUS             18
#define OSPFv3RESTARTAGE                19
#define OSPFv3RESTARTEXITREASON         20
#define OSPFv3NOTIFICATIONENABLE        21
#define OSPFv3STUBROUTERSUPPORT         22
#define OSPFv3STUBROUTERADVERTISEMENT   23
#define OSPFv3DISCONTINUITYTIME         24
#define OSPFv3RESTARTTIME               25

/* OSPFv3 MIB Area Table values: ospfv3AreaTable */
#define OSPFv3IMPORTASEXTERN             2
#define OSPFv3AREASPFRUNS                3
#define OSPFv3AREABDRRTRCOUNT            4
#define OSPFv3AREAASBDRRTRCOUNT          5
#define OSPFv3AREASCOPELSACOUNT          6
#define OSPFv3AREASCOPELSACKSUMSUM       7
#define OSPFv3AREASUMMARY                8
#define OSPFv3AREAROWSTATUS              9
#define OSPFv3AREASTUBMETRIC            10
#define OSPFv3AREANSSATRANSLATORROLE    11
#define OSPFv3AREANSSATRANSLATORSTATE   12
#define OSPFv3AREANSSATRANSLATORSTABINTERVAL    13
#define OSPFv3AREANSSATRANSLATOREVENTS  14
#define OSPFv3AREASTUBMETRICTYPE        15
#define OSPFv3AREATEENABLED             16

/* OSPFv3 MIB * Lsdb Table values: ospfv3*LsdbTable */
#define OSPFv3WWLSDBSEQUENCE             1
#define OSPFv3WWLSDBAGE                  2
#define OSPFv3WWLSDBCHECKSUM             3
#define OSPFv3WWLSDBADVERTISEMENT        4
#define OSPFv3WWLSDBTYPEKNOWN            5

/* Three first bits are to identify column */
#define OSPFv3WWCOLUMN 0x7
/* Then we use other bits to identify table */
#define OSPFv3WWASTABLE   (1 << 3)
#define OSPFv3WWAREATABLE (1 << 4)
#define OSPFv3WWLINKTABLE (1 << 5)
#define OSPFv3WWVIRTLINKTABLE (1 << 6)

/* OSPFv3 MIB Host Table values: ospfv3HostTable */
#define OSPFv3HOSTMETRIC                 3
#define OSPFv3HOSTROWSTATUS              4
#define OSPFv3HOSTAREAID                 5

/* OSPFv3 MIB Interface Table values: ospfv3IfTable */
#define OSPFv3IFAREAID                   3
#define OSPFv3IFTYPE                     4
#define OSPFv3IFADMINSTATUS              5
#define OSPFv3IFRTRPRIORITY              6
#define OSPFv3IFTRANSITDELAY             7
#define OSPFv3IFRETRANSINTERVAL          8
#define OSPFv3IFHELLOINTERVAL            9
#define OSPFv3IFRTRDEADINTERVAL         10
#define OSPFv3IFPOLLINTERVAL            11
#define OSPFv3IFSTATE                   12
#define OSPFv3IFDESIGNATEDROUTER        13
#define OSPFv3IFBACKUPDESIGNATEDROUTER  14
#define OSPFv3IFEVENTS                  15
#define OSPFv3IFROWSTATUS               16
#define OSPFv3IFDEMAND                  17
#define OSPFv3IFMETRICVALUE             18
#define OSPFv3IFLINKSCOPELSACOUNT       19
#define OSPFv3IFLINKLSACKSUMSUM         20
#define OSPFv3IFDEMANDNBRPROBE          21
#define OSPFv3IFDEMANDNBRPROBERETRANSLIMIT 22
#define OSPFv3IFDEMANDNBRPROBEINTERVAL  23
#define OSPFv3IFTEDISABLED              24
#define OSPFv3IFLINKLSASUPPRESSION      25

/* OSPFv3 MIB Virtual Interface Table values: ospfv3VirtIfTable */
#define OSPFv3VIRTIFINDEX           3
#define OSPFv3VIRTIFINSTID          4
#define OSPFv3VIRTIFTRANSITDELAY    5
#define OSPFv3VIRTIFRETRANSINTERVAL 6
#define OSPFv3VIRTIFHELLOINTERVAL   7
#define OSPFv3VIRTIFRTRDEADINTERVAL 8
#define OSPFv3VIRTIFSTATE           9
#define OSPFv3VIRTIFEVENTS         10
#define OSPFv3VIRTIFROWSTATUS      11
#define OSPFv3VIRTIFLINKSCOPELSACOUNT 12
#define OSPFv3VIRTIFLINKLSACKSUMSUM   13

/* OSPFv3 MIB Neighbors Table values: ospfv3NbrTable */
#define OSPFv3NBRADDRESSTYPE      4
#define OSPFv3NBRADDRESS          5
#define OSPFv3NBROPTIONS          6
#define OSPFv3NBRPRIORITY         7
#define OSPFv3NBRSTATE            8
#define OSPFv3NBREVENTS           9
#define OSPFv3NBRLSRETRANSQLEN   10
#define OSPFv3NBRHELLOSUPPRESSED 11
#define OSPFv3NBRIFID            12
#define OSPFv3NBRRESTARTHELPERSTATUS     13
#define OSPFv3NBRRESTARTHELPERAGE        14
#define OSPFv3NBRRESTARTHELPEREXITREASON 15

/* OSPFv3 MIB Configured Neighbors Table values: ospfv3CfgNbrTable */
#define OSPFv3CFGNBRPRIORITY  5
#define OSPFv3CFGNBRROWSTATUS 6

/* OSPFv3 MIB Virtual Neighbors Table values: ospfv3VirtNbrTable */
#define OSPFv3VIRTNBRIFINDEX          3
#define OSPFv3VIRTNBRIFINSTID         4
#define OSPFv3VIRTNBRADDRESSTYPE      5
#define OSPFv3VIRTNBRADDRESS          6
#define OSPFv3VIRTNBROPTIONS          7
#define OSPFv3VIRTNBRSTATE            8
#define OSPFv3VIRTNBREVENTS           9
#define OSPFv3VIRTNBRLSRETRANSQLEN   10
#define OSPFv3VIRTNBRHELLOSUPPRESSED 11
#define OSPFv3VIRTNBRIFID            12
#define OSPFv3VIRTNBRRESTARTHELPERSTATUS     13
#define OSPFv3VIRTNBRRESTARTHELPERAGE        14
#define OSPFv3VIRTNBRRESTARTHELPEREXITREASON 15

/* OSPFv3 MIB Area Aggregate Table values: ospfv3AreaAggregateTable */
#define OSPFv3AREAAGGREGATEROWSTATUS  6
#define OSPFv3AREAAGGREGATEEFFECT     7
#define OSPFv3AREAAGGREGATEROUTETAG   8

/* SYNTAX Status from OSPF-MIB. */
#define OSPF_STATUS_ENABLED  1
#define OSPF_STATUS_DISABLED 2

/* SNMP value hack. */
#define COUNTER     ASN_COUNTER
#define INTEGER     ASN_INTEGER
#define GAUGE       ASN_GAUGE
#define UNSIGNED    ASN_UNSIGNED
#define TIMETICKS   ASN_TIMETICKS
#define IPADDRESS   ASN_IPADDRESS
#define STRING      ASN_OCTET_STR

/* For return values e.g. SNMP_INTEGER macro */
SNMP_LOCAL_VARIABLES

/* OSPFv3-MIB instances. */
static oid ospfv3_oid[] = {OSPFv3MIB};
static oid ospfv3_trap_oid[] = {OSPFv3MIB, 0};

/* Hook functions. */
static uint8_t *ospfv3GeneralGroup(struct variable *, oid *, size_t *, int,
				   size_t *, WriteMethod **);
static uint8_t *ospfv3AreaEntry(struct variable *, oid *, size_t *, int,
				size_t *, WriteMethod **);
static uint8_t *ospfv3WwLsdbEntry(struct variable *, oid *, size_t *, int,
				  size_t *, WriteMethod **);
static uint8_t *ospfv3NbrEntry(struct variable *, oid *, size_t *, int,
			       size_t *, WriteMethod **);
static uint8_t *ospfv3IfEntry(struct variable *, oid *, size_t *, int, size_t *,
			      WriteMethod **);

static struct variable ospfv3_variables[] = {
	/* OSPF general variables */
	{OSPFv3ROUTERID, UNSIGNED, RWRITE, ospfv3GeneralGroup, 3, {1, 1, 1}},
	{OSPFv3ADMINSTAT, INTEGER, RWRITE, ospfv3GeneralGroup, 3, {1, 1, 2}},
	{OSPFv3VERSIONNUMBER, INTEGER, RONLY, ospfv3GeneralGroup, 3, {1, 1, 3}},
	{OSPFv3AREABDRRTRSTATUS,
	 INTEGER,
	 RONLY,
	 ospfv3GeneralGroup,
	 3,
	 {1, 1, 4}},
	{OSPFv3ASBDRRTRSTATUS,
	 INTEGER,
	 RWRITE,
	 ospfv3GeneralGroup,
	 3,
	 {1, 1, 5}},
	{OSPFv3ASSCOPELSACOUNT, GAUGE, RONLY, ospfv3GeneralGroup, 3, {1, 1, 6}},
	{OSPFv3ASSCOPELSACHECKSUMSUM,
	 UNSIGNED,
	 RONLY,
	 ospfv3GeneralGroup,
	 3,
	 {1, 1, 7}},
	{OSPFv3ORIGINATENEWLSAS,
	 COUNTER,
	 RONLY,
	 ospfv3GeneralGroup,
	 3,
	 {1, 1, 8}},
	{OSPFv3RXNEWLSAS, COUNTER, RONLY, ospfv3GeneralGroup, 3, {1, 1, 9}},
	{OSPFv3EXTLSACOUNT, GAUGE, RONLY, ospfv3GeneralGroup, 3, {1, 1, 10}},
	{OSPFv3EXTAREALSDBLIMIT,
	 INTEGER,
	 RWRITE,
	 ospfv3GeneralGroup,
	 3,
	 {1, 1, 11}},
	{OSPFv3EXITOVERFLOWINTERVAL,
	 UNSIGNED,
	 RWRITE,
	 ospfv3GeneralGroup,
	 3,
	 {1, 1, 12}},
	{OSPFv3DEMANDEXTENSIONS,
	 INTEGER,
	 RWRITE,
	 ospfv3GeneralGroup,
	 3,
	 {1, 1, 13}},
	{OSPFv3REFERENCEBANDWIDTH,
	 UNSIGNED,
	 RWRITE,
	 ospfv3GeneralGroup,
	 3,
	 {1, 1, 14}},
	{OSPFv3RESTARTSUPPORT,
	 INTEGER,
	 RWRITE,
	 ospfv3GeneralGroup,
	 3,
	 {1, 1, 15}},
	{OSPFv3RESTARTINTERVAL,
	 UNSIGNED,
	 RWRITE,
	 ospfv3GeneralGroup,
	 3,
	 {1, 1, 16}},
	{OSPFv3RESTARTSTRICTLSACHECKING,
	 INTEGER,
	 RWRITE,
	 ospfv3GeneralGroup,
	 3,
	 {1, 1, 17}},
	{OSPFv3RESTARTSTATUS,
	 INTEGER,
	 RONLY,
	 ospfv3GeneralGroup,
	 3,
	 {1, 1, 18}},
	{OSPFv3RESTARTAGE, UNSIGNED, RONLY, ospfv3GeneralGroup, 3, {1, 1, 19}},
	{OSPFv3RESTARTEXITREASON,
	 INTEGER,
	 RONLY,
	 ospfv3GeneralGroup,
	 3,
	 {1, 1, 20}},
	{OSPFv3NOTIFICATIONENABLE,
	 INTEGER,
	 RWRITE,
	 ospfv3GeneralGroup,
	 3,
	 {1, 1, 21}},
	{OSPFv3STUBROUTERSUPPORT,
	 INTEGER,
	 RONLY,
	 ospfv3GeneralGroup,
	 3,
	 {1, 1, 22}},
	{OSPFv3STUBROUTERADVERTISEMENT,
	 INTEGER,
	 RWRITE,
	 ospfv3GeneralGroup,
	 3,
	 {1, 1, 23}},
	{OSPFv3DISCONTINUITYTIME,
	 TIMETICKS,
	 RONLY,
	 ospfv3GeneralGroup,
	 3,
	 {1, 1, 24}},
	{OSPFv3RESTARTTIME,
	 TIMETICKS,
	 RONLY,
	 ospfv3GeneralGroup,
	 3,
	 {1, 1, 25}},

	/* OSPFv3 Area Data Structure */
	{OSPFv3IMPORTASEXTERN,
	 INTEGER,
	 RWRITE,
	 ospfv3AreaEntry,
	 4,
	 {1, 2, 1, 2}},
	{OSPFv3AREASPFRUNS, COUNTER, RONLY, ospfv3AreaEntry, 4, {1, 2, 1, 3}},
	{OSPFv3AREABDRRTRCOUNT, GAUGE, RONLY, ospfv3AreaEntry, 4, {1, 2, 1, 4}},
	{OSPFv3AREAASBDRRTRCOUNT,
	 GAUGE,
	 RONLY,
	 ospfv3AreaEntry,
	 4,
	 {1, 2, 1, 5}},
	{OSPFv3AREASCOPELSACOUNT,
	 GAUGE,
	 RONLY,
	 ospfv3AreaEntry,
	 4,
	 {1, 2, 1, 6}},
	{OSPFv3AREASCOPELSACKSUMSUM,
	 UNSIGNED,
	 RONLY,
	 ospfv3AreaEntry,
	 4,
	 {1, 2, 1, 7}},
	{OSPFv3AREASUMMARY, INTEGER, RWRITE, ospfv3AreaEntry, 4, {1, 2, 1, 8}},
	{OSPFv3AREAROWSTATUS,
	 INTEGER,
	 RWRITE,
	 ospfv3AreaEntry,
	 4,
	 {1, 2, 1, 9}},
	{OSPFv3AREASTUBMETRIC,
	 INTEGER,
	 RWRITE,
	 ospfv3AreaEntry,
	 4,
	 {1, 2, 1, 10}},
	{OSPFv3AREANSSATRANSLATORROLE,
	 INTEGER,
	 RWRITE,
	 ospfv3AreaEntry,
	 4,
	 {1, 2, 1, 11}},
	{OSPFv3AREANSSATRANSLATORSTATE,
	 INTEGER,
	 RONLY,
	 ospfv3AreaEntry,
	 4,
	 {1, 2, 1, 12}},
	{OSPFv3AREANSSATRANSLATORSTABINTERVAL,
	 UNSIGNED,
	 RWRITE,
	 ospfv3AreaEntry,
	 4,
	 {1, 2, 1, 13}},
	{OSPFv3AREANSSATRANSLATOREVENTS,
	 COUNTER,
	 RONLY,
	 ospfv3AreaEntry,
	 4,
	 {1, 2, 1, 14}},
	{OSPFv3AREASTUBMETRICTYPE,
	 INTEGER,
	 RWRITE,
	 ospfv3AreaEntry,
	 4,
	 {1, 2, 1, 15}},
	{OSPFv3AREATEENABLED,
	 INTEGER,
	 RWRITE,
	 ospfv3AreaEntry,
	 4,
	 {1, 2, 1, 16}},

	/* OSPFv3 AS LSDB */
	{OSPFv3WWLSDBSEQUENCE | OSPFv3WWASTABLE,
	 INTEGER,
	 RONLY,
	 ospfv3WwLsdbEntry,
	 4,
	 {1, 3, 1, 4}},
	{OSPFv3WWLSDBAGE | OSPFv3WWASTABLE,
	 UNSIGNED,
	 RONLY,
	 ospfv3WwLsdbEntry,
	 4,
	 {1, 3, 1, 5}},
	{OSPFv3WWLSDBCHECKSUM | OSPFv3WWASTABLE,
	 INTEGER,
	 RONLY,
	 ospfv3WwLsdbEntry,
	 4,
	 {1, 3, 1, 6}},
	{OSPFv3WWLSDBADVERTISEMENT | OSPFv3WWASTABLE,
	 STRING,
	 RONLY,
	 ospfv3WwLsdbEntry,
	 4,
	 {1, 3, 1, 7}},
	{OSPFv3WWLSDBTYPEKNOWN | OSPFv3WWASTABLE,
	 INTEGER,
	 RONLY,
	 ospfv3WwLsdbEntry,
	 4,
	 {1, 3, 1, 8}},

	/* OSPFv3 Area LSDB */
	{OSPFv3WWLSDBSEQUENCE | OSPFv3WWAREATABLE,
	 INTEGER,
	 RONLY,
	 ospfv3WwLsdbEntry,
	 4,
	 {1, 4, 1, 5}},
	{OSPFv3WWLSDBAGE | OSPFv3WWAREATABLE,
	 UNSIGNED,
	 RONLY,
	 ospfv3WwLsdbEntry,
	 4,
	 {1, 4, 1, 6}},
	{OSPFv3WWLSDBCHECKSUM | OSPFv3WWAREATABLE,
	 INTEGER,
	 RONLY,
	 ospfv3WwLsdbEntry,
	 4,
	 {1, 4, 1, 7}},
	{OSPFv3WWLSDBADVERTISEMENT | OSPFv3WWAREATABLE,
	 STRING,
	 RONLY,
	 ospfv3WwLsdbEntry,
	 4,
	 {1, 4, 1, 8}},
	{OSPFv3WWLSDBTYPEKNOWN | OSPFv3WWAREATABLE,
	 INTEGER,
	 RONLY,
	 ospfv3WwLsdbEntry,
	 4,
	 {1, 4, 1, 9}},

	/* OSPFv3 Link LSDB */
	{OSPFv3WWLSDBSEQUENCE | OSPFv3WWLINKTABLE,
	 INTEGER,
	 RONLY,
	 ospfv3WwLsdbEntry,
	 4,
	 {1, 5, 1, 6}},
	{OSPFv3WWLSDBAGE | OSPFv3WWLINKTABLE,
	 UNSIGNED,
	 RONLY,
	 ospfv3WwLsdbEntry,
	 4,
	 {1, 5, 1, 7}},
	{OSPFv3WWLSDBCHECKSUM | OSPFv3WWLINKTABLE,
	 INTEGER,
	 RONLY,
	 ospfv3WwLsdbEntry,
	 4,
	 {1, 5, 1, 8}},
	{OSPFv3WWLSDBADVERTISEMENT | OSPFv3WWLINKTABLE,
	 STRING,
	 RONLY,
	 ospfv3WwLsdbEntry,
	 4,
	 {1, 5, 1, 9}},
	{OSPFv3WWLSDBTYPEKNOWN | OSPFv3WWLINKTABLE,
	 INTEGER,
	 RONLY,
	 ospfv3WwLsdbEntry,
	 4,
	 {1, 5, 1, 10}},

	/* OSPFv3 interfaces */
	{OSPFv3IFAREAID, UNSIGNED, RONLY, ospfv3IfEntry, 4, {1, 7, 1, 3}},
	{OSPFv3IFTYPE, INTEGER, RONLY, ospfv3IfEntry, 4, {1, 7, 1, 4}},
	{OSPFv3IFADMINSTATUS, INTEGER, RONLY, ospfv3IfEntry, 4, {1, 7, 1, 5}},
	{OSPFv3IFRTRPRIORITY, INTEGER, RONLY, ospfv3IfEntry, 4, {1, 7, 1, 6}},
	{OSPFv3IFTRANSITDELAY, UNSIGNED, RONLY, ospfv3IfEntry, 4, {1, 7, 1, 7}},
	{OSPFv3IFRETRANSINTERVAL,
	 UNSIGNED,
	 RONLY,
	 ospfv3IfEntry,
	 4,
	 {1, 7, 1, 8}},
	{OSPFv3IFHELLOINTERVAL, INTEGER, RONLY, ospfv3IfEntry, 4, {1, 7, 1, 9}},
	{OSPFv3IFRTRDEADINTERVAL,
	 UNSIGNED,
	 RONLY,
	 ospfv3IfEntry,
	 4,
	 {1, 7, 1, 10}},
	{OSPFv3IFPOLLINTERVAL,
	 UNSIGNED,
	 RONLY,
	 ospfv3IfEntry,
	 4,
	 {1, 7, 1, 11}},
	{OSPFv3IFSTATE, INTEGER, RONLY, ospfv3IfEntry, 4, {1, 7, 1, 12}},
	{OSPFv3IFDESIGNATEDROUTER,
	 UNSIGNED,
	 RONLY,
	 ospfv3IfEntry,
	 4,
	 {1, 7, 1, 13}},
	{OSPFv3IFBACKUPDESIGNATEDROUTER,
	 UNSIGNED,
	 RONLY,
	 ospfv3IfEntry,
	 4,
	 {1, 7, 1, 14}},
	{OSPFv3IFEVENTS, COUNTER, RONLY, ospfv3IfEntry, 4, {1, 7, 1, 15}},
	{OSPFv3IFROWSTATUS, INTEGER, RONLY, ospfv3IfEntry, 4, {1, 7, 1, 16}},
	{OSPFv3IFDEMAND, INTEGER, RONLY, ospfv3IfEntry, 4, {1, 7, 1, 17}},
	{OSPFv3IFMETRICVALUE, INTEGER, RONLY, ospfv3IfEntry, 4, {1, 7, 1, 18}},
	{OSPFv3IFLINKSCOPELSACOUNT,
	 GAUGE,
	 RONLY,
	 ospfv3IfEntry,
	 4,
	 {1, 7, 1, 19}},
	{OSPFv3IFLINKLSACKSUMSUM,
	 UNSIGNED,
	 RONLY,
	 ospfv3IfEntry,
	 4,
	 {1, 7, 1, 20}},
	{OSPFv3IFDEMANDNBRPROBE,
	 INTEGER,
	 RONLY,
	 ospfv3IfEntry,
	 4,
	 {1, 7, 1, 21}},
	{OSPFv3IFDEMANDNBRPROBERETRANSLIMIT,
	 UNSIGNED,
	 RONLY,
	 ospfv3IfEntry,
	 4,
	 {1, 7, 1, 22}},
	{OSPFv3IFDEMANDNBRPROBEINTERVAL,
	 UNSIGNED,
	 RONLY,
	 ospfv3IfEntry,
	 4,
	 {1, 7, 1, 23}},
	{OSPFv3IFTEDISABLED, INTEGER, RONLY, ospfv3IfEntry, 4, {1, 7, 1, 24}},
	{OSPFv3IFLINKLSASUPPRESSION,
	 INTEGER,
	 RONLY,
	 ospfv3IfEntry,
	 4,
	 {1, 7, 1, 25}},

	/* OSPFv3 neighbors */
	{OSPFv3NBRADDRESSTYPE, INTEGER, RONLY, ospfv3NbrEntry, 4, {1, 9, 1, 4}},
	{OSPFv3NBRADDRESS, STRING, RONLY, ospfv3NbrEntry, 4, {1, 9, 1, 5}},
	{OSPFv3NBROPTIONS, INTEGER, RONLY, ospfv3NbrEntry, 4, {1, 9, 1, 6}},
	{OSPFv3NBRPRIORITY, INTEGER, RONLY, ospfv3NbrEntry, 4, {1, 9, 1, 7}},
	{OSPFv3NBRSTATE, INTEGER, RONLY, ospfv3NbrEntry, 4, {1, 9, 1, 8}},
	{OSPFv3NBREVENTS, COUNTER, RONLY, ospfv3NbrEntry, 4, {1, 9, 1, 9}},
	{OSPFv3NBRLSRETRANSQLEN,
	 GAUGE,
	 RONLY,
	 ospfv3NbrEntry,
	 4,
	 {1, 9, 1, 10}},
	{OSPFv3NBRHELLOSUPPRESSED,
	 INTEGER,
	 RONLY,
	 ospfv3NbrEntry,
	 4,
	 {1, 9, 1, 11}},
	{OSPFv3NBRIFID, INTEGER, RONLY, ospfv3NbrEntry, 4, {1, 9, 1, 12}},
	{OSPFv3NBRRESTARTHELPERSTATUS,
	 INTEGER,
	 RONLY,
	 ospfv3NbrEntry,
	 4,
	 {1, 9, 1, 13}},
	{OSPFv3NBRRESTARTHELPERAGE,
	 UNSIGNED,
	 RONLY,
	 ospfv3NbrEntry,
	 4,
	 {1, 9, 1, 14}},
	{OSPFv3NBRRESTARTHELPEREXITREASON,
	 INTEGER,
	 RONLY,
	 ospfv3NbrEntry,
	 4,
	 {1, 9, 1, 15}},
};

static uint8_t *ospfv3GeneralGroup(struct variable *v, oid *name,
				   size_t *length, int exact, size_t *var_len,
				   WriteMethod **write_method)
{
	uint16_t sum;
	uint32_t count;
	struct ospf6_lsa *lsa = NULL, *lsanext;
	struct ospf6 *ospf6;

	ospf6 = ospf6_lookup_by_vrf_id(VRF_DEFAULT);
	/* Check whether the instance identifier is valid */
	if (smux_header_generic(v, name, length, exact, var_len, write_method)
	    == MATCH_FAILED)
		return NULL;

	/* Return the current value of the variable */
	switch (v->magic) {
	case OSPFv3ROUTERID:
		/* Router-ID of this OSPF instance. */
		if (ospf6)
			return SNMP_INTEGER(ntohl(ospf6->router_id));
		return SNMP_INTEGER(0);
	case OSPFv3ADMINSTAT:
		if (ospf6)
			return SNMP_INTEGER(
				CHECK_FLAG(ospf6->flag, OSPF6_DISABLED)
					? OSPF_STATUS_DISABLED
					: OSPF_STATUS_ENABLED);
		return SNMP_INTEGER(OSPF_STATUS_DISABLED);
	case OSPFv3VERSIONNUMBER:
		return SNMP_INTEGER(3);
	case OSPFv3AREABDRRTRSTATUS:
		if (ospf6)
			return SNMP_INTEGER(
				ospf6_check_and_set_router_abr(ospf6)
					? SNMP_TRUE
					: SNMP_FALSE);
		return SNMP_INTEGER(SNMP_FALSE);
	case OSPFv3ASBDRRTRSTATUS:
		if (ospf6)
			return SNMP_INTEGER(ospf6_asbr_is_asbr(ospf6)
						    ? SNMP_TRUE
						    : SNMP_FALSE);
		return SNMP_INTEGER(SNMP_FALSE);
	case OSPFv3ASSCOPELSACOUNT:
		if (ospf6)
			return SNMP_INTEGER(ospf6->lsdb->count);
		return SNMP_INTEGER(0);
	case OSPFv3ASSCOPELSACHECKSUMSUM:
		if (ospf6) {
			sum = 0;
			for (ALL_LSDB(ospf6->lsdb, lsa, lsanext))
				sum += ntohs(lsa->header->checksum);
			return SNMP_INTEGER(sum);
		}
		return SNMP_INTEGER(0);
	case OSPFv3ORIGINATENEWLSAS:
		return SNMP_INTEGER(
			0); /* Don't know where to get this value... */
	case OSPFv3RXNEWLSAS:
		return SNMP_INTEGER(
			0); /* Don't know where to get this value... */
	case OSPFv3EXTLSACOUNT:
		if (ospf6) {
			count = 0;
			for (ALL_LSDB_TYPED(ospf6->lsdb,
					    htons(OSPF6_LSTYPE_AS_EXTERNAL),
					    lsa))
				count += 1;
			return SNMP_INTEGER(count);
		}
		return SNMP_INTEGER(0);
	case OSPFv3EXTAREALSDBLIMIT:
		return SNMP_INTEGER(-1);
	case OSPFv3EXITOVERFLOWINTERVAL:
		return SNMP_INTEGER(0); /* Not supported */
	case OSPFv3DEMANDEXTENSIONS:
		return SNMP_INTEGER(0); /* Not supported */
	case OSPFv3REFERENCEBANDWIDTH:
		if (ospf6)
			return SNMP_INTEGER(ospf6->ref_bandwidth);
	/* Otherwise, like for "not implemented". */
	/* fallthru */
	case OSPFv3RESTARTSUPPORT:
	case OSPFv3RESTARTINTERVAL:
	case OSPFv3RESTARTSTRICTLSACHECKING:
	case OSPFv3RESTARTSTATUS:
	case OSPFv3RESTARTAGE:
	case OSPFv3RESTARTEXITREASON:
	case OSPFv3NOTIFICATIONENABLE:
	case OSPFv3STUBROUTERSUPPORT:
	case OSPFv3STUBROUTERADVERTISEMENT:
	case OSPFv3DISCONTINUITYTIME:
	case OSPFv3RESTARTTIME:
		/* TODO: Not implemented */
		return NULL;
	}
	return NULL;
}

static uint8_t *ospfv3AreaEntry(struct variable *v, oid *name, size_t *length,
				int exact, size_t *var_len,
				WriteMethod **write_method)
{
	struct ospf6_area *oa, *area = NULL;
	struct ospf6_lsa *lsa = NULL, *lsanext;
	uint32_t area_id = 0;
	uint32_t count;
	uint16_t sum;
	struct listnode *node;
	unsigned int len;
	char a[16];
	struct ospf6_route *ro;
	struct ospf6 *ospf6;

	ospf6 = ospf6_lookup_by_vrf_id(VRF_DEFAULT);

	if (ospf6 == NULL)
		return NULL;

	if (smux_header_table(v, name, length, exact, var_len, write_method)
	    == MATCH_FAILED)
		return NULL;

	len = *length - v->namelen;
	len = (len >= 1 ? 1 : 0);
	if (exact && len != 1)
		return NULL;
	if (len)
		area_id = htonl(name[v->namelen]);

	inet_ntop(AF_INET, &area_id, a, sizeof(a));
	zlog_debug("SNMP access by area: %s, exact=%d len=%d length=%lu", a,
		   exact, len, (unsigned long)*length);

	for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, node, oa)) {
		if (area == NULL) {
			if (len == 0) /* return first area entry */
				area = oa;
			else if (exact && ntohl(oa->area_id) == ntohl(area_id))
				area = oa;
			else if (ntohl(oa->area_id) > ntohl(area_id))
				area = oa;
		}
	}

	if (area == NULL)
		return NULL;

	*length = v->namelen + 1;
	name[v->namelen] = ntohl(area->area_id);

	inet_ntop(AF_INET, &area->area_id, a, sizeof(a));
	zlog_debug("SNMP found area: %s, exact=%d len=%d length=%lu", a, exact,
		   len, (unsigned long)*length);

	switch (v->magic) {
	case OSPFv3IMPORTASEXTERN:
		/* No NSSA support */
		return SNMP_INTEGER(IS_AREA_STUB(area) ? 2 : 1);
	case OSPFv3AREASPFRUNS:
		return SNMP_INTEGER(area->spf_calculation);
	case OSPFv3AREABDRRTRCOUNT:
	case OSPFv3AREAASBDRRTRCOUNT:
		count = 0;
		for (ro = ospf6_route_head(ospf6->brouter_table); ro;
		     ro = ospf6_route_next(ro)) {
			if (ntohl(ro->path.area_id) != ntohl(area->area_id))
				continue;
			if (v->magic == OSPFv3AREABDRRTRCOUNT
			    && CHECK_FLAG(ro->path.router_bits,
					  OSPF6_ROUTER_BIT_B))
				count++;
			if (v->magic == OSPFv3AREAASBDRRTRCOUNT
			    && CHECK_FLAG(ro->path.router_bits,
					  OSPF6_ROUTER_BIT_E))
				count++;
		}
		return SNMP_INTEGER(count);
	case OSPFv3AREASCOPELSACOUNT:
		return SNMP_INTEGER(area->lsdb->count);
	case OSPFv3AREASCOPELSACKSUMSUM:
		sum = 0;
		for (ALL_LSDB(area->lsdb, lsa, lsanext))
			sum += ntohs(lsa->header->checksum);
		return SNMP_INTEGER(sum);
	case OSPFv3AREASUMMARY:
		return SNMP_INTEGER(2); /* sendAreaSummary */
	case OSPFv3AREAROWSTATUS:
		return SNMP_INTEGER(1); /* Active */
	case OSPFv3AREASTUBMETRIC:
	case OSPFv3AREANSSATRANSLATORROLE:
	case OSPFv3AREANSSATRANSLATORSTATE:
	case OSPFv3AREANSSATRANSLATORSTABINTERVAL:
	case OSPFv3AREANSSATRANSLATOREVENTS:
	case OSPFv3AREASTUBMETRICTYPE:
	case OSPFv3AREATEENABLED:
		/* Not implemented. */
		return NULL;
	}
	return NULL;
}

static int if_icmp_func(struct interface *ifp1, struct interface *ifp2)
{
	return (ifp1->ifindex - ifp2->ifindex);
}

static uint8_t *ospfv3WwLsdbEntry(struct variable *v, oid *name, size_t *length,
				  int exact, size_t *var_len,
				  WriteMethod **write_method)
{
	struct vrf *vrf;
	struct ospf6_lsa *lsa = NULL;
	ifindex_t ifindex;
	uint32_t area_id, id, instid, adv_router;
	uint16_t type;
	int len;
	oid *offset;
	int offsetlen;
	struct ospf6_area *oa = NULL;
	struct listnode *node;
	struct interface *iif;
	struct ospf6_interface *oi = NULL;
	struct list *ifslist;
	struct ospf6 *ospf6;

	ospf6 = ospf6_lookup_by_vrf_id(VRF_DEFAULT);

	if (smux_header_table(v, name, length, exact, var_len, write_method)
	    == MATCH_FAILED)
		return NULL;

	instid = ifindex = area_id = type = id = adv_router = 0;

	/* Check OSPFv3 instance. */
	if (ospf6 == NULL)
		return NULL;

	vrf = vrf_lookup_by_id(ospf6->vrf_id);
	/* Get variable length. */
	offset = name + v->namelen;
	offsetlen = *length - v->namelen;

	if (exact && (v->magic & OSPFv3WWASTABLE) && offsetlen != 3)
		return NULL;
	if (exact && (v->magic & OSPFv3WWAREATABLE) && offsetlen != 4)
		return NULL;
	if (exact && (v->magic & OSPFv3WWLINKTABLE) && offsetlen != 5)
		return NULL;

	if (v->magic & OSPFv3WWLINKTABLE) {
		/* Parse ifindex */
		len = (offsetlen < 1 ? 0 : 1);
		if (len)
			ifindex = *offset;
		offset += len;
		offsetlen -= len;

		/* Parse instance ID */
		len = (offsetlen < 1 ? 0 : 1);
		if (len)
			instid = *offset;
		offset += len;
		offsetlen -= len;
	} else if (v->magic & OSPFv3WWAREATABLE) {
		/* Parse area-id */
		len = (offsetlen < 1 ? 0 : 1);
		if (len)
			area_id = htonl(*offset);
		offset += len;
		offsetlen -= len;
	}

	/* Parse type */
	len = (offsetlen < 1 ? 0 : 1);
	if (len)
		type = htons(*offset);
	offset += len;
	offsetlen -= len;

	/* Parse Router-ID */
	len = (offsetlen < 1 ? 0 : 1);
	if (len)
		adv_router = htonl(*offset);
	offset += len;
	offsetlen -= len;

	/* Parse LS-ID */
	len = (offsetlen < 1 ? 0 : 1);
	if (len)
		id = htonl(*offset);
	offset += len;
	// offsetlen -= len;  // Add back in if we need it again

	if (exact) {
		if (v->magic & OSPFv3WWASTABLE) {
			lsa = ospf6_lsdb_lookup(type, id, adv_router,
						ospf6->lsdb);
		} else if (v->magic & OSPFv3WWAREATABLE) {
			oa = ospf6_area_lookup(area_id, ospf6);
			if (!oa)
				return NULL;
			lsa = ospf6_lsdb_lookup(type, id, adv_router, oa->lsdb);
		} else if (v->magic & OSPFv3WWLINKTABLE) {
			oi = ospf6_interface_lookup_by_ifindex(ifindex,
							       ospf6->vrf_id);
			if (!oi || oi->instance_id != instid)
				return NULL;
			lsa = ospf6_lsdb_lookup(type, id, adv_router, oi->lsdb);
		}
	} else {
		if (v->magic & OSPFv3WWASTABLE) {
			if (ospf6->lsdb->count)
				lsa = ospf6_lsdb_lookup_next(
					type, id, adv_router, ospf6->lsdb);
		} else if (v->magic & OSPFv3WWAREATABLE)
			for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, node, oa)) {
				if (oa->area_id < area_id)
					continue;

				if (oa->lsdb->count)
					lsa = ospf6_lsdb_lookup_next(
						type, id, adv_router, oa->lsdb);
				if (lsa)
					break;
				type = 0;
				id = 0;
				adv_router = 0;
			}
		else if (v->magic & OSPFv3WWLINKTABLE) {
			/* We build a sorted list of interfaces */
			ifslist = list_new();
			ifslist->cmp = (int (*)(void *, void *))if_icmp_func;
			FOR_ALL_INTERFACES (vrf, iif)
				listnode_add_sort(ifslist, iif);

			for (ALL_LIST_ELEMENTS_RO(ifslist, node, iif)) {
				if (!iif->ifindex)
					continue;
				oi = iif->info;
				if (!oi)
					continue;
				if (iif->ifindex < ifindex)
					continue;
				if (oi->instance_id < instid)
					continue;

				if (oi->lsdb->count)
					lsa = ospf6_lsdb_lookup_next(
						type, id, adv_router, oi->lsdb);
				if (lsa)
					break;
				type = 0;
				id = 0;
				adv_router = 0;
				oi = NULL;
			}

			list_delete_all_node(ifslist);
			list_delete(&ifslist);
		}
	}

	if (!lsa)
		return NULL;

	/* Add indexes */
	if (v->magic & OSPFv3WWASTABLE) {
		*length = v->namelen + 3;
		offset = name + v->namelen;
	} else if (v->magic & OSPFv3WWAREATABLE) {
		*length = v->namelen + 4;
		offset = name + v->namelen;
		*offset = ntohl(oa->area_id);
		offset++;
	} else if (v->magic & OSPFv3WWLINKTABLE) {
		*length = v->namelen + 5;
		offset = name + v->namelen;
		*offset = oi->interface->ifindex;
		offset++;
		*offset = oi->instance_id;
		offset++;
	}
	*offset = ntohs(lsa->header->type);
	offset++;
	*offset = ntohl(lsa->header->adv_router);
	offset++;
	*offset = ntohl(lsa->header->id);
	offset++;

	/* Return the current value of the variable */
	switch (v->magic & OSPFv3WWCOLUMN) {
	case OSPFv3WWLSDBSEQUENCE:
		return SNMP_INTEGER(ntohl(lsa->header->seqnum));
	case OSPFv3WWLSDBAGE:
		ospf6_lsa_age_current(lsa);
		return SNMP_INTEGER(ntohs(lsa->header->age));
	case OSPFv3WWLSDBCHECKSUM:
		return SNMP_INTEGER(ntohs(lsa->header->checksum));
	case OSPFv3WWLSDBADVERTISEMENT:
		*var_len = ntohs(lsa->header->length);
		return (uint8_t *)lsa->header;
	case OSPFv3WWLSDBTYPEKNOWN:
		return SNMP_INTEGER(OSPF6_LSA_IS_KNOWN(lsa->header->type)
					    ? SNMP_TRUE
					    : SNMP_FALSE);
	}
	return NULL;
}

static uint8_t *ospfv3IfEntry(struct variable *v, oid *name, size_t *length,
			      int exact, size_t *var_len,
			      WriteMethod **write_method)
{
	struct vrf *vrf;
	ifindex_t ifindex = 0;
	unsigned int instid = 0;
	struct ospf6_interface *oi = NULL;
	struct ospf6_lsa *lsa = NULL, *lsanext;
	struct interface *iif;
	struct listnode *i;
	struct list *ifslist;
	oid *offset;
	int offsetlen, len;
	uint32_t sum;
	struct ospf6 *ospf6;

	ospf6 = ospf6_lookup_by_vrf_id(VRF_DEFAULT);

	if (smux_header_table(v, name, length, exact, var_len, write_method)
	    == MATCH_FAILED)
		return NULL;

	/* Check OSPFv3 instance. */
	if (ospf6 == NULL)
		return NULL;

	vrf = vrf_lookup_by_id(ospf6->vrf_id);
	/* Get variable length. */
	offset = name + v->namelen;
	offsetlen = *length - v->namelen;

	if (exact && offsetlen != 2)
		return NULL;

	/* Parse if index */
	len = (offsetlen < 1 ? 0 : 1);
	if (len)
		ifindex = *offset;
	offset += len;
	offsetlen -= len;

	/* Parse instance ID */
	len = (offsetlen < 1 ? 0 : 1);
	if (len)
		instid = *offset;
	// offset += len; // Add back in if we ever start using again
	// offsetlen -= len;

	if (exact) {
		oi = ospf6_interface_lookup_by_ifindex(ifindex, ospf6->vrf_id);
		if (!oi || oi->instance_id != instid)
			return NULL;
	} else {
		/* We build a sorted list of interfaces */
		ifslist = list_new();
		ifslist->cmp = (int (*)(void *, void *))if_icmp_func;
		FOR_ALL_INTERFACES (vrf, iif)
			listnode_add_sort(ifslist, iif);

		for (ALL_LIST_ELEMENTS_RO(ifslist, i, iif)) {
			if (!iif->ifindex)
				continue;
			oi = iif->info;
			if (!oi)
				continue;
			if (iif->ifindex > ifindex
			    || (iif->ifindex == ifindex
				&& (oi->instance_id > instid)))
				break;
			oi = NULL;
		}

		list_delete_all_node(ifslist);
		list_delete(&ifslist);
	}

	if (!oi)
		return NULL;

	/* Add Index (IfIndex, IfInstId) */
	*length = v->namelen + 2;
	offset = name + v->namelen;
	*offset = oi->interface->ifindex;
	offset++;
	*offset = oi->instance_id;
	offset++;

	/* Return the current value of the variable */
	switch (v->magic) {
	case OSPFv3IFAREAID:
		if (oi->area)
			return SNMP_INTEGER(ntohl(oi->area->area_id));
		break;
	case OSPFv3IFTYPE:
		if (oi->type == OSPF_IFTYPE_BROADCAST)
			return SNMP_INTEGER(1);
		else if (oi->type == OSPF_IFTYPE_POINTOPOINT)
			return SNMP_INTEGER(3);
		else
			break; /* Unknown, don't put anything */
	case OSPFv3IFADMINSTATUS:
		if (oi->area)
			return SNMP_INTEGER(OSPF_STATUS_ENABLED);
		return SNMP_INTEGER(OSPF_STATUS_DISABLED);
	case OSPFv3IFRTRPRIORITY:
		return SNMP_INTEGER(oi->priority);
	case OSPFv3IFTRANSITDELAY:
		return SNMP_INTEGER(oi->transdelay);
	case OSPFv3IFRETRANSINTERVAL:
		return SNMP_INTEGER(oi->rxmt_interval);
	case OSPFv3IFHELLOINTERVAL:
		return SNMP_INTEGER(oi->hello_interval);
	case OSPFv3IFRTRDEADINTERVAL:
		return SNMP_INTEGER(oi->dead_interval);
	case OSPFv3IFPOLLINTERVAL:
		/* No support for NBMA */
		break;
	case OSPFv3IFSTATE:
		return SNMP_INTEGER(oi->state);
	case OSPFv3IFDESIGNATEDROUTER:
		return SNMP_INTEGER(ntohl(oi->drouter));
	case OSPFv3IFBACKUPDESIGNATEDROUTER:
		return SNMP_INTEGER(ntohl(oi->bdrouter));
	case OSPFv3IFEVENTS:
		return SNMP_INTEGER(oi->state_change);
	case OSPFv3IFROWSTATUS:
		return SNMP_INTEGER(1);
	case OSPFv3IFDEMAND:
		return SNMP_INTEGER(SNMP_FALSE);
	case OSPFv3IFMETRICVALUE:
		return SNMP_INTEGER(oi->cost);
	case OSPFv3IFLINKSCOPELSACOUNT:
		return SNMP_INTEGER(oi->lsdb->count);
	case OSPFv3IFLINKLSACKSUMSUM:
		sum = 0;
		for (ALL_LSDB(oi->lsdb, lsa, lsanext))
			sum += ntohs(lsa->header->checksum);
		return SNMP_INTEGER(sum);
	case OSPFv3IFDEMANDNBRPROBE:
	case OSPFv3IFDEMANDNBRPROBERETRANSLIMIT:
	case OSPFv3IFDEMANDNBRPROBEINTERVAL:
	case OSPFv3IFTEDISABLED:
	case OSPFv3IFLINKLSASUPPRESSION:
		/* Not implemented. Only works if all the last ones are not
		   implemented! */
		return NULL;
	}

	/* Try an internal getnext. Some columns are missing in this table. */
	if (!exact && (name[*length - 1] < MAX_SUBID))
		return ospfv3IfEntry(v, name, length, exact, var_len,
				     write_method);
	return NULL;
}

static uint8_t *ospfv3NbrEntry(struct variable *v, oid *name, size_t *length,
			       int exact, size_t *var_len,
			       WriteMethod **write_method)
{
	struct vrf *vrf;
	ifindex_t ifindex = 0;
	unsigned int instid, rtrid;
	struct ospf6_interface *oi = NULL;
	struct ospf6_neighbor *on = NULL;
	struct interface *iif;
	struct listnode *i, *j;
	struct list *ifslist;
	oid *offset;
	int offsetlen, len;
	struct ospf6 *ospf6;

	ospf6 = ospf6_lookup_by_vrf_id(VRF_DEFAULT);

	if (smux_header_table(v, name, length, exact, var_len, write_method)
	    == MATCH_FAILED)
		return NULL;

	instid = rtrid = 0;

	/* Check OSPFv3 instance. */
	if (ospf6 == NULL)
		return NULL;

	vrf = vrf_lookup_by_id(ospf6->vrf_id);
	/* Get variable length. */
	offset = name + v->namelen;
	offsetlen = *length - v->namelen;

	if (exact && offsetlen != 3)
		return NULL;

	/* Parse if index */
	len = (offsetlen < 1 ? 0 : 1);
	if (len)
		ifindex = *offset;
	offset += len;
	offsetlen -= len;

	/* Parse instance ID */
	len = (offsetlen < 1 ? 0 : 1);
	if (len)
		instid = *offset;
	offset += len;
	offsetlen -= len;

	/* Parse router ID */
	len = (offsetlen < 1 ? 0 : 1);
	if (len)
		rtrid = htonl(*offset);
	// offset += len; // Add back in if we ever start looking at data
	// offsetlen -= len;

	if (exact) {
		oi = ospf6_interface_lookup_by_ifindex(ifindex, ospf6->vrf_id);
		if (!oi || oi->instance_id != instid)
			return NULL;
		on = ospf6_neighbor_lookup(rtrid, oi);
	} else {
		/* We build a sorted list of interfaces */
		ifslist = list_new();
		ifslist->cmp = (int (*)(void *, void *))if_icmp_func;
		FOR_ALL_INTERFACES (vrf, iif)
			listnode_add_sort(ifslist, iif);

		for (ALL_LIST_ELEMENTS_RO(ifslist, i, iif)) {
			if (!iif->ifindex)
				continue;
			oi = iif->info;
			if (!oi)
				continue;
			for (ALL_LIST_ELEMENTS_RO(oi->neighbor_list, j, on)) {
				if (iif->ifindex > ifindex
				    || (iif->ifindex == ifindex
					&& (oi->instance_id > instid
					    || (oi->instance_id == instid
						&& ntohl(on->router_id)
							   > ntohl(rtrid)))))
					break;
			}
			if (on)
				break;
			oi = NULL;
			on = NULL;
		}

		list_delete_all_node(ifslist);
		list_delete(&ifslist);
	}

	if (!oi || !on)
		return NULL;

	/* Add Index (IfIndex, IfInstId, RtrId) */
	*length = v->namelen + 3;
	offset = name + v->namelen;
	*offset = oi->interface->ifindex;
	offset++;
	*offset = oi->instance_id;
	offset++;
	*offset = ntohl(on->router_id);
	offset++;

	/* Return the current value of the variable */
	switch (v->magic) {
	case OSPFv3NBRADDRESSTYPE:
		return SNMP_INTEGER(2); /* IPv6 only */
	case OSPFv3NBRADDRESS:
		*var_len = sizeof(struct in6_addr);
		return (uint8_t *)&on->linklocal_addr;
	case OSPFv3NBROPTIONS:
		return SNMP_INTEGER(on->options[2]);
	case OSPFv3NBRPRIORITY:
		return SNMP_INTEGER(on->priority);
	case OSPFv3NBRSTATE:
		return SNMP_INTEGER(on->state);
	case OSPFv3NBREVENTS:
		return SNMP_INTEGER(on->state_change);
	case OSPFv3NBRLSRETRANSQLEN:
		return SNMP_INTEGER(on->retrans_list->count);
	case OSPFv3NBRHELLOSUPPRESSED:
		return SNMP_INTEGER(SNMP_FALSE);
	case OSPFv3NBRIFID:
		return SNMP_INTEGER(on->ifindex);
	case OSPFv3NBRRESTARTHELPERSTATUS:
	case OSPFv3NBRRESTARTHELPERAGE:
	case OSPFv3NBRRESTARTHELPEREXITREASON:
		/* Not implemented. Only works if all the last ones are not
		   implemented! */
		return NULL;
	}

	return NULL;
}

/* OSPF Traps. */
#define NBRSTATECHANGE      2
#define IFSTATECHANGE      10

static struct trap_object ospf6NbrTrapList[] = {
	{-3, {1, 1, OSPFv3ROUTERID}},
	{4, {1, 9, 1, OSPFv3NBRADDRESSTYPE}},
	{4, {1, 9, 1, OSPFv3NBRADDRESS}},
	{4, {1, 9, 1, OSPFv3NBRSTATE}}};

static struct trap_object ospf6IfTrapList[] = {
	{-3, {1, 1, OSPFv3ROUTERID}},
	{4, {1, 7, 1, OSPFv3IFSTATE}},
	{4, {1, 7, 1, OSPFv3IFADMINSTATUS}},
	{4, {1, 7, 1, OSPFv3IFAREAID}}};

static int ospf6TrapNbrStateChange(struct ospf6_neighbor *on, int next_state,
				   int prev_state)
{
	oid index[3];

	/* Terminal state or regression */
	if ((next_state != OSPF6_NEIGHBOR_FULL)
	    && (next_state != OSPF6_NEIGHBOR_TWOWAY)
	    && (next_state >= prev_state))
		return 0;

	index[0] = on->ospf6_if->interface->ifindex;
	index[1] = on->ospf6_if->instance_id;
	index[2] = ntohl(on->router_id);

	smux_trap(ospfv3_variables, array_size(ospfv3_variables),
		  ospfv3_trap_oid, array_size(ospfv3_trap_oid), ospfv3_oid,
		  sizeof(ospfv3_oid) / sizeof(oid), index, 3, ospf6NbrTrapList,
		  array_size(ospf6NbrTrapList), NBRSTATECHANGE);
	return 0;
}

static int ospf6TrapIfStateChange(struct ospf6_interface *oi, int next_state,
				  int prev_state)
{
	oid index[2];

	/* Terminal state or regression */
	if ((next_state != OSPF6_INTERFACE_POINTTOPOINT)
	    && (next_state != OSPF6_INTERFACE_DROTHER)
	    && (next_state != OSPF6_INTERFACE_BDR)
	    && (next_state != OSPF6_INTERFACE_DR) && (next_state >= prev_state))
		return 0;

	index[0] = oi->interface->ifindex;
	index[1] = oi->instance_id;

	smux_trap(ospfv3_variables, array_size(ospfv3_variables),
		  ospfv3_trap_oid, array_size(ospfv3_trap_oid), ospfv3_oid,
		  sizeof(ospfv3_oid) / sizeof(oid), index, 2, ospf6IfTrapList,
		  array_size(ospf6IfTrapList), IFSTATECHANGE);
	return 0;
}

/* Register OSPFv3-MIB. */
static int ospf6_snmp_init(struct thread_master *master)
{
	smux_init(master);
	REGISTER_MIB("OSPFv3MIB", ospfv3_variables, variable, ospfv3_oid);
	return 0;
}

static int ospf6_snmp_module_init(void)
{
	hook_register(ospf6_interface_change, ospf6TrapIfStateChange);
	hook_register(ospf6_neighbor_change, ospf6TrapNbrStateChange);
	hook_register(frr_late_init, ospf6_snmp_init);
	return 0;
}

FRR_MODULE_SETUP(.name = "ospf6d_snmp", .version = FRR_VERSION,
		 .description = "ospf6d AgentX SNMP module",
		 .init = ospf6_snmp_module_init,
);
