Initial revision
diff --git a/ripd/rip_snmp.c b/ripd/rip_snmp.c
new file mode 100644
index 0000000..dc2b621
--- /dev/null
+++ b/ripd/rip_snmp.c
@@ -0,0 +1,577 @@
+/* RIP SNMP support
+ * Copyright (C) 1999 Kunihiro Ishiguro <kunihiro@zebra.org>
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#ifdef HAVE_SNMP
+#include <asn1.h>
+#include <snmp.h>
+#include <snmp_impl.h>
+
+#include "if.h"
+#include "log.h"
+#include "prefix.h"
+#include "command.h"
+#include "table.h"
+#include "smux.h"
+
+#include "ripd/ripd.h"
+
+/* RIPv2-MIB. */
+#define RIPV2MIB 1,3,6,1,2,1,23
+
+/* Zebra enterprise RIP MIB. This variable is used for register
+ RIPv2-MIB to SNMP agent under SMUX protocol. */
+#define RIPDOID 1,3,6,1,4,1,3317,1,2,3
+
+/* RIPv2-MIB rip2Globals values. */
+#define RIP2GLOBALROUTECHANGES 1
+#define RIP2GLOBALQUERIES 2
+
+/* RIPv2-MIB rip2IfStatEntry. */
+#define RIP2IFSTATENTRY 1
+
+/* RIPv2-MIB rip2IfStatTable. */
+#define RIP2IFSTATADDRESS 1
+#define RIP2IFSTATRCVBADPACKETS 2
+#define RIP2IFSTATRCVBADROUTES 3
+#define RIP2IFSTATSENTUPDATES 4
+#define RIP2IFSTATSTATUS 5
+
+/* RIPv2-MIB rip2IfConfTable. */
+#define RIP2IFCONFADDRESS 1
+#define RIP2IFCONFDOMAIN 2
+#define RIP2IFCONFAUTHTYPE 3
+#define RIP2IFCONFAUTHKEY 4
+#define RIP2IFCONFSEND 5
+#define RIP2IFCONFRECEIVE 6
+#define RIP2IFCONFDEFAULTMETRIC 7
+#define RIP2IFCONFSTATUS 8
+#define RIP2IFCONFSRCADDRESS 9
+
+/* RIPv2-MIB rip2PeerTable. */
+#define RIP2PEERADDRESS 1
+#define RIP2PEERDOMAIN 2
+#define RIP2PEERLASTUPDATE 3
+#define RIP2PEERVERSION 4
+#define RIP2PEERRCVBADPACKETS 5
+#define RIP2PEERRCVBADROUTES 6
+
+/* SNMP value hack. */
+#define COUNTER ASN_COUNTER
+#define INTEGER ASN_INTEGER
+#define TIMETICKS ASN_TIMETICKS
+#define IPADDRESS ASN_IPADDRESS
+#define STRING ASN_OCTET_STR
+
+/* Define SNMP local variables. */
+SNMP_LOCAL_VARIABLES
+
+/* RIP-MIB instances. */
+oid rip_oid [] = { RIPV2MIB };
+oid ripd_oid [] = { RIPDOID };
+
+/* Interface cache table sorted by interface's address. */
+struct route_table *rip_ifaddr_table;
+
+/* Hook functions. */
+static u_char *rip2Globals ();
+static u_char *rip2IfStatEntry ();
+static u_char *rip2IfConfAddress ();
+static u_char *rip2PeerTable ();
+
+struct variable rip_variables[] =
+{
+ /* RIP Global Counters. */
+ {RIP2GLOBALROUTECHANGES, COUNTER, RONLY, rip2Globals,
+ 2, {1, 1}},
+ {RIP2GLOBALQUERIES, COUNTER, RONLY, rip2Globals,
+ 2, {1, 2}},
+ /* RIP Interface Tables. */
+ {RIP2IFSTATADDRESS, IPADDRESS, RONLY, rip2IfStatEntry,
+ 3, {2, 1, 1}},
+ {RIP2IFSTATRCVBADPACKETS, COUNTER, RONLY, rip2IfStatEntry,
+ 3, {2, 1, 2}},
+ {RIP2IFSTATRCVBADROUTES, COUNTER, RONLY, rip2IfStatEntry,
+ 3, {2, 1, 3}},
+ {RIP2IFSTATSENTUPDATES, COUNTER, RONLY, rip2IfStatEntry,
+ 3, {2, 1, 4}},
+ {RIP2IFSTATSTATUS, COUNTER, RWRITE, rip2IfStatEntry,
+ 3, {2, 1, 5}},
+ {RIP2IFCONFADDRESS, IPADDRESS, RONLY, rip2IfConfAddress,
+ /* RIP Interface Configuration Table. */
+ 3, {3, 1, 1}},
+ {RIP2IFCONFDOMAIN, STRING, RONLY, rip2IfConfAddress,
+ 3, {3, 1, 2}},
+ {RIP2IFCONFAUTHTYPE, COUNTER, RONLY, rip2IfConfAddress,
+ 3, {3, 1, 3}},
+ {RIP2IFCONFAUTHKEY, STRING, RONLY, rip2IfConfAddress,
+ 3, {3, 1, 4}},
+ {RIP2IFCONFSEND, COUNTER, RONLY, rip2IfConfAddress,
+ 3, {3, 1, 5}},
+ {RIP2IFCONFRECEIVE, COUNTER, RONLY, rip2IfConfAddress,
+ 3, {3, 1, 6}},
+ {RIP2IFCONFDEFAULTMETRIC, COUNTER, RONLY, rip2IfConfAddress,
+ 3, {3, 1, 7}},
+ {RIP2IFCONFSTATUS, COUNTER, RONLY, rip2IfConfAddress,
+ 3, {3, 1, 8}},
+ {RIP2IFCONFSRCADDRESS, IPADDRESS, RONLY, rip2IfConfAddress,
+ 3, {3, 1, 9}},
+ {RIP2PEERADDRESS, IPADDRESS, RONLY, rip2PeerTable,
+ /* RIP Peer Table. */
+ 3, {4, 1, 1}},
+ {RIP2PEERDOMAIN, INTEGER, RONLY, rip2PeerTable,
+ 3, {4, 1, 2}},
+ {RIP2PEERLASTUPDATE, TIMETICKS, RONLY, rip2PeerTable,
+ 3, {4, 1, 3}},
+ {RIP2PEERVERSION, INTEGER, RONLY, rip2PeerTable,
+ 3, {4, 1, 4}},
+ {RIP2PEERRCVBADPACKETS, COUNTER, RONLY, rip2PeerTable,
+ 3, {4, 1, 5}},
+ {RIP2PEERRCVBADROUTES, COUNTER, RONLY, rip2PeerTable,
+ 3, {4, 1, 6}}
+};
+
+static u_char *
+rip2Globals (struct variable *v, oid name[], size_t *length,
+ int exact, size_t *var_len, WriteMethod **write_method)
+{
+ if (smux_header_generic(v, name, length, exact, var_len, write_method)
+ == MATCH_FAILED)
+ return NULL;
+
+ /* Retrun global counter. */
+ switch (v->magic)
+ {
+ case RIP2GLOBALROUTECHANGES:
+ return SNMP_INTEGER (rip_global_route_changes);
+ break;
+ case RIP2GLOBALQUERIES:
+ return SNMP_INTEGER (rip_global_queries);
+ break;
+ default:
+ return NULL;
+ break;
+ }
+ return NULL;
+}
+
+void
+rip_ifaddr_add (struct interface *ifp, struct connected *ifc)
+{
+ struct prefix *p;
+ struct route_node *rn;
+
+ p = ifc->address;
+
+ if (p->family != AF_INET)
+ return;
+
+ rn = route_node_get (rip_ifaddr_table, p);
+ rn->info = ifp;
+}
+
+void
+rip_ifaddr_delete (struct interface *ifp, struct connected *ifc)
+{
+ struct prefix *p;
+ struct route_node *rn;
+ struct interface *i;
+
+ p = ifc->address;
+
+ if (p->family != AF_INET)
+ return;
+
+ rn = route_node_lookup (rip_ifaddr_table, p);
+ if (! rn)
+ return;
+ i = rn->info;
+ if (rn && !strncmp(i->name,ifp->name,INTERFACE_NAMSIZ))
+ {
+ rn->info = NULL;
+ route_unlock_node (rn);
+ route_unlock_node (rn);
+ }
+}
+
+struct interface *
+rip_ifaddr_lookup_next (struct in_addr *addr)
+{
+ struct prefix_ipv4 p;
+ struct route_node *rn;
+ struct interface *ifp;
+
+ p.family = AF_INET;
+ p.prefixlen = IPV4_MAX_BITLEN;
+ p.prefix = *addr;
+
+ rn = route_node_get (rip_ifaddr_table, (struct prefix *) &p);
+
+ for (rn = route_next (rn); rn; rn = route_next (rn))
+ if (rn->info)
+ break;
+
+ if (rn && rn->info)
+ {
+ ifp = rn->info;
+ *addr = rn->p.u.prefix4;
+ route_unlock_node (rn);
+ return ifp;
+ }
+ return NULL;
+}
+
+static struct interface *
+rip2IfLookup (struct variable *v, oid name[], size_t *length,
+ struct in_addr *addr, int exact)
+{
+ int len;
+ struct interface *ifp;
+
+ if (exact)
+ {
+ /* Check the length. */
+ if (*length - v->namelen != sizeof (struct in_addr))
+ return NULL;
+
+ oid2in_addr (name + v->namelen, sizeof (struct in_addr), addr);
+
+ return if_lookup_exact_address (*addr);
+ }
+ else
+ {
+ len = *length - v->namelen;
+ if (len > 4) len = 4;
+
+ oid2in_addr (name + v->namelen, len, addr);
+
+ ifp = rip_ifaddr_lookup_next (addr);
+
+ if (ifp == NULL)
+ return NULL;
+
+ oid_copy_addr (name + v->namelen, addr, sizeof (struct in_addr));
+
+ *length = v->namelen + sizeof (struct in_addr);
+
+ return ifp;
+ }
+ return NULL;
+}
+
+static struct rip_peer *
+rip2PeerLookup (struct variable *v, oid name[], size_t *length,
+ struct in_addr *addr, int exact)
+{
+ int len;
+ struct rip_peer *peer;
+
+ if (exact)
+ {
+ /* Check the length. */
+ if (*length - v->namelen != sizeof (struct in_addr) + 1)
+ return NULL;
+
+ oid2in_addr (name + v->namelen, sizeof (struct in_addr), addr);
+
+ peer = rip_peer_lookup (addr);
+
+ if (peer->domain == name[v->namelen + sizeof (struct in_addr)])
+ return peer;
+
+ return NULL;
+ }
+ else
+ {
+ len = *length - v->namelen;
+ if (len > 4) len = 4;
+
+ oid2in_addr (name + v->namelen, len, addr);
+
+ len = *length - v->namelen;
+ peer = rip_peer_lookup (addr);
+ if (peer)
+ {
+ if ((len < sizeof (struct in_addr) + 1) ||
+ (peer->domain > name[v->namelen + sizeof (struct in_addr)]))
+ {
+ oid_copy_addr (name + v->namelen, &peer->addr,
+ sizeof (struct in_addr));
+ name[v->namelen + sizeof (struct in_addr)] = peer->domain;
+ *length = sizeof (struct in_addr) + v->namelen + 1;
+ return peer;
+ }
+ }
+ peer = rip_peer_lookup_next (addr);
+
+ if (! peer)
+ return NULL;
+
+ oid_copy_addr (name + v->namelen, &peer->addr,
+ sizeof (struct in_addr));
+ name[v->namelen + sizeof (struct in_addr)] = peer->domain;
+ *length = sizeof (struct in_addr) + v->namelen + 1;
+
+ return peer;
+ }
+ return NULL;
+}
+
+static u_char *
+rip2IfStatEntry (struct variable *v, oid name[], size_t *length,
+ int exact, size_t *var_len, WriteMethod **write_method)
+{
+ struct interface *ifp;
+ struct rip_interface *ri;
+ static struct in_addr addr;
+ static long valid = SNMP_VALID;
+
+ memset (&addr, 0, sizeof (struct in_addr));
+
+ /* Lookup interface. */
+ ifp = rip2IfLookup (v, name, length, &addr, exact);
+ if (! ifp)
+ return NULL;
+
+ /* Fetch rip_interface information. */
+ ri = ifp->info;
+
+ switch (v->magic)
+ {
+ case RIP2IFSTATADDRESS:
+ return SNMP_IPADDRESS (addr);
+ break;
+ case RIP2IFSTATRCVBADPACKETS:
+ *var_len = sizeof (long);
+ return (u_char *) &ri->recv_badpackets;
+
+ case RIP2IFSTATRCVBADROUTES:
+ *var_len = sizeof (long);
+ return (u_char *) &ri->recv_badroutes;
+
+ case RIP2IFSTATSENTUPDATES:
+ *var_len = sizeof (long);
+ return (u_char *) &ri->sent_updates;
+
+ case RIP2IFSTATSTATUS:
+ *var_len = sizeof (long);
+ v->type = ASN_INTEGER;
+ return (u_char *) &valid;
+
+ default:
+ return NULL;
+
+ }
+ return NULL;
+}
+
+static long
+rip2IfConfSend (struct rip_interface *ri)
+{
+#define doNotSend 1
+#define ripVersion1 2
+#define rip1Compatible 3
+#define ripVersion2 4
+#define ripV1Demand 5
+#define ripV2Demand 6
+
+ if (! ri->running)
+ return doNotSend;
+
+ if (ri->ri_send & RIPv2)
+ return ripVersion2;
+ else if (ri->ri_send & RIPv1)
+ return ripVersion1;
+ else if (rip)
+ {
+ if (rip->version == RIPv2)
+ return ripVersion2;
+ else if (rip->version == RIPv1)
+ return ripVersion1;
+ }
+ return doNotSend;
+}
+
+static long
+rip2IfConfReceive (struct rip_interface *ri)
+{
+#define rip1 1
+#define rip2 2
+#define rip1OrRip2 3
+#define doNotReceive 4
+
+ if (! ri->running)
+ return doNotReceive;
+
+ if (ri->ri_receive == RI_RIP_VERSION_1_AND_2)
+ return rip1OrRip2;
+ else if (ri->ri_receive & RIPv2)
+ return ripVersion2;
+ else if (ri->ri_receive & RIPv1)
+ return ripVersion1;
+ else
+ return doNotReceive;
+}
+
+static u_char *
+rip2IfConfAddress (struct variable *v, oid name[], size_t *length,
+ int exact, size_t *val_len, WriteMethod **write_method)
+{
+ static struct in_addr addr;
+ static long valid = SNMP_INVALID;
+ static long domain = 0;
+ static long config = 0;
+ static u_int auth = 0;
+ struct interface *ifp;
+ struct rip_interface *ri;
+
+ memset (&addr, 0, sizeof (struct in_addr));
+
+ /* Lookup interface. */
+ ifp = rip2IfLookup (v, name, length, &addr, exact);
+ if (! ifp)
+ return NULL;
+
+ /* Fetch rip_interface information. */
+ ri = ifp->info;
+
+ switch (v->magic)
+ {
+ case RIP2IFCONFADDRESS:
+ *val_len = sizeof (struct in_addr);
+ return (u_char *) &addr;
+
+ case RIP2IFCONFDOMAIN:
+ *val_len = 2;
+ return (u_char *) &domain;
+
+ case RIP2IFCONFAUTHTYPE:
+ auth = ri->auth_type;
+ *val_len = sizeof (long);
+ v->type = ASN_INTEGER;
+ return (u_char *)&auth;
+
+ case RIP2IFCONFAUTHKEY:
+ *val_len = 0;
+ return (u_char *) &domain;
+ case RIP2IFCONFSEND:
+ config = rip2IfConfSend (ri);
+ *val_len = sizeof (long);
+ v->type = ASN_INTEGER;
+ return (u_char *) &config;
+ case RIP2IFCONFRECEIVE:
+ config = rip2IfConfReceive (ri);
+ *val_len = sizeof (long);
+ v->type = ASN_INTEGER;
+ return (u_char *) &config;
+
+ case RIP2IFCONFDEFAULTMETRIC:
+ *val_len = sizeof (long);
+ v->type = ASN_INTEGER;
+ return (u_char *) &ifp->metric;
+ case RIP2IFCONFSTATUS:
+ *val_len = sizeof (long);
+ v->type = ASN_INTEGER;
+ return (u_char *) &valid;
+ case RIP2IFCONFSRCADDRESS:
+ *val_len = sizeof (struct in_addr);
+ return (u_char *) &addr;
+
+ default:
+ return NULL;
+
+ }
+ return NULL;
+}
+
+static u_char *
+rip2PeerTable (struct variable *v, oid name[], size_t *length,
+ int exact, size_t *val_len, WriteMethod **write_method)
+{
+ static struct in_addr addr;
+ static int version;
+ /* static time_t uptime; */
+
+ struct rip_peer *peer;
+
+ memset (&addr, 0, sizeof (struct in_addr));
+
+ /* Lookup interface. */
+ peer = rip2PeerLookup (v, name, length, &addr, exact);
+ if (! peer)
+ return NULL;
+
+ switch (v->magic)
+ {
+ case RIP2PEERADDRESS:
+ *val_len = sizeof (struct in_addr);
+ return (u_char *) &peer->addr;
+
+ case RIP2PEERDOMAIN:
+ *val_len = sizeof (int);
+ return (u_char *) &peer->domain;
+
+ case RIP2PEERLASTUPDATE:
+#if 0
+ /* We don't know the SNMP agent startup time. We have two choices here:
+ * - assume ripd startup time equals SNMP agent startup time
+ * - don't support this variable, at all
+ * Currently, we do the latter...
+ */
+ *val_len = sizeof (time_t);
+ uptime = peer->uptime; /* now - snmp_agent_startup - peer->uptime */
+ return (u_char *) &uptime;
+#else
+ return (u_char *) NULL;
+#endif
+
+ case RIP2PEERVERSION:
+ *val_len = sizeof (int);
+ version = peer->version;
+ return (u_char *) &version;
+
+ case RIP2PEERRCVBADPACKETS:
+ *val_len = sizeof (int);
+ return (u_char *) &peer->recv_badpackets;
+
+ case RIP2PEERRCVBADROUTES:
+ *val_len = sizeof (int);
+ return (u_char *) &peer->recv_badroutes;
+
+ default:
+ return NULL;
+
+ }
+ return NULL;
+}
+
+/* Register RIPv2-MIB. */
+void
+rip_snmp_init ()
+{
+ rip_ifaddr_table = route_table_init ();
+
+ smux_init (ripd_oid, sizeof (ripd_oid) / sizeof (oid));
+ REGISTER_MIB("mibII/rip", rip_variables, variable, rip_oid);
+ smux_start ();
+}
+#endif /* HAVE_SNMP */