1/* RIP SNMP support
2 * Copyright (C) 1999 Kunihiro Ishiguro <kunihiro@zebra.org>
3 *
4 * This file is part of GNU Zebra.
5 *
6 * GNU Zebra is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2, or (at your option) any
9 * later version.
10 *
11 * GNU Zebra is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with GNU Zebra; see the file COPYING.  If not, write to the Free
18 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19 * 02111-1307, USA.
20 */
21
22#include <zebra.h>
23
24#ifdef HAVE_SNMP
25#include <asn1.h>
26#include <snmp.h>
27#include <snmp_impl.h>
28
29#include "if.h"
30#include "log.h"
31#include "prefix.h"
32#include "command.h"
33#include "table.h"
34#include "smux.h"
35
36#include "ripd/ripd.h"
37
38/* RIPv2-MIB. */
39#define RIPV2MIB 1,3,6,1,2,1,23
40
41/* Zebra enterprise RIP MIB.  This variable is used for register
42   RIPv2-MIB to SNMP agent under SMUX protocol.  */
43#define RIPDOID 1,3,6,1,4,1,3317,1,2,3
44
45/* RIPv2-MIB rip2Globals values. */
46#define RIP2GLOBALROUTECHANGES  1
47#define RIP2GLOBALQUERIES       2
48
49/* RIPv2-MIB rip2IfStatEntry. */
50#define RIP2IFSTATENTRY         1
51
52/* RIPv2-MIB rip2IfStatTable. */
53#define RIP2IFSTATADDRESS       1
54#define RIP2IFSTATRCVBADPACKETS 2
55#define RIP2IFSTATRCVBADROUTES  3
56#define RIP2IFSTATSENTUPDATES   4
57#define RIP2IFSTATSTATUS        5
58
59/* RIPv2-MIB rip2IfConfTable. */
60#define RIP2IFCONFADDRESS       1
61#define RIP2IFCONFDOMAIN        2
62#define RIP2IFCONFAUTHTYPE      3
63#define RIP2IFCONFAUTHKEY       4
64#define RIP2IFCONFSEND          5
65#define RIP2IFCONFRECEIVE       6
66#define RIP2IFCONFDEFAULTMETRIC 7
67#define RIP2IFCONFSTATUS        8
68#define RIP2IFCONFSRCADDRESS    9
69
70/* RIPv2-MIB rip2PeerTable. */
71#define RIP2PEERADDRESS         1
72#define RIP2PEERDOMAIN          2
73#define RIP2PEERLASTUPDATE      3
74#define RIP2PEERVERSION         4
75#define RIP2PEERRCVBADPACKETS   5
76#define RIP2PEERRCVBADROUTES    6
77
78/* SNMP value hack. */
79#define COUNTER     ASN_COUNTER
80#define INTEGER     ASN_INTEGER
81#define TIMETICKS   ASN_TIMETICKS
82#define IPADDRESS   ASN_IPADDRESS
83#define STRING      ASN_OCTET_STR
84
85/* Define SNMP local variables. */
86SNMP_LOCAL_VARIABLES
87
88/* RIP-MIB instances. */
89oid rip_oid [] = { RIPV2MIB };
90oid ripd_oid [] = { RIPDOID };
91
92/* Interface cache table sorted by interface's address. */
93struct route_table *rip_ifaddr_table;
94
95/* Hook functions. */
96static u_char *rip2Globals ();
97static u_char *rip2IfStatEntry ();
98static u_char *rip2IfConfAddress ();
99static u_char *rip2PeerTable ();
100
101struct variable rip_variables[] =
102{
103  /* RIP Global Counters. */
104  {RIP2GLOBALROUTECHANGES,    COUNTER, RONLY, rip2Globals,
105   2, {1, 1}},
106  {RIP2GLOBALQUERIES,         COUNTER, RONLY, rip2Globals,
107   2, {1, 2}},
108  /* RIP Interface Tables. */
109  {RIP2IFSTATADDRESS,         IPADDRESS, RONLY, rip2IfStatEntry,
110   3, {2, 1, 1}},
111  {RIP2IFSTATRCVBADPACKETS,   COUNTER, RONLY, rip2IfStatEntry,
112   3, {2, 1, 2}},
113  {RIP2IFSTATRCVBADROUTES,    COUNTER, RONLY, rip2IfStatEntry,
114   3, {2, 1, 3}},
115  {RIP2IFSTATSENTUPDATES,     COUNTER, RONLY, rip2IfStatEntry,
116   3, {2, 1, 4}},
117  {RIP2IFSTATSTATUS,          COUNTER, RWRITE, rip2IfStatEntry,
118   3, {2, 1, 5}},
119  {RIP2IFCONFADDRESS,         IPADDRESS, RONLY, rip2IfConfAddress,
120   /* RIP Interface Configuration Table. */
121   3, {3, 1, 1}},
122  {RIP2IFCONFDOMAIN,          STRING, RONLY, rip2IfConfAddress,
123   3, {3, 1, 2}},
124  {RIP2IFCONFAUTHTYPE,        COUNTER, RONLY, rip2IfConfAddress,
125   3, {3, 1, 3}},
126  {RIP2IFCONFAUTHKEY,         STRING, RONLY, rip2IfConfAddress,
127   3, {3, 1, 4}},
128  {RIP2IFCONFSEND,            COUNTER, RONLY, rip2IfConfAddress,
129   3, {3, 1, 5}},
130  {RIP2IFCONFRECEIVE,         COUNTER, RONLY, rip2IfConfAddress,
131   3, {3, 1, 6}},
132  {RIP2IFCONFDEFAULTMETRIC,   COUNTER, RONLY, rip2IfConfAddress,
133   3, {3, 1, 7}},
134  {RIP2IFCONFSTATUS,          COUNTER, RONLY, rip2IfConfAddress,
135   3, {3, 1, 8}},
136  {RIP2IFCONFSRCADDRESS,      IPADDRESS, RONLY, rip2IfConfAddress,
137   3, {3, 1, 9}},
138  {RIP2PEERADDRESS,           IPADDRESS, RONLY, rip2PeerTable,
139   /* RIP Peer Table. */
140   3, {4, 1, 1}},
141  {RIP2PEERDOMAIN,            INTEGER, RONLY, rip2PeerTable,
142   3, {4, 1, 2}},
143  {RIP2PEERLASTUPDATE,        TIMETICKS, RONLY, rip2PeerTable,
144   3, {4, 1, 3}},
145  {RIP2PEERVERSION,           INTEGER, RONLY, rip2PeerTable,
146   3, {4, 1, 4}},
147  {RIP2PEERRCVBADPACKETS,     COUNTER, RONLY, rip2PeerTable,
148   3, {4, 1, 5}},
149  {RIP2PEERRCVBADROUTES,      COUNTER, RONLY, rip2PeerTable,
150   3, {4, 1, 6}}
151};
152
153static u_char *
154rip2Globals (struct variable *v, oid name[], size_t *length,
155	     int exact, size_t *var_len, WriteMethod **write_method)
156{
157  if (smux_header_generic(v, name, length, exact, var_len, write_method)
158      == MATCH_FAILED)
159    return NULL;
160
161  /* Retrun global counter. */
162  switch (v->magic)
163    {
164    case RIP2GLOBALROUTECHANGES:
165      return SNMP_INTEGER (rip_global_route_changes);
166      break;
167    case RIP2GLOBALQUERIES:
168      return SNMP_INTEGER (rip_global_queries);
169      break;
170    default:
171      return NULL;
172      break;
173    }
174  return NULL;
175}
176
177void
178rip_ifaddr_add (struct interface *ifp, struct connected *ifc)
179{
180  struct prefix *p;
181  struct route_node *rn;
182
183  p = ifc->address;
184
185  if (p->family != AF_INET)
186    return;
187
188  rn = route_node_get (rip_ifaddr_table, p);
189  rn->info = ifp;
190}
191
192void
193rip_ifaddr_delete (struct interface *ifp, struct connected *ifc)
194{
195  struct prefix *p;
196  struct route_node *rn;
197  struct interface *i;
198
199  p = ifc->address;
200
201  if (p->family != AF_INET)
202    return;
203
204  rn = route_node_lookup (rip_ifaddr_table, p);
205  if (! rn)
206    return;
207  i = rn->info;
208  if (rn && !strncmp(i->name,ifp->name,INTERFACE_NAMSIZ))
209    {
210      rn->info = NULL;
211      route_unlock_node (rn);
212      route_unlock_node (rn);
213    }
214}
215
216struct interface *
217rip_ifaddr_lookup_next (struct in_addr *addr)
218{
219  struct prefix_ipv4 p;
220  struct route_node *rn;
221  struct interface *ifp;
222
223  p.family = AF_INET;
224  p.prefixlen = IPV4_MAX_BITLEN;
225  p.prefix = *addr;
226
227  rn = route_node_get (rip_ifaddr_table, (struct prefix *) &p);
228
229  for (rn = route_next (rn); rn; rn = route_next (rn))
230    if (rn->info)
231      break;
232
233  if (rn && rn->info)
234    {
235      ifp = rn->info;
236      *addr = rn->p.u.prefix4;
237      route_unlock_node (rn);
238      return ifp;
239    }
240  return NULL;
241}
242
243static struct interface *
244rip2IfLookup (struct variable *v, oid name[], size_t *length,
245	      struct in_addr *addr, int exact)
246{
247  int len;
248  struct interface *ifp;
249
250  if (exact)
251    {
252      /* Check the length. */
253      if (*length - v->namelen != sizeof (struct in_addr))
254	return NULL;
255
256      oid2in_addr (name + v->namelen, sizeof (struct in_addr), addr);
257
258      return if_lookup_exact_address (*addr);
259    }
260  else
261    {
262      len = *length - v->namelen;
263      if (len > 4) len = 4;
264
265      oid2in_addr (name + v->namelen, len, addr);
266
267      ifp = rip_ifaddr_lookup_next (addr);
268
269      if (ifp == NULL)
270	return NULL;
271
272      oid_copy_addr (name + v->namelen, addr, sizeof (struct in_addr));
273
274      *length = v->namelen + sizeof (struct in_addr);
275
276      return ifp;
277    }
278  return NULL;
279}
280
281static struct rip_peer *
282rip2PeerLookup (struct variable *v, oid name[], size_t *length,
283		struct in_addr *addr, int exact)
284{
285  int len;
286  struct rip_peer *peer;
287
288  if (exact)
289    {
290      /* Check the length. */
291      if (*length - v->namelen != sizeof (struct in_addr) + 1)
292	return NULL;
293
294      oid2in_addr (name + v->namelen, sizeof (struct in_addr), addr);
295
296      peer = rip_peer_lookup (addr);
297
298      if (peer->domain == name[v->namelen + sizeof (struct in_addr)])
299	return peer;
300
301      return NULL;
302    }
303  else
304    {
305      len = *length - v->namelen;
306      if (len > 4) len = 4;
307
308      oid2in_addr (name + v->namelen, len, addr);
309
310      len = *length - v->namelen;
311      peer = rip_peer_lookup (addr);
312      if (peer)
313	{
314	  if ((len < sizeof (struct in_addr) + 1) ||
315	      (peer->domain > name[v->namelen + sizeof (struct in_addr)]))
316	    {
317	      oid_copy_addr (name + v->namelen, &peer->addr,
318			     sizeof (struct in_addr));
319	      name[v->namelen + sizeof (struct in_addr)] = peer->domain;
320	      *length = sizeof (struct in_addr) + v->namelen + 1;
321	      return peer;
322	    }
323        }
324      peer = rip_peer_lookup_next (addr);
325
326      if (! peer)
327	return NULL;
328
329      oid_copy_addr (name + v->namelen, &peer->addr,
330		     sizeof (struct in_addr));
331      name[v->namelen + sizeof (struct in_addr)] = peer->domain;
332      *length = sizeof (struct in_addr) + v->namelen + 1;
333
334      return peer;
335    }
336  return NULL;
337}
338
339static u_char *
340rip2IfStatEntry (struct variable *v, oid name[], size_t *length,
341	         int exact, size_t *var_len, WriteMethod **write_method)
342{
343  struct interface *ifp;
344  struct rip_interface *ri;
345  static struct in_addr addr;
346  static long valid = SNMP_VALID;
347
348  memset (&addr, 0, sizeof (struct in_addr));
349
350  /* Lookup interface. */
351  ifp = rip2IfLookup (v, name, length, &addr, exact);
352  if (! ifp)
353    return NULL;
354
355  /* Fetch rip_interface information. */
356  ri = ifp->info;
357
358  switch (v->magic)
359    {
360    case RIP2IFSTATADDRESS:
361      return SNMP_IPADDRESS (addr);
362      break;
363    case RIP2IFSTATRCVBADPACKETS:
364      *var_len = sizeof (long);
365      return (u_char *) &ri->recv_badpackets;
366
367    case RIP2IFSTATRCVBADROUTES:
368      *var_len = sizeof (long);
369      return (u_char *) &ri->recv_badroutes;
370
371    case RIP2IFSTATSENTUPDATES:
372      *var_len = sizeof (long);
373      return (u_char *) &ri->sent_updates;
374
375    case RIP2IFSTATSTATUS:
376      *var_len = sizeof (long);
377      v->type = ASN_INTEGER;
378      return (u_char *) &valid;
379
380    default:
381      return NULL;
382
383    }
384  return NULL;
385}
386
387static long
388rip2IfConfSend (struct rip_interface *ri)
389{
390#define doNotSend       1
391#define ripVersion1     2
392#define rip1Compatible  3
393#define ripVersion2     4
394#define ripV1Demand     5
395#define ripV2Demand     6
396
397  if (! ri->running)
398    return doNotSend;
399
400  if (ri->ri_send & RIPv2)
401    return ripVersion2;
402  else if (ri->ri_send & RIPv1)
403    return ripVersion1;
404  else if (rip)
405    {
406      if (rip->version == RIPv2)
407	return ripVersion2;
408      else if (rip->version == RIPv1)
409	return ripVersion1;
410    }
411  return doNotSend;
412}
413
414static long
415rip2IfConfReceive (struct rip_interface *ri)
416{
417#define rip1            1
418#define rip2            2
419#define rip1OrRip2      3
420#define doNotReceive    4
421
422  if (! ri->running)
423    return doNotReceive;
424
425  if (ri->ri_receive == RI_RIP_VERSION_1_AND_2)
426    return rip1OrRip2;
427  else if (ri->ri_receive & RIPv2)
428    return ripVersion2;
429  else if (ri->ri_receive & RIPv1)
430    return ripVersion1;
431  else
432    return doNotReceive;
433}
434
435static u_char *
436rip2IfConfAddress (struct variable *v, oid name[], size_t *length,
437	           int exact, size_t *val_len, WriteMethod **write_method)
438{
439  static struct in_addr addr;
440  static long valid = SNMP_INVALID;
441  static long domain = 0;
442  static long config = 0;
443  static u_int auth = 0;
444  struct interface *ifp;
445  struct rip_interface *ri;
446
447  memset (&addr, 0, sizeof (struct in_addr));
448
449  /* Lookup interface. */
450  ifp = rip2IfLookup (v, name, length, &addr, exact);
451  if (! ifp)
452    return NULL;
453
454  /* Fetch rip_interface information. */
455  ri = ifp->info;
456
457  switch (v->magic)
458    {
459    case RIP2IFCONFADDRESS:
460      *val_len = sizeof (struct in_addr);
461      return (u_char *) &addr;
462
463    case RIP2IFCONFDOMAIN:
464      *val_len = 2;
465      return (u_char *) &domain;
466
467    case RIP2IFCONFAUTHTYPE:
468      auth = ri->auth_type;
469      *val_len = sizeof (long);
470      v->type = ASN_INTEGER;
471      return (u_char *)&auth;
472
473    case RIP2IFCONFAUTHKEY:
474      *val_len = 0;
475      return (u_char *) &domain;
476    case RIP2IFCONFSEND:
477      config = rip2IfConfSend (ri);
478      *val_len = sizeof (long);
479      v->type = ASN_INTEGER;
480      return (u_char *) &config;
481    case RIP2IFCONFRECEIVE:
482      config = rip2IfConfReceive (ri);
483      *val_len = sizeof (long);
484      v->type = ASN_INTEGER;
485      return (u_char *) &config;
486
487    case RIP2IFCONFDEFAULTMETRIC:
488      *val_len = sizeof (long);
489      v->type = ASN_INTEGER;
490      return (u_char *) &ifp->metric;
491    case RIP2IFCONFSTATUS:
492      *val_len = sizeof (long);
493      v->type = ASN_INTEGER;
494      return (u_char *) &valid;
495    case RIP2IFCONFSRCADDRESS:
496      *val_len = sizeof (struct in_addr);
497      return (u_char *) &addr;
498
499    default:
500      return NULL;
501
502    }
503  return NULL;
504}
505
506static u_char *
507rip2PeerTable (struct variable *v, oid name[], size_t *length,
508	       int exact, size_t *val_len, WriteMethod **write_method)
509{
510  static struct in_addr addr;
511  static int version;
512  /* static time_t uptime; */
513
514  struct rip_peer *peer;
515
516  memset (&addr, 0, sizeof (struct in_addr));
517
518  /* Lookup interface. */
519  peer = rip2PeerLookup (v, name, length, &addr, exact);
520  if (! peer)
521    return NULL;
522
523  switch (v->magic)
524    {
525    case RIP2PEERADDRESS:
526      *val_len = sizeof (struct in_addr);
527      return (u_char *) &peer->addr;
528
529    case RIP2PEERDOMAIN:
530      *val_len = sizeof (int);
531      return (u_char *) &peer->domain;
532
533    case RIP2PEERLASTUPDATE:
534#if 0
535      /* We don't know the SNMP agent startup time. We have two choices here:
536       * - assume ripd startup time equals SNMP agent startup time
537       * - don't support this variable, at all
538       * Currently, we do the latter...
539       */
540      *val_len = sizeof (time_t);
541      uptime = peer->uptime; /* now - snmp_agent_startup - peer->uptime */
542      return (u_char *) &uptime;
543#else
544      return (u_char *) NULL;
545#endif
546
547    case RIP2PEERVERSION:
548      *val_len = sizeof (int);
549      version = peer->version;
550      return (u_char *) &version;
551
552    case RIP2PEERRCVBADPACKETS:
553      *val_len = sizeof (int);
554      return (u_char *) &peer->recv_badpackets;
555
556    case RIP2PEERRCVBADROUTES:
557      *val_len = sizeof (int);
558      return (u_char *) &peer->recv_badroutes;
559
560    default:
561      return NULL;
562
563    }
564  return NULL;
565}
566
567/* Register RIPv2-MIB. */
568void
569rip_snmp_init ()
570{
571  rip_ifaddr_table = route_table_init ();
572
573  smux_init (ripd_oid, sizeof (ripd_oid) / sizeof (oid));
574  REGISTER_MIB("mibII/rip", rip_variables, variable, rip_oid);
575  smux_start ();
576}
577#endif /* HAVE_SNMP */
578