diff options
Diffstat (limited to 'ripd/rip_snmp.c')
-rw-r--r-- | ripd/rip_snmp.c | 577 |
1 files changed, 577 insertions, 0 deletions
diff --git a/ripd/rip_snmp.c b/ripd/rip_snmp.c new file mode 100644 index 0000000..0e5d4d5 --- /dev/null +++ b/ripd/rip_snmp.c @@ -0,0 +1,577 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* RIP SNMP support + * Copyright (C) 1999 Kunihiro Ishiguro <kunihiro@zebra.org> + */ + +#include <zebra.h> + +#include <net-snmp/net-snmp-config.h> +#include <net-snmp/net-snmp-includes.h> + +#include "if.h" +#include "vrf.h" +#include "log.h" +#include "prefix.h" +#include "command.h" +#include "table.h" +#include "smux.h" +#include "libfrr.h" +#include "lib/version.h" + +#include "ripd/ripd.h" + +/* RIPv2-MIB. */ +#define RIPV2MIB 1,3,6,1,2,1,23 + +/* 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. */ +static oid rip_oid[] = {RIPV2MIB}; + +/* Interface cache table sorted by interface's address. */ +static struct route_table *rip_ifaddr_table; + +/* Hook functions. */ +static uint8_t *rip2Globals(struct variable *, oid[], size_t *, int, size_t *, + WriteMethod **); +static uint8_t *rip2IfStatEntry(struct variable *, oid[], size_t *, int, + size_t *, WriteMethod **); +static uint8_t *rip2IfConfAddress(struct variable *, oid[], size_t *, int, + size_t *, WriteMethod **); +static uint8_t *rip2PeerTable(struct variable *, oid[], size_t *, int, size_t *, + WriteMethod **); + +static 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, STRING, 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}}}; + +extern struct event_loop *master; + +static uint8_t *rip2Globals(struct variable *v, oid name[], size_t *length, + int exact, size_t *var_len, + WriteMethod **write_method) +{ + struct rip *rip; + + if (smux_header_generic(v, name, length, exact, var_len, write_method) + == MATCH_FAILED) + return NULL; + + rip = rip_lookup_by_vrf_id(VRF_DEFAULT); + if (!rip) + return NULL; + + /* Return global counter. */ + switch (v->magic) { + case RIP2GLOBALROUTECHANGES: + return SNMP_INTEGER(rip->counters.route_changes); + case RIP2GLOBALQUERIES: + return SNMP_INTEGER(rip->counters.queries); + default: + return NULL; + } + return NULL; +} + +static int rip_snmp_ifaddr_add(struct connected *ifc) +{ + struct interface *ifp = ifc->ifp; + struct prefix *p; + struct route_node *rn; + + p = ifc->address; + + if (p->family != AF_INET) + return 0; + + rn = route_node_get(rip_ifaddr_table, p); + rn->info = ifp; + return 0; +} + +static int rip_snmp_ifaddr_del(struct connected *ifc) +{ + struct interface *ifp = ifc->ifp; + struct prefix *p; + struct route_node *rn; + struct interface *i; + + p = ifc->address; + + if (p->family != AF_INET) + return 0; + + rn = route_node_lookup(rip_ifaddr_table, p); + if (!rn) + return 0; + i = rn->info; + if (!strncmp(i->name, ifp->name, INTERFACE_NAMSIZ)) { + rn->info = NULL; + route_unlock_node(rn); + route_unlock_node(rn); + } + return 0; +} + +static 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_address_local((void *)addr, AF_INET, + VRF_DEFAULT); + } 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_in_addr(name + v->namelen, 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) +{ + struct rip *rip; + int len; + struct rip_peer *peer; + + rip = rip_lookup_by_vrf_id(VRF_DEFAULT); + if (!rip) + return NULL; + + 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(rip, addr); + + if (peer->domain + == (int)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(rip, addr); + if (peer) { + if ((len < (int)sizeof(struct in_addr) + 1) + || (peer->domain + > (int)name[v->namelen + + sizeof(struct in_addr)])) { + oid_copy_in_addr(name + v->namelen, + &peer->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(rip, addr); + + if (!peer) + return NULL; + + oid_copy_in_addr(name + v->namelen, &peer->addr); + name[v->namelen + sizeof(struct in_addr)] = peer->domain; + *length = sizeof(struct in_addr) + v->namelen + 1; + + return peer; + } + return NULL; +} + +static uint8_t *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; + + if (smux_header_table(v, name, length, exact, var_len, write_method) + == MATCH_FAILED) + return NULL; + + memset(&addr, 0, sizeof(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); + case RIP2IFSTATRCVBADPACKETS: + *var_len = sizeof(long); + return (uint8_t *)&ri->recv_badpackets; + + case RIP2IFSTATRCVBADROUTES: + *var_len = sizeof(long); + return (uint8_t *)&ri->recv_badroutes; + + case RIP2IFSTATSENTUPDATES: + *var_len = sizeof(long); + return (uint8_t *)&ri->sent_updates; + + case RIP2IFSTATSTATUS: + *var_len = sizeof(long); + v->type = ASN_INTEGER; + return (uint8_t *)&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 (ri->rip) { + if (ri->rip->version_send == RIPv2) + return ripVersion2; + else if (ri->rip->version_send == RIPv1) + return ripVersion1; + } + return doNotSend; +} + +static long rip2IfConfReceive(struct rip_interface *ri) +{ +#define rip1 1 +#define rip2 2 +#define rip1OrRip2 3 +#define doNotReceive 4 + + int recvv; + + if (!ri->running) + return doNotReceive; + + recvv = (ri->ri_receive == RI_RIP_UNSPEC) ? ri->rip->version_recv + : ri->ri_receive; + if (recvv == RI_RIP_VERSION_1_AND_2) + return rip1OrRip2; + else if (recvv & RIPv2) + return rip2; + else if (recvv & RIPv1) + return rip1; + else + return doNotReceive; +} + +static uint8_t *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 unsigned int auth = 0; + struct interface *ifp; + struct rip_interface *ri; + + if (smux_header_table(v, name, length, exact, val_len, write_method) + == MATCH_FAILED) + return NULL; + + memset(&addr, 0, sizeof(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 (uint8_t *)&addr; + + case RIP2IFCONFDOMAIN: + *val_len = 2; + return (uint8_t *)&domain; + + case RIP2IFCONFAUTHTYPE: + auth = ri->auth_type; + *val_len = sizeof(long); + v->type = ASN_INTEGER; + return (uint8_t *)&auth; + + case RIP2IFCONFAUTHKEY: + *val_len = 0; + return (uint8_t *)&domain; + case RIP2IFCONFSEND: + config = rip2IfConfSend(ri); + *val_len = sizeof(long); + v->type = ASN_INTEGER; + return (uint8_t *)&config; + case RIP2IFCONFRECEIVE: + config = rip2IfConfReceive(ri); + *val_len = sizeof(long); + v->type = ASN_INTEGER; + return (uint8_t *)&config; + + case RIP2IFCONFDEFAULTMETRIC: + *val_len = sizeof(long); + v->type = ASN_INTEGER; + return (uint8_t *)&ifp->metric; + case RIP2IFCONFSTATUS: + *val_len = sizeof(long); + v->type = ASN_INTEGER; + return (uint8_t *)&valid; + case RIP2IFCONFSRCADDRESS: + *val_len = sizeof(struct in_addr); + return (uint8_t *)&addr; + + default: + return NULL; + } + return NULL; +} + +static uint8_t *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 domain = 0; + static int version; + /* static time_t uptime; */ + + struct rip_peer *peer; + + if (smux_header_table(v, name, length, exact, val_len, write_method) + == MATCH_FAILED) + return NULL; + + memset(&addr, 0, sizeof(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 (uint8_t *)&peer->addr; + + case RIP2PEERDOMAIN: + *val_len = 2; + return (uint8_t *)&domain; + + case RIP2PEERLASTUPDATE: + return (uint8_t *)NULL; + + case RIP2PEERVERSION: + *val_len = sizeof(int); + version = peer->version; + return (uint8_t *)&version; + + case RIP2PEERRCVBADPACKETS: + *val_len = sizeof(int); + return (uint8_t *)&peer->recv_badpackets; + + case RIP2PEERRCVBADROUTES: + *val_len = sizeof(int); + return (uint8_t *)&peer->recv_badroutes; + + default: + return NULL; + } + return NULL; +} + +/* Register RIPv2-MIB. */ +static int rip_snmp_init(struct event_loop *master) +{ + rip_ifaddr_table = route_table_init(); + + smux_init(master); + REGISTER_MIB("mibII/rip", rip_variables, variable, rip_oid); + return 0; +} + +static int rip_snmp_module_init(void) +{ + hook_register(rip_ifaddr_add, rip_snmp_ifaddr_add); + hook_register(rip_ifaddr_del, rip_snmp_ifaddr_del); + + hook_register(frr_late_init, rip_snmp_init); + return 0; +} + +FRR_MODULE_SETUP(.name = "ripd_snmp", .version = FRR_VERSION, + .description = "ripd AgentX SNMP module", + .init = rip_snmp_module_init, +); |