// SPDX-License-Identifier: GPL-2.0-or-later /* RIP SNMP support * Copyright (C) 1999 Kunihiro Ishiguro */ #include #include #include #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, );