blob: c8e9c25c93dcbaa670ae18f123a9fa1a217848e0 [file] [log] [blame]
/**************************************************************
* 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 <net-snmp/net-snmp-config.h>
#if HAVE_STDLIB_H
#include <stdlib.h>
#endif
#if TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# if HAVE_SYS_TIME_H
# include <sys/time.h>
# else
# include <time.h>
# endif
#endif
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
#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);
*/
}