There will be many agents to each NMS and a very very wide variety of implementations, some based upon 8 bit embedded TinyIP others full blown Linux S.O multi-task. The section on the OID has given information for thoughts of a simple Linux agents, so here we will look at sending traps.
Traps are an asynchronous communication from the agent to a NMS (or various NMS). Asynchronous because the information is sent when the agent decided rather than on demand from the NMS. Traps are the result of events eg; "the fan on amplifier 4 has failed" or "Power failure, shut down in 1 minute".
Events can be routine eg: "I am still here", "Radio 4 program has changed to source 3" or alarms eg:"The reflected power form aerial 3 is excessive", for that reason it is frequent that a system of priority is used so that the NMS can sort the traps and take the appropiate action.
#define TRAPCOMMUNITYLENGTH 16 // p u b l i c null null...... static char sSNMPtrapcommunity[TRAPCOMMUNITYLENGTH] = {0x70, 0x75, 0x62, 0x6C, 0x69, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const char* ptr_sSNMPtrapcommunity = (char*)sSNMPtrapcommunity; ....... const char sPSUMessage[] = { " <Power supply alarm> " }; ....... static char sTrapMessage[80]; static char* ptr_sTrapMessage = sTrapMessage; ....... static const uint8_t stop_led_trap[] = {0x2b,6,1,4,1,0x82,0x44,0x4F,1,1,1,3,2,STOP_LED_TRAP,0}; static const uint8_t heartbeat_trap[] = {0x2b,6,1,4,1,0x82,0x44,0x4F,1,1,1,3,3,2,0}; ....... #define SIZEOFTRAPBUFFER 384 static void SendSnmpTrap(int trap_number) { // Send a trap. Eg: when there is an alarm bool bGenerateEvent = true; // By default // The trap should coincide with the corresponding define uint8_t * pBuf; uint8_t * pEnd; uint8_t * pPrev; uint8_t * pCur; uint8_t * pTrapOID; uint8_t count = 0; uint16_t trapOID_size = 0; uint32_t network = 0; char tmp[64]; // There is an alarm_sequence_number that we increment for every trap sent up to 8092 limit // The trap number is offset by the BASE_TRAP value int trap = (trap_number - BASE_TRAP); // Allocate a varbind buffer and ensure enough space... pBuf = MALLOC(SIZEOFTRAPBUFFER); // Write the varbind starting from the end pEnd = pCur = (uint8_t *)((uint8_t HUGE *)pBuf + SIZEOFTRAPBUFFER); // Encode the trap // A configurable severity level is to be encoded on each trap alarm_severity_clasification[] // The traps are encoded with the following fields: // <Sequence Number><OID with value><Severity><Time Stamp> // All traps have a common date_time field so we generate this first of all sTrapMessage[0] = 0x00; // null strcat(sTrapMessage, GetDateTimeString(tmp)); // These traps are converted to events and saved. Except for the heartbeat switch (trap) { case HEARTBEAT_TRAP: bGenerateEvent = false; // Encode sName and heartbeat message strcat(sTrapMessage, GetSequenceNumber(tmp)); strcat(sTrapMessage, GetBracketedName(tmp)); strcat(sTrapMessage, sHeartbeatMessage); strcat(sTrapMessage, GetSeverity(256, tmp)); berRWriteValue((uint8_t HUGE **)&pCur, (const uint8_t HUGE *)sTrapMessage, SNMP_String, strlen((char*)sTrapMessage)); // Encode the heartbeat OID, the index is for heartbeat OID berRWriteValue((uint8_t HUGE **)&pCur, (const uint8_t HUGE *)mibVar[HEARTBEAT_MIB_INDEX].oid.name, SNMP_Identifier, mibVar[HEARTBEAT_MIB_INDEX].oid.nlen); // Encode the sequence berRWriteLength((uint8_t HUGE **)&pCur, SNMP_Sequence, (int)((uint8_t HUGE *)pEnd - (uint8_t HUGE *)pCur)); // Send the trap trapOID_size = sizeof(heartbeat_trap); SnmpTrap((const uint8_t*)sSNMPtrapcommunity, heartbeat_trap, sizeof(heartbeat_trap), (const uint8_t *)pCur, (uint16_t)((uint8_t HUGE *)pEnd - (uint8_t HUGE *)pCur)); pTrapOID = MALLOC(trapOID_size); // Copy OID into the newly reserved space for(count = 0; count < trapOID_size; count++) *(pTrapOID + count) = heartbeat_trap[count]; break; case STOP_LED_TRAP: // bit 17 // Encode the integer value berRWriteInt((uint8_t HUGE **)&pCur, Stop_Led, SNMP_Integer, sizeof(Stop_Led)); // Encode the Stop_Led OID, the index is for the stop_led OID berRWriteValue((uint8_t HUGE **)&pCur, (const uint8_t HUGE *)mibVar[STOP_LED_MIB_INDEX].oid.name, SNMP_Identifier, mibVar[STOP_LED_MIB_INDEX].oid.nlen); // Encode the sequence berRWriteLength((uint8_t HUGE **)&pCur, SNMP_Sequence, (int)((uint8_t HUGE *)pEnd - (uint8_t HUGE *)pCur)); // Save pointer to end for next varbind pPrev = pCur; // Encode sName and stop message // Get the latest alarm_sequence_number as a string strcat(sTrapMessage, GetSequenceNumber(tmp)); strcat(sTrapMessage, GetBracketedName(tmp)); strcat(sTrapMessage, sStopMessage); strcat(sTrapMessage, GetSeverity(17, tmp)); berRWriteValue((uint8_t HUGE **)&pCur, (const uint8_t HUGE *)sTrapMessage, SNMP_String, strlen((char*)sTrapMessage)); // Encode the Stop OID, the index is for stop OID berRWriteValue((uint8_t HUGE **)&pCur, (const uint8_t HUGE *)mibVar[TRAP_MESSAGE_MIB_INDEX].oid.name, SNMP_Identifier, mibVar[TRAP_MESSAGE_MIB_INDEX].oid.nlen); // Encode the sequence berRWriteLength((uint8_t HUGE **)&pCur, SNMP_Sequence, (int)((uint8_t HUGE *)pPrev - (uint8_t HUGE *)pCur)); // Send the trap trapOID_size = sizeof(stop_led_trap); SnmpTrap((const uint8_t*)sSNMPtrapcommunity, stop_led_trap, sizeof(stop_led_trap), (const uint8_t *)pCur, (uint16_t)((uint8_t HUGE *)pEnd - (uint8_t HUGE *)pCur)); pTrapOID = MALLOC(trapOID_size); // Copy OID into the newly reserved space for(count = 0; count < trapOID_size; count++) *(pTrapOID + count) = stop_led_trap[count]; break; default: break; } // Free up the buffer Free(pBuf); network = NetDottedTo32BitNetworkByteOrder((char*)sSNMPaddress2); if(network != 0) CallSendTrapTo((const uint8_t *)sSNMPtrapcommunity, (const uint8_t *)sSNMPaddress2, (const uint8_t *)pTrapOID, trapOID_size, (uint8_t *)pCur, (const uint8_t *)pEnd); network = NetDottedTo32BitNetworkByteOrder((char*)sSNMPaddress3); if(network != 0) CallSendTrapTo((const uint8_t *)sSNMPtrapcommunity, (const uint8_t *)sSNMPaddress3, (const uint8_t *)pTrapOID, trapOID_size, (uint8_t *)pCur, (const uint8_t *)pEnd); // Free up the buffers Free(pBuf); Free(pTrapOID);
First we define the OID for the trap community. then a list of standard messages is required for all the standard traps (ie; alarm associated). Space is reserved for the combined trap message (here limited to 80 chars). Next all the OID's for the possible trap messages have to be organised in a tree within the trap community group (will be a long list).
Finally there is the function to generate the traps in a buffer pBuf[sup to 384], a large switch. Entry is with the trap number of the trap to be generated. Two examples are given for the previously given trap OID. The information sequence is "Sequence Number OID with value Severity Time Stamp". having prepared the trap ANS.1 encoded payload the function CallSendTrapTo() is used to send the same trap to various trap destinations.
static void CallSendTrapTo(const uint8_t *contextName, const uint8_t *trapaddress, const uint8_t *oid, uint16_t oidLen, uint8_t *pCur, const uint8_t *pEnd) { // Send our own trap somewhere // contextName The community string, normally public terminated in null // trapaddress The IP address in dotted string format terminated in null // oid The oid of length oidLen // vbs The ANS.1 coded payload of length vbsLen // Basically we take the predefined SNMPv2c structure and plug in the information we have. // From then on it is standard Berkely socket stuff struct sockaddr_in updSerAddr; int udpSrvSocket = -1; uint32_t uptime_sec = 0; uint32_t uptime_msec = 0; uint32_t ticks = 0; uint8_t * pPrev = 0x00; uint32_t seq_num = 32000; uint16_t vbsLen = (uint16_t)((uint8_t HUGE *)pEnd - (uint8_t HUGE *)pCur); // The datagram is constructed going back from *vbs, The maximum length vbsLen < SIZEOFTRAPBUFFER if(vbsLen > 0xC0) return; pPrev = pCur; // Add on the OID berRWriteValue((uint8_t HUGE **)&pCur, (const uint8_t HUGE *)oid, SNMP_Identifier, oidLen); // Add on snmpTrapOID 1.3.6.1.6.3.1.1.4.1.0 pCur--; // To overwrite the size value that was inserted by berRWriteValue *(pCur--) = 0x00; *(pCur--) = 0x01; *(pCur--) = 0x04; *(pCur--) = 0x01; *(pCur--) = 0x01; *(pCur--) = 0x03; *(pCur--) = 0x06; *(pCur--) = 0x01; *(pCur--) = 0x06; *(pCur--) = 0x2B; *(pCur--) = 0x0A; *(pCur--) = 0x06; vbsLen = (uint16_t)((uint8_t HUGE *)pPrev - (uint8_t HUGE *)pCur) - 1; *(pCur--) = (uint8_t)vbsLen; // < 0xFF *(pCur) = 0x30; // Start to tick OID // Add on uptime uptime_sec = UpTimeSeconds(&uptime_msec); ticks = (uptime_sec * 1000 + uptime_msec) / 10; // ticks of 10mSec pPrev = pCur; berRWriteInt((uint8_t HUGE **)&pCur, ticks, ASN1_UINTEGER32, sizeof(ticks)); *(pCur--) = 0x43; // Start to ticks value // The time ticks OID sysUpTime 1.3.6.1.2.1.1.3.0 *(pCur--) = 0x00; *(pCur--) = 0x03; *(pCur--) = 0x01; *(pCur--) = 0x01; *(pCur--) = 0x02; *(pCur--) = 0x01; *(pCur--) = 0x06; *(pCur--) = 0x2B; *(pCur--) = 0x08; *(pCur--) = 0x06; vbsLen = (uint16_t)((uint8_t HUGE *)pPrev - (uint8_t HUGE *)pCur) -1; *(pCur--) = (uint8_t)vbsLen; // < 0xFF *(pCur--) = 0x30; // Start to tick OID // The length of all the bindings vbsLen = (uint16_t)((uint8_t HUGE *)pEnd - (uint8_t HUGE *)pCur) - 1; *(pCur--) = (uint8_t)vbsLen; // < 0xFF *(pCur--) = 0x81; // Multiple sequences *(pCur--) = 0x30; // Start to bindings sequence // For traps error index is 0 *(pCur--) = 0x00; // The value *(pCur--) = 0x01; // Value length 1 *(pCur--) = 0x02; // Value type integer // For traps error status is no error = 0 *(pCur--) = 0x00; // The value *(pCur--) = 0x01; // Value length 1 *(pCur) = 0x02; // Value type integer // Add on a request id // Since this is a trap not a notification any old identification can be used but in our case since we have a sequence number // we use that sequence_number 0 - 8192 + 32000 seq_num += alarm_sequence_number; berRWriteInt((uint8_t HUGE **)&pCur, seq_num, SNMP_Integer, sizeof(seq_num)); *(pCur--) = 0x02; // sequence number to follow // Add on trap header. Max length 256 byte vbsLen = (uint16_t)((uint8_t HUGE *)pEnd - (uint8_t HUGE *)pCur) - 1; *(pCur--) = (uint8_t)vbsLen; *(pCur--) = 0x81; // 1 byte with a variable context length *(pCur) = 0xA7; // Universal constructed object-identifier ie: a trap // END OF PDU // Now add on the variable length community in sSNMPtrapcommunity berRWriteValue((uint8_t HUGE **)&pCur, (const uint8_t HUGE *)contextName, SNMP_String, strlen((char*)contextName)); *(pCur--) = 0x04; // To define the following string type // Add on SNMPv2c *(pCur--) = 0x01; // The value for SNMPv2c *(pCur--) = 0x01; // Value length 1 *(pCur--) = 0x02; // Value type integer // Add on total of the UDP payload < 0xFF vbsLen = (uint16_t)((uint8_t HUGE *)pEnd - (uint8_t HUGE *)pCur) - 1; if(vbsLen > 0xFE) vbsLen = 0xFE; *(pCur--) = (uint8_t)vbsLen; *(pCur--) = 0x81; // Multiple sequence *(pCur) = 0x30; // To ensure pCur now points to first position of payload // We have the complete payload now so send into the socket // Setup the socket information memset(&updSerAddr, 0, sizeof(updSerAddr)); updSerAddr.sin_family = AF_INET; updSerAddr.sin_port = htons(162); updSerAddr.sin_addr.s_addr = NetDottedTo32BitNetworkByteOrder((const char*)trapaddress); udpSrvSocket = socket(PF_INET, SOCK_DGRAM, 0); if(udpSrvSocket >= 0) { // Socket initialised (void)sendto(udpSrvSocket, (const char*)pCur, (uint16_t)((uint8_t HUGE *)pEnd - (uint8_t HUGE *)pCur), 0, (struct sockaddr *)&updSerAddr, sizeof(updSerAddr)); } closesocket(udpSrvSocket); // Done with socket }
The sending is done by filling in a pre-defined SNMPv2c frame with the payload information, then sending the frame as a UPD datagram to the required IP address.