|
C 로 구현된 SNMP Agent 부분 입니다. RFC 1067 문서를 참고하세요.
int
handle_snmp_packet(int operation, struct snmp_session *session, int reqid,
struct snmp_pdu *pdu, void *magic)
{
struct agent_snmp_session *asp;
int status, allDone, i, error_index = 0;
struct variable_list *var_ptr, *var_ptr2;
if ( magic == NULL ) {
asp = init_agent_snmp_session( session, pdu );
status = SNMP_ERR_NOERROR;
}
else {
asp = (struct agent_snmp_session *)magic;
status = asp->status;
}
if (asp->outstanding_requests != NULL)
return 1;
if ( check_access(pdu) != 0) {
/* access control setup is incorrect */
send_easy_trap(SNMP_TRAP_AUTHFAIL, 0);
if (asp->pdu->version != SNMP_VERSION_1 && asp->pdu->version != SNMP_VERSION_2c) {
asp->pdu->errstat = SNMP_ERR_AUTHORIZATIONERROR;
asp->pdu->command = SNMP_MSG_RESPONSE;
snmp_increment_statistic(STAT_SNMPOUTPKTS);
if (! snmp_send( asp->session, asp->pdu ))
snmp_free_pdu(asp->pdu);
asp->pdu = NULL;
free_agent_snmp_session(asp);
return 1;
} else {
/* drop the request */
free_agent_snmp_session( asp );
return 0;
}
}
switch (pdu->command) {
case SNMP_MSG_GET:
if ( asp->mode != RESERVE1 )
break; /* Single pass */
snmp_increment_statistic(STAT_SNMPINGETREQUESTS);
status = handle_next_pass( asp );
asp->mode = RESERVE2;
break;
case SNMP_MSG_GETNEXT:
if ( asp->mode != RESERVE1 )
break; /* Single pass */
snmp_increment_statistic(STAT_SNMPINGETNEXTS);
asp->exact = FALSE;
status = handle_next_pass( asp );
asp->mode = RESERVE2;
break;
case SNMP_MSG_GETBULK:
/*
* GETBULKS require multiple passes. The first pass handles the
* explicitly requested varbinds, and subsequent passes append
* to the existing var_op_list. Each pass (after the first)
* uses the results of the preceeding pass as the input list
* (delimited by the start & end pointers.
* Processing is terminated if all entries in a pass are
* EndOfMib, or the maximum number of repetitions are made.
*/
if ( asp->mode == RESERVE1 ) {
snmp_increment_statistic(STAT_SNMPINGETREQUESTS);
asp->exact = FALSE;
/*
* Limit max repetitions to something reasonable
* XXX: We should figure out what will fit somehow...
*/
if ( asp->pdu->errindex > 100 )
asp->pdu->errindex = 100;
/*
* If max-repetitions is 0, we shouldn't
* process the non-nonrepeaters at all
* so set up 'asp->end' accordingly
*/
if ( asp->pdu->errindex == 0 ) {
if ( asp->pdu->errstat == 0 ) {
/* Nothing to do at all */
snmp_free_varbind(asp->pdu->variables);
asp->pdu->variables=NULL;
asp->start=NULL;
}
else {
asp->end = asp->pdu->variables;
i = asp->pdu->errstat;
while ( --i > 0 )
if ( asp->end )
asp->end = asp->end->next_variable;
snmp_free_varbind(asp->end->next_variable);
asp->end->next_variable = NULL;
}
}
status = handle_next_pass( asp ); /* First pass */
asp->mode = RESERVE2;
if ( status != SNMP_ERR_NOERROR )
break;
while ( asp->pdu->errstat-- > 0 ) /* Skip non-repeaters */
{
if ( NULL != asp->start ) /* if there are variables ... */
asp->start = asp->start->next_variable;
}
asp->pdu->errindex--; /* Handled first repetition */
if ( asp->outstanding_requests != NULL )
return 1;
}
if ( NULL != asp->start ) /* if there are variables ... */
while ( asp->pdu->errindex-- > 0 ) { /* Process repeaters */
/*
* Add new variable structures for the
* repeating elements, ready for the next pass.
* Also check that these are not all EndOfMib
*/
allDone = TRUE; /* Check for some content */
for ( var_ptr = asp->start;
var_ptr != asp->end->next_variable;
var_ptr = var_ptr->next_variable ) {
/* XXX: we don't know the size of the next
OID, so assume the maximum length */
if ( var_ptr->type != SNMP_ENDOFMIBVIEW ) {
var_ptr2 = snmp_add_null_var(asp->pdu, var_ptr->name, MAX_OID_LEN);
for ( i=var_ptr->name_length ; i<MAX_OID_LEN ; i++)
var_ptr2->name[i] = 0;
var_ptr2->name_length = var_ptr->name_length;
allDone = FALSE;
}
}
if ( allDone )
break;
asp->start = asp->end->next_variable;
while ( asp->end->next_variable != NULL )
asp->end = asp->end->next_variable;
status = handle_next_pass( asp );
if ( status != SNMP_ERR_NOERROR )
break;
if ( asp->outstanding_requests != NULL )
return 1;
}
break;
case SNMP_MSG_SET:
/*
* SETS require 3-4 passes through the var_op_list. The first two
* passes verify that all types, lengths, and values are valid
* and may reserve resources and the third does the set and a
* fourth executes any actions. Then the identical GET RESPONSE
* packet is returned.
* If either of the first two passes returns an error, another
* pass is made so that any reserved resources can be freed.
* If the third pass returns an error, another pass is made so that
* any changes can be reversed.
* If the fourth pass (or any of the error handling passes)
* return an error, we'd rather not know about it!
*/
if ( asp->mode == RESERVE1 ) {
snmp_increment_statistic(STAT_SNMPINSETREQUESTS);
asp->rw = WRITE;
status = handle_next_pass( asp );
if ( status != SNMP_ERR_NOERROR ){
asp->mode = FREE;
error_index = asp->index;
}
else
asp->mode = RESERVE2;
if ( asp->outstanding_requests != NULL )
return 1;
}
if ( asp->mode == RESERVE2 ) {
status = handle_next_pass( asp );
if ( status != SNMP_ERR_NOERROR ){
asp->mode = FREE;
error_index = asp->index;
}
else
asp->mode = ACTION;
if ( asp->outstanding_requests != NULL )
return 1;
}
if ( asp->mode == ACTION ) {
status = handle_next_pass( asp );
if ( status != SNMP_ERR_NOERROR ){
asp->mode = UNDO;
error_index = asp->index;
}
else
asp->mode = COMMIT;
if ( asp->outstanding_requests != NULL )
return 1;
}
if ( asp->mode == COMMIT ) {
status = handle_next_pass( asp );
if ( status != SNMP_ERR_NOERROR ) {
status = SNMP_ERR_COMMITFAILED;
asp->mode = FINISHED_FAILURE;
error_index = asp->index;
}
else
asp->mode = FINISHED_SUCCESS;
if ( asp->outstanding_requests != NULL )
return 1;
}
if ( asp->mode == UNDO ) {
if (handle_next_pass( asp ) != SNMP_ERR_NOERROR ) {
status = SNMP_ERR_UNDOFAILED;
error_index = 0;
}
asp->mode = FINISHED_FAILURE;
}
if ( asp->mode == FREE ) {
(void) handle_next_pass( asp );
}
asp->index = error_index;
break;
case SNMP_MSG_RESPONSE:
snmp_increment_statistic(STAT_SNMPINGETRESPONSES);
free_agent_snmp_session( asp );
return 0;
case SNMP_MSG_TRAP:
case SNMP_MSG_TRAP2:
snmp_increment_statistic(STAT_SNMPINTRAPS);
free_agent_snmp_session( asp );
return 0;
default:
snmp_increment_statistic(STAT_SNMPINASNPARSEERRS);
free_agent_snmp_session( asp );
return 0;
}
if ( asp->outstanding_requests != NULL ) {
asp->status = status;
asp->next = agent_session_list;
agent_session_list = asp;
}
else {
/*
* May need to "dumb down" a SET error status for a
* v1 query. See RFC2576 - section 4.3
*/
if (( asp->pdu ) &&
( asp->pdu->command == SNMP_MSG_SET ) &&
( asp->pdu->version == SNMP_VERSION_1 )) {
switch ( status ) {
case SNMP_ERR_WRONGVALUE:
case SNMP_ERR_WRONGENCODING:
case SNMP_ERR_WRONGTYPE:
case SNMP_ERR_WRONGLENGTH:
case SNMP_ERR_INCONSISTENTVALUE:
status = SNMP_ERR_BADVALUE;
break;
case SNMP_ERR_NOACCESS:
case SNMP_ERR_NOTWRITABLE:
case SNMP_ERR_NOCREATION:
case SNMP_ERR_INCONSISTENTNAME:
case SNMP_ERR_AUTHORIZATIONERROR:
status = SNMP_ERR_NOSUCHNAME;
break;
case SNMP_ERR_RESOURCEUNAVAILABLE:
case SNMP_ERR_COMMITFAILED:
case SNMP_ERR_UNDOFAILED:
status = SNMP_ERR_GENERR;
break;
}
}
/*
* Similarly we may need to "dumb down" v2 exception
* types to throw an error for a v1 query.
* See RFC2576 - section 4.1.2.3
*/
if (( asp->pdu ) &&
( asp->pdu->command != SNMP_MSG_SET ) &&
( asp->pdu->version == SNMP_VERSION_1 )) {
for ( var_ptr = asp->pdu->variables, i=1 ;
var_ptr != NULL ;
var_ptr = var_ptr->next_variable, i++ ) {
switch ( var_ptr->type ) {
case SNMP_NOSUCHOBJECT:
case SNMP_NOSUCHINSTANCE:
case SNMP_ENDOFMIBVIEW:
case ASN_COUNTER64:
status = SNMP_ERR_NOSUCHNAME;
asp->index=i;
break;
}
}
}
/*
* Update the snmp error-count statistics
* XXX - should we include the V2 errors in this or not?
*/
#define INCLUDE_V2ERRORS_IN_V1STATS
switch ( status ) {
#ifdef INCLUDE_V2ERRORS_IN_V1STATS
case SNMP_ERR_WRONGVALUE:
case SNMP_ERR_WRONGENCODING:
case SNMP_ERR_WRONGTYPE:
case SNMP_ERR_WRONGLENGTH:
case SNMP_ERR_INCONSISTENTVALUE:
#endif
case SNMP_ERR_BADVALUE:
snmp_increment_statistic(STAT_SNMPOUTBADVALUES);
break;
#ifdef INCLUDE_V2ERRORS_IN_V1STATS
case SNMP_ERR_NOACCESS:
case SNMP_ERR_NOTWRITABLE:
case SNMP_ERR_NOCREATION:
case SNMP_ERR_INCONSISTENTNAME:
case SNMP_ERR_AUTHORIZATIONERROR:
#endif
case SNMP_ERR_NOSUCHNAME:
snmp_increment_statistic(STAT_SNMPOUTNOSUCHNAMES);
break;
#ifdef INCLUDE_V2ERRORS_IN_V1STATS
case SNMP_ERR_RESOURCEUNAVAILABLE:
case SNMP_ERR_COMMITFAILED:
case SNMP_ERR_UNDOFAILED:
#endif
case SNMP_ERR_GENERR:
snmp_increment_statistic(STAT_SNMPOUTGENERRS);
break;
case SNMP_ERR_TOOBIG:
snmp_increment_statistic(STAT_SNMPOUTTOOBIGS);
break;
}
if (( status == SNMP_ERR_NOERROR ) && ( asp->pdu )) {
snmp_increment_statistic_by(
(asp->pdu->command == SNMP_MSG_SET ?
STAT_SNMPINTOTALSETVARS : STAT_SNMPINTOTALREQVARS ),
count_varbinds( asp->pdu ));
}
else {
/*
* Use a copy of the original request
* to report failures.
*/
snmp_free_pdu( asp->pdu );
asp->pdu = asp->orig_pdu;
asp->orig_pdu = NULL;
}
if ( asp->pdu ) {
asp->pdu->command = SNMP_MSG_RESPONSE;
asp->pdu->errstat = status;
if (status == SNMP_ERR_NOERROR) {
asp->pdu->errindex = 0;
} else {
asp->pdu->errindex = asp->index;
}
if (! snmp_send( asp->session, asp->pdu ))
snmp_free_pdu(asp->pdu);
snmp_increment_statistic(STAT_SNMPOUTPKTS);
snmp_increment_statistic(STAT_SNMPOUTGETRESPONSES);
asp->pdu = NULL;
free_agent_snmp_session( asp );
}
}
return 1;
}
|