/************************************************************** * Copyright (C) 2001 Alex Rozin, Optical Access * * All Rights Reserved * * Permission to use, copy, modify and distribute this software and its * documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appear in all copies and that * both that copyright notice and this permission notice appear in * supporting documentation. * * ALEX ROZIN DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL * ALEX ROZIN BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. ******************************************************************/ #include #if HAVE_STDLIB_H #include #endif #if TIME_WITH_SYS_TIME # include # include #else # if HAVE_SYS_TIME_H # include # else # include # endif #endif #if HAVE_UNISTD_H #include #endif #include #include #include "history.h" /* * Implementation headers */ #include "agutil_api.h" #include "row_api.h" /* * File scope definitions section */ #define historyControlEntryFirstIndexBegin 11 #define CTRL_INDEX 3 #define CTRL_DATASOURCE 4 #define CTRL_BUCKETSREQUESTED 5 #define CTRL_BUCKETSGRANTED 6 #define CTRL_INTERVAL 7 #define CTRL_OWNER 8 #define CTRL_STATUS 9 #define DATA_INDEX 3 #define DATA_SAMPLEINDEX 4 #define DATA_INTERVALSTART 5 #define DATA_DROPEVENTS 6 #define DATA_OCTETS 7 #define DATA_PKTS 8 #define DATA_BROADCASTPKTS 9 #define DATA_MULTICASTPKTS 10 #define DATA_CRCALIGNERRORS 11 #define DATA_UNDERSIZEPKTS 12 #define DATA_OVERSIZEPKTS 13 #define DATA_FRAGMENTS 14 #define DATA_JABBERS 15 #define DATA_COLLISIONS 16 #define DATA_UTILIZATION 17 /* * defaults & limitations */ #define MAX_BUCKETS_IN_CRTL_ENTRY 50 #define HIST_DEF_BUCK_REQ 50 #define HIST_DEF_INTERVAL 1800 static VAR_OID_T DEFAULT_DATA_SOURCE = { 11, /* ifIndex.1 */ {1, 3, 6, 1, 2, 1, 2, 2, 1, 1, 1} }; typedef struct data_struct_t { struct data_struct_t *next; u_long data_index; u_long start_interval; u_long utilization; ETH_STATS_T EthData; } DATA_ENTRY_T; typedef struct { u_long interval; u_long timer_id; VAR_OID_T data_source; u_long coeff; DATA_ENTRY_T previous_bucket; SCROLLER_T scrlr; } CRTL_ENTRY_T; static TABLE_DEFINTION_T HistoryCtrlTable; static TABLE_DEFINTION_T *table_ptr = &HistoryCtrlTable; /* * Main section */ # define Leaf_historyControlDataSource 2 # define Leaf_historyControlBucketsRequested 3 # define Leaf_historyControlInterval 5 # define Leaf_historyControlOwner 6 # define Leaf_historyControlStatus 7 # define MIN_historyControlBucketsRequested 1 # define MAX_historyControlBucketsRequested 65535 # define MIN_historyControlInterval 1 # define MAX_historyControlInterval 3600 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) { long long_temp; int leaf_id, snmp_status; static int prev_action = COMMIT; RMON_ENTRY_T *hdr; CRTL_ENTRY_T *cloned_body; CRTL_ENTRY_T *body; switch (action) { case RESERVE1: case FREE: case UNDO: case ACTION: case COMMIT: default: return ROWAPI_do_another_action(name, historyControlEntryFirstIndexBegin, action, &prev_action, table_ptr, sizeof(CRTL_ENTRY_T)); case RESERVE2: /* * get values from PDU, check them and save them in the cloned entry */ long_temp = name[historyControlEntryFirstIndexBegin]; leaf_id = (int) name[historyControlEntryFirstIndexBegin - 1]; hdr = ROWAPI_find(table_ptr, long_temp); /* it MUST be OK */ cloned_body = (CRTL_ENTRY_T *) hdr->tmp; body = (CRTL_ENTRY_T *) hdr->body; switch (leaf_id) { case Leaf_historyControlDataSource: snmp_status = AGUTIL_get_oid_value(var_val, var_val_type, var_val_len, &cloned_body->data_source); if (SNMP_ERR_NOERROR != snmp_status) { ag_trace("can't browse historyControlDataSource"); return snmp_status; } if (RMON1_ENTRY_UNDER_CREATION != hdr->status && snmp_oid_compare(cloned_body->data_source.objid, cloned_body->data_source.length, body->data_source.objid, body->data_source.length)) { ag_trace ("can't change historyControlDataSource - not Creation"); return SNMP_ERR_BADVALUE; } break; case Leaf_historyControlBucketsRequested: snmp_status = AGUTIL_get_int_value(var_val, var_val_type, var_val_len, MIN_historyControlBucketsRequested, MAX_historyControlBucketsRequested, (long *) &cloned_body->scrlr. data_requested); if (SNMP_ERR_NOERROR != snmp_status) { return snmp_status; } #if 0 if (RMON1_ENTRY_UNDER_CREATION != hdr->status && cloned_body->scrlr.data_requested != body->scrlr.data_requested) return SNMP_ERR_BADVALUE; #endif break; case Leaf_historyControlInterval: snmp_status = AGUTIL_get_int_value(var_val, var_val_type, var_val_len, MIN_historyControlInterval, MAX_historyControlInterval, (long *) &cloned_body->interval); if (SNMP_ERR_NOERROR != snmp_status) { return snmp_status; } #if 0 if (RMON1_ENTRY_UNDER_CREATION != hdr->status && cloned_body->interval != body->interval) return SNMP_ERR_BADVALUE; #endif break; case Leaf_historyControlOwner: if (hdr->new_owner) AGFREE(hdr->new_owner); hdr->new_owner = AGMALLOC(MAX_OWNERSTRING); if (!hdr->new_owner) return SNMP_ERR_TOOBIG; snmp_status = AGUTIL_get_string_value(var_val, var_val_type, var_val_len, MAX_OWNERSTRING, 1, NULL, hdr->new_owner); if (SNMP_ERR_NOERROR != snmp_status) { return snmp_status; } break; case Leaf_historyControlStatus: snmp_status = AGUTIL_get_int_value(var_val, var_val_type, var_val_len, RMON1_ENTRY_VALID, RMON1_ENTRY_INVALID, &long_temp); if (SNMP_ERR_NOERROR != snmp_status) { return snmp_status; } hdr->new_status = long_temp; break; default: ag_trace("%s:unknown leaf_id=%d\n", table_ptr->name, (int) leaf_id); return SNMP_ERR_NOSUCHNAME; } /* of switch by 'leaf_id' */ break; } /* of switch by actions */ prev_action = action; return SNMP_ERR_NOERROR; } /* * var_historyControlTable(): */ unsigned char * var_historyControlTable(struct variable *vp, oid * name, size_t * length, int exact, size_t * var_len, WriteMethod ** write_method) { static unsigned char zero_octet_string[1]; static long long_ret; static CRTL_ENTRY_T theEntry; RMON_ENTRY_T *hdr; *write_method = write_historyControl; hdr = ROWAPI_header_ControlEntry(vp, name, length, exact, var_len, table_ptr, &theEntry, sizeof(CRTL_ENTRY_T)); if (!hdr) return NULL; *var_len = sizeof(long); /* default */ switch (vp->magic) { case CTRL_INDEX: long_ret = hdr->ctrl_index; return (unsigned char *) &long_ret; case CTRL_DATASOURCE: *var_len = sizeof(oid) * theEntry.data_source.length; return (unsigned char *) theEntry.data_source.objid; case CTRL_BUCKETSREQUESTED: long_ret = theEntry.scrlr.data_requested; return (unsigned char *) &long_ret; case CTRL_BUCKETSGRANTED: long_ret = theEntry.scrlr.data_granted; return (unsigned char *) &long_ret; case CTRL_INTERVAL: long_ret = theEntry.interval; return (unsigned char *) &long_ret; case CTRL_OWNER: if (hdr->owner) { *var_len = strlen(hdr->owner); return (unsigned char *) hdr->owner; } else { *var_len = 0; return zero_octet_string; } case CTRL_STATUS: long_ret = hdr->status; return (unsigned char *) &long_ret; default: ag_trace("HistoryControlTable: unknown vp->magic=%d", (int) vp->magic); ERROR_MSG(""); } return NULL; } /* * history row management control callbacks */ static void compute_delta(ETH_STATS_T * delta, ETH_STATS_T * newval, ETH_STATS_T * prevval) { #define CNT_DIF(X) delta->X = newval->X - prevval->X CNT_DIF(octets); CNT_DIF(packets); CNT_DIF(bcast_pkts); CNT_DIF(mcast_pkts); CNT_DIF(crc_align); CNT_DIF(undersize); CNT_DIF(oversize); CNT_DIF(fragments); CNT_DIF(jabbers); CNT_DIF(collisions); } static void history_get_backet(unsigned int clientreg, void *clientarg) { RMON_ENTRY_T *hdr_ptr; CRTL_ENTRY_T *body; DATA_ENTRY_T *bptr; ETH_STATS_T newSample; /* * ag_trace ("history_get_backet: timer_id=%d", (int) clientreg); */ hdr_ptr = (RMON_ENTRY_T *) clientarg; if (!hdr_ptr) { ag_trace ("Err: history_get_backet: hdr_ptr=NULL ? (Inserted in shock)"); return; } body = (CRTL_ENTRY_T *) hdr_ptr->body; if (!body) { ag_trace ("Err: history_get_backet: body=NULL ? (Inserted in shock)"); return; } if (RMON1_ENTRY_VALID != hdr_ptr->status) { ag_trace("Err: history_get_backet when entry %d is not valid ?!!", (int) hdr_ptr->ctrl_index); /* * snmp_alarm_print_list (); */ snmp_alarm_unregister(body->timer_id); ag_trace("Err: unregistered %ld", (long) body->timer_id); return; } SYSTEM_get_eth_statistics(&body->data_source, &newSample); bptr = ROWDATAAPI_locate_new_data(&body->scrlr); if (!bptr) { ag_trace ("Err: history_get_backet for %d: empty bucket's list !??\n", (int) hdr_ptr->ctrl_index); return; } bptr->data_index = ROWDATAAPI_get_total_number(&body->scrlr); bptr->start_interval = body->previous_bucket.start_interval; compute_delta(&bptr->EthData, &newSample, &body->previous_bucket.EthData); bptr->utilization = bptr->EthData.octets * 8 + bptr->EthData.packets * (96 + 64); bptr->utilization /= body->coeff; /* * update previous_bucket */ body->previous_bucket.start_interval = AGUTIL_sys_up_time(); memcpy(&body->previous_bucket.EthData, &newSample, sizeof(ETH_STATS_T)); } /* * Control Table RowApi Callbacks */ int history_Create(RMON_ENTRY_T * eptr) { /* create the body: alloc it and set defaults */ CRTL_ENTRY_T *body; eptr->body = AGMALLOC(sizeof(CRTL_ENTRY_T)); if (!eptr->body) return -3; body = (CRTL_ENTRY_T *) eptr->body; /* * set defaults */ body->interval = HIST_DEF_INTERVAL; body->timer_id = 0; memcpy(&body->data_source, &DEFAULT_DATA_SOURCE, sizeof(VAR_OID_T)); ROWDATAAPI_init(&body->scrlr, HIST_DEF_BUCK_REQ, MAX_BUCKETS_IN_CRTL_ENTRY, sizeof(DATA_ENTRY_T), NULL); return 0; } int history_Validate(RMON_ENTRY_T * eptr) { /* * T.B.D. (system dependent) check valid inteface in body->data_source; */ return 0; } int history_Activate(RMON_ENTRY_T * eptr) { CRTL_ENTRY_T *body = (CRTL_ENTRY_T *) eptr->body; body->coeff = 100000L * (long) body->interval; ROWDATAAPI_set_size(&body->scrlr, body->scrlr.data_requested, (u_char)(RMON1_ENTRY_VALID == eptr->status) ); SYSTEM_get_eth_statistics(&body->data_source, &body->previous_bucket.EthData); body->previous_bucket.start_interval = AGUTIL_sys_up_time(); body->scrlr.current_data_ptr = body->scrlr.first_data_ptr; /* * ag_trace ("Dbg: registered in history_Activate"); */ body->timer_id = snmp_alarm_register(body->interval, SA_REPEAT, history_get_backet, eptr); return 0; } int history_Deactivate(RMON_ENTRY_T * eptr) { CRTL_ENTRY_T *body = (CRTL_ENTRY_T *) eptr->body; snmp_alarm_unregister(body->timer_id); /* * ag_trace ("Dbg: unregistered in history_Deactivate timer_id=%d", * (int) body->timer_id); */ /* * free data list */ ROWDATAAPI_descructor(&body->scrlr); return 0; } int history_Copy(RMON_ENTRY_T * eptr) { CRTL_ENTRY_T *body = (CRTL_ENTRY_T *) eptr->body; CRTL_ENTRY_T *clone = (CRTL_ENTRY_T *) eptr->tmp; if (body->scrlr.data_requested != clone->scrlr.data_requested) { ROWDATAAPI_set_size(&body->scrlr, clone->scrlr.data_requested, (u_char)(RMON1_ENTRY_VALID == eptr->status) ); } if (body->interval != clone->interval) { if (RMON1_ENTRY_VALID == eptr->status) { snmp_alarm_unregister(body->timer_id); body->timer_id = snmp_alarm_register(clone->interval, SA_REPEAT, history_get_backet, eptr); } body->interval = clone->interval; } if (snmp_oid_compare (clone->data_source.objid, clone->data_source.length, body->data_source.objid, body->data_source.length)) { memcpy(&body->data_source, &clone->data_source, sizeof(VAR_OID_T)); } return 0; } static SCROLLER_T * history_extract_scroller(void *v_body) { CRTL_ENTRY_T *body = (CRTL_ENTRY_T *) v_body; return &body->scrlr; } /* * var_etherHistoryTable(): */ unsigned char * var_etherHistoryTable(struct variable *vp, oid * name, size_t * length, int exact, size_t * var_len, WriteMethod ** write_method) { static long long_ret; static DATA_ENTRY_T theBucket; RMON_ENTRY_T *hdr; *write_method = NULL; hdr = ROWDATAAPI_header_DataEntry(vp, name, length, exact, var_len, table_ptr, &history_extract_scroller, sizeof(DATA_ENTRY_T), &theBucket); if (!hdr) return NULL; *var_len = sizeof(long); /* default */ switch (vp->magic) { case DATA_INDEX: long_ret = hdr->ctrl_index; return (unsigned char *) &long_ret; case DATA_SAMPLEINDEX: long_ret = theBucket.data_index; return (unsigned char *) &long_ret; case DATA_INTERVALSTART: long_ret = 0; return (unsigned char *) &theBucket.start_interval; case DATA_DROPEVENTS: long_ret = 0; return (unsigned char *) &long_ret; case DATA_OCTETS: long_ret = 0; return (unsigned char *) &theBucket.EthData.octets; case DATA_PKTS: long_ret = 0; return (unsigned char *) &theBucket.EthData.packets; case DATA_BROADCASTPKTS: long_ret = 0; return (unsigned char *) &theBucket.EthData.bcast_pkts; case DATA_MULTICASTPKTS: long_ret = 0; return (unsigned char *) &theBucket.EthData.mcast_pkts; case DATA_CRCALIGNERRORS: long_ret = 0; return (unsigned char *) &theBucket.EthData.crc_align; case DATA_UNDERSIZEPKTS: long_ret = 0; return (unsigned char *) &theBucket.EthData.undersize; case DATA_OVERSIZEPKTS: long_ret = 0; return (unsigned char *) &theBucket.EthData.oversize; case DATA_FRAGMENTS: long_ret = 0; return (unsigned char *) &theBucket.EthData.fragments; case DATA_JABBERS: long_ret = 0; return (unsigned char *) &theBucket.EthData.jabbers; case DATA_COLLISIONS: long_ret = 0; return (unsigned char *) &theBucket.EthData.collisions; case DATA_UTILIZATION: long_ret = 0; return (unsigned char *) &theBucket.utilization; default: ag_trace("etherHistoryTable: unknown vp->magic=%d", (int) vp->magic); ERROR_MSG(""); } return NULL; } #if 1 /* debug, but may be used for init. TBD: may be token snmpd.conf ? */ int add_hist_entry(int ctrl_index, int ifIndex, u_long interval, u_long requested) { register RMON_ENTRY_T *eptr; register CRTL_ENTRY_T *body; int ierr; ierr = ROWAPI_new(table_ptr, ctrl_index); if (ierr) { ag_trace("ROWAPI_new failed with %d", ierr); return ierr; } eptr = ROWAPI_find(table_ptr, ctrl_index); if (!eptr) { ag_trace("ROWAPI_find failed"); return -4; } body = (CRTL_ENTRY_T *) eptr->body; /* * set parameters */ body->data_source.objid[body->data_source.length - 1] = ifIndex; body->interval = interval; body->scrlr.data_requested = requested; eptr->new_status = RMON1_ENTRY_VALID; ierr = ROWAPI_commit(table_ptr, ctrl_index); if (ierr) { ag_trace("ROWAPI_commit failed with %d", ierr); } return ierr; } #endif /* * Registration & Initializatio section */ oid historyControlTable_variables_oid[] = { 1, 3, 6, 1, 2, 1, 16, 2, 1 }; struct variable2 historyControlTable_variables[] = { /* * magic number , variable type, ro/rw , callback fn , L, oidsuffix */ {CTRL_INDEX, ASN_INTEGER, NETSNMP_OLDAPI_RONLY, var_historyControlTable, 2, {1, 1}}, {CTRL_DATASOURCE, ASN_OBJECT_ID, NETSNMP_OLDAPI_RWRITE, var_historyControlTable, 2, {1, 2}}, {CTRL_BUCKETSREQUESTED, ASN_INTEGER, NETSNMP_OLDAPI_RWRITE, var_historyControlTable, 2, {1, 3}}, {CTRL_BUCKETSGRANTED, ASN_INTEGER, NETSNMP_OLDAPI_RONLY, var_historyControlTable, 2, {1, 4}}, {CTRL_INTERVAL, ASN_INTEGER, NETSNMP_OLDAPI_RWRITE, var_historyControlTable, 2, {1, 5}}, {CTRL_OWNER, ASN_OCTET_STR, NETSNMP_OLDAPI_RWRITE, var_historyControlTable, 2, {1, 6}}, {CTRL_STATUS, ASN_INTEGER, NETSNMP_OLDAPI_RWRITE, var_historyControlTable, 2, {1, 7}}, }; oid etherHistoryTable_variables_oid[] = { 1, 3, 6, 1, 2, 1, 16, 2, 2 }; struct variable2 etherHistoryTable_variables[] = { /* * magic number , variable type , ro/rw , callback fn , L, oidsuffix */ {DATA_INDEX, ASN_INTEGER, NETSNMP_OLDAPI_RONLY, var_etherHistoryTable, 2, {1, 1}}, {DATA_SAMPLEINDEX, ASN_INTEGER, NETSNMP_OLDAPI_RONLY, var_etherHistoryTable, 2, {1, 2}}, {DATA_INTERVALSTART, ASN_TIMETICKS, NETSNMP_OLDAPI_RONLY, var_etherHistoryTable, 2, {1, 3}}, {DATA_DROPEVENTS, ASN_COUNTER, NETSNMP_OLDAPI_RONLY, var_etherHistoryTable, 2, {1, 4}}, {DATA_OCTETS, ASN_COUNTER, NETSNMP_OLDAPI_RONLY, var_etherHistoryTable, 2, {1, 5}}, {DATA_PKTS, ASN_COUNTER, NETSNMP_OLDAPI_RONLY, var_etherHistoryTable, 2, {1, 6}}, {DATA_BROADCASTPKTS, ASN_COUNTER, NETSNMP_OLDAPI_RONLY, var_etherHistoryTable, 2, {1, 7}}, {DATA_MULTICASTPKTS, ASN_COUNTER, NETSNMP_OLDAPI_RONLY, var_etherHistoryTable, 2, {1, 8}}, {DATA_CRCALIGNERRORS, ASN_COUNTER, NETSNMP_OLDAPI_RONLY, var_etherHistoryTable, 2, {1, 9}}, {DATA_UNDERSIZEPKTS, ASN_COUNTER, NETSNMP_OLDAPI_RONLY, var_etherHistoryTable, 2, {1, 10}}, {DATA_OVERSIZEPKTS, ASN_COUNTER, NETSNMP_OLDAPI_RONLY, var_etherHistoryTable, 2, {1, 11}}, {DATA_FRAGMENTS, ASN_COUNTER, NETSNMP_OLDAPI_RONLY, var_etherHistoryTable, 2, {1, 12}}, {DATA_JABBERS, ASN_COUNTER, NETSNMP_OLDAPI_RONLY, var_etherHistoryTable, 2, {1, 13}}, {DATA_COLLISIONS, ASN_COUNTER, NETSNMP_OLDAPI_RONLY, var_etherHistoryTable, 2, {1, 14}}, {DATA_UTILIZATION, ASN_INTEGER, NETSNMP_OLDAPI_RONLY, var_etherHistoryTable, 2, {1, 15}}, }; void init_history(void) { REGISTER_MIB("historyControlTable", historyControlTable_variables, variable2, historyControlTable_variables_oid); REGISTER_MIB("etherHistoryTable", etherHistoryTable_variables, variable2, etherHistoryTable_variables_oid); ROWAPI_init_table(&HistoryCtrlTable, "History", 0, &history_Create, NULL, /* &history_Clone, */ NULL, /* &history_Delete, */ &history_Validate, &history_Activate, &history_Deactivate, &history_Copy); /* * add_hist_entry (2, 3, 4, 2); */ }