1 /**************************************************************
2  * Copyright (C) 2001 Alex Rozin, Optical Access
3  *
4  *                     All Rights Reserved
5  *
6  * Permission to use, copy, modify and distribute this software and its
7  * documentation for any purpose and without fee is hereby granted,
8  * provided that the above copyright notice appear in all copies and that
9  * both that copyright notice and this permission notice appear in
10  * supporting documentation.
11  *
12  * ALEX ROZIN DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
13  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
14  * ALEX ROZIN BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
15  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
16  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
17  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
18  * SOFTWARE.
19  ******************************************************************/
20 
21 #include <net-snmp/net-snmp-config.h>
22 
23 #if HAVE_STDLIB_H
24 #include <stdlib.h>
25 #endif
26 #if TIME_WITH_SYS_TIME
27 # include <sys/time.h>
28 # include <time.h>
29 #else
30 # if HAVE_SYS_TIME_H
31 #  include <sys/time.h>
32 # else
33 #  include <time.h>
34 # endif
35 #endif
36 #if HAVE_UNISTD_H
37 #include <unistd.h>
38 #endif
39 
40 #include <net-snmp/net-snmp-includes.h>
41 #include <net-snmp/agent/net-snmp-agent-includes.h>
42 
43 #include "history.h"
44 
45 /*
46  * Implementation headers
47  */
48 #include "agutil_api.h"
49 #include "row_api.h"
50 
51 /*
52  * File scope definitions section
53  */
54 
55 #define historyControlEntryFirstIndexBegin      11
56 
57 #define CTRL_INDEX		3
58 #define CTRL_DATASOURCE		4
59 #define CTRL_BUCKETSREQUESTED	5
60 #define CTRL_BUCKETSGRANTED	6
61 #define CTRL_INTERVAL		7
62 #define CTRL_OWNER		8
63 #define CTRL_STATUS		9
64 
65 #define DATA_INDEX		3
66 #define DATA_SAMPLEINDEX	4
67 #define DATA_INTERVALSTART	5
68 #define DATA_DROPEVENTS		6
69 #define DATA_OCTETS		7
70 #define DATA_PKTS		8
71 #define DATA_BROADCASTPKTS	9
72 #define DATA_MULTICASTPKTS	10
73 #define DATA_CRCALIGNERRORS	11
74 #define DATA_UNDERSIZEPKTS	12
75 #define DATA_OVERSIZEPKTS	13
76 #define DATA_FRAGMENTS		14
77 #define DATA_JABBERS		15
78 #define DATA_COLLISIONS		16
79 #define DATA_UTILIZATION	17
80 
81 /*
82  * defaults & limitations
83  */
84 
85 #define MAX_BUCKETS_IN_CRTL_ENTRY	50
86 #define HIST_DEF_BUCK_REQ		50
87 #define HIST_DEF_INTERVAL		1800
88 static VAR_OID_T DEFAULT_DATA_SOURCE = { 11,    /* ifIndex.1 */
89     {1, 3, 6, 1, 2, 1, 2, 2, 1, 1, 1}
90 };
91 
92 typedef struct data_struct_t {
93     struct data_struct_t *next;
94     u_long          data_index;
95     u_long          start_interval;
96     u_long          utilization;
97     ETH_STATS_T     EthData;
98 } DATA_ENTRY_T;
99 
100 typedef struct {
101     u_long          interval;
102     u_long          timer_id;
103     VAR_OID_T       data_source;
104 
105     u_long          coeff;
106     DATA_ENTRY_T    previous_bucket;
107     SCROLLER_T      scrlr;
108 
109 } CRTL_ENTRY_T;
110 
111 static TABLE_DEFINTION_T HistoryCtrlTable;
112 static TABLE_DEFINTION_T *table_ptr = &HistoryCtrlTable;
113 
114 /*
115  * Main section
116  */
117 
118 #  define Leaf_historyControlDataSource                    2
119 #  define Leaf_historyControlBucketsRequested              3
120 #  define Leaf_historyControlInterval                      5
121 #  define Leaf_historyControlOwner                         6
122 #  define Leaf_historyControlStatus                        7
123 #  define MIN_historyControlBucketsRequested               1
124 #  define MAX_historyControlBucketsRequested               65535
125 #  define MIN_historyControlInterval                       1
126 #  define MAX_historyControlInterval                       3600
127 
128 static int
write_historyControl(int action,u_char * var_val,u_char var_val_type,size_t var_val_len,u_char * statP,oid * name,size_t name_len)129 write_historyControl(int action, u_char * var_val, u_char var_val_type,
130                      size_t var_val_len, u_char * statP,
131                      oid * name, size_t name_len)
132 {
133     long            long_temp;
134     int             leaf_id, snmp_status;
135     static int      prev_action = COMMIT;
136     RMON_ENTRY_T   *hdr;
137     CRTL_ENTRY_T   *cloned_body;
138     CRTL_ENTRY_T   *body;
139 
140     switch (action) {
141     case RESERVE1:
142     case FREE:
143     case UNDO:
144     case ACTION:
145     case COMMIT:
146     default:
147         return ROWAPI_do_another_action(name,
148                                         historyControlEntryFirstIndexBegin,
149                                         action, &prev_action, table_ptr,
150                                         sizeof(CRTL_ENTRY_T));
151     case RESERVE2:
152         /*
153          * get values from PDU, check them and save them in the cloned entry
154          */
155         long_temp = name[historyControlEntryFirstIndexBegin];
156         leaf_id = (int) name[historyControlEntryFirstIndexBegin - 1];
157         hdr = ROWAPI_find(table_ptr, long_temp);        /* it MUST be OK */
158         cloned_body = (CRTL_ENTRY_T *) hdr->tmp;
159         body = (CRTL_ENTRY_T *) hdr->body;
160         switch (leaf_id) {
161         case Leaf_historyControlDataSource:
162             snmp_status = AGUTIL_get_oid_value(var_val, var_val_type,
163                                                var_val_len,
164                                                &cloned_body->data_source);
165             if (SNMP_ERR_NOERROR != snmp_status) {
166                 ag_trace("can't browse historyControlDataSource");
167                 return snmp_status;
168             }
169             if (RMON1_ENTRY_UNDER_CREATION != hdr->status &&
170                 snmp_oid_compare(cloned_body->data_source.objid,
171                                  cloned_body->data_source.length,
172                                  body->data_source.objid,
173                                  body->data_source.length)) {
174                 ag_trace
175                     ("can't change historyControlDataSource - not Creation");
176                 return SNMP_ERR_BADVALUE;
177             }
178             break;
179         case Leaf_historyControlBucketsRequested:
180             snmp_status = AGUTIL_get_int_value(var_val, var_val_type,
181                                                var_val_len,
182                                                MIN_historyControlBucketsRequested,
183                                                MAX_historyControlBucketsRequested,
184                                                (long *) &cloned_body->scrlr.
185                                                data_requested);
186             if (SNMP_ERR_NOERROR != snmp_status) {
187                 return snmp_status;
188             }
189 #if 0
190             if (RMON1_ENTRY_UNDER_CREATION != hdr->status &&
191                 cloned_body->scrlr.data_requested !=
192                 body->scrlr.data_requested)
193                 return SNMP_ERR_BADVALUE;
194 #endif
195             break;
196         case Leaf_historyControlInterval:
197             snmp_status = AGUTIL_get_int_value(var_val, var_val_type,
198                                                var_val_len,
199                                                MIN_historyControlInterval,
200                                                MAX_historyControlInterval,
201                                                (long *) &cloned_body->interval);
202             if (SNMP_ERR_NOERROR != snmp_status) {
203                 return snmp_status;
204             }
205 #if 0
206             if (RMON1_ENTRY_UNDER_CREATION != hdr->status &&
207                 cloned_body->interval != body->interval)
208                 return SNMP_ERR_BADVALUE;
209 #endif
210             break;
211         case Leaf_historyControlOwner:
212             if (hdr->new_owner)
213                 AGFREE(hdr->new_owner);
214             hdr->new_owner = AGMALLOC(MAX_OWNERSTRING);
215             if (!hdr->new_owner)
216                 return SNMP_ERR_TOOBIG;
217             snmp_status = AGUTIL_get_string_value(var_val, var_val_type,
218                                                   var_val_len,
219                                                   MAX_OWNERSTRING,
220                                                   1, NULL, hdr->new_owner);
221             if (SNMP_ERR_NOERROR != snmp_status) {
222                 return snmp_status;
223             }
224 
225             break;
226         case Leaf_historyControlStatus:
227             snmp_status = AGUTIL_get_int_value(var_val, var_val_type,
228                                                var_val_len,
229                                                RMON1_ENTRY_VALID,
230                                                RMON1_ENTRY_INVALID,
231                                                &long_temp);
232             if (SNMP_ERR_NOERROR != snmp_status) {
233                 return snmp_status;
234             }
235             hdr->new_status = long_temp;
236             break;
237         default:
238             ag_trace("%s:unknown leaf_id=%d\n", table_ptr->name,
239                      (int) leaf_id);
240             return SNMP_ERR_NOSUCHNAME;
241         }                       /* of switch by 'leaf_id' */
242         break;
243 
244     }                           /* of switch by actions */
245 
246     prev_action = action;
247     return SNMP_ERR_NOERROR;
248 }
249 
250 /*
251  * var_historyControlTable():
252  */
253 unsigned char  *
var_historyControlTable(struct variable * vp,oid * name,size_t * length,int exact,size_t * var_len,WriteMethod ** write_method)254 var_historyControlTable(struct variable *vp,
255                         oid * name,
256                         size_t * length,
257                         int exact,
258                         size_t * var_len, WriteMethod ** write_method)
259 {
260     static unsigned char zero_octet_string[1];
261     static long     long_ret;
262     static CRTL_ENTRY_T theEntry;
263     RMON_ENTRY_T   *hdr;
264 
265     *write_method = write_historyControl;
266     hdr = ROWAPI_header_ControlEntry(vp, name, length, exact, var_len,
267                                      table_ptr,
268                                      &theEntry, sizeof(CRTL_ENTRY_T));
269     if (!hdr)
270         return NULL;
271 
272     *var_len = sizeof(long);    /* default */
273 
274     switch (vp->magic) {
275     case CTRL_INDEX:
276         long_ret = hdr->ctrl_index;
277         return (unsigned char *) &long_ret;
278 
279     case CTRL_DATASOURCE:
280         *var_len = sizeof(oid) * theEntry.data_source.length;
281         return (unsigned char *) theEntry.data_source.objid;
282 
283     case CTRL_BUCKETSREQUESTED:
284         long_ret = theEntry.scrlr.data_requested;
285         return (unsigned char *) &long_ret;
286 
287     case CTRL_BUCKETSGRANTED:
288 
289         long_ret = theEntry.scrlr.data_granted;
290         return (unsigned char *) &long_ret;
291 
292     case CTRL_INTERVAL:
293         long_ret = theEntry.interval;
294         return (unsigned char *) &long_ret;
295 
296     case CTRL_OWNER:
297         if (hdr->owner) {
298             *var_len = strlen(hdr->owner);
299             return (unsigned char *) hdr->owner;
300         } else {
301             *var_len = 0;
302             return zero_octet_string;
303         }
304 
305     case CTRL_STATUS:
306         long_ret = hdr->status;
307         return (unsigned char *) &long_ret;
308 
309     default:
310         ag_trace("HistoryControlTable: unknown vp->magic=%d",
311                  (int) vp->magic);
312         ERROR_MSG("");
313     }
314     return NULL;
315 }
316 
317 /*
318  * history row management control callbacks
319  */
320 
321 static void
compute_delta(ETH_STATS_T * delta,ETH_STATS_T * newval,ETH_STATS_T * prevval)322 compute_delta(ETH_STATS_T * delta,
323               ETH_STATS_T * newval, ETH_STATS_T * prevval)
324 {
325 #define CNT_DIF(X) delta->X = newval->X - prevval->X
326 
327     CNT_DIF(octets);
328     CNT_DIF(packets);
329     CNT_DIF(bcast_pkts);
330     CNT_DIF(mcast_pkts);
331     CNT_DIF(crc_align);
332     CNT_DIF(undersize);
333     CNT_DIF(oversize);
334     CNT_DIF(fragments);
335     CNT_DIF(jabbers);
336     CNT_DIF(collisions);
337 }
338 
339 static void
history_get_backet(unsigned int clientreg,void * clientarg)340 history_get_backet(unsigned int clientreg, void *clientarg)
341 {
342     RMON_ENTRY_T   *hdr_ptr;
343     CRTL_ENTRY_T   *body;
344     DATA_ENTRY_T   *bptr;
345     ETH_STATS_T     newSample;
346 
347     /*
348      * ag_trace ("history_get_backet: timer_id=%d", (int) clientreg);
349      */
350     hdr_ptr = (RMON_ENTRY_T *) clientarg;
351     if (!hdr_ptr) {
352         ag_trace
353             ("Err: history_get_backet: hdr_ptr=NULL ? (Inserted in shock)");
354         return;
355     }
356 
357     body = (CRTL_ENTRY_T *) hdr_ptr->body;
358     if (!body) {
359         ag_trace
360             ("Err: history_get_backet: body=NULL ? (Inserted in shock)");
361         return;
362     }
363 
364     if (RMON1_ENTRY_VALID != hdr_ptr->status) {
365         ag_trace("Err: history_get_backet when entry %d is not valid ?!!",
366                  (int) hdr_ptr->ctrl_index);
367         /*
368          * snmp_alarm_print_list ();
369          */
370         snmp_alarm_unregister(body->timer_id);
371         ag_trace("Err: unregistered %ld", (long) body->timer_id);
372         return;
373     }
374 
375     SYSTEM_get_eth_statistics(&body->data_source, &newSample);
376 
377     bptr = ROWDATAAPI_locate_new_data(&body->scrlr);
378     if (!bptr) {
379         ag_trace
380             ("Err: history_get_backet for %d: empty bucket's list !??\n",
381              (int) hdr_ptr->ctrl_index);
382         return;
383     }
384 
385     bptr->data_index = ROWDATAAPI_get_total_number(&body->scrlr);
386 
387     bptr->start_interval = body->previous_bucket.start_interval;
388 
389     compute_delta(&bptr->EthData, &newSample,
390                   &body->previous_bucket.EthData);
391 
392     bptr->utilization =
393         bptr->EthData.octets * 8 + bptr->EthData.packets * (96 + 64);
394     bptr->utilization /= body->coeff;
395 
396     /*
397      * update previous_bucket
398      */
399     body->previous_bucket.start_interval = AGUTIL_sys_up_time();
400     memcpy(&body->previous_bucket.EthData, &newSample,
401            sizeof(ETH_STATS_T));
402 }
403 
404 /*
405  * Control Table RowApi Callbacks
406  */
407 
408 int
history_Create(RMON_ENTRY_T * eptr)409 history_Create(RMON_ENTRY_T * eptr)
410 {                               /* create the body: alloc it and set defaults */
411     CRTL_ENTRY_T   *body;
412 
413     eptr->body = AGMALLOC(sizeof(CRTL_ENTRY_T));
414     if (!eptr->body)
415         return -3;
416     body = (CRTL_ENTRY_T *) eptr->body;
417 
418     /*
419      * set defaults
420      */
421     body->interval = HIST_DEF_INTERVAL;
422     body->timer_id = 0;
423     memcpy(&body->data_source, &DEFAULT_DATA_SOURCE, sizeof(VAR_OID_T));
424 
425     ROWDATAAPI_init(&body->scrlr, HIST_DEF_BUCK_REQ,
426                     MAX_BUCKETS_IN_CRTL_ENTRY, sizeof(DATA_ENTRY_T), NULL);
427 
428     return 0;
429 }
430 
431 int
history_Validate(RMON_ENTRY_T * eptr)432 history_Validate(RMON_ENTRY_T * eptr)
433 {
434     /*
435      * T.B.D. (system dependent) check valid inteface in body->data_source;
436      */
437     return 0;
438 }
439 
440 int
history_Activate(RMON_ENTRY_T * eptr)441 history_Activate(RMON_ENTRY_T * eptr)
442 {
443     CRTL_ENTRY_T   *body = (CRTL_ENTRY_T *) eptr->body;
444 
445     body->coeff = 100000L * (long) body->interval;
446 
447     ROWDATAAPI_set_size(&body->scrlr,
448                         body->scrlr.data_requested,
449                         (u_char)(RMON1_ENTRY_VALID == eptr->status) );
450 
451     SYSTEM_get_eth_statistics(&body->data_source,
452                               &body->previous_bucket.EthData);
453     body->previous_bucket.start_interval = AGUTIL_sys_up_time();
454 
455     body->scrlr.current_data_ptr = body->scrlr.first_data_ptr;
456     /*
457      * ag_trace ("Dbg:   registered in history_Activate");
458      */
459     body->timer_id = snmp_alarm_register(body->interval, SA_REPEAT,
460                                          history_get_backet, eptr);
461     return 0;
462 }
463 
464 int
history_Deactivate(RMON_ENTRY_T * eptr)465 history_Deactivate(RMON_ENTRY_T * eptr)
466 {
467     CRTL_ENTRY_T   *body = (CRTL_ENTRY_T *) eptr->body;
468 
469     snmp_alarm_unregister(body->timer_id);
470     /*
471      * ag_trace ("Dbg: unregistered in history_Deactivate timer_id=%d",
472      * (int) body->timer_id);
473      */
474 
475     /*
476      * free data list
477      */
478     ROWDATAAPI_descructor(&body->scrlr);
479 
480     return 0;
481 }
482 
483 int
history_Copy(RMON_ENTRY_T * eptr)484 history_Copy(RMON_ENTRY_T * eptr)
485 {
486     CRTL_ENTRY_T   *body = (CRTL_ENTRY_T *) eptr->body;
487     CRTL_ENTRY_T   *clone = (CRTL_ENTRY_T *) eptr->tmp;
488 
489     if (body->scrlr.data_requested != clone->scrlr.data_requested) {
490         ROWDATAAPI_set_size(&body->scrlr, clone->scrlr.data_requested,
491                             (u_char)(RMON1_ENTRY_VALID == eptr->status) );
492     }
493 
494     if (body->interval != clone->interval) {
495         if (RMON1_ENTRY_VALID == eptr->status) {
496             snmp_alarm_unregister(body->timer_id);
497             body->timer_id =
498                 snmp_alarm_register(clone->interval, SA_REPEAT,
499                                     history_get_backet, eptr);
500         }
501 
502         body->interval = clone->interval;
503     }
504 
505     if (snmp_oid_compare
506         (clone->data_source.objid, clone->data_source.length,
507          body->data_source.objid, body->data_source.length)) {
508         memcpy(&body->data_source, &clone->data_source, sizeof(VAR_OID_T));
509     }
510 
511     return 0;
512 }
513 
514 static SCROLLER_T *
history_extract_scroller(void * v_body)515 history_extract_scroller(void *v_body)
516 {
517     CRTL_ENTRY_T   *body = (CRTL_ENTRY_T *) v_body;
518     return &body->scrlr;
519 }
520 
521 /*
522  * var_etherHistoryTable():
523  */
524 unsigned char  *
var_etherHistoryTable(struct variable * vp,oid * name,size_t * length,int exact,size_t * var_len,WriteMethod ** write_method)525 var_etherHistoryTable(struct variable *vp,
526                       oid * name,
527                       size_t * length,
528                       int exact,
529                       size_t * var_len, WriteMethod ** write_method)
530 {
531     static long     long_ret;
532     static DATA_ENTRY_T theBucket;
533     RMON_ENTRY_T   *hdr;
534 
535     *write_method = NULL;
536     hdr = ROWDATAAPI_header_DataEntry(vp, name, length, exact, var_len,
537                                       table_ptr,
538                                       &history_extract_scroller,
539                                       sizeof(DATA_ENTRY_T), &theBucket);
540     if (!hdr)
541         return NULL;
542 
543     *var_len = sizeof(long);    /* default */
544 
545     switch (vp->magic) {
546     case DATA_INDEX:
547         long_ret = hdr->ctrl_index;
548         return (unsigned char *) &long_ret;
549     case DATA_SAMPLEINDEX:
550         long_ret = theBucket.data_index;
551         return (unsigned char *) &long_ret;
552     case DATA_INTERVALSTART:
553         long_ret = 0;
554         return (unsigned char *) &theBucket.start_interval;
555     case DATA_DROPEVENTS:
556         long_ret = 0;
557         return (unsigned char *) &long_ret;
558     case DATA_OCTETS:
559         long_ret = 0;
560         return (unsigned char *) &theBucket.EthData.octets;
561     case DATA_PKTS:
562         long_ret = 0;
563         return (unsigned char *) &theBucket.EthData.packets;
564     case DATA_BROADCASTPKTS:
565         long_ret = 0;
566         return (unsigned char *) &theBucket.EthData.bcast_pkts;
567     case DATA_MULTICASTPKTS:
568         long_ret = 0;
569         return (unsigned char *) &theBucket.EthData.mcast_pkts;
570     case DATA_CRCALIGNERRORS:
571         long_ret = 0;
572         return (unsigned char *) &theBucket.EthData.crc_align;
573     case DATA_UNDERSIZEPKTS:
574         long_ret = 0;
575         return (unsigned char *) &theBucket.EthData.undersize;
576     case DATA_OVERSIZEPKTS:
577         long_ret = 0;
578         return (unsigned char *) &theBucket.EthData.oversize;
579     case DATA_FRAGMENTS:
580         long_ret = 0;
581         return (unsigned char *) &theBucket.EthData.fragments;
582     case DATA_JABBERS:
583         long_ret = 0;
584         return (unsigned char *) &theBucket.EthData.jabbers;
585     case DATA_COLLISIONS:
586         long_ret = 0;
587         return (unsigned char *) &theBucket.EthData.collisions;
588     case DATA_UTILIZATION:
589         long_ret = 0;
590         return (unsigned char *) &theBucket.utilization;
591     default:
592         ag_trace("etherHistoryTable: unknown vp->magic=%d",
593                  (int) vp->magic);
594         ERROR_MSG("");
595     }
596     return NULL;
597 }
598 
599 #if 1                           /* debug, but may be used for init. TBD: may be token snmpd.conf ? */
600 int
add_hist_entry(int ctrl_index,int ifIndex,u_long interval,u_long requested)601 add_hist_entry(int ctrl_index, int ifIndex,
602                u_long interval, u_long requested)
603 {
604     register RMON_ENTRY_T *eptr;
605     register CRTL_ENTRY_T *body;
606     int             ierr;
607 
608     ierr = ROWAPI_new(table_ptr, ctrl_index);
609     if (ierr) {
610         ag_trace("ROWAPI_new failed with %d", ierr);
611         return ierr;
612     }
613 
614     eptr = ROWAPI_find(table_ptr, ctrl_index);
615     if (!eptr) {
616         ag_trace("ROWAPI_find failed");
617         return -4;
618     }
619 
620     body = (CRTL_ENTRY_T *) eptr->body;
621 
622     /*
623      * set parameters
624      */
625 
626     body->data_source.objid[body->data_source.length - 1] = ifIndex;
627     body->interval = interval;
628     body->scrlr.data_requested = requested;
629 
630     eptr->new_status = RMON1_ENTRY_VALID;
631     ierr = ROWAPI_commit(table_ptr, ctrl_index);
632     if (ierr) {
633         ag_trace("ROWAPI_commit failed with %d", ierr);
634     }
635 
636     return ierr;
637 
638 }
639 
640 #endif
641 
642 /*
643  * Registration & Initializatio section
644  */
645 
646 oid             historyControlTable_variables_oid[] =
647     { 1, 3, 6, 1, 2, 1, 16, 2, 1 };
648 
649 struct variable2 historyControlTable_variables[] = {
650     /*
651      * magic number        , variable type, ro/rw , callback fn  ,           L, oidsuffix
652      */
653     {CTRL_INDEX, ASN_INTEGER, NETSNMP_OLDAPI_RONLY,
654      var_historyControlTable, 2, {1, 1}},
655     {CTRL_DATASOURCE, ASN_OBJECT_ID, NETSNMP_OLDAPI_RWRITE,
656      var_historyControlTable, 2, {1, 2}},
657     {CTRL_BUCKETSREQUESTED, ASN_INTEGER, NETSNMP_OLDAPI_RWRITE,
658      var_historyControlTable, 2, {1, 3}},
659     {CTRL_BUCKETSGRANTED, ASN_INTEGER, NETSNMP_OLDAPI_RONLY,
660      var_historyControlTable, 2, {1, 4}},
661     {CTRL_INTERVAL, ASN_INTEGER, NETSNMP_OLDAPI_RWRITE,
662      var_historyControlTable, 2, {1, 5}},
663     {CTRL_OWNER, ASN_OCTET_STR, NETSNMP_OLDAPI_RWRITE,
664      var_historyControlTable, 2, {1, 6}},
665     {CTRL_STATUS, ASN_INTEGER, NETSNMP_OLDAPI_RWRITE,
666      var_historyControlTable, 2, {1, 7}},
667 
668 };
669 
670 oid             etherHistoryTable_variables_oid[] =
671     { 1, 3, 6, 1, 2, 1, 16, 2, 2 };
672 
673 struct variable2 etherHistoryTable_variables[] = {
674     /*
675      * magic number     , variable type , ro/rw , callback fn  ,        L, oidsuffix
676      */
677     {DATA_INDEX, ASN_INTEGER, NETSNMP_OLDAPI_RONLY,
678      var_etherHistoryTable, 2, {1, 1}},
679     {DATA_SAMPLEINDEX, ASN_INTEGER, NETSNMP_OLDAPI_RONLY,
680      var_etherHistoryTable, 2, {1, 2}},
681     {DATA_INTERVALSTART, ASN_TIMETICKS, NETSNMP_OLDAPI_RONLY,
682      var_etherHistoryTable, 2, {1, 3}},
683     {DATA_DROPEVENTS, ASN_COUNTER, NETSNMP_OLDAPI_RONLY,
684      var_etherHistoryTable, 2, {1, 4}},
685     {DATA_OCTETS, ASN_COUNTER, NETSNMP_OLDAPI_RONLY,
686      var_etherHistoryTable, 2, {1, 5}},
687     {DATA_PKTS, ASN_COUNTER, NETSNMP_OLDAPI_RONLY,
688      var_etherHistoryTable, 2, {1, 6}},
689     {DATA_BROADCASTPKTS, ASN_COUNTER, NETSNMP_OLDAPI_RONLY,
690      var_etherHistoryTable, 2, {1, 7}},
691     {DATA_MULTICASTPKTS, ASN_COUNTER, NETSNMP_OLDAPI_RONLY,
692      var_etherHistoryTable, 2, {1, 8}},
693     {DATA_CRCALIGNERRORS, ASN_COUNTER, NETSNMP_OLDAPI_RONLY,
694      var_etherHistoryTable, 2, {1, 9}},
695     {DATA_UNDERSIZEPKTS, ASN_COUNTER, NETSNMP_OLDAPI_RONLY,
696      var_etherHistoryTable, 2, {1, 10}},
697     {DATA_OVERSIZEPKTS, ASN_COUNTER, NETSNMP_OLDAPI_RONLY,
698      var_etherHistoryTable, 2, {1, 11}},
699     {DATA_FRAGMENTS, ASN_COUNTER, NETSNMP_OLDAPI_RONLY,
700      var_etherHistoryTable, 2, {1, 12}},
701     {DATA_JABBERS, ASN_COUNTER, NETSNMP_OLDAPI_RONLY,
702      var_etherHistoryTable, 2, {1, 13}},
703     {DATA_COLLISIONS, ASN_COUNTER, NETSNMP_OLDAPI_RONLY,
704      var_etherHistoryTable, 2, {1, 14}},
705     {DATA_UTILIZATION, ASN_INTEGER, NETSNMP_OLDAPI_RONLY,
706      var_etherHistoryTable, 2, {1, 15}},
707 
708 };
709 
710 void
init_history(void)711 init_history(void)
712 {
713     REGISTER_MIB("historyControlTable", historyControlTable_variables,
714                  variable2, historyControlTable_variables_oid);
715     REGISTER_MIB("etherHistoryTable", etherHistoryTable_variables,
716                  variable2, etherHistoryTable_variables_oid);
717 
718     ROWAPI_init_table(&HistoryCtrlTable, "History", 0, &history_Create, NULL,   /* &history_Clone, */
719                       NULL,     /* &history_Delete, */
720                       &history_Validate,
721                       &history_Activate,
722                       &history_Deactivate, &history_Copy);
723 
724     /*
725      * add_hist_entry (2, 3, 4, 2);
726      */
727 }
728