/*
 * Soft:        Keepalived is a failover program for the LVS project
 *              <www.linuxvirtualserver.org>. It monitor & manipulate
 *              a loadbalanced server pool using multi-layer checks.
 *
 * Part:        SNMP agent
 *
 * Author:      Vincent Bernat <bernat@luffy.cx>
 *
 *              This program 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.
 *
 *              This program 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 of the License, or (at your option) any later version.
 *
 * Copyright (C) 2001-2017 Alexandre Cassen, <acassen@gmail.com>
 */

/*
 * To test this code, one can use the following:
 *
 * Build keepalived with SNMP support with one or more of the following options
     ./configure--enable-snmp-vrrp --enable-snmp-checker [or --enable-snmp to enable previous two options] \
		 --enable-snmp-rfcv2 --enable-snmp-rfcv3 [or --enable-snmp-rfc to enable previous two options]

 * Edit /etc/snmp/snmpd.conf to contain the following:
     rocommunity public
     rwcommunity private
     master agentx

     # agentaddress is what clients such as snmpwalk talk over to make SNMP requests and receive responses.
     #   This can be specified as the "LISTENING ADDRESS" option to snmpd, and is the AGENT parameter to most
     #   SNMP client commands. Processes using udp or tcp addresses will need to be in the same network
     #    namespace.
     # agentaddress udp:localhost:161		# default

     # agentxsocket is what sub-agents (such as keepalived) communicate via with snmp. This is what keepalived
     #   needs to match with its snmp_socket parameter, and can be specified to snmpd with the -x option.
     # agentxsocket unix:/var/agentx/master	# default

     trapcommunity public
     trap2sink localhost:162

 * Edit /etc/snmp/snmptrapd.conf to uncomment the following line:
     authCommunity  log,execute,net public

 * Put the MIB definition files in a place that will be found:
     cp doc/[VK]*-MIB.txt /usr/share/snmp/mibs
 *  or
     cp doc/[VK]*-MIB.txt ~/.snmp/mibs

 * Run snmpd (in background)
     snmpd -LS0-6d [-x agentxsocket] [AGENT]
    If running in a network namespace, run snmpd, snmptrapd etc in that network namespace. To specify
    a different port/socket to listen on, see above in sample snmpd.conf

 * Run snmptrapd (in foreground)
     MIBS="+KEEPALIVED-MIB:VRRP-MIB:VRRPV3-MIB" snmptrapd -f -Lo
 *  or if MIB files copied to ~/.snmp/mibs
     MIBS="+KEEPALIVED-MIB:VRRP-MIB:VRRPV3-MIB" snmptrapd -f -M "+$HOME/.snmp/mibs" -Lo

 * Enable SNMP in config file, by adding some or all of the following, depending on which configure options were chosen
     enable_snmp	(enables enable_snmp_vrrp and enable_snmp_checker)
     enable_snmp_vrrp
     enable_snmp_checker
     enable_snmp_rfc	(enables enable_snmp_rfcv2 enable_snmp_rfcv3)
     enable_snmp_rfcv2
     enable_snmp_rfcv3
     enable_traps

 * Run keepalived. Some traps/notifications should be generated which will be displayed on the terminal running snmptrapd

 * To see the MIB trees, where localhost can be replaced by an appropriate AGENT, run (in the same namespace as snmpd)
     MIBS="+KEEPALIVED-MIB" snmpwalk -v2c -c public localhost KEEPALIVED-MIB::keepalived
    or
     MIBS="+VRRP-MIB" snmpwalk -v2c -c public localhost VRRP-MIB::vrrpMIB
    or
     MIBS="+VRRPV3-MIB" snmpwalk -v2c -c public localhost VRRPV3-MIB::vrrpv3MIB

 * To check the validity of a MIB file:
     smilint doc/KEEPALIVED-MIB.txt

 * Multiple instances of keepalived cannot register the same MIB
     with the same instance of snmpd. In order for snmpd to work
     with multiple instances of keepalived, there would need to be
     one instance of snmpd per keepalived instance. Using unix domain
     sockets will not work for this, unless each instance of snmpd
     is configured to use a different socket, so use network domain
     sockets e.g. to udp:localhost:705 which will enable keepalived to communicate
     with its own instance of snmpd running in the same network namespace,
     and then set snmp_socket in the keepalived global configuration.
     To run snmpd use snmpd -LS0-6d -x udp:localhost:705, which it appears
     should work but it doesn't seem to.

 */

#include "config.h"

#if HAVE_DECL_RTA_PREF
#include <linux/icmpv6.h>
#endif
#if HAVE_DECL_RTA_ENCAP
#include <linux/lwtunnel.h>
#endif
#include <linux/fib_rules.h>
#include <stdint.h>
#include <inttypes.h>

#include "vrrp.h"
#include "vrrp_snmp.h"
#include "vrrp_track.h"
#include "vrrp_ipaddress.h"
#include "vrrp_iproute.h"
#include "vrrp_iprule.h"
#include "vrrp_scheduler.h"
#include "track_file.h"
#include "config.h"
#include "logger.h"
#include "global_data.h"
#include "bitops.h"
#include "main.h"
#include "rttables.h"
#include "parser.h"
#include "warnings.h"
#include "utils.h"

#include "snmp.h"

#ifdef _WITH_SNMP_VRRP_
/* VRRP SNMP defines */
#define VRRP_OID KEEPALIVED_OID, 2

enum snmp_vrrp_magic {
	VRRP_SNMP_SCRIPT_NAME = 3,
	VRRP_SNMP_SCRIPT_COMMAND,
	VRRP_SNMP_SCRIPT_INTERVAL,
	VRRP_SNMP_SCRIPT_WEIGHT,
	VRRP_SNMP_SCRIPT_WEIGHT_REVERSE,
	VRRP_SNMP_SCRIPT_RESULT,
	VRRP_SNMP_SCRIPT_RISE,
	VRRP_SNMP_SCRIPT_FALL,
	VRRP_SNMP_SCRIPT_PATH,
	VRRP_SNMP_SCRIPT_INTERVAL_USEC,
	VRRP_SNMP_SCRIPT_TIMEOUT_USEC,
	VRRP_SNMP_FILE_NAME,
	VRRP_SNMP_FILE_PATH,
	VRRP_SNMP_FILE_RESULT,
	VRRP_SNMP_FILE_WEIGHT,
	VRRP_SNMP_FILE_WEIGHT_REVERSE,
	VRRP_SNMP_BFD_NAME,
	VRRP_SNMP_BFD_RESULT,
	VRRP_SNMP_BFD_WEIGHT,
	VRRP_SNMP_BFD_WEIGHT_REVERSE,
	VRRP_SNMP_PROCESS_NAME,
	VRRP_SNMP_PROCESS_PATH,
	VRRP_SNMP_PROCESS_PARAMS,
	VRRP_SNMP_PROCESS_PARAM_MATCH,
	VRRP_SNMP_PROCESS_WEIGHT,
	VRRP_SNMP_PROCESS_WEIGHT_REVERSE,
	VRRP_SNMP_PROCESS_QUORUM,
	VRRP_SNMP_PROCESS_QUORUM_MAX,
	VRRP_SNMP_PROCESS_FORKDELAY,
	VRRP_SNMP_PROCESS_TERMINATEDELAY,
	VRRP_SNMP_PROCESS_FULLCOMMAND,
	VRRP_SNMP_PROCESS_CURPROC,
	VRRP_SNMP_PROCESS_RESULT,
	VRRP_SNMP_ADDRESS_ADDRESSTYPE,
	VRRP_SNMP_ADDRESS_VALUE,
	VRRP_SNMP_ADDRESS_BROADCAST,
	VRRP_SNMP_ADDRESS_MASK,
	VRRP_SNMP_ADDRESS_SCOPE,
	VRRP_SNMP_ADDRESS_IFINDEX,
	VRRP_SNMP_ADDRESS_IFNAME,
	VRRP_SNMP_ADDRESS_IFALIAS,
	VRRP_SNMP_ADDRESS_ISSET,
	VRRP_SNMP_ADDRESS_ISADVERTISED,
	VRRP_SNMP_ADDRESS_PEER,
	VRRP_SNMP_SYNCGROUP_NAME,
	VRRP_SNMP_SYNCGROUP_STATE,
	VRRP_SNMP_SYNCGROUP_TRACKINGWEIGHT,
	VRRP_SNMP_SYNCGROUP_SMTPALERT,
	VRRP_SNMP_SYNCGROUP_NOTIFYEXEC,
	VRRP_SNMP_SYNCGROUP_SCRIPTMASTER,
	VRRP_SNMP_SYNCGROUP_SCRIPTBACKUP,
	VRRP_SNMP_SYNCGROUP_SCRIPTFAULT,
	VRRP_SNMP_SYNCGROUP_SCRIPTSTOP,
	VRRP_SNMP_SYNCGROUP_SCRIPT,
	VRRP_SNMP_SYNCGROUPMEMBER_INSTANCE,
	VRRP_SNMP_SYNCGROUPMEMBER_NAME,
	VRRP_SNMP_INSTANCE_NAME,
	VRRP_SNMP_INSTANCE_VIRTUALROUTERID,
	VRRP_SNMP_INSTANCE_STATE,
	VRRP_SNMP_INSTANCE_INITIALSTATE,
	VRRP_SNMP_INSTANCE_WANTEDSTATE,
	VRRP_SNMP_INSTANCE_BASEPRIORITY,
	VRRP_SNMP_INSTANCE_EFFECTIVEPRIORITY,
	VRRP_SNMP_INSTANCE_VIPSENABLED,
	VRRP_SNMP_INSTANCE_PRIMARYINTERFACE,
	VRRP_SNMP_INSTANCE_TRACKPRIMARYIF,
	VRRP_SNMP_INSTANCE_ADVERTISEMENTSINT,
	VRRP_SNMP_INSTANCE_PREEMPT,
	VRRP_SNMP_INSTANCE_PREEMPTDELAY,
	VRRP_SNMP_INSTANCE_AUTHTYPE,
	VRRP_SNMP_INSTANCE_USELVSSYNCDAEMON,
	VRRP_SNMP_INSTANCE_LVSSYNCINTERFACE,
	VRRP_SNMP_INSTANCE_SYNCGROUP,
	VRRP_SNMP_INSTANCE_GARPDELAY,
	VRRP_SNMP_INSTANCE_SMTPALERT,
	VRRP_SNMP_INSTANCE_NOTIFYEXEC,
	VRRP_SNMP_INSTANCE_SCRIPTMASTER,
	VRRP_SNMP_INSTANCE_SCRIPTBACKUP,
	VRRP_SNMP_INSTANCE_SCRIPTFAULT,
	VRRP_SNMP_INSTANCE_SCRIPTSTOP,
	VRRP_SNMP_INSTANCE_SCRIPT,
	VRRP_SNMP_INSTANCE_ACCEPT,
	VRRP_SNMP_INSTANCE_PROMOTE_SECONDARIES,
	VRRP_SNMP_INSTANCE_USE_LINKBEAT,
	VRRP_SNMP_INSTANCE_VRRP_VERSION,
	VRRP_SNMP_INSTANCE_SCRIPTMASTER_RX_LOWER_PRI,
	VRRP_SNMP_INSTANCE_SCRIPTDELETED,
	VRRP_SNMP_INSTANCE_NOTIFY_DELETED,
	VRRP_SNMP_INSTANCE_MULTICAST_ADDRESSTYPE,
	VRRP_SNMP_INSTANCE_MULTICAST_ADDRESS,
	VRRP_SNMP_INSTANCE_V3_CHECKSUM_AS_V2,
	VRRP_SNMP_TRACKEDINTERFACE_NAME,
	VRRP_SNMP_TRACKEDINTERFACE_WEIGHT,
	VRRP_SNMP_TRACKEDINTERFACE_WEIGHT_REVERSE,
	VRRP_SNMP_TRACKEDSCRIPT_NAME,
	VRRP_SNMP_TRACKEDSCRIPT_WEIGHT,
	VRRP_SNMP_TRACKEDSCRIPT_WEIGHT_REVERSE,
	VRRP_SNMP_TRACKEDFILE_NAME,
	VRRP_SNMP_TRACKEDFILE_WEIGHT,
	VRRP_SNMP_TRACKEDFILE_WEIGHT_REVERSE,
	VRRP_SNMP_TRACKEDBFD_NAME,
	VRRP_SNMP_TRACKEDBFD_WEIGHT,
	VRRP_SNMP_TRACKEDBFD_WEIGHT_REVERSE,
	VRRP_SNMP_TRACKEDPROCESS_NAME,
	VRRP_SNMP_TRACKEDPROCESS_WEIGHT,
	VRRP_SNMP_TRACKEDPROCESS_WEIGHT_REVERSE,
	VRRP_SNMP_SGROUPTRACKEDINTERFACE_NAME,
	VRRP_SNMP_SGROUPTRACKEDINTERFACE_WEIGHT,
	VRRP_SNMP_SGROUPTRACKEDINTERFACE_WEIGHT_REVERSE,
	VRRP_SNMP_SGROUPTRACKEDSCRIPT_NAME,
	VRRP_SNMP_SGROUPTRACKEDSCRIPT_WEIGHT,
	VRRP_SNMP_SGROUPTRACKEDSCRIPT_WEIGHT_REVERSE,
	VRRP_SNMP_SGROUPTRACKEDFILE_NAME,
	VRRP_SNMP_SGROUPTRACKEDFILE_WEIGHT,
	VRRP_SNMP_SGROUPTRACKEDFILE_WEIGHT_REVERSE,
	VRRP_SNMP_SGROUPTRACKEDBFD_NAME,
	VRRP_SNMP_SGROUPTRACKEDBFD_WEIGHT,
	VRRP_SNMP_SGROUPTRACKEDBFD_WEIGHT_REVERSE,
	VRRP_SNMP_SGROUPTRACKEDPROCESS_NAME,
	VRRP_SNMP_SGROUPTRACKEDPROCESS_WEIGHT,
	VRRP_SNMP_SGROUPTRACKEDPROCESS_WEIGHT_REVERSE,
};

enum snmp_rule_magic {
	VRRP_SNMP_RULE_DIRECTION = 2,
	VRRP_SNMP_RULE_ADDRESSTYPE,
	VRRP_SNMP_RULE_ADDRESS,
	VRRP_SNMP_RULE_ADDRESSMASK,
	VRRP_SNMP_RULE_ROUTINGTABLE,
	VRRP_SNMP_RULE_ISSET,
	VRRP_SNMP_RULE_INVERT,
	VRRP_SNMP_RULE_DESTINATIONADDRESSTYPE,
	VRRP_SNMP_RULE_DESTINATIONADDRESS,
	VRRP_SNMP_RULE_DESTINATIONADDRESSMASK,
	VRRP_SNMP_RULE_SOURCEADDRESSTYPE,
	VRRP_SNMP_RULE_SOURCEADDRESS,
	VRRP_SNMP_RULE_SOURCEADDRESSMASK,
	VRRP_SNMP_RULE_TOS,
	VRRP_SNMP_RULE_FWMARK,
	VRRP_SNMP_RULE_FWMASK,
	VRRP_SNMP_RULE_REALM_DST,
	VRRP_SNMP_RULE_REALM_SRC,
	VRRP_SNMP_RULE_ININTERFACE,
	VRRP_SNMP_RULE_OUTINTERFACE,
	VRRP_SNMP_RULE_TARGET,
	VRRP_SNMP_RULE_ACTION,
	VRRP_SNMP_RULE_TABLE_NO,
	VRRP_SNMP_RULE_PREFERENCE,
	VRRP_SNMP_RULE_SUPPRESSPREFIXLEN,
	VRRP_SNMP_RULE_SUPPRESSGROUP,
	VRRP_SNMP_RULE_TUNNELID_HIGH,
	VRRP_SNMP_RULE_TUNNELID_LOW,
	VRRP_SNMP_RULE_UID_RANGE_START,
	VRRP_SNMP_RULE_UID_RANGE_END,
	VRRP_SNMP_RULE_L3MDEV,
	VRRP_SNMP_RULE_PROTOCOL,
	VRRP_SNMP_RULE_IP_PROTO,
	VRRP_SNMP_RULE_SRC_PORT_START,
	VRRP_SNMP_RULE_SRC_PORT_END,
	VRRP_SNMP_RULE_DST_PORT_START,
	VRRP_SNMP_RULE_DST_PORT_END,
};

enum snmp_route_magic {
	VRRP_SNMP_ROUTE_ADDRESSTYPE = 2,
	VRRP_SNMP_ROUTE_DESTINATION,
	VRRP_SNMP_ROUTE_DESTINATIONMASK,
	VRRP_SNMP_ROUTE_GATEWAY,
	VRRP_SNMP_ROUTE_SECONDARYGATEWAY,
	VRRP_SNMP_ROUTE_SOURCE,
	VRRP_SNMP_ROUTE_METRIC,
	VRRP_SNMP_ROUTE_SCOPE,
	VRRP_SNMP_ROUTE_TYPE,
	VRRP_SNMP_ROUTE_IFINDEX,
	VRRP_SNMP_ROUTE_IFNAME,
	VRRP_SNMP_ROUTE_ROUTINGTABLE,
	VRRP_SNMP_ROUTE_ISSET,
	VRRP_SNMP_ROUTE_FROM_ADDRESS,
	VRRP_SNMP_ROUTE_FROM_ADDRESS_MASK,
	VRRP_SNMP_ROUTE_TOS,
	VRRP_SNMP_ROUTE_PROTOCOL,
	VRRP_SNMP_ROUTE_ECN,
	VRRP_SNMP_ROUTE_QUICK_ACK,
	VRRP_SNMP_ROUTE_EXPIRES,
	VRRP_SNMP_ROUTE_MTU,
	VRRP_SNMP_ROUTE_MTU_LOCK,
	VRRP_SNMP_ROUTE_HOP_LIMIT,
	VRRP_SNMP_ROUTE_ADVMSS,
	VRRP_SNMP_ROUTE_ADVMSS_LOCK,
	VRRP_SNMP_ROUTE_RTT,
	VRRP_SNMP_ROUTE_RTT_LOCK,
	VRRP_SNMP_ROUTE_RTTVAR,
	VRRP_SNMP_ROUTE_RTTVAR_LOCK,
	VRRP_SNMP_ROUTE_REORDERING,
	VRRP_SNMP_ROUTE_REORDERING_LOCK,
	VRRP_SNMP_ROUTE_WINDOW,
	VRRP_SNMP_ROUTE_CWND,
	VRRP_SNMP_ROUTE_CWND_LOCK,
	VRRP_SNMP_ROUTE_SSTHRESH,
	VRRP_SNMP_ROUTE_SSTHRESH_LOCK,
	VRRP_SNMP_ROUTE_RTOMIN,
	VRRP_SNMP_ROUTE_RTOMIN_LOCK,
	VRRP_SNMP_ROUTE_INIT_CWND,
	VRRP_SNMP_ROUTE_INIT_RWND,
	VRRP_SNMP_ROUTE_CONG_CTL,
	VRRP_SNMP_ROUTE_PREF,
	VRRP_SNMP_ROUTE_REALM_DST,
	VRRP_SNMP_ROUTE_REALM_SRC,
	VRRP_SNMP_ROUTE_ENCAP_TYPE,
	VRRP_SNMP_ROUTE_ENCAP_MPLS_LABELS,
	VRRP_SNMP_ROUTE_ENCAP_ID,
	VRRP_SNMP_ROUTE_ENCAP_DST_ADDRESS,
	VRRP_SNMP_ROUTE_ENCAP_SRC_ADDRESS,
	VRRP_SNMP_ROUTE_ENCAP_TOS,
	VRRP_SNMP_ROUTE_ENCAP_TTL,
	VRRP_SNMP_ROUTE_ENCAP_FLAGS,
	VRRP_SNMP_ROUTE_ENCAP_ILA_LOCATOR,
	VRRP_SNMP_ROUTE_FASTOPEN_NO_COOKIE,
};

enum snmp_next_hop_magic {
	VRRP_SNMP_ROUTE_NEXT_HOP_ADDRESS_TYPE = 2,
	VRRP_SNMP_ROUTE_NEXT_HOP_ADDRESS,
	VRRP_SNMP_ROUTE_NEXT_HOP_IF_INDEX,
	VRRP_SNMP_ROUTE_NEXT_HOP_IF_NAME,
	VRRP_SNMP_ROUTE_NEXT_HOP_WEIGHT,
	VRRP_SNMP_ROUTE_NEXT_HOP_ONLINK,
	VRRP_SNMP_ROUTE_NEXT_HOP_REALM_DST,
	VRRP_SNMP_ROUTE_NEXT_HOP_REALM_SRC,
	VRRP_SNMP_ROUTE_NEXT_HOP_ENCAP_TYPE,
	VRRP_SNMP_ROUTE_NEXT_HOP_ENCAP_MPLS_LABELS,
	VRRP_SNMP_ROUTE_NEXT_HOP_ENCAP_ID,
	VRRP_SNMP_ROUTE_NEXT_HOP_ENCAP_DST_ADDRESS,
	VRRP_SNMP_ROUTE_NEXT_HOP_ENCAP_SRC_ADDRESS,
	VRRP_SNMP_ROUTE_NEXT_HOP_ENCAP_TOS,
	VRRP_SNMP_ROUTE_NEXT_HOP_ENCAP_TTL,
	VRRP_SNMP_ROUTE_NEXT_HOP_ENCAP_FLAGS,
	VRRP_SNMP_ROUTE_NEXT_HOP_ENCAP_ILA_LOCATOR,
};

enum iter_type {
        ITER_ADDRESSES,
        ITER_ROUTES,
        ITER_RULES
};

#endif

#ifdef _WITH_SNMP_RFCV2_
/* RFC SNMP defines */
#define VRRP_RFC_OID SNMP_OID_MIB2, 68
#define VRRP_RFC_TRAP_OID VRRP_RFC_OID, 0

/* Magic for RFC MIB functions */
enum rfcv2_snmp_node_magic {
	VRRP_RFC_SNMP_NODE_VER = 2,
	VRRP_RFC_SNMP_NOTIF_CNTL
};

enum rfcv2_snmp_oper_magic {
	VRRP_RFC_SNMP_OPER_AUTH_KEY = 3,
	VRRP_RFC_SNMP_OPER_ADVERT_INT,
	VRRP_RFC_SNMP_OPER_PREEMPT,
	VRRP_RFC_SNMP_OPER_VR_UPTIME,
	VRRP_RFC_SNMP_OPER_PROTO,
	VRRP_RFC_SNMP_OPER_ROW_STAT,
	VRRP_RFC_SNMP_OPER_VMAC,
	VRRP_RFC_SNMP_OPER_STATE,
	VRRP_RFC_SNMP_OPER_ADM_STATE,
	VRRP_RFC_SNMP_OPER_PRI,
	VRRP_RFC_SNMP_OPER_ADDR_CNT,
	VRRP_RFC_SNMP_OPER_MIP,
	VRRP_RFC_SNMP_OPER_PIP,
	VRRP_RFC_SNMP_OPER_AUTH_TYPE
};

enum rfcv2_snmp_assoc_ip_magic {
	VRRP_RFC_SNMP_ASSOC_IP_ADDR_ROW = 3
};

enum rfcv2_snmp_stats_err_magic {
	VRRP_RFC_SNMP_STATS_CHK_ERR = 2,
	VRRP_RFC_SNMP_STATS_VER_ERR,
	VRRP_RFC_SNMP_STATS_VRID_ERR
};

enum rfcv2_snmp_stats_magic {
	VRRP_RFC_SNMP_STATS_MASTER = 2,
	VRRP_RFC_SNMP_STATS_AUTH_INV,
	VRRP_RFC_SNMP_STATS_AUTH_MIS,
	VRRP_RFC_SNMP_STATS_PL_ERR,
	VRRP_RFC_SNMP_STATS_ADV_RCVD,
	VRRP_RFC_SNMP_STATS_ADV_INT_ERR,
	VRRP_RFC_SNMP_STATS_AUTH_FAIL,
	VRRP_RFC_SNMP_STATS_TTL_ERR,
	VRRP_RFC_SNMP_STATS_PRI_0_RCVD,
	VRRP_RFC_SNMP_STATS_PRI_0_SENT,
	VRRP_RFC_SNMP_STATS_INV_TYPE_RCVD,
	VRRP_RFC_SNMP_STATS_ADDR_LIST_ERR
};

/*
	VRRP_RFC_SNMP_CNFRM_MIB,
	VRRP_RFC_SNMP_GRP_OPER,
	VRRP_RFC_SNMP_GRP_STATS,
	VRRP_RFC_SNMP_GRP_TRAP,
	VRRP_RFC_SNMP_GRP_NOTIF
*/
#endif

#ifdef _WITH_SNMP_RFCV3_
/* RFC SNMP defines */
#define VRRP_RFCv3_OID SNMP_OID_MIB2, 207
#define VRRP_RFCv3_NOTIFY_OID VRRP_RFCv3_OID, 0

/* Magic for RFC MIB functions */
enum rfcv3_snmp_oper_magic {
	VRRP_RFCv3_SNMP_OPER_MIP,
	VRRP_RFCv3_SNMP_OPER_PIP,
	VRRP_RFCv3_SNMP_OPER_VMAC,
	VRRP_RFCv3_SNMP_OPER_STATE,
	VRRP_RFCv3_SNMP_OPER_PRI,
	VRRP_RFCv3_SNMP_OPER_ADDR_CNT,
	VRRP_RFCv3_SNMP_OPER_ADVERT_INT,
	VRRP_RFCv3_SNMP_OPER_PREEMPT,
	VRRP_RFCv3_SNMP_OPER_ACCEPT,
	VRRP_RFCv3_SNMP_OPER_VR_UPTIME,
	VRRP_RFCv3_SNMP_OPER_ROW_STATUS
};

enum rfcv3_snmp_assoc_ip_magic {
	VRRP_RFCv3_SNMP_ASSOC_IP_ADDR_ROW_STATUS = 3
};

enum rfcv3_snmp_stats_err_magic {
	VRRP_RFCv3_SNMP_STATS_CHK_ERR = 2,
	VRRP_RFCv3_SNMP_STATS_VER_ERR,
	VRRP_RFCv3_SNMP_STATS_VRID_ERR,
	VRRP_RFCv3_SNMP_STATS_DISC_TIME
};

enum rfcv3_snmp_stats_magic {
	VRRP_RFCv3_SNMP_STATS_MASTER = 2,
	VRRP_RFCv3_SNMP_STATS_MASTER_REASON,
	VRRP_RFCv3_SNMP_STATS_ADV_RCVD,
	VRRP_RFCv3_SNMP_STATS_ADV_INT_ERR,
	VRRP_RFCv3_SNMP_STATS_TTL_ERR,
	VRRP_RFCv3_SNMP_STATS_PROTO_ERR_REASON,
	VRRP_RFCv3_SNMP_STATS_PRI_0_RCVD,
	VRRP_RFCv3_SNMP_STATS_PRI_0_SENT,
	VRRP_RFCv3_SNMP_STATS_INV_TYPE_RCVD,
	VRRP_RFCv3_SNMP_STATS_ADDR_LIST_ERR,
	VRRP_RFCv3_SNMP_STATS_PL_ERR,
	VRRP_RFCv3_SNMP_STATS_ROW_DISC_TIME,
	VRRP_RFCv3_SNMP_STATS_REFRESH_RATE
};

/*
	VRRP_RFCv3_SNMP_CNFRM_MIB,
	VRRP_RFCv3_SNMP_GRP_OPER,
	VRRP_RFCv3_SNMP_GRP_STATS,
	VRRP_RFCv3_SNMP_GRP_TRAP,
	VRRP_RFCv3_SNMP_GRP_NOTIF
*/
#endif

/* Static return value */
static longret_t long_ret;
#ifdef _WITH_SNMP_VRRP_
static char buf[MAXBUF];
#endif

/* global variable */
#ifdef _WITH_SNMP_RFC_
timeval_t snmp_vrrp_start_time;
#endif


/* For some reason net-snmp doesn't use a uint64_t for 64 bit counters, but rather uses
 * a struct, with the high word at the lower address, so we need to assign values according. */
inline static void
set_counter64 (struct counter64 *c64, uint64_t val)
{
	c64->high = val >> 32;
	c64->low = val & 0xffffffff;
}

#ifdef _FOR_DEBUGGING_
static void
sprint_oid(char *str, const oid* oid, int len)
{
	int offs = 0;
	int i;

	if (!len) {
		str[0] = '.';
		str[1] = 0;
		return;
	}

	for (i = 0; i < len; i++)
		offs += sprintf(str + offs, ".%lu", oid[i]);
}
#endif

#ifdef _WITH_SNMP_VRRP_
/* Convert VRRP state to SNMP state */
static int
vrrp_snmp_state(int state)
{
	return state <= VRRP_STATE_FAULT ? state : 4;
}

static u_char*
vrrp_snmp_script(struct variable *vp, oid *name, size_t *length,
		 int exact, size_t *var_len, WriteMethod **write_method)
{
	vrrp_script_t *scr;
	list_head_t *e;
	snmp_ret_t ret;

	if ((e = snmp_header_list_head_table(vp, name, length, exact,
					     var_len, write_method,
					     &vrrp_data->vrrp_script)) == NULL)
		return NULL;
	scr = list_entry(e, vrrp_script_t, e_list);

	switch (vp->magic) {
	case VRRP_SNMP_SCRIPT_NAME:
		*var_len = strlen(scr->sname);
		ret.cp = scr->sname;
		return ret.p;
	case VRRP_SNMP_SCRIPT_COMMAND:
		cmd_str_r(&scr->script, buf, sizeof(buf));
		*var_len = strlen(buf);
		return PTR_CAST(u_char, buf);
	case VRRP_SNMP_SCRIPT_INTERVAL:
		long_ret.u = scr->interval / TIMER_HZ;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_SCRIPT_WEIGHT:
		long_ret.s = scr->weight;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_SCRIPT_WEIGHT_REVERSE:
		long_ret.u = SNMP_TruthValue(scr->weight_reverse);
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_SCRIPT_RESULT:
		switch (scr->init_state) {
		case SCRIPT_INIT_STATE_INIT:
		case SCRIPT_INIT_STATE_INIT_RELOAD:
			long_ret.u = 1; break;
		case SCRIPT_INIT_STATE_FAILED:
			long_ret.u = 5; break;
		default:
			long_ret.u = (scr->result >= scr->rise) ? 3 : 2;
		}
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_SCRIPT_RISE:
		long_ret.s = scr->rise;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_SCRIPT_FALL:
		long_ret.s = scr->fall;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_SCRIPT_PATH:
		ret.cp = scr->script.path ? scr->script.path : scr->script.args[0];
		*var_len = strlen(ret.cp);
		return ret.p;
	case VRRP_SNMP_SCRIPT_INTERVAL_USEC:
		long_ret.u = scr->interval;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_SCRIPT_TIMEOUT_USEC:
		long_ret.u = scr->timeout;
		return PTR_CAST(u_char, &long_ret);
	default:
		break;
	}
	return NULL;
}

static u_char*
vrrp_snmp_file(struct variable *vp, oid *name, size_t *length,
		 int exact, size_t *var_len, WriteMethod **write_method)
{
	tracked_file_t *file;
	list_head_t *e;
	snmp_ret_t ret;

	if ((e = snmp_header_list_head_table(vp, name, length, exact,
					     var_len, write_method,
					     &vrrp_data->vrrp_track_files)) == NULL)
		return NULL;
	file = list_entry(e, tracked_file_t, e_list);

	switch (vp->magic) {
	case VRRP_SNMP_FILE_NAME:
		*var_len = strlen(file->fname);
		ret.cp = file->fname;
		return ret.p;
	case VRRP_SNMP_FILE_PATH:
		*var_len = strlen(file->file_path);
		ret.cp = file->file_path;
		return ret.p;
	case VRRP_SNMP_FILE_RESULT:
		long_ret.s = file->last_status;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_FILE_WEIGHT:
		long_ret.s = file->weight;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_FILE_WEIGHT_REVERSE:
		long_ret.u = SNMP_TruthValue(file->weight_reverse);
		return PTR_CAST(u_char, &long_ret);
	default:
		break;
	}
	return NULL;
}

#ifdef _WITH_BFD_
static u_char*
vrrp_snmp_bfd(struct variable *vp, oid *name, size_t *length,
		 int exact, size_t *var_len, WriteMethod **write_method)
{
	vrrp_tracked_bfd_t *bfd;
	list_head_t *e;
	snmp_ret_t ret;

	if ((e = snmp_header_list_head_table(vp, name, length, exact,
					     var_len, write_method,
					     &vrrp_data->vrrp_track_bfds)) == NULL)
		return NULL;
	bfd = list_entry(e, vrrp_tracked_bfd_t, e_list);

	switch (vp->magic) {
	case VRRP_SNMP_BFD_NAME:
		*var_len = strlen(bfd->bname);
		ret.cp = bfd->bname;
		return ret.p;
	case VRRP_SNMP_BFD_RESULT:
		long_ret.s = bfd->bfd_up;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_BFD_WEIGHT:
		long_ret.s = bfd->weight;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_BFD_WEIGHT_REVERSE:
		long_ret.u = SNMP_TruthValue(bfd->weight_reverse);
		return PTR_CAST(u_char, &long_ret);
	default:
		break;
	}
	return NULL;
}
#endif

#ifdef _WITH_TRACK_PROCESS_
static u_char*
vrrp_snmp_process(struct variable *vp, oid *name, size_t *length,
		 int exact, size_t *var_len, WriteMethod **write_method)
{
	vrrp_tracked_process_t *proc;
	list_head_t *e;
	snmp_ret_t ret;

	if ((e = snmp_header_list_head_table(vp, name, length, exact,
					     var_len, write_method,
					     &vrrp_data->vrrp_track_processes)) == NULL)
		return NULL;
	proc = list_entry(e, vrrp_tracked_process_t, e_list);

	switch (vp->magic) {
	case VRRP_SNMP_PROCESS_NAME:
		*var_len = strlen(proc->pname);
		ret.cp = proc->pname;
		return ret.p;
	case VRRP_SNMP_PROCESS_PATH:
		*var_len = strlen(proc->process_path);
		ret.cp = proc->process_path;
		return ret.p;
	case VRRP_SNMP_PROCESS_PARAMS:
		if (!proc->process_params_len) {
			*var_len = 0;
			ret.cp = "";
		} else {
			/* We need to replace the nul terminators with spaces */
			size_t len;
			unsigned i;

			len = proc->process_params_len - 1 < sizeof(buf) ? proc->process_params_len - 1 : sizeof(buf);
			memcpy(buf, proc->process_params, len);
			buf[sizeof(buf) - 1] = '\0';

			for (i = strlen(buf); i < len; i += strlen(buf + i)) {
				buf[i++] = ' ';
				if (i >= len)
					break;
			}
			*var_len = len;
			ret.cp = buf;
		}
		return ret.p;
	case VRRP_SNMP_PROCESS_PARAM_MATCH:
		long_ret.u = proc->param_match;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_PROCESS_WEIGHT:
		long_ret.u = proc->weight;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_PROCESS_WEIGHT_REVERSE:
		long_ret.u = SNMP_TruthValue(proc->weight_reverse);
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_PROCESS_QUORUM:
		long_ret.u = proc->quorum;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_PROCESS_QUORUM_MAX:
		long_ret.u = proc->quorum_max;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_PROCESS_FORKDELAY:
		long_ret.u = proc->fork_delay;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_PROCESS_TERMINATEDELAY:
		long_ret.u = proc->terminate_delay;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_PROCESS_FULLCOMMAND:
		long_ret.u = SNMP_TruthValue(proc->full_command);
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_PROCESS_CURPROC:
		long_ret.u = proc->num_cur_proc;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_PROCESS_RESULT:
		long_ret.u = SNMP_TruthValue(proc->have_quorum);
		return PTR_CAST(u_char, &long_ret);
	default:
		break;
	}
	return NULL;
}
#endif

static list_head_t *
vrrp_header_ar_table(struct variable *vp, oid *name, size_t *length,
		     int exact, size_t *var_len, WriteMethod **write_method,
		     enum iter_type type, int *adv)
{
	oid *target, current[2];
	int result;
	size_t target_len;
	list_head_t *l2;
	list_head_t *e;
	vrrp_t *vrrp;

	if ((result = snmp_oid_compare(name, *length, vp->name, vp->namelen)) < 0) {
		memcpy(name, vp->name, sizeof(oid) * vp->namelen);
		*length = vp->namelen;
	}

	*write_method = 0;
	*var_len = sizeof(unsigned long);

	/* We search the best match: equal if exact, the lowest OID in
	   the set of the OID strictly superior to the target
	   otherwise. */
	target = &name[vp->namelen];   /* Our target match */
	target_len = *length - vp->namelen;

	l2 = NULL;
	vrrp = NULL;
	current[0] = 0;
	current[1] = 0;
	while (true) {
		/* Start with static addresses, then for each vrrp instance the VIPs then the eVIPs */
		if (!l2) {
			if (type == ITER_ADDRESSES)
				l2 = &vrrp_data->static_addresses;
			else if (type == ITER_ROUTES)
				l2 = &vrrp_data->static_routes;
			else /* if (type == ITER_RULES) */
				l2 = &vrrp_data->static_rules;
		} else if (!vrrp) {
			if (list_empty(&vrrp_data->vrrp))
				break;
			vrrp = list_first_entry(&vrrp_data->vrrp, vrrp_t, e_list);
			if (type == ITER_ADDRESSES)
				l2 = &vrrp->vip;
			else if (type == ITER_ROUTES)
				l2 = &vrrp->vroutes;
			else /* if (type == ITER_RULES) */
				l2 = &vrrp->vrules;
			current[0]++;
			current[1] = 0;
			*adv = 1;
		} else if (type == ITER_ADDRESSES && l2 == &vrrp->vip) {
			l2 = &vrrp->evip;
			*adv = 0;
		} else {		/* if ITER_ADDRESSES, l2 == vrrp->evip */
			if (list_is_last(&vrrp->e_list, &vrrp_data->vrrp))
				break;
			vrrp = list_entry(vrrp->e_list.next, vrrp_t, e_list);
			if (type == ITER_ADDRESSES)
				l2 = &vrrp->vip;
			else if (type == ITER_ROUTES)
				l2 = &vrrp->vroutes;
			else /* if (type == ITER_RULES) */
				l2 = &vrrp->vrules;
			current[0]++;
			current[1] = 0;
		}

		if (target_len && current[0] < target[0])
			continue;

		/* Should not happen... */
		if (!l2)
			return NULL;

		list_for_each(e, l2) {
			current[1]++;
			result = snmp_oid_compare(current, 2, target, target_len);
			if (result < 0)
				continue;
			if (result == 0) {
				if (!exact)
					continue;
			} else if (result > 0 && exact)
				return NULL;

			/* This is our best match */
			if (!exact) {
				memcpy(target, current, sizeof(oid) * 2);
				*length = (unsigned)vp->namelen + 2;
			}

			return e;
		}
	}

	return NULL;
}


#define MAX_PTR ((void*)~0)

static nexthop_t *
vrrp_header_nh_table(struct variable *vp, oid *name, size_t *length,
		     int exact, size_t *var_len, WriteMethod **write_method)
{
	oid *target;
	int result;
	size_t target_len;
	list_head_t *l2;
	oid curinstance[3];
	vrrp_t *vrrp;
	ip_route_t *route;
	nexthop_t *nh;
	bool same;

	if ((result = snmp_oid_compare(name, *length, vp->name, vp->namelen)) < 0) {
		memcpy(name, vp->name, sizeof(oid) * vp->namelen);
		*length = vp->namelen;
	}

	*write_method = 0;
	*var_len = sizeof(unsigned long);

	/* We search the best match: equal if exact, the lower OID in
	   the set of the OID strictly superior to the target
	   otherwise. */
	target = &name[vp->namelen];   /* Our target match */
	target_len = *length - vp->namelen;
	if (exact && !target_len)
		return NULL;

	/* Perform static routes */

	for (vrrp = MAX_PTR, curinstance[0] = 0; vrrp;
	     vrrp = ((vrrp == MAX_PTR) ? (list_empty(&vrrp_data->vrrp) ? NULL :
	       list_first_entry(&vrrp_data->vrrp, vrrp_t, e_list)) :
		 list_is_last(&vrrp->e_list, &vrrp_data->vrrp) ? NULL :
		   list_entry(vrrp->e_list.next, vrrp_t, e_list)), curinstance[0]++) {
		if (exact && curinstance[0] > target[0])
			return NULL;
		if (target_len && curinstance[0] < target[0])
			continue;
		same = (target_len && curinstance[0] == target[0]);
		l2 = (vrrp == MAX_PTR) ? &vrrp_data->static_routes : &vrrp->vroutes;
		if (list_empty(l2))
			continue;
		curinstance[1] = 0;
		list_for_each_entry(route, l2, e_list) {
			curinstance[1]++;
			if (exact && curinstance[1] > target[1])
				return NULL;
			if (same && curinstance[1] < target[1])
				continue;
			same = (same && curinstance[1] == target[1]);
			if (list_empty(&route->nhs))
				continue;
			curinstance[2] = 0;
			list_for_each_entry(nh, &route->nhs, e_list) {
				curinstance[2]++;
				if (exact && target_len && curinstance[2] > target[2])
					return NULL;
				if (same && curinstance[2] < target[2])
					continue;

				if (target_len && !exact && curinstance[0] == target[0] &&
				    curinstance[1] == target[1] && curinstance[2] == target[2])
					continue;

				memcpy(target, curinstance, sizeof(oid) * 3);
				*length = (unsigned)vp->namelen + 3;

				return nh;
			}
		}
	}
	return NULL;
}

static u_char *
vrrp_snmp_address(struct variable *vp, oid *name, size_t *length,
		 int exact, size_t *var_len, WriteMethod **write_method)
{
	ip_address_t *addr;
	list_head_t *e;
	int adv = 0;

	if ((e = vrrp_header_ar_table(vp, name, length, exact,
				      var_len, write_method,
				      ITER_ADDRESSES, &adv)) == NULL)
		return NULL;
	addr = list_entry(e, ip_address_t, e_list);

	switch (vp->magic) {
	case VRRP_SNMP_ADDRESS_ADDRESSTYPE:
		long_ret.u = SNMP_InetAddressType(addr->ifa.ifa_family);
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_ADDRESS_VALUE:
		if (addr->ifa.ifa_family == AF_INET6) {
			*var_len = sizeof addr->u.sin6_addr;
			return PTR_CAST(u_char, &addr->u.sin6_addr);
		} else {
			*var_len = sizeof addr->u.sin.sin_addr;
			return PTR_CAST(u_char, &addr->u.sin.sin_addr);
		}
		break;
	case VRRP_SNMP_ADDRESS_BROADCAST:
		if (addr->ifa.ifa_family == AF_INET6) break;
		*var_len = sizeof addr->u.sin.sin_brd;
		return PTR_CAST(u_char, &addr->u.sin.sin_brd);
	case VRRP_SNMP_ADDRESS_MASK:
		long_ret.u = addr->ifa.ifa_prefixlen;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_ADDRESS_SCOPE:
		long_ret.u = snmp_scope(addr->ifa.ifa_scope);
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_ADDRESS_IFINDEX:
		long_ret.u = addr->ifp->ifindex;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_ADDRESS_IFNAME:
		*var_len = strlen(addr->ifp->ifname);
		return PTR_CAST(u_char, addr->ifp->ifname);
	case VRRP_SNMP_ADDRESS_IFALIAS:
		if (addr->label) {
			*var_len = strlen(addr->label);
			return PTR_CAST(u_char, addr->label);
		}
		break;
	case VRRP_SNMP_ADDRESS_ISSET:
		long_ret.u = SNMP_TruthValue(addr->set);
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_ADDRESS_ISADVERTISED:
		long_ret.u = SNMP_TruthValue(adv);
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_ADDRESS_PEER:
		if (!addr->have_peer)
			break;
		if (addr->ifa.ifa_family == AF_INET6) {
			*var_len = sizeof addr->peer.sin6_addr;
			return PTR_CAST(u_char, &addr->peer.sin6_addr);
		} else {
			*var_len = sizeof addr->peer.sin_addr;
			return PTR_CAST(u_char, &addr->peer.sin_addr);
		}
		break;
	default:
		return NULL;
	}
	/* If we are here, we asked for a non existent data. Try the
	   next one. */
	if (!exact && (name[*length-1] < MAX_SUBID))
		return vrrp_snmp_address(vp, name, length,
					 exact, var_len, write_method);
	return NULL;
}

static u_char*
vrrp_snmp_route(struct variable *vp, oid *name, size_t *length,
		 int exact, size_t *var_len, WriteMethod **write_method)
{
	ip_route_t *route;
	list_head_t *e;
	nexthop_t *gw2;
	int gw2_cnt = 0, adv;

	if ((e = vrrp_header_ar_table(vp, name, length, exact,
				      var_len, write_method,
				      ITER_ROUTES, &adv)) == NULL)
		return NULL;
	route = list_entry(e, ip_route_t, e_list);

	switch (vp->magic) {
	case VRRP_SNMP_ROUTE_ADDRESSTYPE:
		long_ret.u = AF_INET;	/* IPv4 only */
		if (route->dst)
			long_ret.u = route->dst->ifa.ifa_family;
		else if (route->src)
			long_ret.u = route->src->ifa.ifa_family;
		else if (route->pref_src)
			long_ret.u = route->pref_src->ifa.ifa_family;
		if (long_ret.u == AF_INET6)
			long_ret.u = 2;
		else
			long_ret.u = 1;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_ROUTE_DESTINATION:
		if (!route->dst)
			break;
		if (route->dst->ifa.ifa_family == AF_INET6) {
			*var_len = sizeof route->dst->u.sin6_addr;
			return PTR_CAST(u_char, &route->dst->u.sin6_addr);
		}
		*var_len = sizeof route->dst->u.sin.sin_addr;
		return PTR_CAST(u_char, &route->dst->u.sin.sin_addr);
	case VRRP_SNMP_ROUTE_DESTINATIONMASK:
		if (!route->dst)
			break;
		long_ret.u = route->dst->ifa.ifa_prefixlen;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_ROUTE_GATEWAY:
		if (!route->via)
			break;
		if (route->via->ifa.ifa_family == AF_INET6) {
			*var_len = sizeof route->via->u.sin6_addr;
			return PTR_CAST(u_char, &route->via->u.sin6_addr);
		}
		*var_len = sizeof route->via->u.sin.sin_addr;
		return PTR_CAST(u_char, &route->via->u.sin.sin_addr);
	case VRRP_SNMP_ROUTE_SECONDARYGATEWAY:
		if (list_empty(&route->nhs))
			break;
		list_for_each_entry(gw2, &route->nhs, e_list)
			gw2_cnt++;
		if (gw2_cnt != 1)
			break;
		gw2 = list_first_entry(&route->nhs, nexthop_t, e_list);
#if HAVE_DECL_RTA_ENCAP
		if (gw2->encap.type != LWTUNNEL_ENCAP_NONE)
			break;
#endif
		if (gw2->addr->ifa.ifa_family == AF_INET6) {
			*var_len = sizeof gw2->addr->u.sin6_addr;
			return PTR_CAST(u_char, &gw2->addr->u.sin6_addr);
		}
		*var_len = sizeof gw2->addr->u.sin.sin_addr;
		return PTR_CAST(u_char, &gw2->addr->u.sin.sin_addr);
	case VRRP_SNMP_ROUTE_SOURCE:
		if (!route->pref_src)
			break;
		if (route->pref_src->ifa.ifa_family == AF_INET6) {
			*var_len = sizeof route->pref_src->u.sin6_addr;
			return PTR_CAST(u_char, &route->pref_src->u.sin6_addr);
		}
		*var_len = sizeof route->pref_src->u.sin.sin_addr;
		return PTR_CAST(u_char, &route->pref_src->u.sin.sin_addr);
	case VRRP_SNMP_ROUTE_METRIC:
		long_ret.u = route->metric;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_ROUTE_SCOPE:
		long_ret.u = snmp_scope(route->scope);
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_ROUTE_TYPE:
		if (!list_empty(&route->nhs))
			long_ret.u = 2;
		else if (route->type == RTN_BLACKHOLE)
			long_ret.u = 3;
		else if (route->type == RTN_ANYCAST)
			long_ret.u = 4;
		else if (route->type == RTN_MULTICAST)
			long_ret.u = 5;
		else if (route->type == RTN_BROADCAST)
			long_ret.u = 6;
		else if (route->type == RTN_UNREACHABLE)
			long_ret.u = 7;
		else if (route->type == RTN_PROHIBIT)
			long_ret.u = 8;
		else if (route->type == RTN_THROW)
			long_ret.u = 9;
		else if (route->type == RTN_NAT)
			long_ret.u = 10;
		else if (route->type == RTN_XRESOLVE)
			long_ret.u = 11;
		else
			long_ret.u = 1;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_ROUTE_IFINDEX:
		if (!route->oif)
			break;
		long_ret.u = route->oif->ifindex;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_ROUTE_IFNAME:
		if (!route->oif)
			break;
		*var_len = strlen(IF_NAME(route->oif));
		return PTR_CAST(u_char, &IF_NAME(route->oif));
	case VRRP_SNMP_ROUTE_ROUTINGTABLE:
		long_ret.u = route->table;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_ROUTE_ISSET:
		long_ret.u = SNMP_TruthValue(route->set);
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_ROUTE_FROM_ADDRESS:
		if (!route->src)
			break;
		if (route->src->ifa.ifa_family == AF_INET6) {
			*var_len = sizeof route->src->u.sin6_addr;
			return PTR_CAST(u_char, &route->src->u.sin6_addr);
		} else {
			*var_len = sizeof route->src->u.sin.sin_addr;
			return PTR_CAST(u_char, &route->src->u.sin.sin_addr);
		}
	case VRRP_SNMP_ROUTE_FROM_ADDRESS_MASK:
		if (!route->src)
			break;
		long_ret.u = route->src->ifa.ifa_prefixlen;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_ROUTE_TOS:
		if (!(route->mask & IPROUTE_BIT_DSFIELD))
			break;
		long_ret.u = route->tos;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_ROUTE_PROTOCOL:
		if (!(route->mask & IPROUTE_BIT_PROTOCOL))
			break;
		long_ret.s = route->protocol + 1;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_ROUTE_ECN:
		long_ret.u = SNMP_TruthValue(route->features & RTAX_FEATURE_ECN);
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_ROUTE_QUICK_ACK:
		long_ret.u = SNMP_TruthValue(route->mask & IPROUTE_BIT_QUICKACK);
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_ROUTE_EXPIRES:
#if !HAVE_DECL_RTA_EXPIRES
		break;
#else
		if (!(route->mask & IPROUTE_BIT_EXPIRES))
			break;
		long_ret.u = route->expires;
		return PTR_CAST(u_char, &long_ret);
#endif
	case VRRP_SNMP_ROUTE_MTU:
		if (!(route->mask & IPROUTE_BIT_MTU))
			break;
		long_ret.u = route->mtu;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_ROUTE_MTU_LOCK:
		if (!(route->mask & IPROUTE_BIT_MTU))
			break;
		long_ret.u = SNMP_TruthValue(route->lock & (1<<RTAX_MTU));
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_ROUTE_HOP_LIMIT:
		if (!(route->mask & IPROUTE_BIT_HOPLIMIT))
			break;
		long_ret.u = route->hoplimit;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_ROUTE_ADVMSS:
		if (!(route->mask & IPROUTE_BIT_HOPLIMIT))
			break;
		long_ret.u = route->advmss;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_ROUTE_ADVMSS_LOCK:
		if (!(route->mask & IPROUTE_BIT_ADVMSS))
			break;
		long_ret.u = SNMP_TruthValue(route->lock & (1<<RTAX_ADVMSS));
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_ROUTE_RTT:
		if (!(route->mask & IPROUTE_BIT_RTT))
			break;
		long_ret.u = route->rtt / 8;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_ROUTE_RTT_LOCK:
		if (!(route->mask & IPROUTE_BIT_RTT))
			break;
		long_ret.u = SNMP_TruthValue(route->lock & (1<<RTAX_RTT));
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_ROUTE_RTTVAR:
		if (!(route->mask & IPROUTE_BIT_RTTVAR))
			break;
		long_ret.u = route->rttvar / 4;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_ROUTE_RTTVAR_LOCK:
		if (!(route->mask & IPROUTE_BIT_RTTVAR))
			break;
		long_ret.u = SNMP_TruthValue(route->lock & (1<<RTAX_RTTVAR));
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_ROUTE_REORDERING:
		if (!(route->mask & IPROUTE_BIT_REORDERING))
			break;
		long_ret.u = route->reordering;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_ROUTE_REORDERING_LOCK:
		if (!(route->mask & IPROUTE_BIT_REORDERING))
			break;
		long_ret.u = SNMP_TruthValue(route->lock & (1<<RTAX_REORDERING));
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_ROUTE_WINDOW:
		if (!(route->mask & IPROUTE_BIT_WINDOW))
			break;
		long_ret.u = route->window;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_ROUTE_CWND:
		if (!(route->mask & IPROUTE_BIT_CWND))
			break;
		long_ret.u = route->cwnd;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_ROUTE_CWND_LOCK:
		if (!(route->mask & IPROUTE_BIT_CWND))
			break;
		long_ret.u = SNMP_TruthValue(route->lock & (1<<RTAX_CWND));
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_ROUTE_SSTHRESH:
		if (!(route->mask & IPROUTE_BIT_SSTHRESH))
			break;
		long_ret.u = route->ssthresh;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_ROUTE_SSTHRESH_LOCK:
		if (!(route->mask & IPROUTE_BIT_SSTHRESH))
			break;
		long_ret.u = SNMP_TruthValue(route->lock & (1<<RTAX_SSTHRESH));
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_ROUTE_RTOMIN:
		if (!(route->mask & IPROUTE_BIT_RTO_MIN))
			break;
		long_ret.u = route->rto_min;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_ROUTE_RTOMIN_LOCK:
		if (!(route->mask & IPROUTE_BIT_RTO_MIN))
			break;
		long_ret.u = SNMP_TruthValue(route->lock & (1<<RTAX_RTO_MIN));
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_ROUTE_INIT_CWND:
		if (!(route->mask & IPROUTE_BIT_INITCWND))
			break;
		long_ret.u = route->initcwnd;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_ROUTE_INIT_RWND:
		if (!(route->mask & IPROUTE_BIT_INITRWND))
			break;
		long_ret.u = route->initrwnd;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_ROUTE_CONG_CTL:
#if !HAVE_DECL_RTAX_CC_ALGO
		break;
#else
		if (!route->congctl)
			break;
		*var_len = strlen(route->congctl);
		return PTR_CAST(u_char, route->congctl);
#endif
	case VRRP_SNMP_ROUTE_PREF:
#if !HAVE_DECL_RTA_PREF
		break;
#else
		if (!(route->mask & IPROUTE_BIT_PREF))
			break;
		long_ret.u =
			route->pref == ICMPV6_ROUTER_PREF_LOW ? 1 :
			route->pref == ICMPV6_ROUTER_PREF_MEDIUM ? 2 :
			route->pref == ICMPV6_ROUTER_PREF_HIGH ? 3 : 0;
		return PTR_CAST(u_char, &long_ret);
#endif
	case VRRP_SNMP_ROUTE_FASTOPEN_NO_COOKIE:
#if !HAVE_DECL_RTAX_FASTOPEN_NO_COOKIE
		break;
#else
		if (!(route->mask & IPROUTE_BIT_FASTOPEN_NO_COOKIE))
			break;
		long_ret.u = SNMP_TruthValue(route->fastopen_no_cookie);
		return PTR_CAST(u_char, &long_ret);
#endif
	case VRRP_SNMP_ROUTE_REALM_DST:
		if (!route->realms)
			break;
		long_ret.u = route->realms & 0xFFFF;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_ROUTE_REALM_SRC:
		if (!(route->realms & 0xFFFF0000))
			break;
		long_ret.u = route->realms >> 16;
		return PTR_CAST(u_char, &long_ret);
	default:
		return NULL;
	}

	/* If we are here, we asked for a non existent data. Try the
	   next one. */
	if (!exact && (name[*length-1] < MAX_SUBID))
		return vrrp_snmp_route(vp, name, length,
				       exact, var_len, write_method);
	return NULL;
}

#if HAVE_DECL_RTA_ENCAP
static u_char*
vrrp_snmp_encap(struct variable *vp, oid *name, size_t *length,
		 int exact, size_t *var_len, WriteMethod **write_method)
{
	ip_route_t *route;
	list_head_t *e;
	nexthop_t *nh;
	encap_t *encap;
	int adv = 0;
	static struct counter64 c64;
#if HAVE_DECL_LWTUNNEL_ENCAP_MPLS
	static char labels[11*MAX_MPLS_LABELS];
	char *op;
	unsigned i;
#endif

	if (vp->name[vp->namelen - 3] == 7) {
		if ((e = vrrp_header_ar_table(vp, name, length, exact,
					      var_len, write_method,
					      ITER_ROUTES, &adv)) == NULL)
			return NULL;
		route = list_entry(e, ip_route_t, e_list);
		encap = &route->encap;
	} else {
		if ((nh = vrrp_header_nh_table(vp, name, length, exact,
					      var_len, write_method)) == NULL)
			return NULL;
		encap = &nh->encap;
	}

// TODO - enable this to work for main route or next hop - following in a separate function callable by both
	if (encap->type != LWTUNNEL_ENCAP_NONE) {
		if (vp->magic == VRRP_SNMP_ROUTE_ENCAP_TYPE) {
			long_ret.s = encap->type + 1;
			return PTR_CAST(u_char, &long_ret);
		}

		if (encap->type == LWTUNNEL_ENCAP_IP ||
			 encap->type == LWTUNNEL_ENCAP_IP6) {
			switch(vp->magic) {
			case VRRP_SNMP_ROUTE_ENCAP_ID:
				if (!(encap->flags & IPROUTE_BIT_ENCAP_ID))
					break;
				*var_len = sizeof(c64);
				set_counter64 (&c64, encap->ip.id);
				return PTR_CAST(u_char, &c64);
			case VRRP_SNMP_ROUTE_ENCAP_DST_ADDRESS:
				if (!encap->ip.dst)
					break;
				if (encap->ip.dst->ifa.ifa_family == AF_INET6) {
					*var_len = sizeof(encap->ip.dst->u.sin6_addr);
					return PTR_CAST(u_char, &encap->ip.dst->u.sin6_addr);
				}
				*var_len = sizeof(encap->ip.dst->u.sin.sin_addr.s_addr);
				return PTR_CAST(u_char, &encap->ip.dst->u.sin.sin_addr.s_addr);
			case VRRP_SNMP_ROUTE_ENCAP_SRC_ADDRESS:
				if (!encap->ip.src)
					break;
				if (encap->ip.src->ifa.ifa_family == AF_INET6) {
					*var_len = sizeof(encap->ip.src->u.sin6_addr);
					return PTR_CAST(u_char, &encap->ip.src->u.sin6_addr);
				}
				*var_len = sizeof(encap->ip.src->u.sin.sin_addr.s_addr);
				return PTR_CAST(u_char, &encap->ip.src->u.sin.sin_addr.s_addr);
			case VRRP_SNMP_ROUTE_ENCAP_TOS:
				if (!(encap->flags & IPROUTE_BIT_ENCAP_DSFIELD))
					break;
				long_ret.u = encap->ip.tos;
				return PTR_CAST(u_char, &long_ret);
			case VRRP_SNMP_ROUTE_ENCAP_TTL:
				if (!(encap->flags & IPROUTE_BIT_ENCAP_TTL))
					break;
				long_ret.u = encap->ip.ttl;
				return PTR_CAST(u_char, &long_ret);
			case VRRP_SNMP_ROUTE_ENCAP_FLAGS:
				if (!(encap->flags & IPROUTE_BIT_ENCAP_FLAGS))
					break;
				long_ret.u = encap->ip.flags;
				return PTR_CAST(u_char, &long_ret);
			}
		}
#if HAVE_DECL_LWTUNNEL_ENCAP_MPLS
		else if (encap->type == LWTUNNEL_ENCAP_MPLS) {
			if (vp->magic == VRRP_SNMP_ROUTE_ENCAP_MPLS_LABELS) {
				op = labels;
				for (i = 0; i < encap->mpls.num_labels; i++)
					op += snprintf(op, (size_t)(labels + sizeof(labels) - op), "%s%u", i ? "/" : "", encap->mpls.addr[i].entry);
				*var_len = strlen(labels);
				return PTR_CAST(u_char, labels);
			}
		}
#endif
#if HAVE_DECL_LWTUNNEL_ENCAP_ILA
		else if (encap->type == LWTUNNEL_ENCAP_ILA) {
			if (vp->magic == VRRP_SNMP_ROUTE_ENCAP_ILA_LOCATOR) {
				*var_len = sizeof(c64);
				set_counter64 (&c64, encap->ila.locator);
				return PTR_CAST(u_char, &c64);
			}
		}
#endif
	}

	/* If we are here, we asked for a non existent data. Try the
	   next one. */
	if (!exact && (name[*length-1] < MAX_SUBID))
		return vrrp_snmp_encap(vp, name, length,
				       exact, var_len, write_method);
	return NULL;
}
#endif

static u_char*
vrrp_snmp_next_hop(struct variable *vp, oid *name, size_t *length,
		   int exact, size_t *var_len, WriteMethod **write_method)
{
	nexthop_t *nh;

	if ((nh = vrrp_header_nh_table(vp, name, length, exact,
				       var_len, write_method)) == NULL)
		return NULL;

	switch (vp->magic) {
	case VRRP_SNMP_ROUTE_NEXT_HOP_ADDRESS_TYPE:
		if (!nh->addr)
			break;
		long_ret.u = SNMP_InetAddressType(nh->addr->ifa.ifa_family);
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_ROUTE_NEXT_HOP_ADDRESS:
		if (!nh->addr)
			break;
		if (nh->addr->ifa.ifa_family == AF_INET6) {
			*var_len = sizeof nh->addr->u.sin6_addr;
			return PTR_CAST(u_char, &nh->addr->u.sin6_addr);
		}
		*var_len = sizeof nh->addr->u.sin.sin_addr;
		return PTR_CAST(u_char, &nh->addr->u.sin.sin_addr);
	case VRRP_SNMP_ROUTE_NEXT_HOP_IF_INDEX:
		if (!nh->ifp)
			break;
		long_ret.u = nh->ifp->ifindex;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_ROUTE_NEXT_HOP_IF_NAME:
		if (!nh->ifp)
			break;
		*var_len = strlen(nh->ifp->ifname);
		return PTR_CAST(u_char, &nh->ifp->ifname);
	case VRRP_SNMP_ROUTE_NEXT_HOP_WEIGHT:
		 if (!(nh->mask & IPROUTE_BIT_WEIGHT))
			break;
		long_ret.s = nh->weight + 1;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_ROUTE_NEXT_HOP_ONLINK:
		long_ret.u = SNMP_TruthValue(nh->flags & RTNH_F_ONLINK);
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_ROUTE_NEXT_HOP_REALM_DST:
		if (!nh->realms)
			break;
		long_ret.u = nh->realms & 0xFFFF;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_ROUTE_NEXT_HOP_REALM_SRC:
		if (!(nh->realms & 0xFFFF0000))
			break;
		long_ret.u = nh->realms >> 16;
		return PTR_CAST(u_char, &long_ret);
	default:
		break;
	}

	/* If we are here, we asked for a non existent data. Try the
	   next one. */
	if (!exact && (name[*length-1] < MAX_SUBID))
		return vrrp_snmp_next_hop(vp, name, length,
					  exact, var_len, write_method);
	return NULL;
}

static u_char*
vrrp_snmp_rule(struct variable *vp, oid *name, size_t *length,
		 int exact, size_t *var_len, WriteMethod **write_method)
{
	ip_rule_t *rule;
	list_head_t *e;
	int adv = 0;
	const char *str;
	ip_address_t *addr;

	if ((e = vrrp_header_ar_table(vp, name, length, exact,
				      var_len, write_method,
				      ITER_RULES, &adv)) == NULL)
		return NULL;
	rule = list_entry(e, ip_rule_t, e_list);

	switch (vp->magic) {
	case VRRP_SNMP_RULE_DIRECTION:	/* obsolete */
		str = rule->to_addr ? rule->from_addr ? "both" : "to" : rule->from_addr ? "from" : "";
		*var_len = strlen(str);
		return PTR_CAST(u_char, no_const_char_p(str));
	case VRRP_SNMP_RULE_ADDRESSTYPE:	/* obsolete */
		addr = rule->to_addr ? rule->to_addr : rule->from_addr;
		if (!addr)
			break;
		long_ret.u = SNMP_InetAddressType(addr->ifa.ifa_family);
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_RULE_ADDRESS:	/* obsolete */
		addr = rule->to_addr ? rule->to_addr : rule->from_addr;
		if (!addr)
			break;
		if (addr->ifa.ifa_family == AF_INET6) {
			*var_len = sizeof(addr->u.sin6_addr);
			return PTR_CAST(u_char, &addr->u.sin6_addr);
		}
		*var_len = sizeof(addr->u.sin.sin_addr);
		return PTR_CAST(u_char, &addr->u.sin.sin_addr);
	case VRRP_SNMP_RULE_ADDRESSMASK:	/* obsolete */
		addr = rule->to_addr ? rule->to_addr : rule->from_addr;
		if (!addr)
			break;
		long_ret.u = addr->ifa.ifa_prefixlen;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_RULE_ROUTINGTABLE:
		if (rule->action != FR_ACT_TO_TBL)
			break;
		long_ret.u = rule->table;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_RULE_ISSET:
		long_ret.u = SNMP_TruthValue(rule->set);
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_RULE_INVERT:
		long_ret.u = SNMP_TruthValue(rule->invert);
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_RULE_DESTINATIONADDRESSTYPE:
		if (!rule->to_addr)
			break;
		long_ret.u = SNMP_InetAddressType(rule->to_addr->ifa.ifa_family);
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_RULE_DESTINATIONADDRESS:
		if (!rule->to_addr)
			break;
		if (rule->to_addr->ifa.ifa_family == AF_INET6) {
			*var_len = sizeof(rule->to_addr->u.sin6_addr);
			return PTR_CAST(u_char, &rule->to_addr->u.sin6_addr);
		}
		*var_len = sizeof(rule->to_addr->u.sin.sin_addr);
		return PTR_CAST(u_char, &rule->to_addr->u.sin.sin_addr);
	case VRRP_SNMP_RULE_DESTINATIONADDRESSMASK:
		if (!rule->to_addr)
			break;
		long_ret.u = rule->to_addr->ifa.ifa_prefixlen;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_RULE_SOURCEADDRESSTYPE:
		if (!rule->from_addr)
			break;
		long_ret.u = SNMP_InetAddressType(rule->from_addr->ifa.ifa_family);
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_RULE_SOURCEADDRESS:
		if (!rule->from_addr)
			break;
		if (rule->from_addr->ifa.ifa_family == AF_INET6) {
			*var_len = sizeof(rule->from_addr->u.sin6_addr);
			return PTR_CAST(u_char, &rule->from_addr->u.sin6_addr);
		}
		*var_len = sizeof(rule->from_addr->u.sin.sin_addr);
		return PTR_CAST(u_char, &rule->from_addr->u.sin.sin_addr);
	case VRRP_SNMP_RULE_SOURCEADDRESSMASK:
		if (!rule->from_addr)
			break;
		long_ret.u = rule->from_addr->ifa.ifa_prefixlen;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_RULE_TOS:
		long_ret.u = rule->tos;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_RULE_FWMARK:
		if (rule->mask & IPRULE_BIT_FWMARK)
			long_ret.u = rule->fwmark;
		else
			break;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_RULE_FWMASK:
		if (rule->mask & IPRULE_BIT_FWMASK)
			long_ret.u = rule->fwmask;
		else
			break;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_RULE_REALM_DST:
		if (!rule->realms)
			break;
		long_ret.u = rule->realms & 0xFFFF;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_RULE_REALM_SRC:
		if (!(rule->realms & 0xFFFF0000))
			break;
		long_ret.u = rule->realms >> 16;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_RULE_ININTERFACE:
		if (!rule->iif)
			break;
		*var_len = strlen(rule->iif->ifname);
		return PTR_CAST(u_char, rule->iif->ifname);
	case VRRP_SNMP_RULE_OUTINTERFACE:
		if (!rule->oif)
			break;
		*var_len = strlen(rule->oif->ifname);
		return PTR_CAST(u_char, rule->oif->ifname);
	case VRRP_SNMP_RULE_TARGET:
		if (!(rule->action == FR_ACT_GOTO))
			break;
		long_ret.u = rule->goto_target;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_RULE_ACTION:
		long_ret.u = rule->action;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_RULE_TABLE_NO:
		if (rule->action != FR_ACT_TO_TBL)
			break;
		long_ret.u = rule->table;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_RULE_PREFERENCE:
		if (!rule->priority)
			break;
		long_ret.u = rule->priority;
		return PTR_CAST(u_char, &long_ret);
#if HAVE_DECL_FRA_SUPPRESS_PREFIXLEN
	case VRRP_SNMP_RULE_SUPPRESSPREFIXLEN:
		if (rule->suppress_prefix_len != -1)
			long_ret.u = rule->suppress_prefix_len;
		else
			break;
		return PTR_CAST(u_char, &long_ret);
#endif
#if HAVE_DECL_FRA_SUPPRESS_IFGROUP
	case VRRP_SNMP_RULE_SUPPRESSGROUP:
		if (rule->mask & IPRULE_BIT_SUP_GROUP) {
RELAX_CAST_QUAL_START
			str = PTR_CAST_CONST(char, get_rttables_group(rule->suppress_group));
RELAX_CAST_QUAL_END
			*var_len = strlen(str);
		}
		else
			break;
		return PTR_CAST(u_char, no_const_char_p(str));
#endif
#if HAVE_DECL_FRA_TUN_ID
	case VRRP_SNMP_RULE_TUNNELID_HIGH:
		if (rule->tunnel_id)
			long_ret.u = rule->tunnel_id >> 32;
		else
			break;
		return PTR_CAST(u_char, &long_ret);
#endif
#if HAVE_DECL_FRA_TUN_ID
	case VRRP_SNMP_RULE_TUNNELID_LOW:
		if (rule->tunnel_id)
			long_ret.u = rule->tunnel_id & 0xffffffff;
		else
			break;
		return PTR_CAST(u_char, &long_ret);
#endif
#if HAVE_DECL_FRA_UID_RANGE
	case VRRP_SNMP_RULE_UID_RANGE_START:
		if (rule->mask & IPRULE_BIT_UID_RANGE)
			long_ret.u = rule->uid_range.start;
		else
			break;
		return PTR_CAST(u_char, &long_ret);
#endif
#if HAVE_DECL_FRA_UID_RANGE
	case VRRP_SNMP_RULE_UID_RANGE_END:
		if (rule->mask & IPRULE_BIT_UID_RANGE)
			long_ret.u = rule->uid_range.end;
		else
			break;
		return PTR_CAST(u_char, &long_ret);
#endif
#if HAVE_DECL_FRA_L3MDEV
	case VRRP_SNMP_RULE_L3MDEV:
		if (rule->l3mdev)
			long_ret.u = 1;
		else
			break;
		return PTR_CAST(u_char, &long_ret);
#endif
#if HAVE_DECL_FRA_PROTOCOL
	case VRRP_SNMP_RULE_PROTOCOL:
		if (rule->mask & IPRULE_BIT_PROTOCOL)
			long_ret.u = rule->protocol;
		else
			break;
		return PTR_CAST(u_char, &long_ret);
#endif
#if HAVE_DECL_FRA_IP_PROTO
	case VRRP_SNMP_RULE_IP_PROTO:
		if (rule->mask & IPRULE_BIT_IP_PROTO)
			long_ret.u = rule->ip_proto;
		else
			break;
		return PTR_CAST(u_char, &long_ret);
#endif
#if HAVE_DECL_FRA_SPORT_RANGE
	case VRRP_SNMP_RULE_SRC_PORT_START:
		if (rule->mask & IPRULE_BIT_SPORT_RANGE)
			long_ret.u = rule->src_port.start;
		else
			break;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_RULE_SRC_PORT_END:
		if (rule->mask & IPRULE_BIT_SPORT_RANGE)
			long_ret.u = rule->src_port.end;
		else
			break;
		return PTR_CAST(u_char, &long_ret);
#endif
#if HAVE_DECL_FRA_DPORT_RANGE
	case VRRP_SNMP_RULE_DST_PORT_START:
		if (rule->mask & IPRULE_BIT_DPORT_RANGE)
			long_ret.u = rule->dst_port.start;
		else
			break;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_RULE_DST_PORT_END:
		if (rule->mask & IPRULE_BIT_DPORT_RANGE)
			long_ret.u = rule->dst_port.end;
		else
			break;
		return PTR_CAST(u_char, &long_ret);
#endif
	default:
		return NULL;
	}
	/* If we are here, we asked for a non existent data. Try the
	   next one. */
	if (!exact && (name[*length-1] < MAX_SUBID))
		return vrrp_snmp_rule(vp, name, length,
				       exact, var_len, write_method);
	return NULL;
}

static u_char *
vrrp_snmp_syncgroup(struct variable *vp, oid *name, size_t *length,
		    int exact, size_t *var_len, WriteMethod **write_method)
{
	vrrp_sgroup_t *group;
	list_head_t *e;
	snmp_ret_t ret;

	if ((e = snmp_header_list_head_table(vp, name, length, exact,
					     var_len, write_method,
					     &vrrp_data->vrrp_sync_group)) == NULL)
		return NULL;
	group = list_entry(e, vrrp_sgroup_t, e_list);

	switch (vp->magic) {
	case VRRP_SNMP_SYNCGROUP_NAME:
		*var_len = strlen(group->gname);
		ret.cp = group->gname;
		return ret.p;
	case VRRP_SNMP_SYNCGROUP_STATE:
		long_ret.s = vrrp_snmp_state(group->state);
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_SYNCGROUP_TRACKINGWEIGHT:
		long_ret.u = SNMP_TruthValue(group->sgroup_tracking_weight);
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_SYNCGROUP_SMTPALERT:
		long_ret.u = SNMP_TruthValue(group->smtp_alert);
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_SYNCGROUP_NOTIFYEXEC:
		long_ret.u = SNMP_TruthValue(group->notify_exec);
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_SYNCGROUP_SCRIPTMASTER:
		if (group->script_master) {
			cmd_str_r(group->script_master, buf, sizeof(buf));
			*var_len = strlen(buf);
			return PTR_CAST(u_char, buf);
		}
		break;
	case VRRP_SNMP_SYNCGROUP_SCRIPTBACKUP:
		if (group->script_backup) {
			cmd_str_r(group->script_backup, buf, sizeof(buf));
			*var_len = strlen(buf);
			return PTR_CAST(u_char, buf);
		}
		break;
	case VRRP_SNMP_SYNCGROUP_SCRIPTFAULT:
		if (group->script_fault) {
			cmd_str_r(group->script_fault, buf, sizeof(buf));
			*var_len = strlen(buf);
			return PTR_CAST(u_char, buf);
		}
		break;
	case VRRP_SNMP_SYNCGROUP_SCRIPTSTOP:
		if (group->script_stop) {
			cmd_str_r(group->script_stop, buf, sizeof(buf));
			*var_len = strlen(buf);
			return PTR_CAST(u_char, buf);
		}
		break;
	case VRRP_SNMP_SYNCGROUP_SCRIPT:
		if (group->script) {
			cmd_str_r(group->script, buf, sizeof(buf));
			*var_len = strlen(buf);
			return PTR_CAST(u_char, buf);
		}
		break;
	default:
		return NULL;
	}
	/* If we are here, we asked for a non existent data. Try the
	   next one. */
	if (!exact && (name[*length-1] < MAX_SUBID))
		return vrrp_snmp_syncgroup(vp, name, length,
					   exact, var_len, write_method);
	return NULL;
}

static u_char *
vrrp_snmp_syncgroupmember(struct variable *vp, oid *name, size_t *length,
			  int exact, size_t *var_len, WriteMethod **write_method)
{
	snmp_ret_t ret;
	vrrp_t *vrrp;
	list_head_t *e;

	e = snmp_find_element(vp, name, length, exact, var_len, write_method,
			      &vrrp_data->vrrp_sync_group,
			      offsetof(vrrp_sgroup_t, e_list),
			      offsetof(vrrp_sgroup_t, vrrp_instances));
	if (!e)
		return NULL;

	vrrp = list_entry(e, vrrp_t, s_list);
	ret.cp = vrrp->iname;
	*var_len = strlen(ret.cp);
	return ret.p;
}

static vrrp_t *
_get_instance(oid *name, size_t name_len)
{
	oid instance;
	vrrp_t *vrrp = NULL;

	if (name_len < 1)
		return NULL;
	instance = name[name_len - 1];

	list_for_each_entry(vrrp, &vrrp_data->vrrp, e_list) {
		if (--instance == 0) break;
	}
	return vrrp;
}

#ifdef _WITH_FIREWALL_
static int
#ifndef ALLOW_SNMP_SET_ACCEPT
vrrp_snmp_instance_accept(__attribute__((unused)) int action,
			  __attribute__((unused)) u_char *var_val, __attribute__((unused)) u_char var_val_type,
			  __attribute__((unused)) size_t var_val_len, __attribute__((unused)) u_char *statP,
			  __attribute__((unused)) oid *name, __attribute__((unused)) size_t name_len)
{
	return SNMP_ERR_NOACCESS;
}

#else
vrrp_snmp_instance_accept(int action,
			  u_char *var_val, u_char var_val_type,
			  size_t var_val_len, __attribute__((unused)) u_char *statP,
			  oid *name, size_t name_len)
{
	vrrp_t *vrrp = NULL;

	switch (action) {
	case RESERVE1:
		/* Check that the proposed value is acceptable */
		if (var_val_type != ASN_INTEGER)
			return SNMP_ERR_WRONGTYPE;
		if (var_val_len > sizeof(long))
			return SNMP_ERR_WRONGLENGTH;
		switch ((long)(*var_val)) {
// TODO - We must check that we are not address owner (especially if disabling)
		case 1:		/* enable accept */
		case 2:		/* disable accept */
			break;
		default:
			return SNMP_ERR_WRONGVALUE;
		}
		break;
	case RESERVE2:	/* Check that we can find the instance.*/
	case COMMIT:
		/* Find the instance */
		vrrp = _get_instance(name, name_len);
		if (!vrrp)
			return SNMP_ERR_NOSUCHNAME;
		if (action == RESERVE2)
			break;
		/* Commit: change values. There is no way to fail. */
		switch ((long)(*var_val)) {
		case 1:
			log_message(LOG_INFO,
				    "(%s) accept mode enabled with SNMP",
				     vrrp->iname);
// TODO - What do we do about adding/removing iptables blocks?
// RFC6527 requires the instance to be down to change this - can't find now where it says that
			vrrp->accept = true;
			break;
		case 2:
			log_message(LOG_INFO,
				    "(%s) accept mode disabled with SNMP",
				    vrrp->iname);
			vrrp->accept = false;
			break;
			}
		break;
		}
	return SNMP_ERR_NOERROR;
}
#endif
#endif

static int
vrrp_snmp_instance_priority(int action,
			    u_char *var_val, u_char var_val_type, size_t var_val_len,
			    __attribute__((unused)) u_char *statP, oid *name, size_t name_len)
{
	vrrp_t *vrrp = NULL;

	switch (action) {
	case RESERVE1:
		/* Check that the proposed priority is acceptable */
		if (var_val_type != ASN_INTEGER)
			return SNMP_ERR_WRONGTYPE;
		if (var_val_len > sizeof(long))
			return SNMP_ERR_WRONGLENGTH;
		if (*var_val == 0)
			return SNMP_ERR_WRONGVALUE;
		break;
	case RESERVE2:		/* Check that we can find the instance. We should. */
	case COMMIT:
		/* Find the instance */
		vrrp = _get_instance(name, name_len);
		if (!vrrp)
			return SNMP_ERR_NOSUCHNAME;
		if (action == RESERVE2)
			break;
		/* Commit: change values. There is no way to fail. */
		log_message(LOG_INFO,
			    "(%s) base priority changed from"
			    " %u to %u via SNMP.",
			    vrrp->iname, vrrp->base_priority, *var_val);
		vrrp->total_priority += *var_val - vrrp->base_priority;
		vrrp->base_priority = *var_val;
		vrrp_set_effective_priority(vrrp);
//TODO - could affect accept
		break;
	}
	return SNMP_ERR_NOERROR;
}

static int
vrrp_snmp_instance_preempt(int action,
			   u_char *var_val, u_char var_val_type, size_t var_val_len,
			   __attribute__((unused)) u_char *statP, oid *name, size_t name_len)
{
	vrrp_t *vrrp = NULL;
	switch (action) {
	case RESERVE1:
		/* Check that the proposed value is acceptable */
		if (var_val_type != ASN_INTEGER)
			return SNMP_ERR_WRONGTYPE;
		if (var_val_len > sizeof(long))
			return SNMP_ERR_WRONGLENGTH;
		switch ((long)(*var_val)) {
		case 1:		/* enable preemption */
		case 2:		/* disable preemption */
			break;
		default:
			return SNMP_ERR_WRONGVALUE;
		}
		break;
	case RESERVE2:		/* Check that we can find the instance. We should. */
	case COMMIT:
		/* Find the instance */
		vrrp = _get_instance(name, name_len);
		if (!vrrp) return SNMP_ERR_NOSUCHNAME;
		if (action == RESERVE2)
			break;
		/* Commit: change values. There is no way to fail. */
		switch ((long)(*var_val)) {
		case 1:
			log_message(LOG_INFO,
				    "(%s) preemption enabled with SNMP",
				    vrrp->iname);
			__clear_bit(VRRP_FLAG_NOPREEMPT, &vrrp->flags);
			break;
		case 2:
			log_message(LOG_INFO,
				    "(%s) preemption disabled with SNMP",
				    vrrp->iname);
			__set_bit(VRRP_FLAG_NOPREEMPT, &vrrp->flags);
			break;
		}
		break;
	}
	return SNMP_ERR_NOERROR;
}

static u_char*
vrrp_snmp_instance(struct variable *vp, oid *name, size_t *length,
		   int exact, size_t *var_len, WriteMethod **write_method)
{
	snmp_ret_t ret;
	list_head_t *e;
	vrrp_t *rt;

	if ((e = snmp_header_list_head_table(vp, name, length, exact,
					     var_len, write_method,
					     &vrrp_data->vrrp)) == NULL)
		return NULL;
	rt = list_entry(e, vrrp_t, e_list);

	switch (vp->magic) {
	case VRRP_SNMP_INSTANCE_NAME:
		*var_len = strlen(rt->iname);
		ret.cp = rt->iname;
		return ret.p;
	case VRRP_SNMP_INSTANCE_VIRTUALROUTERID:
		long_ret.u = rt->vrid;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_INSTANCE_STATE:
		long_ret.s = vrrp_snmp_state(rt->state);
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_INSTANCE_INITIALSTATE:
		long_ret.s = vrrp_snmp_state(rt->configured_state);
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_INSTANCE_WANTEDSTATE:
		long_ret.s = vrrp_snmp_state(rt->wantstate);
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_INSTANCE_BASEPRIORITY:
		long_ret.u = rt->base_priority;
		*write_method = vrrp_snmp_instance_priority;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_INSTANCE_EFFECTIVEPRIORITY:
		long_ret.u = rt->effective_priority;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_INSTANCE_VIPSENABLED:
		long_ret.u = SNMP_TruthValue(rt->vipset);
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_INSTANCE_PRIMARYINTERFACE:
		if (!rt->ifp)
			return NULL;
		*var_len = strlen(rt->ifp->ifname);
		return PTR_CAST(u_char, &rt->ifp->ifname);
	case VRRP_SNMP_INSTANCE_TRACKPRIMARYIF:
		long_ret.u = SNMP_TruthValue(!list_empty(&rt->track_ifp));
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_INSTANCE_ADVERTISEMENTSINT:
		long_ret.u = (rt->version == VRRP_VERSION_2) ?
			    rt->adver_int / TIMER_HZ :
			    rt->adver_int / TIMER_CENTI_HZ;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_INSTANCE_PREEMPT:
		long_ret.u = SNMP_TruthValue(!__test_bit(VRRP_FLAG_NOPREEMPT, &rt->flags));
		*write_method = vrrp_snmp_instance_preempt;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_INSTANCE_PREEMPTDELAY:
		long_ret.u = rt->preempt_delay / TIMER_HZ;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_INSTANCE_AUTHTYPE:
		long_ret.u = 0;
		if (rt->version == VRRP_VERSION_2) {
#ifdef _WITH_VRRP_AUTH_
			long_ret.u = rt->auth_type;
#endif
		}
		return PTR_CAST(u_char, &long_ret);
#ifdef _WITH_LVS_
	case VRRP_SNMP_INSTANCE_USELVSSYNCDAEMON:
		long_ret.u = SNMP_TruthValue(global_data->lvs_syncd.vrrp == rt);
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_INSTANCE_LVSSYNCINTERFACE:
		if (global_data->lvs_syncd.vrrp == rt) {
			*var_len = strlen(global_data->lvs_syncd.ifname);
			ret.cp = global_data->lvs_syncd.ifname;
			return ret.p;
		}
		break;
#endif
	case VRRP_SNMP_INSTANCE_SYNCGROUP:
		if (rt->sync) {
			*var_len = strlen(rt->sync->gname);
			ret.cp = rt->sync->gname;
			return ret.p;
		}
		break;
	case VRRP_SNMP_INSTANCE_GARPDELAY:
		long_ret.u = rt->garp_delay / TIMER_HZ;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_INSTANCE_SMTPALERT:
		long_ret.u = SNMP_TruthValue(rt->smtp_alert);
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_INSTANCE_NOTIFYEXEC:
		long_ret.u = SNMP_TruthValue(rt->notify_exec);
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_INSTANCE_SCRIPTMASTER:
		if (rt->script_master) {
			cmd_str_r(rt->script_master, buf, sizeof(buf));
			*var_len = strlen(buf);
			return PTR_CAST(u_char, buf);
		}
		break;
	case VRRP_SNMP_INSTANCE_SCRIPTBACKUP:
		if (rt->script_backup) {
			cmd_str_r(rt->script_backup, buf, sizeof(buf));
			*var_len = strlen(buf);
			return PTR_CAST(u_char, buf);
		}
		break;
	case VRRP_SNMP_INSTANCE_SCRIPTFAULT:
		if (rt->script_fault) {
			cmd_str_r(rt->script_fault, buf, sizeof(buf));
			*var_len = strlen(buf);
			return PTR_CAST(u_char, buf);
		}
		break;
	case VRRP_SNMP_INSTANCE_SCRIPTSTOP:
		if (rt->script_stop) {
			cmd_str_r(rt->script_stop, buf, sizeof(buf));
			*var_len = strlen(buf);
			return PTR_CAST(u_char, buf);
		}
		break;
	case VRRP_SNMP_INSTANCE_SCRIPTDELETED:
		if (rt->script_deleted) {
			cmd_str_r(rt->script_deleted, buf, sizeof(buf));
			*var_len = strlen(buf);
			return PTR_CAST(u_char, buf);
		}
		break;
	case VRRP_SNMP_INSTANCE_SCRIPTMASTER_RX_LOWER_PRI:
		if (rt->script_master_rx_lower_pri) {
			cmd_str_r(rt->script_master_rx_lower_pri, buf, sizeof(buf));
			*var_len = strlen(buf);
			return PTR_CAST(u_char, buf);
		}
		break;
	case VRRP_SNMP_INSTANCE_SCRIPT:
		if (rt->script) {
			cmd_str_r(rt->script, buf, sizeof(buf));
			*var_len = strlen(buf);
			return PTR_CAST(u_char, buf);
		}
		break;
	case VRRP_SNMP_INSTANCE_ACCEPT:
		long_ret.u = 0;
#ifdef _WITH_FIREWALL_
		if (rt->version == VRRP_VERSION_3) {
			long_ret.u = SNMP_TruthValue(rt->accept);
			*write_method = vrrp_snmp_instance_accept;
		}
#endif
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_INSTANCE_PROMOTE_SECONDARIES:
		long_ret.u = SNMP_TruthValue(__test_bit(VRRP_FLAG_PROMOTE_SECONDARIES, &rt->flags));
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_INSTANCE_USE_LINKBEAT:
		long_ret.u = SNMP_TruthValue(__test_bit(VRRP_FLAG_LINKBEAT_USE_POLLING, &rt->flags));
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_INSTANCE_VRRP_VERSION:
		long_ret.u = rt->version;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_INSTANCE_NOTIFY_DELETED:
		long_ret.u = SNMP_TruthValue(rt->notify_deleted);
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_INSTANCE_MULTICAST_ADDRESSTYPE:
		if (__test_bit(VRRP_FLAG_UNICAST, &rt->flags))
			break;
		long_ret.u = SNMP_InetAddressType(rt->mcast_daddr.ss_family);
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_INSTANCE_MULTICAST_ADDRESS:
		if (__test_bit(VRRP_FLAG_UNICAST, &rt->flags))
			break;
		if (rt->mcast_daddr.ss_family == AF_INET6) {
			*var_len = sizeof PTR_CAST(struct sockaddr_in6, &rt->mcast_daddr)->sin6_addr;
			return PTR_CAST(u_char, &PTR_CAST(struct sockaddr_in6, &rt->mcast_daddr)->sin6_addr);
		} else {
			*var_len = sizeof PTR_CAST(struct sockaddr_in, &rt->mcast_daddr)->sin_addr;
			return PTR_CAST(u_char, &PTR_CAST(struct sockaddr_in, &rt->mcast_daddr)->sin_addr);
		}
		break;
	case VRRP_SNMP_INSTANCE_V3_CHECKSUM_AS_V2:
		long_ret.u = (__test_bit(VRRP_FLAG_V3_CHECKSUM_AS_V2, &rt->flags)) ? 1 : 2;
		return PTR_CAST(u_char, &long_ret);
	default:
		return NULL;
	}
	/* If we are here, we asked for a non existent data. Try the
	   next one. */
	if (!exact && (name[*length-1] < MAX_SUBID))
		return vrrp_snmp_instance(vp, name, length,
					  exact, var_len, write_method);
	return NULL;
}

static u_char *
vrrp_snmp_trackedinterface(struct variable *vp, oid *name, size_t *length,
			   int exact, size_t *var_len, WriteMethod **write_method)
{
	const tracked_if_t *bifp;
	list_head_t *e;
	snmp_ret_t ret;

	e = snmp_find_element(vp, name, length, exact, var_len, write_method,
			      &vrrp_data->vrrp,
			      offsetof(vrrp_t, e_list),
			      offsetof(vrrp_t, track_ifp));
	if (!e)
		return NULL;

	bifp = list_entry(e, tracked_if_t, e_list);

	switch (vp->magic) {
	case VRRP_SNMP_TRACKEDINTERFACE_NAME:
		ret.cp = bifp->ifp->ifname;
		*var_len = strlen(ret.cp);
		return ret.p;
	case VRRP_SNMP_TRACKEDINTERFACE_WEIGHT:
		long_ret.s = bifp->weight;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_TRACKEDINTERFACE_WEIGHT_REVERSE:
		long_ret.u = SNMP_TruthValue(bifp->weight_reverse);
		return PTR_CAST(u_char, &long_ret);
	}
	return NULL;
}

static u_char*
vrrp_snmp_trackedscript(struct variable *vp, oid *name, size_t *length,
			int exact, size_t *var_len, WriteMethod **write_method)
{
	const tracked_sc_t *bscr;
	list_head_t *e;
	snmp_ret_t ret;

	e = snmp_find_element(vp, name, length, exact, var_len, write_method,
			      &vrrp_data->vrrp,
			      offsetof(vrrp_t, e_list),
			      offsetof(vrrp_t, track_script));
	if (!e)
		return NULL;

	bscr = list_entry(e, tracked_sc_t, e_list);

	switch (vp->magic) {
	case VRRP_SNMP_TRACKEDSCRIPT_NAME:
		ret.cp = bscr->scr->sname;
		*var_len = strlen(ret.cp);
		return ret.p;
	case VRRP_SNMP_TRACKEDSCRIPT_WEIGHT:
		long_ret.s = bscr->weight;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_TRACKEDSCRIPT_WEIGHT_REVERSE:
		long_ret.u = SNMP_TruthValue(bscr->weight_reverse);
		return PTR_CAST(u_char, &long_ret);
	}
	return NULL;
}

static u_char*
vrrp_snmp_trackedfile(struct variable *vp, oid *name, size_t *length,
			int exact, size_t *var_len, WriteMethod **write_method)
{
	const tracked_file_monitor_t *bfile;
	list_head_t *e;
	snmp_ret_t ret;

	e = snmp_find_element(vp, name, length, exact, var_len, write_method,
			      &vrrp_data->vrrp,
			      offsetof(vrrp_t, e_list),
			      offsetof(vrrp_t, track_file));
	if (!e)
		return NULL;

	bfile = list_entry(e, tracked_file_monitor_t, e_list);

	switch(vp->magic) {
	case VRRP_SNMP_TRACKEDFILE_NAME:
		ret.cp = bfile->file->fname;
		*var_len = strlen(ret.cp);
		return ret.p;
	case VRRP_SNMP_TRACKEDFILE_WEIGHT:
		long_ret.s = bfile->file->weight;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_TRACKEDFILE_WEIGHT_REVERSE:
		long_ret.u = SNMP_TruthValue(bfile->file->weight_reverse);
		return PTR_CAST(u_char, &long_ret);
	}

	return NULL;
}

#ifdef _WITH_BFD_
static u_char*
vrrp_snmp_trackedbfd(struct variable *vp, oid *name, size_t *length,
			int exact, size_t *var_len, WriteMethod **write_method)
{
	const tracked_bfd_t *bbfd;
	list_head_t *e;
	snmp_ret_t ret;

	e = snmp_find_element(vp, name, length, exact, var_len, write_method,
			      &vrrp_data->vrrp,
			      offsetof(vrrp_t, e_list),
			      offsetof(vrrp_t, track_bfd));
	if (!e)
		return NULL;

	bbfd = list_entry(e, tracked_bfd_t, e_list);

	switch(vp->magic) {
	case VRRP_SNMP_TRACKEDBFD_NAME:
		ret.cp = bbfd->bfd->bname;
		*var_len = strlen(ret.cp);
		return ret.p;
	case VRRP_SNMP_TRACKEDBFD_WEIGHT:
		long_ret.s = bbfd->bfd->weight;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_TRACKEDBFD_WEIGHT_REVERSE:
		long_ret.u = SNMP_TruthValue(bbfd->bfd->weight_reverse);
		return PTR_CAST(u_char, &long_ret);
	}

	return NULL;
}
#endif

#ifdef _WITH_TRACK_PROCESS_
static u_char*
vrrp_snmp_trackedprocess(struct variable *vp, oid *name, size_t *length,
			int exact, size_t *var_len, WriteMethod **write_method)
{
	const tracked_process_t *bproc;
	list_head_t *e;
	snmp_ret_t ret;

	e = snmp_find_element(vp, name, length, exact, var_len, write_method,
			      &vrrp_data->vrrp,
			      offsetof(vrrp_t, e_list),
			      offsetof(vrrp_t, track_process));
	if (!e)
		return NULL;

	bproc = list_entry(e, tracked_process_t, e_list);

	switch(vp->magic) {
	case VRRP_SNMP_TRACKEDPROCESS_NAME:
		ret.cp = bproc->process->pname;
		*var_len = strlen(ret.cp);
		return ret.p;
	case VRRP_SNMP_TRACKEDPROCESS_WEIGHT:
		long_ret.s = bproc->process->weight;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_TRACKEDPROCESS_WEIGHT_REVERSE:
		long_ret.u = SNMP_TruthValue(bproc->process->weight_reverse);
		return PTR_CAST(u_char, &long_ret);
	}

	return NULL;
}
#endif

static u_char*
vrrp_snmp_group_trackedinterface(struct variable *vp, oid *name, size_t *length,
			   int exact, size_t *var_len, WriteMethod **write_method)
{
	const tracked_if_t *bifp;
	list_head_t *e;
	snmp_ret_t ret;

	e = snmp_find_element(vp, name, length, exact, var_len, write_method,
			      &vrrp_data->vrrp_sync_group,
			      offsetof(vrrp_sgroup_t, e_list),
			      offsetof(vrrp_sgroup_t, track_ifp));
	if (!e)
		return NULL;

	bifp = list_entry(e, tracked_if_t, e_list);

	switch (vp->magic) {
	case VRRP_SNMP_SGROUPTRACKEDINTERFACE_NAME:
		ret.cp = bifp->ifp->ifname;
		*var_len = strlen(ret.cp);
		return ret.p;
	case VRRP_SNMP_SGROUPTRACKEDINTERFACE_WEIGHT:
		long_ret.s = bifp->weight;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_SGROUPTRACKEDINTERFACE_WEIGHT_REVERSE:
		long_ret.u = SNMP_TruthValue(bifp->weight_reverse);
		return PTR_CAST(u_char, &long_ret);
	}
	return NULL;
}

static u_char*
vrrp_snmp_group_trackedscript(struct variable *vp, oid *name, size_t *length,
			int exact, size_t *var_len, WriteMethod **write_method)
{
	const tracked_sc_t *bscr;
	list_head_t *e;
	snmp_ret_t ret;

	e = snmp_find_element(vp, name, length, exact, var_len, write_method,
			      &vrrp_data->vrrp_sync_group,
			      offsetof(vrrp_sgroup_t, e_list),
			      offsetof(vrrp_sgroup_t, track_script));
	if (!e)
		return NULL;

	bscr = list_entry(e, tracked_sc_t, e_list);

	switch (vp->magic) {
	case VRRP_SNMP_SGROUPTRACKEDSCRIPT_NAME:
		ret.cp = bscr->scr->sname;
		*var_len = strlen(ret.cp);
		return ret.p;
	case VRRP_SNMP_SGROUPTRACKEDSCRIPT_WEIGHT:
		long_ret.s = bscr->weight;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_SGROUPTRACKEDSCRIPT_WEIGHT_REVERSE:
		long_ret.u = SNMP_TruthValue(bscr->weight_reverse);
		return PTR_CAST(u_char, &long_ret);
	}
	return NULL;
}

static u_char*
vrrp_snmp_group_trackedfile(struct variable *vp, oid *name, size_t *length,
			int exact, size_t *var_len, WriteMethod **write_method)
{
	const tracked_file_monitor_t *bfile;
	list_head_t *e;
	snmp_ret_t ret;

	e = snmp_find_element(vp, name, length, exact, var_len, write_method,
			      &vrrp_data->vrrp_sync_group,
			      offsetof(vrrp_sgroup_t, e_list),
			      offsetof(vrrp_sgroup_t, track_file));
	if (!e)
		return NULL;

	bfile = list_entry(e, tracked_file_monitor_t, e_list);

	switch(vp->magic) {
	case VRRP_SNMP_SGROUPTRACKEDFILE_NAME:
		ret.cp = bfile->file->fname;
		*var_len = strlen(ret.cp);
		return ret.p;
	case VRRP_SNMP_SGROUPTRACKEDFILE_WEIGHT:
		long_ret.s = bfile->file->weight;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_SGROUPTRACKEDFILE_WEIGHT_REVERSE:
		long_ret.u = SNMP_TruthValue(bfile->file->weight_reverse);
		return PTR_CAST(u_char, &long_ret);
	}

	return NULL;
}

#ifdef _WITH_BFD_
static u_char*
vrrp_snmp_group_trackedbfd(struct variable *vp, oid *name, size_t *length,
			int exact, size_t *var_len, WriteMethod **write_method)
{
	const tracked_bfd_t *bbfd;
	list_head_t *e;
	snmp_ret_t ret;

	e = snmp_find_element(vp, name, length, exact, var_len, write_method,
			      &vrrp_data->vrrp_sync_group,
			      offsetof(vrrp_sgroup_t, e_list),
			      offsetof(vrrp_sgroup_t, track_bfd));
	if (!e)
		return NULL;

	bbfd = list_entry(e, tracked_bfd_t, e_list);

	switch(vp->magic) {
	case VRRP_SNMP_SGROUPTRACKEDBFD_NAME:
		ret.cp = bbfd->bfd->bname;
		*var_len = strlen(ret.cp);
		return ret.p;
	case VRRP_SNMP_SGROUPTRACKEDBFD_WEIGHT:
		long_ret.s = bbfd->bfd->weight;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_SGROUPTRACKEDBFD_WEIGHT_REVERSE:
		long_ret.u = SNMP_TruthValue(bbfd->bfd->weight_reverse);
		return PTR_CAST(u_char, &long_ret);
	}

	return NULL;
}
#endif

#ifdef _WITH_TRACK_PROCESS_
static u_char*
vrrp_snmp_group_trackedprocess(struct variable *vp, oid *name, size_t *length,
			int exact, size_t *var_len, WriteMethod **write_method)
{
	const tracked_process_t *bproc;
	list_head_t *e;
	snmp_ret_t ret;

	e = snmp_find_element(vp, name, length, exact, var_len, write_method,
			      &vrrp_data->vrrp_sync_group,
			      offsetof(vrrp_sgroup_t, e_list),
			      offsetof(vrrp_sgroup_t, track_process));
	if (!e)
		return NULL;

	bproc = list_entry(e, tracked_process_t, e_list);

	switch(vp->magic) {
	case VRRP_SNMP_SGROUPTRACKEDPROCESS_NAME:
		ret.cp = bproc->process->pname;
		*var_len = strlen(ret.cp);
		return ret.p;
	case VRRP_SNMP_SGROUPTRACKEDPROCESS_WEIGHT:
		long_ret.s = bproc->process->weight;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_SNMP_SGROUPTRACKEDPROCESS_WEIGHT_REVERSE:
		long_ret.u = SNMP_TruthValue(bproc->process->weight_reverse);
		return PTR_CAST(u_char, &long_ret);
	}

	return NULL;
}
#endif

static oid vrrp_oid[] = {VRRP_OID};
static struct variable3 vrrp_vars[] = {
	/* vrrpSyncGroupTable */
	{VRRP_SNMP_SYNCGROUP_NAME, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_syncgroup, 3, {1, 1, 2}},
	{VRRP_SNMP_SYNCGROUP_STATE, ASN_INTEGER, RONLY,
	 vrrp_snmp_syncgroup, 3, {1, 1, 3}},
	{VRRP_SNMP_SYNCGROUP_SMTPALERT, ASN_INTEGER, RONLY,
	 vrrp_snmp_syncgroup, 3, {1, 1, 4}},
	{VRRP_SNMP_SYNCGROUP_NOTIFYEXEC, ASN_INTEGER, RONLY,
	 vrrp_snmp_syncgroup, 3, {1, 1, 5}},
	{VRRP_SNMP_SYNCGROUP_SCRIPTMASTER, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_syncgroup, 3, {1, 1, 6}},
	{VRRP_SNMP_SYNCGROUP_SCRIPTBACKUP, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_syncgroup, 3, {1, 1, 7}},
	{VRRP_SNMP_SYNCGROUP_SCRIPTFAULT, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_syncgroup, 3, {1, 1, 8}},
	{VRRP_SNMP_SYNCGROUP_SCRIPT, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_syncgroup, 3, {1, 1, 9}},
	{VRRP_SNMP_SYNCGROUP_TRACKINGWEIGHT, ASN_INTEGER, RONLY,
	 vrrp_snmp_syncgroup, 3, {1, 1, 10}},
	{VRRP_SNMP_SYNCGROUP_SCRIPTSTOP, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_syncgroup, 3, {1, 1, 11}},

	/* vrrpSyncGroupMemberTable */
	{VRRP_SNMP_SYNCGROUPMEMBER_NAME, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_syncgroupmember, 3, {2, 1, 2}},

	/* vrrpInstanceTable */
	{VRRP_SNMP_INSTANCE_NAME, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_instance, 3, {3, 1, 2}},
	{VRRP_SNMP_INSTANCE_VIRTUALROUTERID, ASN_UNSIGNED, RONLY,
	 vrrp_snmp_instance, 3, {3, 1, 3}},
	{VRRP_SNMP_INSTANCE_STATE, ASN_INTEGER, RONLY,
	 vrrp_snmp_instance, 3, {3, 1, 4}},
	{VRRP_SNMP_INSTANCE_INITIALSTATE, ASN_INTEGER, RONLY,
	 vrrp_snmp_instance, 3, {3, 1, 5}},
	{VRRP_SNMP_INSTANCE_WANTEDSTATE, ASN_INTEGER, RONLY,
	 vrrp_snmp_instance, 3, {3, 1, 6}},
	{VRRP_SNMP_INSTANCE_BASEPRIORITY, ASN_INTEGER, RWRITE,
	 vrrp_snmp_instance, 3, {3, 1, 7}},
	{VRRP_SNMP_INSTANCE_EFFECTIVEPRIORITY, ASN_INTEGER, RONLY,
	 vrrp_snmp_instance, 3, {3, 1, 8}},
	{VRRP_SNMP_INSTANCE_VIPSENABLED, ASN_INTEGER, RONLY,
	 vrrp_snmp_instance, 3, {3, 1, 9}},
	{VRRP_SNMP_INSTANCE_PRIMARYINTERFACE, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_instance, 3, {3, 1, 10}},
	{VRRP_SNMP_INSTANCE_TRACKPRIMARYIF, ASN_INTEGER, RONLY,
	 vrrp_snmp_instance, 3, {3, 1, 11}},
	{VRRP_SNMP_INSTANCE_ADVERTISEMENTSINT, ASN_UNSIGNED, RONLY,
	 vrrp_snmp_instance, 3, {3, 1, 12}},
	{VRRP_SNMP_INSTANCE_PREEMPT, ASN_INTEGER, RWRITE,
	 vrrp_snmp_instance, 3, {3, 1, 13}},
	{VRRP_SNMP_INSTANCE_PREEMPTDELAY, ASN_UNSIGNED, RONLY,
	 vrrp_snmp_instance, 3, {3, 1, 14}},
	{VRRP_SNMP_INSTANCE_AUTHTYPE, ASN_INTEGER, RONLY,
	 vrrp_snmp_instance, 3, {3, 1, 15}},
	{VRRP_SNMP_INSTANCE_USELVSSYNCDAEMON, ASN_INTEGER, RONLY,
	 vrrp_snmp_instance, 3, {3, 1, 16}},
	{VRRP_SNMP_INSTANCE_LVSSYNCINTERFACE, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_instance, 3, {3, 1, 17}},
	{VRRP_SNMP_INSTANCE_SYNCGROUP, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_instance, 3, {3, 1, 18}},
	{VRRP_SNMP_INSTANCE_GARPDELAY, ASN_UNSIGNED, RONLY,
	 vrrp_snmp_instance, 3, {3, 1, 19}},
	{VRRP_SNMP_INSTANCE_SMTPALERT, ASN_INTEGER, RONLY,
	 vrrp_snmp_instance, 3, {3, 1, 20}},
	{VRRP_SNMP_INSTANCE_NOTIFYEXEC, ASN_INTEGER, RONLY,
	 vrrp_snmp_instance, 3, {3, 1, 21}},
	{VRRP_SNMP_INSTANCE_SCRIPTMASTER, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_instance, 3, {3, 1, 22}},
	{VRRP_SNMP_INSTANCE_SCRIPTBACKUP, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_instance, 3, {3, 1, 23}},
	{VRRP_SNMP_INSTANCE_SCRIPTFAULT, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_instance, 3, {3, 1, 24}},
	{VRRP_SNMP_INSTANCE_SCRIPTSTOP, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_instance, 3, {3, 1, 25}},
	{VRRP_SNMP_INSTANCE_SCRIPT, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_instance, 3, {3, 1, 26}},
	{VRRP_SNMP_INSTANCE_ACCEPT, ASN_INTEGER, RWRITE,
	 vrrp_snmp_instance, 3, {3, 1, 27}},
	{VRRP_SNMP_INSTANCE_PROMOTE_SECONDARIES, ASN_INTEGER, RWRITE,
	 vrrp_snmp_instance, 3, {3, 1, 28}},
	{VRRP_SNMP_INSTANCE_USE_LINKBEAT, ASN_INTEGER, RWRITE,
	 vrrp_snmp_instance, 3, {3, 1, 29}},
	{VRRP_SNMP_INSTANCE_VRRP_VERSION, ASN_INTEGER, RONLY,
	 vrrp_snmp_instance, 3, {3, 1, 30}},
	{VRRP_SNMP_INSTANCE_SCRIPTMASTER_RX_LOWER_PRI, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_instance, 3, {3, 1, 31}},
	{VRRP_SNMP_INSTANCE_SCRIPTDELETED, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_instance, 3, {3, 1, 32}},
	{VRRP_SNMP_INSTANCE_NOTIFY_DELETED, ASN_INTEGER, RONLY,
	 vrrp_snmp_instance, 3, {3, 1, 33}},
	{VRRP_SNMP_INSTANCE_MULTICAST_ADDRESSTYPE, ASN_INTEGER, RONLY,
	 vrrp_snmp_instance, 3, {3, 1, 34}},
	{VRRP_SNMP_INSTANCE_MULTICAST_ADDRESS, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_instance, 3, {3, 1, 35}},
	{VRRP_SNMP_INSTANCE_V3_CHECKSUM_AS_V2, ASN_INTEGER, RONLY,
	 vrrp_snmp_instance, 3, {3, 1, 36}},

	/* vrrpTrackedInterfaceTable */
	{VRRP_SNMP_TRACKEDINTERFACE_NAME, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_trackedinterface, 3, {4, 1, 1}},
	{VRRP_SNMP_TRACKEDINTERFACE_WEIGHT, ASN_INTEGER, RONLY,
	 vrrp_snmp_trackedinterface, 3, {4, 1, 2}},
	{VRRP_SNMP_TRACKEDINTERFACE_WEIGHT_REVERSE, ASN_INTEGER, RONLY,
	 vrrp_snmp_trackedinterface, 3, {4, 1, 3}},

	/* vrrpTrackedScriptTable */
	{VRRP_SNMP_TRACKEDSCRIPT_NAME, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_trackedscript, 3, {5, 1, 2}},
	{VRRP_SNMP_TRACKEDSCRIPT_WEIGHT, ASN_INTEGER, RONLY,
	 vrrp_snmp_trackedscript, 3, {5, 1, 3}},
	{VRRP_SNMP_TRACKEDSCRIPT_WEIGHT_REVERSE, ASN_INTEGER, RONLY,
	 vrrp_snmp_trackedscript, 3, {5, 1, 4}},

	/* vrrpAddressTable */
	{VRRP_SNMP_ADDRESS_ADDRESSTYPE, ASN_INTEGER, RONLY,
	 vrrp_snmp_address, 3, {6, 1, 2}},
	{VRRP_SNMP_ADDRESS_VALUE, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_address, 3, {6, 1, 3}},
	{VRRP_SNMP_ADDRESS_BROADCAST, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_address, 3, {6, 1, 4}},
	{VRRP_SNMP_ADDRESS_MASK, ASN_UNSIGNED, RONLY,
	 vrrp_snmp_address, 3, {6, 1, 5}},
	{VRRP_SNMP_ADDRESS_SCOPE, ASN_INTEGER, RONLY,
	 vrrp_snmp_address, 3, {6, 1, 6}},
	{VRRP_SNMP_ADDRESS_IFINDEX, ASN_INTEGER, RONLY,
	 vrrp_snmp_address, 3, {6, 1, 7}},
	{VRRP_SNMP_ADDRESS_IFNAME, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_address, 3, {6, 1, 8}},
	{VRRP_SNMP_ADDRESS_IFALIAS, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_address, 3, {6, 1, 9}},
	{VRRP_SNMP_ADDRESS_ISSET, ASN_INTEGER, RONLY,
	 vrrp_snmp_address, 3, {6, 1, 10}},
	{VRRP_SNMP_ADDRESS_ISADVERTISED, ASN_INTEGER, RONLY,
	 vrrp_snmp_address, 3, {6, 1, 11}},
	{VRRP_SNMP_ADDRESS_PEER, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_address, 3, {6, 1, 12}},

	/* vrrpRouteTable */
	{VRRP_SNMP_ROUTE_ADDRESSTYPE, ASN_INTEGER, RONLY,
	 vrrp_snmp_route, 3, {7, 1, 2}},
	{VRRP_SNMP_ROUTE_DESTINATION, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_route, 3, {7, 1, 3}},
	{VRRP_SNMP_ROUTE_DESTINATIONMASK, ASN_UNSIGNED, RONLY,
	 vrrp_snmp_route, 3, {7, 1, 4}},
	{VRRP_SNMP_ROUTE_GATEWAY, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_route, 3, {7, 1, 5}},
	{VRRP_SNMP_ROUTE_SECONDARYGATEWAY, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_route, 3, {7, 1, 6}},
	{VRRP_SNMP_ROUTE_SOURCE, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_route, 3, {7, 1, 7}},
	{VRRP_SNMP_ROUTE_METRIC, ASN_UNSIGNED, RONLY,
	 vrrp_snmp_route, 3, {7, 1, 8}},
	{VRRP_SNMP_ROUTE_SCOPE, ASN_INTEGER, RONLY,
	 vrrp_snmp_route, 3, {7, 1, 9}},
	{VRRP_SNMP_ROUTE_TYPE, ASN_INTEGER, RONLY,
	 vrrp_snmp_route, 3, {7, 1, 10}},
	{VRRP_SNMP_ROUTE_IFINDEX, ASN_INTEGER, RONLY,
	 vrrp_snmp_route, 3, {7, 1, 11}},
	{VRRP_SNMP_ROUTE_IFNAME, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_route, 3, {7, 1, 12}},
	{VRRP_SNMP_ROUTE_ROUTINGTABLE, ASN_UNSIGNED, RONLY,
	 vrrp_snmp_route, 3, {7, 1, 13}},
	{VRRP_SNMP_ROUTE_ISSET, ASN_INTEGER, RONLY,
	 vrrp_snmp_route, 3, {7, 1, 14}},
	{VRRP_SNMP_ROUTE_FROM_ADDRESS, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_route, 3, {7, 1, 15}},
	{VRRP_SNMP_ROUTE_FROM_ADDRESS_MASK, ASN_UNSIGNED, RONLY,
	 vrrp_snmp_route, 3, {7, 1, 16}},
	{VRRP_SNMP_ROUTE_TOS, ASN_UNSIGNED, RONLY,
	 vrrp_snmp_route, 3, {7, 1, 17}},
	{VRRP_SNMP_ROUTE_PROTOCOL, ASN_INTEGER, RONLY,
	 vrrp_snmp_route, 3, {7, 1, 18}},
	{VRRP_SNMP_ROUTE_ECN, ASN_INTEGER, RONLY,
	 vrrp_snmp_route, 3, {7, 1, 19}},
	{VRRP_SNMP_ROUTE_QUICK_ACK, ASN_INTEGER, RONLY,
	 vrrp_snmp_route, 3, {7, 1, 20}},
	{VRRP_SNMP_ROUTE_EXPIRES, ASN_INTEGER, RONLY,
	 vrrp_snmp_route, 3, {7, 1, 21}},
	{VRRP_SNMP_ROUTE_MTU, ASN_UNSIGNED, RONLY,
	 vrrp_snmp_route, 3, {7, 1, 22}},
	{VRRP_SNMP_ROUTE_MTU_LOCK, ASN_INTEGER, RONLY,
	 vrrp_snmp_route, 3, {7, 1, 23}},
	{VRRP_SNMP_ROUTE_HOP_LIMIT, ASN_UNSIGNED, RONLY,
	 vrrp_snmp_route, 3, {7, 1, 24}},
	{VRRP_SNMP_ROUTE_ADVMSS, ASN_UNSIGNED, RONLY,
	 vrrp_snmp_route, 3, {7, 1, 25}},
	{VRRP_SNMP_ROUTE_ADVMSS_LOCK, ASN_INTEGER, RONLY,
	 vrrp_snmp_route, 3, {7, 1, 26}},
	{VRRP_SNMP_ROUTE_RTT, ASN_UNSIGNED, RONLY,
	 vrrp_snmp_route, 3, {7, 1, 27}},
	{VRRP_SNMP_ROUTE_RTT_LOCK, ASN_INTEGER, RONLY,
	 vrrp_snmp_route, 3, {7, 1, 28}},
	{VRRP_SNMP_ROUTE_RTTVAR, ASN_UNSIGNED, RONLY,
	 vrrp_snmp_route, 3, {7, 1, 29}},
	{VRRP_SNMP_ROUTE_RTTVAR_LOCK, ASN_INTEGER, RONLY,
	 vrrp_snmp_route, 3, {7, 1, 30}},
	{VRRP_SNMP_ROUTE_REORDERING, ASN_UNSIGNED, RONLY,
	 vrrp_snmp_route, 3, {7, 1, 31}},
	{VRRP_SNMP_ROUTE_REORDERING_LOCK, ASN_INTEGER, RONLY,
	 vrrp_snmp_route, 3, {7, 1, 32}},
	{VRRP_SNMP_ROUTE_WINDOW, ASN_UNSIGNED, RONLY,
	 vrrp_snmp_route, 3, {7, 1, 33}},
	{VRRP_SNMP_ROUTE_CWND, ASN_UNSIGNED, RONLY,
	 vrrp_snmp_route, 3, {7, 1, 34}},
	{VRRP_SNMP_ROUTE_CWND_LOCK, ASN_INTEGER, RONLY,
	 vrrp_snmp_route, 3, {7, 1, 35}},
	{VRRP_SNMP_ROUTE_SSTHRESH, ASN_UNSIGNED, RONLY,
	 vrrp_snmp_route, 3, {7, 1, 36}},
	{VRRP_SNMP_ROUTE_SSTHRESH_LOCK, ASN_INTEGER, RONLY,
	 vrrp_snmp_route, 3, {7, 1, 37}},
	{VRRP_SNMP_ROUTE_RTOMIN, ASN_UNSIGNED, RONLY,
	 vrrp_snmp_route, 3, {7, 1, 38}},
	{VRRP_SNMP_ROUTE_RTOMIN_LOCK, ASN_INTEGER, RONLY,
	 vrrp_snmp_route, 3, {7, 1, 39}},
	{VRRP_SNMP_ROUTE_INIT_CWND, ASN_UNSIGNED, RONLY,
	 vrrp_snmp_route, 3, {7, 1, 40}},
	{VRRP_SNMP_ROUTE_INIT_RWND, ASN_UNSIGNED, RONLY,
	 vrrp_snmp_route, 3, {7, 1, 41}},
	{VRRP_SNMP_ROUTE_CONG_CTL, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_route, 3, {7, 1, 42}},
	{VRRP_SNMP_ROUTE_PREF, ASN_INTEGER, RONLY,
	 vrrp_snmp_route, 3, {7, 1, 43}},
	{VRRP_SNMP_ROUTE_REALM_DST, ASN_UNSIGNED, RONLY,
	 vrrp_snmp_route, 3, {7, 1, 44}},
	{VRRP_SNMP_ROUTE_REALM_SRC, ASN_UNSIGNED, RONLY,
	 vrrp_snmp_route, 3, {7, 1, 45}},
#if HAVE_DECL_RTA_ENCAP
	{VRRP_SNMP_ROUTE_ENCAP_TYPE, ASN_INTEGER, RONLY,
	 vrrp_snmp_encap, 3, {7, 1, 46}},
#if HAVE_DECL_LWTUNNEL_ENCAP_MPLS
	{VRRP_SNMP_ROUTE_ENCAP_MPLS_LABELS, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_encap, 3, {7, 1, 47}},
#endif
	{VRRP_SNMP_ROUTE_ENCAP_ID, ASN_COUNTER64, RONLY,
	 vrrp_snmp_encap, 3, {7, 1, 48}},
	{VRRP_SNMP_ROUTE_ENCAP_DST_ADDRESS, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_encap, 3, {7, 1, 49}},
	{VRRP_SNMP_ROUTE_ENCAP_SRC_ADDRESS, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_encap, 3, {7, 1, 50}},
	{VRRP_SNMP_ROUTE_ENCAP_TOS, ASN_UNSIGNED, RONLY,
	 vrrp_snmp_encap, 3, {7, 1, 51}},
	{VRRP_SNMP_ROUTE_ENCAP_TTL, ASN_UNSIGNED, RONLY,
	 vrrp_snmp_encap, 3, {7, 1, 52}},
	{VRRP_SNMP_ROUTE_ENCAP_FLAGS, ASN_UNSIGNED, RONLY,
	 vrrp_snmp_encap, 3, {7, 1, 53}},
#if HAVE_DECL_LWTUNNEL_ENCAP_ILA
	{VRRP_SNMP_ROUTE_ENCAP_ILA_LOCATOR, ASN_COUNTER64, RONLY,
	 vrrp_snmp_encap, 3, {7, 1, 54}},
#endif
#if HAVE_DECL_RTAX_FASTOPEN_NO_COOKIE
	{VRRP_SNMP_ROUTE_FASTOPEN_NO_COOKIE, ASN_INTEGER, RONLY,
	 vrrp_snmp_route, 3, {7, 1, 55}},
#endif
#endif

	 /* vrrpRuleTable */
	{VRRP_SNMP_RULE_DIRECTION, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_rule, 3, {8, 1, 2}},
	{VRRP_SNMP_RULE_ADDRESSTYPE, ASN_INTEGER, RONLY,
	 vrrp_snmp_rule, 3, {8, 1, 3}},
	{VRRP_SNMP_RULE_ADDRESS, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_rule, 3, {8, 1, 4}},
	{VRRP_SNMP_RULE_ADDRESSMASK, ASN_UNSIGNED, RONLY,
	 vrrp_snmp_rule, 3, {8, 1, 5}},
	{VRRP_SNMP_RULE_ROUTINGTABLE, ASN_UNSIGNED, RONLY,
	 vrrp_snmp_rule, 3, {8, 1, 6}},
	{VRRP_SNMP_RULE_ISSET, ASN_INTEGER, RONLY,
	 vrrp_snmp_rule, 3, {8, 1, 7}},
	{VRRP_SNMP_RULE_INVERT, ASN_INTEGER, RONLY,
	 vrrp_snmp_rule, 3, {8, 1, 8}},
	{VRRP_SNMP_RULE_DESTINATIONADDRESSTYPE, ASN_INTEGER, RONLY,
	 vrrp_snmp_rule, 3, {8, 1, 9}},
	{VRRP_SNMP_RULE_DESTINATIONADDRESS, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_rule, 3, {8, 1, 10}},
	{VRRP_SNMP_RULE_DESTINATIONADDRESSMASK, ASN_UNSIGNED, RONLY,
	 vrrp_snmp_rule, 3, {8, 1, 11}},
	{VRRP_SNMP_RULE_SOURCEADDRESSTYPE, ASN_INTEGER, RONLY,
	 vrrp_snmp_rule, 3, {8, 1, 12}},
	{VRRP_SNMP_RULE_SOURCEADDRESS, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_rule, 3, {8, 1, 13}},
	{VRRP_SNMP_RULE_SOURCEADDRESSMASK, ASN_UNSIGNED, RONLY,
	 vrrp_snmp_rule, 3, {8, 1, 14}},
	{VRRP_SNMP_RULE_TOS, ASN_UNSIGNED, RONLY,
	 vrrp_snmp_rule, 3, {8, 1, 15}},
	{VRRP_SNMP_RULE_FWMARK, ASN_UNSIGNED, RONLY,
	 vrrp_snmp_rule, 3, {8, 1, 16}},
	{VRRP_SNMP_RULE_FWMASK, ASN_UNSIGNED, RONLY,
	 vrrp_snmp_rule, 3, {8, 1, 17}},
	{VRRP_SNMP_RULE_REALM_DST, ASN_UNSIGNED, RONLY,
	 vrrp_snmp_rule, 3, {8, 1, 18}},
	{VRRP_SNMP_RULE_REALM_SRC, ASN_UNSIGNED, RONLY,
	 vrrp_snmp_rule, 3, {8, 1, 19}},
	{VRRP_SNMP_RULE_ININTERFACE, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_rule, 3, {8, 1, 20}},
	{VRRP_SNMP_RULE_OUTINTERFACE, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_rule, 3, {8, 1, 21}},
	{VRRP_SNMP_RULE_TARGET, ASN_UNSIGNED, RONLY,
	 vrrp_snmp_rule, 3, {8, 1, 22}},
	{VRRP_SNMP_RULE_ACTION, ASN_INTEGER, RONLY,
	 vrrp_snmp_rule, 3, {8, 1, 23}},
	{VRRP_SNMP_RULE_TABLE_NO, ASN_UNSIGNED, RONLY,
	 vrrp_snmp_rule, 3, {8, 1, 24}},
	{VRRP_SNMP_RULE_PREFERENCE, ASN_UNSIGNED, RONLY,
	 vrrp_snmp_rule, 3, {8, 1, 25}},
	{VRRP_SNMP_RULE_SUPPRESSPREFIXLEN, ASN_UNSIGNED, RONLY,
	 vrrp_snmp_rule, 3, {8, 1, 26}},
	{VRRP_SNMP_RULE_SUPPRESSGROUP, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_rule, 3, {8, 1, 27}},
	{VRRP_SNMP_RULE_TUNNELID_HIGH, ASN_UNSIGNED, RONLY,
	 vrrp_snmp_rule, 3, {8, 1, 28}},
	{VRRP_SNMP_RULE_TUNNELID_LOW, ASN_UNSIGNED, RONLY,
	 vrrp_snmp_rule, 3, {8, 1, 29}},
#if HAVE_DECL_FRA_UID_RANGE
	{VRRP_SNMP_RULE_UID_RANGE_START, ASN_UNSIGNED, RONLY,
	 vrrp_snmp_rule, 3, {8, 1, 30}},
	{VRRP_SNMP_RULE_UID_RANGE_END, ASN_UNSIGNED, RONLY,
	 vrrp_snmp_rule, 3, {8, 1, 31}},
#endif
#if HAVE_DECL_FRA_L3MDEV
	{VRRP_SNMP_RULE_L3MDEV, ASN_INTEGER, RONLY,
	 vrrp_snmp_rule, 3, {8, 1, 32}},
#endif

	/* vrrpScriptTable */
	{VRRP_SNMP_SCRIPT_NAME, ASN_OCTET_STR, RONLY, vrrp_snmp_script, 3, {9, 1, 2}},
	{VRRP_SNMP_SCRIPT_COMMAND, ASN_OCTET_STR, RONLY, vrrp_snmp_script, 3, {9, 1, 3}},
	{VRRP_SNMP_SCRIPT_INTERVAL, ASN_INTEGER, RONLY, vrrp_snmp_script, 3, {9, 1, 4}},
	{VRRP_SNMP_SCRIPT_WEIGHT, ASN_INTEGER, RONLY, vrrp_snmp_script, 3, {9, 1, 5}},
	{VRRP_SNMP_SCRIPT_RESULT, ASN_INTEGER, RONLY, vrrp_snmp_script, 3, {9, 1, 6}},
	{VRRP_SNMP_SCRIPT_RISE, ASN_UNSIGNED, RONLY, vrrp_snmp_script, 3, {9, 1, 7}},
	{VRRP_SNMP_SCRIPT_FALL, ASN_UNSIGNED, RONLY, vrrp_snmp_script, 3, {9, 1, 8}},
	{VRRP_SNMP_SCRIPT_WEIGHT_REVERSE, ASN_INTEGER, RONLY, vrrp_snmp_script, 3, {9, 1, 9}},
	{VRRP_SNMP_SCRIPT_PATH, ASN_OCTET_STR, RONLY, vrrp_snmp_script, 3, {9, 1, 10}},
	{VRRP_SNMP_SCRIPT_INTERVAL_USEC, ASN_UNSIGNED, RONLY, vrrp_snmp_script, 3, {9, 1, 11}},
	{VRRP_SNMP_SCRIPT_TIMEOUT_USEC, ASN_UNSIGNED, RONLY, vrrp_snmp_script, 3, {9, 1, 12}},

	/* vrrpRouteNextHopTable */
	{VRRP_SNMP_ROUTE_NEXT_HOP_ADDRESS_TYPE, ASN_INTEGER, RONLY,
	 vrrp_snmp_next_hop, 3, {11, 1, 2}},
	{VRRP_SNMP_ROUTE_NEXT_HOP_ADDRESS, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_next_hop, 3, {11, 1, 3}},
	{VRRP_SNMP_ROUTE_NEXT_HOP_IF_INDEX, ASN_INTEGER, RONLY,
	 vrrp_snmp_next_hop, 3, {11, 1, 4}},
	{VRRP_SNMP_ROUTE_NEXT_HOP_IF_NAME, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_next_hop, 3, {11, 1, 5}},
	{VRRP_SNMP_ROUTE_NEXT_HOP_WEIGHT, ASN_UNSIGNED, RONLY,
	 vrrp_snmp_next_hop, 3, {11, 1, 6}},
	{VRRP_SNMP_ROUTE_NEXT_HOP_ONLINK, ASN_INTEGER, RONLY,
	 vrrp_snmp_next_hop, 3, {11, 1, 7}},
	{VRRP_SNMP_ROUTE_NEXT_HOP_REALM_DST, ASN_UNSIGNED, RONLY,
	 vrrp_snmp_next_hop, 3, {11, 1, 8}},
	{VRRP_SNMP_ROUTE_NEXT_HOP_REALM_SRC, ASN_UNSIGNED, RONLY,
	 vrrp_snmp_next_hop, 3, {11, 1, 9}},
#if HAVE_DECL_RTA_ENCAP
	{VRRP_SNMP_ROUTE_NEXT_HOP_ENCAP_TYPE, ASN_INTEGER, RONLY,
	 vrrp_snmp_encap, 3, {11, 1, 10}},
#if HAVE_DECL_LWTUNNEL_ENCAP_MPLS
	{VRRP_SNMP_ROUTE_NEXT_HOP_ENCAP_MPLS_LABELS, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_encap, 3, {11, 1, 11}},
#endif
	{VRRP_SNMP_ROUTE_NEXT_HOP_ENCAP_ID, ASN_COUNTER64, RONLY,
	 vrrp_snmp_encap, 3, {11, 1, 12}},
	{VRRP_SNMP_ROUTE_NEXT_HOP_ENCAP_DST_ADDRESS, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_encap, 3, {11, 1, 13}},
	{VRRP_SNMP_ROUTE_NEXT_HOP_ENCAP_SRC_ADDRESS, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_encap, 3, {11, 1, 14}},
	{VRRP_SNMP_ROUTE_NEXT_HOP_ENCAP_TOS, ASN_UNSIGNED, RONLY,
	 vrrp_snmp_encap, 3, {11, 1, 15}},
	{VRRP_SNMP_ROUTE_NEXT_HOP_ENCAP_TTL, ASN_UNSIGNED, RONLY,
	 vrrp_snmp_encap, 3, {11, 1, 16}},
	{VRRP_SNMP_ROUTE_NEXT_HOP_ENCAP_FLAGS, ASN_UNSIGNED, RONLY,
	 vrrp_snmp_encap, 3, {11, 1, 17}},
#if HAVE_DECL_LWTUNNEL_ENCAP_ILA
	{VRRP_SNMP_ROUTE_NEXT_HOP_ENCAP_ILA_LOCATOR, ASN_COUNTER64, RONLY,
	 vrrp_snmp_encap, 3, {11, 1, 18}},
#endif
#endif

	/* vrrpTrackedFileTable */
	{VRRP_SNMP_TRACKEDFILE_NAME, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_trackedfile, 3, {12, 1, 2}},
	{VRRP_SNMP_TRACKEDFILE_WEIGHT, ASN_INTEGER, RONLY,
	 vrrp_snmp_trackedfile, 3, {12, 1, 3}},
	{VRRP_SNMP_TRACKEDFILE_WEIGHT_REVERSE, ASN_INTEGER, RONLY,
	 vrrp_snmp_trackedfile, 3, {12, 1, 4}},

	/* vrrpFileTable */
	{VRRP_SNMP_FILE_NAME, ASN_OCTET_STR, RONLY, vrrp_snmp_file, 3, {13, 1, 2}},
	{VRRP_SNMP_FILE_PATH, ASN_OCTET_STR, RONLY, vrrp_snmp_file, 3, {13, 1, 3}},
	{VRRP_SNMP_FILE_RESULT, ASN_INTEGER, RONLY, vrrp_snmp_file, 3, {13, 1, 4}},
	{VRRP_SNMP_FILE_WEIGHT, ASN_INTEGER, RONLY, vrrp_snmp_file, 3, {13, 1, 5}},
	{VRRP_SNMP_FILE_WEIGHT_REVERSE, ASN_INTEGER, RONLY, vrrp_snmp_file, 3, {13, 1, 6}},

#ifdef _WITH_BFD_
	/* vrrpTrackedBfdTable */
	{VRRP_SNMP_TRACKEDBFD_NAME, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_trackedbfd, 3, {17, 1, 2}},
	{VRRP_SNMP_TRACKEDBFD_WEIGHT, ASN_INTEGER, RONLY,
	 vrrp_snmp_trackedbfd, 3, {17, 1, 3}},
	{VRRP_SNMP_TRACKEDBFD_WEIGHT_REVERSE, ASN_INTEGER, RONLY,
	 vrrp_snmp_trackedbfd, 3, {17, 1, 4}},

	/* vrrpBfdTable */
	{VRRP_SNMP_BFD_NAME, ASN_OCTET_STR, RONLY, vrrp_snmp_bfd, 3, {18, 1, 2}},
	{VRRP_SNMP_BFD_RESULT, ASN_INTEGER, RONLY, vrrp_snmp_bfd, 3, {18, 1, 3}},
	{VRRP_SNMP_BFD_WEIGHT, ASN_INTEGER, RONLY, vrrp_snmp_bfd, 3, {18, 1, 4}},
	{VRRP_SNMP_BFD_WEIGHT_REVERSE, ASN_INTEGER, RONLY, vrrp_snmp_bfd, 3, {18, 1, 5}},
#endif

#ifdef _WITH_TRACK_PROCESS_
	/* vrrpTrackedProcessTable */
	{VRRP_SNMP_TRACKEDPROCESS_NAME, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_trackedprocess, 3, {20, 1, 2}},
	{VRRP_SNMP_TRACKEDPROCESS_WEIGHT, ASN_INTEGER, RONLY,
	 vrrp_snmp_trackedprocess, 3, {20, 1, 3}},
	{VRRP_SNMP_TRACKEDPROCESS_WEIGHT_REVERSE, ASN_INTEGER, RONLY,
	 vrrp_snmp_trackedprocess, 3, {20, 1, 4}},

	/* vrrpProcessTable */
	{VRRP_SNMP_PROCESS_NAME, ASN_OCTET_STR, RONLY, vrrp_snmp_process, 3, {21, 1, 2}},
	{VRRP_SNMP_PROCESS_PATH, ASN_OCTET_STR, RONLY, vrrp_snmp_process, 3, {21, 1, 3}},
	{VRRP_SNMP_PROCESS_PARAMS, ASN_OCTET_STR, RONLY, vrrp_snmp_process, 3, {21, 1, 4}},
	{VRRP_SNMP_PROCESS_PARAM_MATCH, ASN_INTEGER, RONLY, vrrp_snmp_process, 3, {21, 1, 5}},
	{VRRP_SNMP_PROCESS_WEIGHT, ASN_INTEGER, RONLY, vrrp_snmp_process, 3, {21, 1, 6}},
	{VRRP_SNMP_PROCESS_WEIGHT_REVERSE, ASN_INTEGER, RONLY, vrrp_snmp_process, 3, {21, 1, 7}},
	{VRRP_SNMP_PROCESS_QUORUM, ASN_UNSIGNED, RONLY, vrrp_snmp_process, 3, {21, 1, 8}},
	{VRRP_SNMP_PROCESS_QUORUM_MAX, ASN_INTEGER, RONLY, vrrp_snmp_process, 3, {21, 1, 9}},
	{VRRP_SNMP_PROCESS_FORKDELAY, ASN_UNSIGNED, RONLY, vrrp_snmp_process, 3, {21, 1, 10}},
	{VRRP_SNMP_PROCESS_TERMINATEDELAY, ASN_UNSIGNED, RONLY, vrrp_snmp_process, 3, {21, 1, 11}},
	{VRRP_SNMP_PROCESS_FULLCOMMAND, ASN_INTEGER, RONLY, vrrp_snmp_process, 3, {21, 1, 12}},
	{VRRP_SNMP_PROCESS_CURPROC, ASN_INTEGER, RONLY, vrrp_snmp_process, 3, {21, 1, 13}},
	{VRRP_SNMP_PROCESS_RESULT, ASN_INTEGER, RONLY, vrrp_snmp_process, 3, {21, 1, 14}},
#endif

	/* syncGroupTrackedInterfaceTable */
	{VRRP_SNMP_SGROUPTRACKEDINTERFACE_NAME, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_group_trackedinterface, 3, {14, 1, 1}},
	{VRRP_SNMP_SGROUPTRACKEDINTERFACE_WEIGHT, ASN_INTEGER, RONLY,
	 vrrp_snmp_group_trackedinterface, 3, {14, 1, 2}},
	{VRRP_SNMP_SGROUPTRACKEDINTERFACE_WEIGHT_REVERSE, ASN_INTEGER, RONLY,
	 vrrp_snmp_group_trackedinterface, 3, {14, 1, 3}},

	/* syncGroupTrackedScriptTable */
	{VRRP_SNMP_SGROUPTRACKEDSCRIPT_NAME, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_group_trackedscript, 3, {15, 1, 2}},
	{VRRP_SNMP_SGROUPTRACKEDSCRIPT_WEIGHT, ASN_INTEGER, RONLY,
	 vrrp_snmp_group_trackedscript, 3, {15, 1, 3}},
	{VRRP_SNMP_SGROUPTRACKEDSCRIPT_WEIGHT_REVERSE, ASN_INTEGER, RONLY,
	 vrrp_snmp_group_trackedscript, 3, {15, 1, 4}},

	/* syncGroupTrackedFileTable */
	{VRRP_SNMP_SGROUPTRACKEDFILE_NAME, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_group_trackedfile, 3, {16, 1, 2}},
	{VRRP_SNMP_SGROUPTRACKEDFILE_WEIGHT, ASN_INTEGER, RONLY,
	 vrrp_snmp_group_trackedfile, 3, {16, 1, 3}},
	{VRRP_SNMP_SGROUPTRACKEDFILE_WEIGHT_REVERSE, ASN_INTEGER, RONLY,
	 vrrp_snmp_group_trackedfile, 3, {16, 1, 4}},

#ifdef _WITH_BFD_
	/* syncGroupTrackedBfdTable */
	{VRRP_SNMP_SGROUPTRACKEDBFD_NAME, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_group_trackedbfd, 3, {19, 1, 2}},
	{VRRP_SNMP_SGROUPTRACKEDBFD_WEIGHT, ASN_INTEGER, RONLY,
	 vrrp_snmp_group_trackedbfd, 3, {19, 1, 3}},
	{VRRP_SNMP_SGROUPTRACKEDBFD_WEIGHT_REVERSE, ASN_INTEGER, RONLY,
	 vrrp_snmp_group_trackedbfd, 3, {19, 1, 4}},
#endif

#ifdef _WITH_TRACK_PROCESS_
	/* syncGroupTrackedProcessTable */
	{VRRP_SNMP_SGROUPTRACKEDPROCESS_NAME, ASN_OCTET_STR, RONLY,
	 vrrp_snmp_group_trackedprocess, 3, {22, 1, 2}},
	{VRRP_SNMP_SGROUPTRACKEDPROCESS_WEIGHT, ASN_INTEGER, RONLY,
	 vrrp_snmp_group_trackedprocess, 3, {22, 1, 3}},
	{VRRP_SNMP_SGROUPTRACKEDPROCESS_WEIGHT_REVERSE, ASN_INTEGER, RONLY,
	 vrrp_snmp_group_trackedprocess, 3, {22, 1, 4}},
#endif

};

void
vrrp_snmp_instance_trap(vrrp_t *vrrp)
{
	/* OID of the notification */
	oid notification_oid[] = { VRRP_OID, 10, 0, 2 };
	size_t notification_oid_len = OID_LENGTH(notification_oid);
	/* OID for snmpTrapOID.0 */
	oid objid_snmptrap[] = { SNMPTRAP_OID };
	size_t objid_snmptrap_len = OID_LENGTH(objid_snmptrap);
	snmp_ret_t ptr_conv;

	/* Other OID */
	oid name_oid[] = { VRRP_OID, 3, 1, 2 };
	size_t name_oid_len = OID_LENGTH(name_oid);
	oid state_oid[] = { VRRP_OID, 3, 1, 4 };
	size_t state_oid_len = OID_LENGTH(state_oid);
	oid initialstate_oid[] = { VRRP_OID, 3, 1, 5};
	size_t initialstate_oid_len = OID_LENGTH(initialstate_oid);
	oid routerId_oid[] = { KEEPALIVED_OID, 1, 2, 0 };
	size_t routerId_oid_len = OID_LENGTH(routerId_oid);

	netsnmp_variable_list *notification_vars = NULL;
	int state = 4;		/* unknown */

	if (!global_data->enable_traps || !global_data->enable_snmp_vrrp)
		return;

	if (vrrp->state == VRRP_STATE_INIT ||
	    vrrp->state == VRRP_STATE_BACK ||
	    vrrp->state == VRRP_STATE_MAST ||
	    vrrp->state == VRRP_STATE_FAULT)
		state = vrrp->state;
	else if (vrrp->state == VRRP_STATE_STOP)
		state = 5;
	else if (vrrp->state == VRRP_STATE_DELETED)
		state = 6;

	/* snmpTrapOID */
	snmp_varlist_add_variable(&notification_vars,
				  objid_snmptrap, objid_snmptrap_len,
				  ASN_OBJECT_ID,
				  PTR_CAST(u_char, notification_oid),
				  notification_oid_len * sizeof(oid));
	/* vrrpInstanceName */
	ptr_conv.cp = vrrp->iname;
	snmp_varlist_add_variable(&notification_vars,
				  name_oid, name_oid_len,
				  ASN_OCTET_STR,
				  ptr_conv.p,
				  strlen(vrrp->iname));
	/* vrrpInstanceState */
	snmp_varlist_add_variable(&notification_vars,
				  state_oid, state_oid_len,
				  ASN_INTEGER,
				  PTR_CAST(u_char, &state),
				  sizeof(state));
	/* vrrpInstanceInitialState */
	snmp_varlist_add_variable(&notification_vars,
				  initialstate_oid, initialstate_oid_len,
				  ASN_INTEGER,
				  PTR_CAST(u_char, &vrrp->configured_state),
				  sizeof(vrrp->configured_state));

	/* routerId */
	ptr_conv.cp = global_data->router_id;
	snmp_varlist_add_variable(&notification_vars,
				  routerId_oid, routerId_oid_len,
				  ASN_OCTET_STR,
				  ptr_conv.p,
				  strlen(global_data->router_id));

	if (__test_bit(LOG_DETAIL_BIT, &debug))
		log_message(LOG_INFO,
			    "(%s) Sending SNMP notification",
			    vrrp->iname);
	send_v2trap(notification_vars);
	snmp_free_varbind(notification_vars);
}

void
vrrp_snmp_group_trap(vrrp_sgroup_t *group)
{
	/* OID of the notification */
	oid notification_oid[] = { VRRP_OID, 10, 0, 1 };
	size_t notification_oid_len = OID_LENGTH(notification_oid);
	/* OID for snmpTrapOID.0 */
	oid objid_snmptrap[] = { SNMPTRAP_OID };
	size_t objid_snmptrap_len = OID_LENGTH(objid_snmptrap);
	snmp_ret_t ptr_conv;

	/* Other OID */
	oid name_oid[] = { VRRP_OID, 1, 1, 2 };
	size_t name_oid_len = OID_LENGTH(name_oid);
	oid state_oid[] = { VRRP_OID, 1, 1, 3 };
	size_t state_oid_len = OID_LENGTH(state_oid);
	oid routerId_oid[] = { KEEPALIVED_OID, 1, 2, 0 };
	size_t routerId_oid_len = OID_LENGTH(routerId_oid);

	netsnmp_variable_list *notification_vars = NULL;
	int state = 4;		/* unknown */

	if (!global_data->enable_traps || !global_data->enable_snmp_vrrp)
		return;

	if (group->state == VRRP_STATE_INIT ||
	    group->state == VRRP_STATE_BACK ||
	    group->state == VRRP_STATE_MAST ||
	    group->state == VRRP_STATE_FAULT)
		state = group->state;
	else if (group->state == VRRP_STATE_STOP)
		state = 5;

	/* snmpTrapOID */
	snmp_varlist_add_variable(&notification_vars,
				  objid_snmptrap, objid_snmptrap_len,
				  ASN_OBJECT_ID,
				  PTR_CAST(u_char, notification_oid),
				  notification_oid_len * sizeof(oid));

	/* vrrpSyncGroupName */
	ptr_conv.cp = group->gname;
	snmp_varlist_add_variable(&notification_vars,
				  name_oid, name_oid_len,
				  ASN_OCTET_STR,
				  ptr_conv.p,
				  strlen(group->gname));
	/* vrrpSyncGroupState */
	snmp_varlist_add_variable(&notification_vars,
				  state_oid, state_oid_len,
				  ASN_INTEGER,
				  PTR_CAST(u_char, &state),
				  sizeof(state));

	/* routerId */
	ptr_conv.cp = global_data->router_id;
	snmp_varlist_add_variable(&notification_vars,
				  routerId_oid, routerId_oid_len,
				  ASN_OCTET_STR,
				  ptr_conv.p,
				  strlen(global_data->router_id));

	if (__test_bit(LOG_DETAIL_BIT, &debug))
		log_message(LOG_INFO,
			    "VRRP_Group(%s): Sending SNMP notification",
			    group->gname);
	send_v2trap(notification_vars);
	snmp_free_varbind(notification_vars);
}
#endif


#if defined _WITH_SNMP_RFC_
/* Convert VRRP state to RFC SNMP state */
static int
vrrp_snmp_rfc_state(int state)
{
	if (state <= VRRP_STATE_MAST)
		return state + 1;
	return VRRP_STATE_INIT + 1;
}
#endif

#ifdef _WITH_SNMP_RFCV2_
static bool
suitable_for_rfc2787(const vrrp_t* vrrp)
{
#ifdef _WITH_SNMP_RFCV3_
	/* We mustn't return any VRRP instances that aren't version 2 */
	if (vrrp->version != VRRP_VERSION_2)
		return false;
#endif

	/* We have to skip VRRPv2 with IPv6 since it won't be understood */
	if (vrrp->family == AF_INET6)
		return false;

	/* We are expected to have at least one VIP */
	if (list_empty(&vrrp->vip))
		return false;

	return true;
}

static ip_address_t*
vrrp_rfcv2_header_ar_table(struct variable *vp, oid *name, size_t *length,
			   int exact, size_t *var_len, WriteMethod **write_method)
{
	ip_address_t *vip;
	vrrp_t *vrrp;
	ip_address_t *bel = NULL;
	oid * target, current[2], best[2];
	struct in_addr best_addr, target_addr, current_addr;
	int result, result2 = 0;
	size_t target_len;
	bool found_exact = false;
	bool found_better;

	*write_method = 0;
	*var_len = sizeof(unsigned long);

	if (list_empty(&vrrp_data->vrrp))
		return NULL;

	if ((result = snmp_oid_compare(name, *length, vp->name, vp->namelen)) < 0) {
		memcpy(name, vp->name, sizeof(oid) * vp->namelen);
		*length = vp->namelen;
	}

	/* We search the best match: equal if exact, the lower OID in
	   the set of the OID strictly superior to the target
	   otherwise. */
	best[0] = best[1] = MAX_SUBID; /* Our best match */
	best_addr.s_addr = 0xffffffff;
	target = &name[vp->namelen];   /* Our target match */
	target_len = *length - vp->namelen;
	if (target_len > 2 ) {
		target_len = 2;
		target_addr.s_addr = (in_addr_t)(name[*length - 4] << 24 |
						 name[*length - 3] << 16 |
						 name[*length - 2] << 8 |
						 name[*length - 1]);
	}
	else
		target_addr.s_addr = 0;

	list_for_each_entry(vrrp, &vrrp_data->vrrp, e_list) {
		if (!suitable_for_rfc2787(vrrp))
			continue;

		current[0] = vrrp->ifp ? IF_BASE_INDEX(vrrp->ifp) : 0;
		current[1] = vrrp->vrid;

		if ((result = snmp_oid_compare(current, 2, target, target_len)) < 0)
			continue;

		if (exact) {
			if (result > 0)
				continue;
		}
		else {
			if ((result2 = snmp_oid_compare(current, 2, best, 2)) > 0)
				continue;
		}

		if (list_empty(&vrrp->vip)) {
			if (exact)
				return NULL;
			continue;
		}

		found_better = false;
		list_for_each_entry(vip, &vrrp->vip, e_list) {
			/* We need the address to be MSB first, for numerical comparison */
			current_addr.s_addr = htonl(vip->u.sin.sin_addr.s_addr);

			if (exact) {
				if (target_addr.s_addr == current_addr.s_addr) {
					memcpy(best, current, sizeof(best));
					best_addr = current_addr;
					bel = vip;
					found_exact = true;
					break;
				}

				continue;
			}

			if (result == 0 && target_len && current_addr.s_addr <= target_addr.s_addr)
				continue;
			if (result2 == 0 && current_addr.s_addr >= best_addr.s_addr)
				continue;

			memcpy(best, current, sizeof(best));
			best_addr = current_addr;
			bel = vip;
			result2 = 0;
			found_better = true;
		}

		if (found_exact)
			break;
		if (exact)
			return NULL;
		if (result == 0 && found_better)
			break;
	}

	if (bel == NULL)	/* No best match */
		return NULL;

	/* Let's use our best match */
	memcpy(target, best, sizeof(best));
	*length = (unsigned)vp->namelen + 2;
	name[*length]   =  best_addr.s_addr >> 24;
	name[*length+1] = (best_addr.s_addr >> 16) & 0xff;
	name[*length+2] = (best_addr.s_addr >>  8) & 0xff;
	name[*length+3] = (best_addr.s_addr      ) & 0xff;
	*length += 4;

	return bel;
}

static u_char*
vrrp_rfcv2_snmp_node_info(struct variable *vp, oid *name, size_t *length,
			int exact, size_t *var_len, WriteMethod **write_method)
{

	if (header_generic(vp, name, length, exact, var_len, write_method))
		return NULL;

	if (vp->magic == VRRP_RFC_SNMP_NODE_VER) {
		long_ret.u = 2;
		return PTR_CAST(u_char, &long_ret);
	}

	if (vp->magic == VRRP_RFC_SNMP_NOTIF_CNTL) {
		long_ret.u = global_data->enable_traps ? 1 : 2 ;
		return PTR_CAST(u_char, &long_ret);
	}

	return NULL;
}

static vrrp_t*
snmp_rfcv2_header_list_table(struct variable *vp, oid *name, size_t *length,
		  int exact, size_t *var_len, WriteMethod **write_method)
{
	vrrp_t *bel = NULL, *vrrp;
	oid * target, current[2], best[2];
	int result;
	size_t target_len;

	*write_method = 0;
	*var_len = sizeof (unsigned long);

	if (list_empty(&vrrp_data->vrrp))
		return NULL;

	if ((result = snmp_oid_compare(name, *length, vp->name, vp->namelen)) < 0) {
		memcpy(name, vp->name, sizeof(oid) * vp->namelen);
		*length = vp->namelen;
	}

	/* We search the best match: equal if exact, the lower OID in
	   the set of the OID strictly superior to the target
	   otherwise. */
	best[0] = best[1] = MAX_SUBID; /* Our best match */
	target = &name[vp->namelen];   /* Our target match */
	target_len = *length - vp->namelen;

	list_for_each_entry(vrrp, &vrrp_data->vrrp, e_list) {
		if (!suitable_for_rfc2787(vrrp))
			continue;

		if (target_len && (vrrp->ifp ? IF_BASE_INDEX(vrrp->ifp) : 0 < target[0]))
			continue; /* Optimization: cannot be part of our set */

		current[0] = vrrp->ifp ? IF_BASE_INDEX(vrrp->ifp) : 0;
		current[1] = vrrp->vrid;
		if ((result = snmp_oid_compare(current, 2, target, target_len)) < 0)
			continue;
		if (result == 0) {
			if (!exact)
				continue;
			return vrrp;
		}

		if (snmp_oid_compare(current, 2, best, 2) < 0) {
			/* This is our best match */
			memcpy(best, current, sizeof(best));

			bel = vrrp;
		}
	}

	if (bel == NULL)
		/* No best match */
		return NULL;
	if (exact)
		/* No exact match */
		return NULL;
	/* Let's use our best match */
	memcpy(target, best, sizeof(best));
	*length = (unsigned)vp->namelen + 2;
	return bel;
}

static u_char*
vrrp_rfcv2_snmp_opertable(struct variable *vp, oid *name, size_t *length,
			int exact, size_t *var_len, WriteMethod **write_method)
{
	vrrp_t *rt;
	interface_t* ifp;
	timeval_t uptime;

	if ((rt = snmp_rfcv2_header_list_table(vp, name, length, exact,
					     var_len, write_method)) == NULL)
		return NULL;

	switch (vp->magic) {
	case VRRP_RFC_SNMP_OPER_VMAC:
		if (!rt->ifp)
			return NULL;
		*var_len = rt->ifp->hw_addr_len;
		return PTR_CAST(u_char, &rt->ifp->hw_addr);
	case VRRP_RFC_SNMP_OPER_STATE:
		long_ret.s = vrrp_snmp_rfc_state(rt->state);
		return PTR_CAST(u_char, &long_ret);
	case VRRP_RFC_SNMP_OPER_ADM_STATE:
		/* If we implement write access, then this could be 2 for down */
		long_ret.u = 1;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_RFC_SNMP_OPER_PRI:
		long_ret.u = rt->base_priority;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_RFC_SNMP_OPER_ADDR_CNT:
		long_ret.u = rt->vip_cnt;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_RFC_SNMP_OPER_MIP:
		*var_len = sizeof PTR_CAST(struct sockaddr_in, &rt->master_saddr)->sin_addr.s_addr;
		return PTR_CAST2(u_char, struct sockaddr_in, &rt->master_saddr, sin_addr.s_addr);
	case VRRP_RFC_SNMP_OPER_PIP:
		if (!rt->ifp)
			return NULL;
#ifdef _HAVE_VRRP_VMAC_
		if (IS_MAC_IP_VLAN(rt->ifp))
			ifp = rt->ifp->base_ifp;
		else
#endif
			ifp = rt->ifp;
		*var_len = sizeof ifp->sin_addr;
		return PTR_CAST(u_char, &ifp->sin_addr);
	case VRRP_RFC_SNMP_OPER_AUTH_TYPE:
#ifdef _WITH_VRRP_AUTH_
		long_ret.s = rt->auth_type + 1;
#else
		long_ret.s = 1;
#endif
		return PTR_CAST(u_char, &long_ret);
	case VRRP_RFC_SNMP_OPER_AUTH_KEY:
		*var_len = 0;		// Not readable
		return NULL;
	case VRRP_RFC_SNMP_OPER_ADVERT_INT:
		long_ret.u = rt->adver_int / TIMER_HZ;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_RFC_SNMP_OPER_PREEMPT:
		long_ret.u = SNMP_TruthValue(!__test_bit(VRRP_FLAG_NOPREEMPT, &rt->flags));
		return PTR_CAST(u_char, &long_ret);
	case VRRP_RFC_SNMP_OPER_VR_UPTIME:
		if (rt->state == VRRP_STATE_BACK ||
		    rt->state == VRRP_STATE_MAST) {
			timersub(&rt->stats->uptime, &snmp_vrrp_start_time, &uptime);
			long_ret.s = uptime.tv_sec * 100 + uptime.tv_usec / 10000;	// unit is centi-seconds
		}
		else
			long_ret.u = 0;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_RFC_SNMP_OPER_PROTO:
		long_ret.u = 1;	// IP
		return PTR_CAST(u_char, &long_ret);
	case VRRP_RFC_SNMP_OPER_ROW_STAT:
		long_ret.u = 1;	// active - 1, notInService - 2, notReady - 3, createAndGo - 4, createAndWait - 5
		return PTR_CAST(u_char, &long_ret);
	}

	/* If we are here, we asked for a non existent data. Try the
	   next one. */
	if (!exact && (name[*length-1] < MAX_SUBID))
		return vrrp_rfcv2_snmp_opertable(vp, name, length,
					  exact, var_len, write_method);

	return NULL;
}

static u_char*
vrrp_rfcv2_snmp_assoiptable(struct variable *vp, oid *name, size_t *length,
			int exact, size_t *var_len, WriteMethod **write_method)
{
	ip_address_t *addr;

	if (snmp_oid_compare(name, *length, vp->name, vp->namelen) < 0) {
		memcpy(name, vp->name, sizeof(oid) * vp->namelen);
		*length = vp->namelen;
		*var_len = 0;
		return NULL;
	}
	if ((addr = vrrp_rfcv2_header_ar_table(vp, name, length, exact,
				  var_len, write_method)) == NULL)
		return NULL;

	switch (vp->magic) {
	case VRRP_RFC_SNMP_ASSOC_IP_ADDR_ROW:
		/* If we implement write access, then this could be 2 for down */
		long_ret.u = 1;
		return PTR_CAST(u_char, &long_ret);
	}

	/* If we are here, we asked for a non existent data. Try the
	   next one. */
	if (!exact && (name[*length-1] < MAX_SUBID))
		return vrrp_rfcv2_snmp_assoiptable(vp, name, length,
					  exact, var_len, write_method);

	return NULL;
}

static u_char*
vrrp_rfcv2_snmp_stats(struct variable *vp, oid *name, size_t *length,
			int exact, size_t *var_len, WriteMethod **write_method)
{
	vrrp_t *vrrp;

	if (header_generic(vp, name, length, exact, var_len, write_method))
		return NULL;

	if (vp->magic != VRRP_RFC_SNMP_STATS_CHK_ERR &&
	    vp->magic != VRRP_RFC_SNMP_STATS_VER_ERR &&
	    vp->magic != VRRP_RFC_SNMP_STATS_VRID_ERR)
		return NULL;

	long_ret.u = 0;

	if (list_empty(&vrrp_data->vrrp))
		return PTR_CAST(u_char, &long_ret);

	/* Work through all the vrrp instances that we can respond for */
	list_for_each_entry(vrrp, &vrrp_data->vrrp, e_list) {
		if (!suitable_for_rfc2787(vrrp))
			continue;

		switch (vp->magic) {
		case VRRP_RFC_SNMP_STATS_CHK_ERR:
			long_ret.u += vrrp->stats->chk_err;
			break;
		case VRRP_RFC_SNMP_STATS_VER_ERR:
			long_ret.u += vrrp->stats->vers_err;
			break;
		case VRRP_RFC_SNMP_STATS_VRID_ERR:
			long_ret.u += vrrp->stats->vrid_err;
			break;
		}
	}

	return PTR_CAST(u_char, &long_ret);
}

static u_char*
vrrp_rfcv2_snmp_statstable(struct variable *vp, oid *name, size_t *length,
			int exact, size_t *var_len, WriteMethod **write_method)
{
	vrrp_t *rt;

	if ((rt = snmp_rfcv2_header_list_table(vp, name, length, exact,
					     var_len, write_method)) == NULL)
		return NULL;

	switch (vp->magic) {
	case VRRP_RFC_SNMP_STATS_MASTER:
		long_ret.u = rt->stats->become_master;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_RFC_SNMP_STATS_ADV_RCVD:
		long_ret.u = rt->stats->advert_rcvd;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_RFC_SNMP_STATS_ADV_INT_ERR:
		long_ret.u = rt->stats->advert_interval_err;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_RFC_SNMP_STATS_AUTH_FAIL:
#ifdef _WITH_VRRP_AUTH_
		long_ret.u = rt->stats->auth_failure;
#else
		long_ret.u = 0;
#endif
		return PTR_CAST(u_char, &long_ret);
	case VRRP_RFC_SNMP_STATS_TTL_ERR:
		long_ret.u = rt->stats->ip_ttl_err;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_RFC_SNMP_STATS_PRI_0_RCVD:
		long_ret.u = rt->stats->pri_zero_rcvd;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_RFC_SNMP_STATS_PRI_0_SENT:
		long_ret.u = rt->stats->pri_zero_sent;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_RFC_SNMP_STATS_INV_TYPE_RCVD:
		long_ret.u = rt->stats->invalid_type_rcvd;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_RFC_SNMP_STATS_ADDR_LIST_ERR:
		long_ret.u = rt->stats->addr_list_err;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_RFC_SNMP_STATS_AUTH_INV:
		long_ret.u = rt->stats->invalid_authtype;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_RFC_SNMP_STATS_AUTH_MIS:
#ifdef _WITH_VRRP_AUTH_
		long_ret.u = rt->stats->authtype_mismatch;
#else
		long_ret.u = 0;
#endif
		return PTR_CAST(u_char, &long_ret);
	case VRRP_RFC_SNMP_STATS_PL_ERR:
		long_ret.u = rt->stats->packet_len_err;
		return PTR_CAST(u_char, &long_ret);
	}

	/* If we are here, we asked for a non existent data. Try the
	   next one. */
	if (!exact && (name[*length-1] < MAX_SUBID))
		return vrrp_rfcv2_snmp_statstable(vp, name, length,
					  exact, var_len, write_method);

	return NULL;
}

static oid vrrp_rfcv2_oid[] = {VRRP_RFC_OID};
static struct variable4 vrrp_rfcv2_vars[] = {
	{ VRRP_RFC_SNMP_NODE_VER, ASN_INTEGER, RONLY,
	  vrrp_rfcv2_snmp_node_info, 2, {1, 1}},
	{ VRRP_RFC_SNMP_NOTIF_CNTL, ASN_INTEGER, RONLY,
	  vrrp_rfcv2_snmp_node_info, 2, {1, 2}},
	/* vrrpOperTable */
	{ VRRP_RFC_SNMP_OPER_VMAC, ASN_OCTET_STR, RONLY,
	  vrrp_rfcv2_snmp_opertable, 4, {1, 3, 1, 2}},
	{ VRRP_RFC_SNMP_OPER_STATE, ASN_INTEGER, RONLY,
	  vrrp_rfcv2_snmp_opertable, 4, {1, 3, 1, 3}},
	{ VRRP_RFC_SNMP_OPER_ADM_STATE, ASN_INTEGER, RONLY,
	  vrrp_rfcv2_snmp_opertable, 4, {1, 3, 1, 4}},
	{ VRRP_RFC_SNMP_OPER_PRI, ASN_INTEGER, RONLY,
	  vrrp_rfcv2_snmp_opertable, 4, {1, 3, 1, 5}},
	{ VRRP_RFC_SNMP_OPER_ADDR_CNT, ASN_INTEGER, RONLY,
	  vrrp_rfcv2_snmp_opertable, 4, {1, 3, 1, 6}},
	{ VRRP_RFC_SNMP_OPER_MIP, ASN_IPADDRESS, RONLY,
	  vrrp_rfcv2_snmp_opertable, 4, {1, 3, 1, 7}},
	{ VRRP_RFC_SNMP_OPER_PIP, ASN_IPADDRESS, RONLY,
	  vrrp_rfcv2_snmp_opertable, 4, {1, 3, 1, 8}},
	{ VRRP_RFC_SNMP_OPER_AUTH_TYPE, ASN_INTEGER, RONLY,
	  vrrp_rfcv2_snmp_opertable, 4, {1, 3, 1, 9}},
	{ VRRP_RFC_SNMP_OPER_AUTH_KEY, ASN_OCTET_STR, RONLY,
	  vrrp_rfcv2_snmp_opertable, 4, {1, 3, 1, 10}},
	{ VRRP_RFC_SNMP_OPER_ADVERT_INT, ASN_INTEGER, RONLY,
	  vrrp_rfcv2_snmp_opertable, 4, {1, 3, 1, 11}},
	{ VRRP_RFC_SNMP_OPER_PREEMPT, ASN_INTEGER, RONLY,
	  vrrp_rfcv2_snmp_opertable, 4, {1, 3, 1, 12}},
	{ VRRP_RFC_SNMP_OPER_VR_UPTIME, ASN_TIMETICKS, RONLY,
	  vrrp_rfcv2_snmp_opertable, 4, {1, 3, 1, 13}},
	{ VRRP_RFC_SNMP_OPER_PROTO, ASN_INTEGER, RONLY,
	  vrrp_rfcv2_snmp_opertable, 4, {1, 3, 1, 14}},
	{ VRRP_RFC_SNMP_OPER_ROW_STAT, ASN_INTEGER, RONLY,
	  vrrp_rfcv2_snmp_opertable, 4, {1, 3, 1, 15}},
	/* vrrpAssoIpAddrTable */
	{ VRRP_RFC_SNMP_ASSOC_IP_ADDR_ROW, ASN_INTEGER, RONLY,
	  vrrp_rfcv2_snmp_assoiptable, 4, {1, 4, 1, 2}},
	/* vrrpRouterStats */
	{ VRRP_RFC_SNMP_STATS_CHK_ERR, ASN_COUNTER, RONLY,
	  vrrp_rfcv2_snmp_stats, 2, {2, 1}},
	{ VRRP_RFC_SNMP_STATS_VER_ERR, ASN_COUNTER, RONLY,
	  vrrp_rfcv2_snmp_stats, 2, {2, 2}},
	{ VRRP_RFC_SNMP_STATS_VRID_ERR, ASN_COUNTER, RONLY,
	  vrrp_rfcv2_snmp_stats, 2, {2, 3}},
	/* vrrpRouterStatsTable */
	{ VRRP_RFC_SNMP_STATS_MASTER, ASN_COUNTER, RONLY,
	  vrrp_rfcv2_snmp_statstable, 4, {2, 4, 1, 1}},
	{ VRRP_RFC_SNMP_STATS_ADV_RCVD, ASN_COUNTER, RONLY,
	  vrrp_rfcv2_snmp_statstable, 4, {2, 4, 1, 2}},
	{ VRRP_RFC_SNMP_STATS_ADV_INT_ERR, ASN_COUNTER, RONLY,
	  vrrp_rfcv2_snmp_statstable, 4, {2, 4, 1, 3}},
	{ VRRP_RFC_SNMP_STATS_AUTH_FAIL, ASN_COUNTER, RONLY,
	  vrrp_rfcv2_snmp_statstable, 4, {2, 4, 1, 4}},
	{ VRRP_RFC_SNMP_STATS_TTL_ERR, ASN_COUNTER, RONLY,
	  vrrp_rfcv2_snmp_statstable, 4, {2, 4, 1, 5}},
	{ VRRP_RFC_SNMP_STATS_PRI_0_RCVD, ASN_COUNTER, RONLY,
	  vrrp_rfcv2_snmp_statstable, 4, {2, 4, 1, 6}},
	{ VRRP_RFC_SNMP_STATS_PRI_0_SENT, ASN_COUNTER , RONLY,
	  vrrp_rfcv2_snmp_statstable, 4, {2, 4, 1, 7}},
	{ VRRP_RFC_SNMP_STATS_INV_TYPE_RCVD, ASN_COUNTER, RONLY,
	  vrrp_rfcv2_snmp_statstable, 4, {2, 4, 1, 8}},
	{ VRRP_RFC_SNMP_STATS_ADDR_LIST_ERR, ASN_COUNTER, RONLY,
	  vrrp_rfcv2_snmp_statstable, 4, {2, 4, 1, 9}},
	{ VRRP_RFC_SNMP_STATS_AUTH_INV, ASN_COUNTER, RONLY,
	  vrrp_rfcv2_snmp_statstable, 4, {2, 4, 1, 10}},
	{ VRRP_RFC_SNMP_STATS_AUTH_MIS, ASN_COUNTER, RONLY,
	  vrrp_rfcv2_snmp_statstable, 4, {2, 4, 1, 11}},
	{ VRRP_RFC_SNMP_STATS_PL_ERR, ASN_COUNTER, RONLY,
	  vrrp_rfcv2_snmp_statstable, 4, {2, 4, 1, 12}}
};

void
vrrp_rfcv2_snmp_new_master_trap(vrrp_t *vrrp)
{
	/* OID of the notification vrrpTrapNewMaster */
	oid notification_oid[] = { VRRP_RFC_TRAP_OID, 1 };
	size_t notification_oid_len = OID_LENGTH(notification_oid);
	/* OID for snmpTrapOID.0 */
	oid objid_snmptrap[] = { SNMPTRAP_OID };
	size_t objid_snmptrap_len = OID_LENGTH(objid_snmptrap);
	/* OID for trap data vrrpOperMasterIPAddr */
	oid masterip_oid[] = { VRRP_RFC_OID, 1, 3, 1, 7, vrrp->ifp ? IF_BASE_INDEX(vrrp->ifp) : 0, vrrp->vrid };
	size_t masterip_oid_len = OID_LENGTH(masterip_oid);

	netsnmp_variable_list *notification_vars = NULL;

	if (!global_data->enable_traps || !global_data->enable_snmp_rfcv2)
		return;

	if (!suitable_for_rfc2787(vrrp))
		return;

	/* snmpTrapOID */
	snmp_varlist_add_variable(&notification_vars,
				  objid_snmptrap, objid_snmptrap_len,
				  ASN_OBJECT_ID,
				  PTR_CAST(u_char, notification_oid),
				  notification_oid_len * sizeof(oid));
	/* vrrpInstanceName */
	snmp_varlist_add_variable(&notification_vars,
				  masterip_oid, masterip_oid_len,
				  ASN_IPADDRESS,
				  PTR_CAST2(u_char, struct sockaddr_in, &vrrp->saddr, sin_addr.s_addr),
				  sizeof PTR_CAST(struct sockaddr_in, &vrrp->saddr)->sin_addr.s_addr);
	if (__test_bit(LOG_DETAIL_BIT, &debug))
		log_message(LOG_INFO, "(%s) Sending SNMP notification"
				      " vrrpTrapNewMaster"
				    , vrrp->iname);
	send_v2trap(notification_vars);
	snmp_free_varbind(notification_vars);
}

void
vrrp_rfcv2_snmp_auth_err_trap(vrrp_t *vrrp, struct in_addr src, enum rfcv2_trap_auth_error_type auth_err)
{
	/* OID of the notification vrrpTrapNewMaster */
	oid notification_oid[] = { VRRP_RFC_TRAP_OID, 2 };
	size_t notification_oid_len = OID_LENGTH(notification_oid);
	/* OID for snmpTrapOID.0 */
	oid objid_snmptrap[] = { SNMPTRAP_OID };
	size_t objid_snmptrap_len = OID_LENGTH(objid_snmptrap);
	/* OID for trap data vrrpTrapPacketSrc */
	oid packet_src_oid[] = { VRRP_RFC_OID, 1, 5, vrrp->ifp ? IF_INDEX(vrrp->ifp) : 0, vrrp->vrid };
	size_t packet_src_oid_len = OID_LENGTH(packet_src_oid);
	/* OID for trap data vrrpTrapAuthErrorType */
	oid err_type_oid[] = { VRRP_RFC_OID, 1, 6, vrrp->ifp ? IF_INDEX(vrrp->ifp) : 0, vrrp->vrid };
	size_t err_type_oid_len = OID_LENGTH(err_type_oid);

	netsnmp_variable_list *notification_vars = NULL;

	if (!global_data->enable_traps || !global_data->enable_snmp_rfcv2)
		return;

	if (!suitable_for_rfc2787(vrrp))
		return;

	/* snmpTrapOID */
	snmp_varlist_add_variable(&notification_vars,
				  objid_snmptrap, objid_snmptrap_len,
				  ASN_OBJECT_ID,
				  PTR_CAST(u_char, notification_oid),
				  notification_oid_len * sizeof(oid));
	/* vrrpPacketSrc */
	snmp_varlist_add_variable(&notification_vars,
				  packet_src_oid, packet_src_oid_len,
				  ASN_IPADDRESS,
				  PTR_CAST(u_char, &src),
				  sizeof(src));
	/* vrrpAuthErrorType */
	snmp_varlist_add_variable(&notification_vars,
				  err_type_oid, err_type_oid_len,
				  ASN_INTEGER,
				  PTR_CAST(u_char, &auth_err),
				  sizeof(auth_err));
	if (__test_bit(LOG_DETAIL_BIT, &debug))
		log_message(LOG_INFO, "(%s) Sending SNMP notification"
				      " vrrpTrapAuthFailure"
				    , vrrp->iname);
	send_v2trap(notification_vars);
	snmp_free_varbind(notification_vars);
}
#endif

#ifdef _WITH_SNMP_RFCV3_

static bool
suitable_for_rfc6527(const vrrp_t* vrrp)
{
#ifndef _SNMP_REPLY_V3_FOR_V2_
	/* We mustn't return any VRRP instances that don't match version */
	if (vrrp->version != VRRP_VERSION_3)
		return false;
#endif

	/* We are expected to have at least one VIP */
	if (list_empty(&vrrp->vip))
		return false;

	return true;
}

static inline int
inet6_addr_compare(const struct in6_addr* l, const struct in6_addr* r)
{
	size_t i;
	uint32_t l1, r1;

	for (i = 0; i < sizeof(l->s6_addr32) / sizeof(l->s6_addr32[0]); i++)
	{
		l1 = htonl(l->s6_addr32[i]);
		r1 = htonl(r->s6_addr32[i]);
		if (l1 != r1)
			return ((l1 > r1) & 1) * 2 - 1;
	}

	return 0;
}

static ip_address_t*
vrrp_rfcv3_header_ar_table(struct variable *vp, oid *name, size_t *length,
			   int exact, size_t *var_len, WriteMethod **write_method)
{
	ip_address_t *vip;
	vrrp_t *vrrp;
	ip_address_t *bel = NULL;
	oid * target, current[3], best[3];
	struct in_addr target_addr, current_addr, best_addr;
	struct in6_addr target_addr6, current_addr6, best_addr6;
	int result, result2 = 0;
	size_t target_len;
	bool found_exact = false;
	bool found_better;
	size_t i;

	*write_method = 0;
	*var_len = sizeof(unsigned long);

	if (list_empty(&vrrp_data->vrrp))
		return NULL;

	if ((result = snmp_oid_compare(name, *length, vp->name, vp->namelen)) < 0) {
		memcpy(name, vp->name, sizeof(oid) * vp->namelen);
		*length = vp->namelen;
	}

	/* We search the best match: equal if exact, the lower OID in
	   the set of the OID strictly superior to the target
	   otherwise. */
	best[0] = best[1] = best[2] = MAX_SUBID; /* Our best match */
	best_addr.s_addr = 0xffffffff;
	memset(&best_addr6, 0xff, sizeof(best_addr6));
	target = &name[vp->namelen];   /* Our target match */
	target_len = *length - vp->namelen;
	target_addr.s_addr = 0;		/* Avoid compiler uninitialised warning */
	if (target_len == 3 + sizeof(struct in_addr)) {
		target_len = 3;
		target_addr.s_addr = (in_addr_t)(name[*length - 4] << 24 |
						 name[*length - 3] << 16 |
						 name[*length - 2] << 8 |
						 name[*length - 1]);
	}
	else if (target_len == 3 + sizeof(struct in6_addr)) {
		target_len = 3;
		for (i = 0; i < sizeof (struct in6_addr); i++)
			target_addr6.s6_addr[i] = (uint8_t)name[*length - sizeof(struct in6_addr) + i];
	}

	list_for_each_entry(vrrp, &vrrp_data->vrrp, e_list) {
		if (!suitable_for_rfc6527(vrrp))
			continue;

		current[0] = vrrp->ifp ? IF_BASE_INDEX(vrrp->ifp) : 0;
		current[1] = vrrp->vrid;
		current[2] = SNMP_InetAddressType(vrrp->family);

		if ((result = snmp_oid_compare(current, 3, target, target_len)) < 0)
			continue;

		if (exact) {
			if (result != 0)
				continue;
		}
		else {
			if ((result2 = snmp_oid_compare(current, 3, best, 3)) > 0)
				continue;
		}

		if (list_empty(&vrrp->vip)) {
			if (exact)
				return NULL;
			continue;
		}

		found_better = false;
		list_for_each_entry(vip, &vrrp->vip, e_list) {
			if (vrrp->family == AF_INET) {
				current_addr.s_addr = htonl(vip->u.sin.sin_addr.s_addr);

				if (exact) {
					if (target_addr.s_addr == current_addr.s_addr) {
						memcpy(best, current, sizeof(best));
						best_addr = current_addr;
						bel = vip;
						found_exact = true;
						break;
					}

					continue;
				}

				if (result == 0 && target_len && current_addr.s_addr <= target_addr.s_addr)
					continue;
				if (result2 == 0 && current_addr.s_addr >= best_addr.s_addr)
					continue;

				memcpy(best, current, sizeof(best));
				best_addr = current_addr;
				bel = vip;
				result2 = 0;
				found_better = true;
			}
			else
			{
				current_addr6 = vip->u.sin6_addr;

				if (exact) {
					if (inet6_addr_compare(&target_addr6, &current_addr6) == 0) {
						memcpy(best, current, sizeof(best));
						best_addr6 = current_addr6;
						bel = vip;
						found_exact = true;
						break;
					}

					continue;
				}
				if (result == 0 && target_len && inet6_addr_compare(&current_addr6, &target_addr6) <= 0)
					continue;
				if (result2 == 0 && inet6_addr_compare(&current_addr6, &best_addr6) >= 0)
					continue;

				memcpy(best, current, sizeof(best));
				best_addr6 = current_addr6;
				bel = vip;
				result2 = 0;
				found_better = true;
			}
		}

		if (found_exact)
			break;
		if (exact)
			return NULL;
		if (result == 0 && found_better)
			break;
	}

	if (bel == NULL)	/* No best match */
		return NULL;

	/* Let's use our best match */
	memcpy(target, best, sizeof(best));
	*length = (unsigned)vp->namelen + 3;
	if (name[*length - 1] == 1) {
		name[*length  ] =  best_addr.s_addr >> 24;
		name[*length+1] = (best_addr.s_addr >> 16) & 0xff;
		name[*length+2] = (best_addr.s_addr >>  8) & 0xff;
		name[*length+3] = (best_addr.s_addr      ) & 0xff;
		*length += sizeof(struct in_addr);
	}
	else {
		for (i = 0; i < sizeof(struct in6_addr); i++)
			name[*length + i] = best_addr6.s6_addr[i];
		*length += sizeof(struct in6_addr);
	}

	return bel;
}

static vrrp_t*
snmp_rfcv3_header_list_table(struct variable *vp, oid *name, size_t *length,
		  int exact, size_t *var_len, WriteMethod **write_method)
{
	vrrp_t *bel = NULL, *vrrp;
	oid * target, current[3], best[3];
	int result;
	size_t target_len;

	*write_method = 0;
	*var_len = sizeof (unsigned long);

	if (list_empty(&vrrp_data->vrrp))
		return NULL;

	if ((result = snmp_oid_compare(name, *length, vp->name, vp->namelen)) < 0) {
		memcpy(name, vp->name, sizeof(oid) * vp->namelen);
		*length = vp->namelen;
	}

	/* We search the best match: equal if exact, the lower OID in
	   the set of the OID strictly superior to the target
	   otherwise. */
	best[0] = best[1] = best[2] = MAX_SUBID; /* Our best match */
	target = &name[vp->namelen];   /* Our target match */
	target_len = *length - vp->namelen;

	list_for_each_entry(vrrp, &vrrp_data->vrrp, e_list) {
		if (!suitable_for_rfc6527(vrrp))
			continue;

		if (target_len && ((vrrp->ifp ? IF_BASE_INDEX(vrrp->ifp) : 0) < target[0] ||
				   ((vrrp->ifp ? IF_BASE_INDEX(vrrp->ifp) : 0) == target[0] &&
				    vrrp->vrid < target[1])))
			continue; /* Optimization: cannot be part of our set */

		current[0] = vrrp->ifp ? IF_BASE_INDEX(vrrp->ifp) : 0;
		current[1] = vrrp->vrid;
		current[2] = SNMP_InetAddressType(vrrp->family);
		if ((result = snmp_oid_compare(current, 3, target, target_len)) < 0)
			continue;
		if (result == 0) {
			if (!exact)
				continue;
			return vrrp;
		}

		if (snmp_oid_compare(current, 3, best, 3) < 0) {
			/* This is our best match */
			memcpy(best, current, sizeof(best));

			bel = vrrp;
		}
	}

	if (bel == NULL)
		/* No best match */
		return NULL;
	if (exact)
		/* No exact match */
		return NULL;
	/* Let's use our best match */
	memcpy(target, best, sizeof(best));
	*length = (unsigned)vp->namelen + 3;
	return bel;
}

static u_char*
vrrp_rfcv3_snmp_opertable(struct variable *vp, oid *name, size_t *length,
			int exact, size_t *var_len, WriteMethod **write_method)
{
	vrrp_t *rt;
	interface_t* ifp;
	timeval_t uptime, cur_time;

	if ((rt = snmp_rfcv3_header_list_table(vp, name, length, exact,
					     var_len, write_method)) == NULL)
		return NULL;

	switch (vp->magic) {
	case VRRP_RFCv3_SNMP_OPER_MIP:
		if (rt->state != VRRP_STATE_MAST) {
			if (rt->family == AF_INET) {
				*var_len = sizeof(struct in_addr);
				return PTR_CAST2(u_char, struct sockaddr_in, &rt->master_saddr, sin_addr);
			}
			*var_len = sizeof(struct in6_addr);
			return PTR_CAST2(u_char, struct sockaddr_in6, &rt->master_saddr, sin6_addr);
		}
		/* If we are master, we want to return the Primary IP address */
		/* Falls through. */
	case VRRP_RFCv3_SNMP_OPER_PIP:
		if (!rt->ifp)
			return NULL;
#ifdef _HAVE_VRRP_VMAC_
		if (IS_MAC_IP_VLAN(rt->ifp))
			ifp = rt->ifp->base_ifp;
		else
#endif
			ifp = rt->ifp;
		if (rt->family == AF_INET) {
			*var_len = sizeof(struct in_addr);
			return PTR_CAST(u_char, &ifp->sin_addr);
		}
		*var_len = sizeof(struct in6_addr);
		return PTR_CAST(u_char, &ifp->sin6_addr);
	case VRRP_RFCv3_SNMP_OPER_VMAC:
		if (!rt->ifp)
			return NULL;
		*var_len = rt->ifp->hw_addr_len;
		return PTR_CAST(u_char, &rt->ifp->hw_addr);
	case VRRP_RFCv3_SNMP_OPER_STATE:
		long_ret.s = vrrp_snmp_rfc_state(rt->state);
		return PTR_CAST(u_char, &long_ret);
	case VRRP_RFCv3_SNMP_OPER_PRI:
		long_ret.u = rt->base_priority;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_RFCv3_SNMP_OPER_ADDR_CNT:
		long_ret.u = rt->vip_cnt;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_RFCv3_SNMP_OPER_ADVERT_INT:
		long_ret.u = rt->adver_int / TIMER_CENTI_HZ;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_RFCv3_SNMP_OPER_PREEMPT:
		long_ret.u = SNMP_TruthValue(!__test_bit(VRRP_FLAG_NOPREEMPT, &rt->flags));
		return PTR_CAST(u_char, &long_ret);
#ifdef _WITH_FIREWALL_
	case VRRP_RFCv3_SNMP_OPER_ACCEPT:
		long_ret.u = SNMP_TruthValue(rt->accept);
		return PTR_CAST(u_char, &long_ret);
#endif
	case VRRP_RFCv3_SNMP_OPER_VR_UPTIME:
		if (rt->state == VRRP_STATE_BACK ||
		    rt->state == VRRP_STATE_MAST) {
			cur_time = timer_now();
			timersub(&cur_time, &rt->stats->uptime, &uptime);
			long_ret.s = uptime.tv_sec * 100 + uptime.tv_usec / 10000;	// unit is centi-seconds
		}
		else
			long_ret.s = 0;
		return PTR_CAST(u_char, &long_ret);
	case VRRP_RFCv3_SNMP_OPER_ROW_STATUS:
		long_ret.u = 1;	// active - 1, notInService - 2, notReady - 3, createAndGo - 4, createAndWait - 5
		return PTR_CAST(u_char, &long_ret);
	}

	/* If we are here, we asked for a non existent data. Try the
	   next one. */
	if (!exact && (name[*length-1] < MAX_SUBID))
		return vrrp_rfcv3_snmp_opertable(vp, name, length,
					  exact, var_len, write_method);

	return NULL;
}

static u_char*
vrrp_rfcv3_snmp_assoiptable(struct variable *vp, oid *name, size_t *length,
			int exact, size_t *var_len, WriteMethod **write_method)
{
	ip_address_t *addr;

	if (snmp_oid_compare(name, *length, vp->name, vp->namelen) < 0) {
		memcpy(name, vp->name, sizeof(oid) * vp->namelen);
		*length = vp->namelen;
		*var_len = 0;
	}
	if ((addr = vrrp_rfcv3_header_ar_table(vp, name, length, exact,
				  var_len, write_method)) == NULL)
		return NULL;

	switch (vp->magic) {
	case VRRP_RFCv3_SNMP_ASSOC_IP_ADDR_ROW_STATUS:
		/* If we implement write access, then this could be 2 for down */
		long_ret.u = 1;
		return PTR_CAST(u_char, &long_ret);
	}

	/* If we are here, we asked for a non existent data. Try the
	   next one.
	   NOTE: This never appears to be true, so could be removed.
	 */
	if (!exact && (name[*length-1] < MAX_SUBID))
		return vrrp_rfcv3_snmp_assoiptable(vp, name, length,
					  exact, var_len, write_method);

	return NULL;
}

static u_char*
vrrp_rfcv3_snmp_stats(struct variable *vp, oid *name, size_t *length,
			int exact, size_t *var_len, WriteMethod **write_method)
{
	static struct counter64 c64;
	static uint32_t ret;
	vrrp_t *vrrp;
	uint64_t count;

	if (header_generic(vp, name, length, exact, var_len, write_method))
		return NULL;

	if (vp->magic != VRRP_RFCv3_SNMP_STATS_CHK_ERR &&
	    vp->magic != VRRP_RFCv3_SNMP_STATS_VER_ERR &&
	    vp->magic != VRRP_RFCv3_SNMP_STATS_VRID_ERR &&
	    vp->magic != VRRP_RFCv3_SNMP_STATS_DISC_TIME)
		return NULL;

	c64.high = c64.low = 0;
	*var_len = sizeof(c64);

	if (list_empty(&vrrp_data->vrrp))
		return PTR_CAST(u_char, &c64);

	count = 0;

	/* We don't do discontinuity time at the moment */
	if (vp->magic == VRRP_RFCv3_SNMP_STATS_DISC_TIME) {
		// We don't "do" discontinuities
		*var_len = sizeof(ret);
		ret = 0;
		return PTR_CAST(u_char, &ret);
	}

	/* Work through all the vrrp instances that we can respond for */
	list_for_each_entry(vrrp, &vrrp_data->vrrp, e_list) {
		if (!suitable_for_rfc6527(vrrp))
			continue;

		switch (vp->magic) {
		case VRRP_RFCv3_SNMP_STATS_CHK_ERR:
			count += vrrp->stats->chk_err;
			break;
		case VRRP_RFCv3_SNMP_STATS_VER_ERR:
			count += vrrp->stats->vers_err;
			break;
		case VRRP_RFCv3_SNMP_STATS_VRID_ERR:
			count += vrrp->stats->vrid_err;
			break;
		}
	}

	set_counter64(&c64, count);
	return PTR_CAST(u_char, &c64);
}

static u_char*
vrrp_rfcv3_snmp_statstable(struct variable *vp, oid *name, size_t *length,
			int exact, size_t *var_len, WriteMethod **write_method)
{
	static uint32_t ret;
	static struct counter64 c64;
	vrrp_t *rt;

	if ((rt = snmp_rfcv3_header_list_table(vp, name, length, exact,
					       var_len, write_method)) == NULL)
		return NULL;

	*var_len = sizeof(c64);
	switch (vp->magic) {
	case VRRP_RFCv3_SNMP_STATS_MASTER:
		*var_len = sizeof(ret);
		ret = rt->stats->become_master;
		return PTR_CAST(u_char, &ret);
	case VRRP_RFCv3_SNMP_STATS_MASTER_REASON:
		ret = rt->stats->master_reason;
		*var_len = sizeof(ret);
		return PTR_CAST(u_char, &ret);
	case VRRP_RFCv3_SNMP_STATS_ADV_RCVD:
		set_counter64(&c64, rt->stats->advert_rcvd);
		return PTR_CAST(u_char, &c64);
	case VRRP_RFCv3_SNMP_STATS_ADV_INT_ERR:
		set_counter64(&c64, rt->stats->advert_interval_err);
		return PTR_CAST(u_char, &c64);
	case VRRP_RFCv3_SNMP_STATS_TTL_ERR:
		set_counter64(&c64, rt->stats->ip_ttl_err);
		return PTR_CAST(u_char, &c64);
	case VRRP_RFCv3_SNMP_STATS_PROTO_ERR_REASON:
		*var_len = sizeof(ret);
		ret = rt->stats->proto_err_reason;
		return PTR_CAST(u_char, &ret);
	case VRRP_RFCv3_SNMP_STATS_PRI_0_RCVD:
		set_counter64(&c64, rt->stats->pri_zero_rcvd);
		return PTR_CAST(u_char, &c64);
	case VRRP_RFCv3_SNMP_STATS_PRI_0_SENT:
		set_counter64(&c64, rt->stats->pri_zero_sent);
		return PTR_CAST(u_char, &c64);
	case VRRP_RFCv3_SNMP_STATS_INV_TYPE_RCVD:
		set_counter64(&c64, rt->stats->invalid_type_rcvd);
		return PTR_CAST(u_char, &c64);
	case VRRP_RFCv3_SNMP_STATS_ADDR_LIST_ERR:
		set_counter64(&c64, rt->stats->addr_list_err);
		return PTR_CAST(u_char, &c64);
	case VRRP_RFCv3_SNMP_STATS_PL_ERR:
		set_counter64(&c64, rt->stats->packet_len_err);
		return PTR_CAST(u_char, &c64);
	case VRRP_RFCv3_SNMP_STATS_ROW_DISC_TIME:
		// We don't "do" discontinuities
		*var_len = sizeof(ret);
		ret = 0;
		return PTR_CAST(u_char, &ret);
	case VRRP_RFCv3_SNMP_STATS_REFRESH_RATE:
		*var_len = sizeof(ret);
		ret = rt->adver_int / TIMER_CENTI_HZ * 10;	/* milliseconds */
		return PTR_CAST(u_char, &ret);
	}

	/* If we are here, we asked for a non existent data. Try the
	   next one. */
	if (!exact && (name[*length-1] < MAX_SUBID))
		return vrrp_rfcv3_snmp_statstable(vp, name, length,
					  exact, var_len, write_method);

	return NULL;
}

static oid vrrp_rfcv3_oid[] = {VRRP_RFCv3_OID};
static struct variable7 vrrp_rfcv3_vars[] = {
	/* vrrpOperTable */
	{ VRRP_RFCv3_SNMP_OPER_MIP, ASN_OCTET_STR, RONLY,
	  vrrp_rfcv3_snmp_opertable, 5, {1, 1, 1, 1, 3}},
	{ VRRP_RFCv3_SNMP_OPER_PIP, ASN_OCTET_STR, RONLY,
	  vrrp_rfcv3_snmp_opertable, 5, {1, 1, 1, 1, 4}},
	{ VRRP_RFCv3_SNMP_OPER_VMAC, ASN_OCTET_STR, RONLY,
	  vrrp_rfcv3_snmp_opertable, 5, {1, 1, 1, 1, 5}},
	{ VRRP_RFCv3_SNMP_OPER_STATE, ASN_INTEGER, RONLY,
	  vrrp_rfcv3_snmp_opertable, 5, {1, 1, 1, 1, 6}},
	{ VRRP_RFCv3_SNMP_OPER_PRI, ASN_UNSIGNED, RONLY,
	  vrrp_rfcv3_snmp_opertable, 5, {1, 1, 1, 1, 7}},
	{ VRRP_RFCv3_SNMP_OPER_ADDR_CNT, ASN_INTEGER, RONLY,
	  vrrp_rfcv3_snmp_opertable, 5, {1, 1, 1, 1, 8}},
	{ VRRP_RFCv3_SNMP_OPER_ADVERT_INT, ASN_INTEGER, RONLY,
	  vrrp_rfcv3_snmp_opertable, 5, {1, 1, 1, 1, 9}},
	{ VRRP_RFCv3_SNMP_OPER_PREEMPT, ASN_INTEGER, RONLY,
	  vrrp_rfcv3_snmp_opertable, 5, {1, 1, 1, 1, 10}},
	{ VRRP_RFCv3_SNMP_OPER_ACCEPT, ASN_INTEGER, RONLY,
	  vrrp_rfcv3_snmp_opertable, 5, {1, 1, 1, 1, 11}},
	{ VRRP_RFCv3_SNMP_OPER_VR_UPTIME, ASN_TIMETICKS, RONLY,
	  vrrp_rfcv3_snmp_opertable, 5, {1, 1, 1, 1, 12}},
	{ VRRP_RFCv3_SNMP_OPER_ROW_STATUS, ASN_INTEGER, RONLY,
	  vrrp_rfcv3_snmp_opertable, 5, {1, 1, 1, 1, 13}},
	/* vrrpAssoIpAddrTable */
	{ VRRP_RFCv3_SNMP_ASSOC_IP_ADDR_ROW_STATUS, ASN_INTEGER, RONLY,
	  vrrp_rfcv3_snmp_assoiptable, 5, {1, 1, 2, 1, 2}},
	/* vrrpRouterStats */
	{ VRRP_RFCv3_SNMP_STATS_CHK_ERR, ASN_COUNTER64, RONLY,
	  vrrp_rfcv3_snmp_stats, 3, {1, 2, 1}},
	{ VRRP_RFCv3_SNMP_STATS_VER_ERR, ASN_COUNTER64, RONLY,
	  vrrp_rfcv3_snmp_stats, 3, {1, 2, 2}},
	{ VRRP_RFCv3_SNMP_STATS_VRID_ERR, ASN_COUNTER64, RONLY,
	  vrrp_rfcv3_snmp_stats, 3, {1, 2, 3}},
	{ VRRP_RFCv3_SNMP_STATS_DISC_TIME, ASN_TIMETICKS, RONLY,
	  vrrp_rfcv3_snmp_stats, 3, {1, 2, 4}},
	/* vrrpRouterStatsTable */
	{ VRRP_RFCv3_SNMP_STATS_MASTER, ASN_COUNTER, RONLY,
	  vrrp_rfcv3_snmp_statstable, 5, {1, 2, 5, 1, 1}},
	{ VRRP_RFCv3_SNMP_STATS_MASTER_REASON, ASN_INTEGER, RONLY,
	  vrrp_rfcv3_snmp_statstable, 5, {1, 2, 5, 1, 2}},
	{ VRRP_RFCv3_SNMP_STATS_ADV_RCVD, ASN_COUNTER64, RONLY,
	  vrrp_rfcv3_snmp_statstable, 5, {1, 2, 5, 1, 3}},
	{ VRRP_RFCv3_SNMP_STATS_ADV_INT_ERR, ASN_COUNTER64, RONLY,
	  vrrp_rfcv3_snmp_statstable, 5, {1, 2, 5, 1, 4}},
	{ VRRP_RFCv3_SNMP_STATS_TTL_ERR, ASN_COUNTER64, RONLY,
	  vrrp_rfcv3_snmp_statstable, 5, {1, 2, 5, 1, 5}},
	{ VRRP_RFCv3_SNMP_STATS_PROTO_ERR_REASON, ASN_INTEGER, RONLY,
	  vrrp_rfcv3_snmp_statstable, 5, {1, 2, 5, 1, 6}},
	{ VRRP_RFCv3_SNMP_STATS_PRI_0_RCVD, ASN_COUNTER64, RONLY,
	  vrrp_rfcv3_snmp_statstable, 5, {1, 2, 5, 1, 7}},
	{ VRRP_RFCv3_SNMP_STATS_PRI_0_SENT, ASN_COUNTER64 , RONLY,
	  vrrp_rfcv3_snmp_statstable, 5, {1, 2, 5, 1, 8}},
	{ VRRP_RFCv3_SNMP_STATS_INV_TYPE_RCVD, ASN_COUNTER64, RONLY,
	  vrrp_rfcv3_snmp_statstable, 5, {1, 2, 5, 1, 9}},
	{ VRRP_RFCv3_SNMP_STATS_ADDR_LIST_ERR, ASN_COUNTER64, RONLY,
	  vrrp_rfcv3_snmp_statstable, 5, {1, 2, 5, 1, 10}},
	{ VRRP_RFCv3_SNMP_STATS_PL_ERR, ASN_COUNTER64, RONLY,
	  vrrp_rfcv3_snmp_statstable, 5, {1, 2, 5, 1, 11}},
	{ VRRP_RFCv3_SNMP_STATS_ROW_DISC_TIME, ASN_TIMETICKS, RONLY,
	  vrrp_rfcv3_snmp_statstable, 5, {1, 2, 5, 1, 12}},
	{ VRRP_RFCv3_SNMP_STATS_REFRESH_RATE, ASN_UNSIGNED, RONLY,
	  vrrp_rfcv3_snmp_statstable, 5, {1, 2, 5, 1, 13}}
};

void
vrrp_rfcv3_snmp_new_master_notify(vrrp_t *vrrp)
{
	/* OID of the notification vrrpTrapNewMaster */
	oid notification_oid[] = { VRRP_RFCv3_NOTIFY_OID, 1 };
	size_t notification_oid_len = OID_LENGTH(notification_oid);
	/* OID for snmpNotifyOID.0 */
	oid objid_snmptrap[] = { SNMPTRAP_OID };
	size_t objid_snmptrap_len = OID_LENGTH(objid_snmptrap);
	/* OID for trap data vrrpOperMasterIPAddr */
	oid masterip_oid[] = { VRRP_RFCv3_OID, 1, 1, 1, 1, 3, vrrp->ifp ? IF_BASE_INDEX(vrrp->ifp) : 0, vrrp->vrid, vrrp->family == AF_INET ? 1 : 2 };
	size_t masterip_oid_len = OID_LENGTH(masterip_oid);
	oid master_reason_oid[] = { VRRP_RFCv3_OID, 1, 2, 5, 1, 2, vrrp->ifp ? IF_BASE_INDEX(vrrp->ifp) : 0, vrrp->vrid, vrrp->family == AF_INET ? 1 : 2 };
	size_t master_reason_oid_len = OID_LENGTH(master_reason_oid);
	uint32_t reason = vrrp->stats->master_reason;

	netsnmp_variable_list *notification_vars = NULL;

	if (!global_data->enable_traps || !global_data->enable_snmp_rfcv3)
		return;

	if (!suitable_for_rfc6527(vrrp))
		return;

	/* snmpTrapOID */
	snmp_varlist_add_variable(&notification_vars,
				  objid_snmptrap, objid_snmptrap_len,
				  ASN_OBJECT_ID,
				  PTR_CAST(u_char, notification_oid),
				  notification_oid_len * sizeof(oid));
	/* vrrpInstanceName */
	if (vrrp->family == AF_INET)
		snmp_varlist_add_variable(&notification_vars,
					  masterip_oid, masterip_oid_len,
					  ASN_OCTET_STR,
					  PTR_CAST2(u_char, struct sockaddr_in, &vrrp->saddr, sin_addr.s_addr),
					  sizeof PTR_CAST(struct sockaddr_in, &vrrp->saddr)->sin_addr.s_addr);
	else
		snmp_varlist_add_variable(&notification_vars,
					  masterip_oid, masterip_oid_len,
					  ASN_OCTET_STR,
					  PTR_CAST2(u_char, struct sockaddr_in6, &vrrp->saddr, sin6_addr),
					  sizeof PTR_CAST(struct sockaddr_in6, &vrrp->saddr)->sin6_addr);

	snmp_varlist_add_variable(&notification_vars,
				  master_reason_oid, master_reason_oid_len,
				  ASN_INTEGER,
				  PTR_CAST(u_char, &reason),
				  sizeof(reason));
	if (__test_bit(LOG_DETAIL_BIT, &debug))
		log_message(LOG_INFO, "(%s) Sending SNMP notification"
				      " vrrpv3NotifyNewMaster, reason %" PRIu32
				    , vrrp->iname, reason);
	send_v2trap(notification_vars);
	snmp_free_varbind(notification_vars);
}

void
vrrp_rfcv3_snmp_proto_err_notify(vrrp_t *vrrp)
{
	/* OID of the notification vrrpTrapNewMaster */
	oid notification_oid[] = { VRRP_RFCv3_NOTIFY_OID, 2 };
	size_t notification_oid_len = OID_LENGTH(notification_oid);
	/* OID for snmpTrapOID.0 */
	oid objid_snmptrap[] = { SNMPTRAP_OID };
	size_t objid_snmptrap_len = OID_LENGTH(objid_snmptrap);
	/* OID for notify data vrrpTrapProtoErrorType */
	oid err_type_oid[] = { VRRP_RFCv3_OID, 1, 2, 5, 1, 6, vrrp->ifp ? IF_INDEX(vrrp->ifp) : 0, vrrp->vrid, vrrp->family == AF_INET ? 1 : 2 };
	size_t err_type_oid_len = OID_LENGTH(err_type_oid);

	netsnmp_variable_list *notification_vars = NULL;

	if (!global_data->enable_traps || !global_data->enable_snmp_rfcv3)
		return;

	if (!suitable_for_rfc6527(vrrp))
		return;

	/* snmpTrapOID */
	snmp_varlist_add_variable(&notification_vars,
				  objid_snmptrap, objid_snmptrap_len,
				  ASN_OBJECT_ID,
				  PTR_CAST(u_char, notification_oid),
				  notification_oid_len * sizeof(oid));
	/* vrrpProtoErrorType */
	snmp_varlist_add_variable(&notification_vars,
				  err_type_oid, err_type_oid_len,
				  ASN_INTEGER,
				  PTR_CAST(u_char, &vrrp->stats->proto_err_reason),
				  sizeof(vrrp->stats->proto_err_reason));
	if (__test_bit(LOG_DETAIL_BIT, &debug))
		log_message(LOG_INFO, "(%s) Sending SNMP notification"
				      " vrrpTrapProtoError"
				    , vrrp->iname);
	send_v2trap(notification_vars);
	snmp_free_varbind(notification_vars);
}
#endif

static bool
vrrp_handles_global_oid(const data_t *global_data_in_use)
{
#ifdef _WITH_SNMP_VRRP_
	if (global_data_in_use->enable_snmp_vrrp) {
#ifdef _WITH_LVS_
		if (!running_checker())
			return true;
#ifdef _WITH_SNMP_CHECKER_
		if (!global_data_in_use->enable_snmp_checker)
			return true;
#endif
#else
		return true;
#endif
	}
#endif

	return false;
}

void
vrrp_snmp_agent_init(const char *snmp_socket_name)
{
	if (snmp_running)
		return;

	/* We let the check process handle the global OID if it is running and with snmp */
	snmp_agent_init(snmp_socket_name, vrrp_handles_global_oid(global_data));

#ifdef _WITH_SNMP_VRRP_
	if (global_data->enable_snmp_vrrp)
		snmp_register_mib(vrrp_oid, OID_LENGTH(vrrp_oid), "KEEPALIVED-VRRP",
				  PTR_CAST(struct variable, vrrp_vars),
				  sizeof(vrrp_vars[0]),
				  sizeof(vrrp_vars)/sizeof(vrrp_vars[0]));
#endif
#ifdef _WITH_SNMP_RFCV2_
	if (global_data->enable_snmp_rfcv2)
		snmp_register_mib(vrrp_rfcv2_oid, OID_LENGTH(vrrp_rfcv2_oid), "VRRP",
				  PTR_CAST(struct variable, vrrp_rfcv2_vars),
				  sizeof(vrrp_rfcv2_vars[0]),
				  sizeof(vrrp_rfcv2_vars)/sizeof(vrrp_rfcv2_vars[0]));
#endif
#ifdef _WITH_SNMP_RFCV3_
	if (global_data->enable_snmp_rfcv3)
		snmp_register_mib(vrrp_rfcv3_oid, OID_LENGTH(vrrp_rfcv3_oid), "VRRPV3",
				  PTR_CAST(struct variable, vrrp_rfcv3_vars),
				  sizeof(vrrp_rfcv3_vars[0]),
				  sizeof(vrrp_rfcv3_vars)/sizeof(vrrp_rfcv3_vars[0]));
#endif
}

void
vrrp_snmp_agent_close(const data_t *global_data_in_use)
{
	if (!snmp_running)
		return;

#ifdef _WITH_SNMP_VRRP_
	if (global_data_in_use->enable_snmp_vrrp)
		snmp_unregister_mib(vrrp_oid, OID_LENGTH(vrrp_oid));
#endif
#ifdef _WITH_SNMP_RFCV2_
	if (global_data_in_use->enable_snmp_rfcv2)
		snmp_unregister_mib(vrrp_rfcv2_oid, OID_LENGTH(vrrp_rfcv2_oid));
#endif
#ifdef _WITH_SNMP_RFCV3_
	if (global_data_in_use->enable_snmp_rfcv3)
		snmp_unregister_mib(vrrp_rfcv3_oid, OID_LENGTH(vrrp_rfcv3_oid));
#endif
	snmp_agent_close(vrrp_handles_global_oid(global_data_in_use));
}
