#include #include #include #include #include netsnmp_feature_require(container_fifo); #include "deliverByNotify.h" /* we should never split beyond this */ #define MAX_MESSAGE_COUNT 128 #define BASE_PACKET_SIZE 100 /* should be enough to store SNMPv3 msg headers */ /* if v is !NULL, then estimate it's likely size */ #define ESTIMATE_VAR_SIZE(v) (v?(v->name_length + v->val_len + 8):0) void parse_deliver_config(const char *, char *); void parse_deliver_maxsize_config(const char *, char *); void parse_data_notification_oid_config(const char *, char *); void parse_periodic_time_oid_config(const char *, char *); void parse_message_number_oid_config(const char *, char *); void parse_max_message_number_oid_config(const char *, char *); void free_deliver_config(void); static void _schedule_next_execute_time(void); oid data_notification_oid[MAX_OID_LEN] = { 1, 3, 6, 1, 4, 1, 8072, 3, 1, 5, 4, 0, 1 }; size_t data_notification_oid_len = 13; oid netsnmp_periodic_time_oid[MAX_OID_LEN] = { 1, 3, 6, 1, 4, 1, 8072, 3, 1, 5, 3, 1, 0 }; size_t netsnmp_periodic_time_oid_len = 13; oid netsnmp_message_number_oid[MAX_OID_LEN] = { 1, 3, 6, 1, 4, 1, 8072, 3, 1, 5, 3, 2, 0 }; size_t netsnmp_message_number_oid_len = 13; oid netsnmp_max_message_number_oid[MAX_OID_LEN] = { 1, 3, 6, 1, 4, 1, 8072, 3, 1, 5, 3, 3, 0 }; size_t netsnmp_max_message_number_oid_len = 13; oid objid_snmptrap[] = { 1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0 }; #define DEFAULT_MAX_DELIVER_SIZE -1; static int default_max_size; unsigned int alarm_reg; static netsnmp_container *deliver_container; static int _deliver_compare(deliver_by_notify *lhs, deliver_by_notify *rhs) { /* sort by the next_run time */ if (lhs->next_run < rhs->next_run) return -1; else return 1; } /** Initializes the mteTrigger module */ void init_deliverByNotify(void) { /* register the config tokens */ snmpd_register_config_handler("deliverByNotify", &parse_deliver_config, &free_deliver_config, "[-p] [-m] [-s maxsize] FREQUENCY OID"); snmpd_register_config_handler("deliverByNotifyMaxPacketSize", &parse_deliver_maxsize_config, NULL, "sizeInBytes"); snmpd_register_config_handler("deliverByNotifyOid", &parse_data_notification_oid_config, NULL, /* XXX: reset to default */ "OID"); snmpd_register_config_handler("deliverByNotifyFrequencyOid", &parse_periodic_time_oid_config, NULL, /* XXX: reset to default */ "OID"); snmpd_register_config_handler("deliverByNotifyMessageNumberOid", &parse_message_number_oid_config, NULL, /* XXX: reset to default */ "OID"); snmpd_register_config_handler("deliverByNotifyMaxMessageNumberOid", &parse_max_message_number_oid_config, NULL, /* XXX: reset to default */ "OID"); /* create the container to store the config objects*/ deliver_container = netsnmp_container_find("deliverByNotify:fifo"); if (NULL == deliver_container) { snmp_log(LOG_ERR, "deliverByNotify: failed to initialize our data container\n"); return; } deliver_container->container_name = strdup("deliverByNotify"); deliver_container->compare = (netsnmp_container_compare *) _deliver_compare; /* set the defaults */ default_max_size = DEFAULT_MAX_DELIVER_SIZE; alarm_reg = 0; } void _parse_config_oid(const char *token, char *line, oid *oid_store, size_t *oid_store_len) { size_t tmp_len = MAX_OID_LEN; /* parse the OID given */ if (!snmp_parse_oid(line, oid_store, &tmp_len)) { char buf[SPRINT_MAX_LEN]; snprintf(buf, SPRINT_MAX_LEN-1, "unknown %s OID: %s", token, line); config_perror(buf); return; } *oid_store_len = tmp_len; } void parse_data_notification_oid_config(const char *token, char *line) { _parse_config_oid(token, line, data_notification_oid, &data_notification_oid_len); } void parse_periodic_time_oid_config(const char *token, char *line) { _parse_config_oid(token, line, netsnmp_periodic_time_oid, &netsnmp_periodic_time_oid_len); } void parse_message_number_oid_config(const char *token, char *line) { _parse_config_oid(token, line, netsnmp_message_number_oid, &netsnmp_message_number_oid_len); } void parse_max_message_number_oid_config(const char *token, char *line) { _parse_config_oid(token, line, netsnmp_max_message_number_oid, &netsnmp_max_message_number_oid_len); } void parse_deliver_config(const char *token, char *line) { const char *cp = line; char buf[SPRINT_MAX_LEN]; size_t buf_len = SPRINT_MAX_LEN; int max_size = DEFAULT_MAX_DELIVER_SIZE; int frequency; oid target_oid[MAX_OID_LEN]; size_t target_oid_len = MAX_OID_LEN; deliver_by_notify *new_notify = NULL; int flags = 0; while(cp && *cp == '-') { switch (*(cp+1)) { case 's': cp = skip_token_const(cp); if (!cp) { config_perror("no argument given to -s"); return; } max_size = atoi(cp); break; case 'p': flags = flags | NETSNMP_DELIVER_NO_PERIOD_OID; break; case 'm': flags = flags | NETSNMP_DELIVER_NO_MSG_COUNTS; break; default: config_perror("unknown flag"); return; } cp = skip_token_const(cp); } if (!cp) { config_perror("no frequency given"); return; } copy_nword_const(cp, buf, buf_len); frequency = netsnmp_string_time_to_secs(buf); cp = skip_token_const(cp); if (frequency <= 0) { config_perror("illegal frequency given"); return; } if (!cp) { config_perror("no OID given"); return; } /* parse the OID given */ if (!snmp_parse_oid(cp, target_oid, &target_oid_len)) { config_perror("unknown deliverByNotify OID"); DEBUGMSGTL(("deliverByNotify", "The OID with the problem: %s\n", cp)); return; } /* set up the object to store all the data */ new_notify = SNMP_MALLOC_TYPEDEF(deliver_by_notify); new_notify->frequency = frequency; new_notify->max_packet_size = max_size; new_notify->last_run = time(NULL); new_notify->next_run = new_notify->last_run + frequency; new_notify->flags = flags; new_notify->target = malloc(target_oid_len * sizeof(oid)); new_notify->target_len = target_oid_len; memcpy(new_notify->target, target_oid, target_oid_len*sizeof(oid)); /* XXX: need to do the whole container */ snmp_alarm_register(calculate_time_until_next_run(new_notify, NULL), 0, &deliver_execute, NULL); /* add it to the container */ CONTAINER_INSERT(deliver_container, new_notify); _schedule_next_execute_time(); } void parse_deliver_maxsize_config(const char *token, char *line) { default_max_size = atoi(line); } static void _free_deliver_obj(deliver_by_notify *obj, void *context) { netsnmp_assert_or_return(obj != NULL, ); SNMP_FREE(obj->target); SNMP_FREE(obj); } void free_deliver_config(void) { default_max_size = DEFAULT_MAX_DELIVER_SIZE; CONTAINER_CLEAR(deliver_container, (netsnmp_container_obj_func *) _free_deliver_obj, NULL); if (alarm_reg) { snmp_alarm_unregister(alarm_reg); alarm_reg = 0; } } void deliver_execute(unsigned int clientreg, void *clientarg) { netsnmp_variable_list *vars, *walker, *deliver_notification, *vartmp; netsnmp_variable_list *ready_for_delivery[MAX_MESSAGE_COUNT]; netsnmp_session *sess; int rc, i; deliver_by_notify *obj; netsnmp_iterator *iterator; time_t now = time(NULL); u_long message_count, max_message_count, tmp_long; u_long *max_message_count_ptrs[MAX_MESSAGE_COUNT]; size_t estimated_pkt_size; DEBUGMSGTL(("deliverByNotify", "Starting the execute routine\n")); /* XXX: need to do the whole container */ iterator = CONTAINER_ITERATOR(deliver_container); netsnmp_assert_or_return(iterator != NULL,); sess = netsnmp_query_get_default_session(); for(obj = ITERATOR_FIRST(iterator); obj; obj = ITERATOR_NEXT(iterator)) { /* check if we need to run this one yet */ if (obj->next_run > now) continue; max_message_count = 1; message_count = 0; /* fill the varbind list with the target object */ vars = SNMP_MALLOC_TYPEDEF( netsnmp_variable_list ); snmp_set_var_objid( vars, obj->target, obj->target_len ); vars->type = ASN_NULL; /* walk the OID tree for the data */ rc = netsnmp_query_walk(vars, sess); if (rc != SNMP_ERR_NOERROR) { /* XXX: disable? and reset the next query time point! */ snmp_log(LOG_ERR, "deliverByNotify: failed to issue the query"); ITERATOR_RELEASE(iterator); return; } walker = vars; while (walker) { /* Set up the notification itself */ deliver_notification = NULL; estimated_pkt_size = BASE_PACKET_SIZE; /* add in the notification type */ snmp_varlist_add_variable(&deliver_notification, objid_snmptrap, OID_LENGTH(objid_snmptrap), ASN_OBJECT_ID, data_notification_oid, data_notification_oid_len * sizeof(oid)); estimated_pkt_size += ESTIMATE_VAR_SIZE(deliver_notification); /* add in the current message number in this sequence */ if (!(obj->flags & NETSNMP_DELIVER_NO_PERIOD_OID)) { tmp_long = obj->frequency; snmp_varlist_add_variable(&deliver_notification, netsnmp_periodic_time_oid, netsnmp_periodic_time_oid_len, ASN_UNSIGNED, (const void *) &tmp_long, sizeof(tmp_long)); estimated_pkt_size += ESTIMATE_VAR_SIZE(deliver_notification); } /* add in the current message number in this sequence */ message_count++; if (message_count > MAX_MESSAGE_COUNT) { snmp_log(LOG_ERR, "delivery construct grew too large... giving up\n"); /* XXX: disable it */ /* XXX: send a notification about it? */ ITERATOR_RELEASE(iterator); return; } /* store this for later updating and sending */ ready_for_delivery[message_count-1] = deliver_notification; if (!(obj->flags & NETSNMP_DELIVER_NO_MSG_COUNTS)) { snmp_varlist_add_variable(&deliver_notification, netsnmp_message_number_oid, netsnmp_message_number_oid_len, ASN_UNSIGNED, (const void *) &message_count, sizeof(message_count)); estimated_pkt_size += ESTIMATE_VAR_SIZE(deliver_notification); /* add in the max message number count for this sequence */ vartmp = snmp_varlist_add_variable(&deliver_notification, netsnmp_max_message_number_oid, netsnmp_max_message_number_oid_len, ASN_UNSIGNED, (const void *) &max_message_count, sizeof(max_message_count)); estimated_pkt_size += ESTIMATE_VAR_SIZE(deliver_notification); /* we'll need to update this counter later */ max_message_count_ptrs[message_count-1] = (u_long *) vartmp->val.integer; } else { /* just to be sure */ max_message_count_ptrs[message_count-1] = NULL; } /* copy in the collected data */ while(walker) { snmp_varlist_add_variable(&deliver_notification, walker->name, walker->name_length, walker->type, walker->val.string, walker->val_len); /* 8 byte padding for ASN encodings an a few extra OID bytes */ estimated_pkt_size += ESTIMATE_VAR_SIZE(walker); walker = walker->next_variable; /* if the current size PLUS the next one (which is now in 'walker') is greater than the limet then we stop here */ if (obj->max_packet_size > 0 && estimated_pkt_size + ESTIMATE_VAR_SIZE(walker) >= obj->max_packet_size) { break; } } /* send out the notification */ send_v2trap(deliver_notification); } for(i = 0; i < message_count; i++) { /* update the max count pointer */ if (max_message_count_ptrs[i]) *(max_message_count_ptrs[i]) = message_count; send_v2trap(ready_for_delivery[i]); snmp_free_varbind(ready_for_delivery[i]); } snmp_free_varbind(vars); /* record this as the time processed */ /* XXX: this may creep by a few seconds when processing and maybe we want to do the time stamp at the beginning? */ obj->last_run = time(NULL); } ITERATOR_RELEASE(iterator); /* calculate the next time to sleep for */ _schedule_next_execute_time(); } int calculate_time_until_next_run(deliver_by_notify *it, time_t *now) { time_t local_now; /* if we weren't passed a valid time, fake it */ if (NULL == now) { now = &local_now; time(&local_now); } netsnmp_assert_or_return(it->last_run != 0, -1); /* set the timestamp for the next run */ it->next_run = it->last_run + it->frequency; /* how long until the next run? */ return it->next_run - *now; } static void _schedule_next_execute_time(void) { time_t local_now = time(NULL); int sleep_for = INT_MAX; int next_time; netsnmp_iterator *iterator; deliver_by_notify *obj; DEBUGMSGTL(("deliverByNotify", "Calculating scheduling needed\n")); if (alarm_reg) { snmp_alarm_unregister(alarm_reg); alarm_reg = 0; } iterator = CONTAINER_ITERATOR(deliver_container); if (NULL == iterator) return; for(obj = ITERATOR_FIRST(iterator); obj; obj = ITERATOR_NEXT(iterator)) { next_time = calculate_time_until_next_run(obj, &local_now); DEBUGMSGTL(("deliverByNotify", " obj: %d (last=%d, next_run=%d)\n", next_time, obj->last_run, obj->next_run)); if (next_time < sleep_for) sleep_for = next_time; } if (sleep_for != INT_MAX) { if (sleep_for < 1) sleep_for = 1; /* give at least a small pause */ DEBUGMSGTL(("deliverByNotify", "Next execution in %d (max = %d) seconds\n", sleep_for, INT_MAX)); alarm_reg = snmp_alarm_register(sleep_for, 0, &deliver_execute, NULL); } ITERATOR_RELEASE(iterator); }