snmp_agent_inform.c
Go to the documentation of this file.
1 /**
2  * @file snmp_agent_inform.c
3  * @brief SNMP inform notifications
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2010-2024 Oryx Embedded SARL. All rights reserved.
10  *
11  * This file is part of CycloneTCP Open.
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26  *
27  * @author Oryx Embedded SARL (www.oryx-embedded.com)
28  * @version 2.4.0
29  **/
30 
31 //Switch to the appropriate trace level
32 #define TRACE_LEVEL SNMP_TRACE_LEVEL
33 
34 //Dependencies
35 #include "core/net.h"
36 #include "snmp/snmp_agent.h"
37 #include "snmp/snmp_agent_misc.h"
38 #include "snmp/snmp_agent_inform.h"
39 #include "mibs/mib2_module.h"
40 #include "debug.h"
41 
42 //Check TCP/IP stack configuration
43 #if (SNMP_AGENT_SUPPORT == ENABLED && SNMP_AGENT_INFORM_SUPPORT == ENABLED)
44 
45 
46 /**
47  * @brief Format SNMP InformRequest message
48  * @param[in] context Pointer to the SNMP agent context
49  * @param[in] version SNMP version identifier
50  * @param[in] userName User name or community name
51  * @param[in] genericTrapType Generic trap type
52  * @param[in] specificTrapCode Specific code
53  * @param[in] objectList List of object names
54  * @param[in] objectListSize Number of entries in the list
55  * @return Error code
56  **/
57 
59  SnmpVersion version, const char_t *userName, uint_t genericTrapType,
60  uint_t specificTrapCode, const SnmpTrapObject *objectList,
61  uint_t objectListSize)
62 {
63  error_t error;
64 
65 #if (SNMP_V2C_SUPPORT == ENABLED)
66  //SNMPv2c version?
68  {
69  //Format InformRequest-PDU
70  error = snmpFormatInformRequestPdu(context, version, userName,
71  genericTrapType, specificTrapCode, objectList, objectListSize);
72  //Any error to report?
73  if(error)
74  return error;
75 
76  //Format SMNP message header
77  error = snmpWriteMessageHeader(&context->response);
78  //Any error to report?
79  if(error)
80  return error;
81  }
82  else
83 #endif
84 #if (SNMP_V3_SUPPORT == ENABLED)
85  //SNMPv3 version?
86  if(version == SNMP_VERSION_3)
87  {
88  SnmpUserEntry *user;
89 
90  //Information about the user name is extracted from the local
91  //configuration datastore
92  user = snmpFindUserEntry(context, userName, osStrlen(userName));
93 
94  //Invalid user name?
95  if(user == NULL || user->status != MIB_ROW_STATUS_ACTIVE)
97 
98  //Save the security profile associated with the current user
99  context->user = *user;
100 
101  //Localize the authentication key with the engine ID of the
102  //remote SNMP device
103  if(context->user.authProtocol != SNMP_AUTH_PROTOCOL_NONE)
104  {
105  //Key localization algorithm
106  error = snmpLocalizeKey(context->user.authProtocol,
107  context->informContextEngine, context->informContextEngineLen,
108  &context->user.rawAuthKey, &context->user.localizedAuthKey);
109  //Any error to report?
110  if(error)
111  return error;
112  }
113 
114  //Localize the privacy key with the engine ID of the remote
115  //SNMP device
116  if(context->user.privProtocol != SNMP_PRIV_PROTOCOL_NONE)
117  {
118  //Key localization algorithm
119  error = snmpLocalizeKey(context->user.authProtocol,
120  context->informContextEngine, context->informContextEngineLen,
121  &context->user.rawPrivKey, &context->user.localizedPrivKey);
122  //Any error to report?
123  if(error)
124  return error;
125  }
126 
127  //Format InformRequest-PDU
128  error = snmpFormatInformRequestPdu(context, version, userName,
129  genericTrapType, specificTrapCode, objectList, objectListSize);
130  //Any error to report?
131  if(error)
132  return error;
133 
134  //Format scopedPDU
135  error = snmpWriteScopedPdu(&context->response);
136  //Any error to report?
137  if(error)
138  return error;
139 
140  //Check whether the privFlag is set
141  if((context->response.msgFlags & SNMP_MSG_FLAG_PRIV) != 0)
142  {
143  //Encrypt data
144  error = snmpEncryptData(&context->user, &context->response,
145  &context->salt);
146  //Any error to report?
147  if(error)
148  return error;
149  }
150 
151  //Format SMNP message header
152  error = snmpWriteMessageHeader(&context->response);
153  //Any error to report?
154  if(error)
155  return error;
156 
157  //Check whether the authFlag is set
158  if((context->response.msgFlags & SNMP_MSG_FLAG_AUTH) != 0)
159  {
160  //Authenticate outgoing SNMP message
161  error = snmpAuthOutgoingMessage(&context->user, &context->response);
162  //Any error to report?
163  if(error)
164  return error;
165  }
166  }
167  else
168 #endif
169  //Invalid SNMP version?
170  {
171  //Debug message
172  TRACE_WARNING(" Invalid SNMP version!\r\n");
173  //Report an error
174  return ERROR_INVALID_VERSION;
175  }
176 
177  //Successful processing
178  return NO_ERROR;
179 }
180 
181 
182 /**
183  * @brief Format InformRequest-PDU
184  * @param[in] context Pointer to the SNMP agent context
185  * @param[in] version SNMP version identifier
186  * @param[in] userName User name or community name
187  * @param[in] genericTrapType Generic trap type
188  * @param[in] specificTrapCode Specific code
189  * @param[in] objectList List of object names
190  * @param[in] objectListSize Number of entries in the list
191  * @return Error code
192  **/
193 
195  SnmpVersion version, const char_t *userName, uint_t genericTrapType,
196  uint_t specificTrapCode, const SnmpTrapObject *objectList,
197  uint_t objectListSize)
198 {
199  error_t error;
201 
202  //Point to the SNMP message
203  message = &context->response;
204  //Initialize SNMP message
206 
207  //SNMP version identifier
208  message->version = version;
209 
210 #if (SNMP_V2C_SUPPORT == ENABLED)
211  //SNMPv2c version?
212  if(version == SNMP_VERSION_2C)
213  {
214  //Community name
215  message->community = userName;
216  message->communityLen = osStrlen(userName);
217  }
218  else
219 #endif
220 #if (SNMP_V3_SUPPORT == ENABLED)
221  //SNMPv3 version?
222  if(version == SNMP_VERSION_3)
223  {
224  //Increment message identifier
225  message->msgId = context->msgId++;
226  //Wrap around if necessary
227  if(context->msgId < 0)
228  context->msgId = 0;
229 
230  //Save the value of the msgId field
231  context->informMsgId = message->msgId;
232 
233  //Maximum message size supported by the sender
234  message->msgMaxSize = SNMP_MAX_MSG_SIZE;
235 
236  //Bit fields which control processing of the message
237  if(context->user.authProtocol != SNMP_AUTH_PROTOCOL_NONE)
238  message->msgFlags |= SNMP_MSG_FLAG_AUTH;
239  if(context->user.privProtocol != SNMP_PRIV_PROTOCOL_NONE)
240  message->msgFlags |= SNMP_MSG_FLAG_PRIV;
241 
242  //The reportable flag must always be set for acknowledged
243  //notification-type PDUs (refer to RFC 3412, section 6.4)
244  message->msgFlags |= SNMP_MSG_FLAG_REPORTABLE;
245 
246  //Security model used by the sender
247  message->msgSecurityModel = SNMP_SECURITY_MODEL_USM;
248 
249  //Authoritative engine identifier
250  message->msgAuthEngineId = context->informContextEngine;
251  message->msgAuthEngineIdLen = context->informContextEngineLen;
252  //Number of times the SNMP engine has rebooted
253  message->msgAuthEngineBoots = context->informEngineBoots;
254  //Number of seconds since last reboot
255  message->msgAuthEngineTime = context->informEngineTime;
256  //User name
257  message->msgUserName = userName;
258  message->msgUserNameLen = osStrlen(userName);
259  //Authentication parameters
260  message->msgAuthParameters = NULL;
261  //Length of the authentication parameters
262  message->msgAuthParametersLen = snmpGetMacLength(context->user.authProtocol);
263  //Privacy parameters
264  message->msgPrivParameters = context->privParameters;
265 
266  //Length of the privacy parameters
267  if(context->user.privProtocol == SNMP_PRIV_PROTOCOL_DES)
268  {
269  message->msgPrivParametersLen = 8;
270  }
271  else if(context->user.privProtocol == SNMP_PRIV_PROTOCOL_AES)
272  {
273  message->msgPrivParametersLen = 8;
274  }
275  else
276  {
277  message->msgPrivParametersLen = 0;
278  }
279 
280  //Context engine identifier
281  message->contextEngineId = context->contextEngine;
282  message->contextEngineIdLen = context->contextEngineLen;
283  //Context name
284  message->contextName = context->contextName;
285  message->contextNameLen = osStrlen(context->contextName);
286  }
287  else
288 #endif
289  //Invalid SNMP version?
290  {
291  //Report an error
292  return ERROR_INVALID_VERSION;
293  }
294 
295  //Prepare to send an InformRequest-PDU
296  message->pduType = SNMP_PDU_INFORM_REQUEST;
297 
298  //Increment request identifier
299  message->requestId = context->requestId++;
300  //Wrap around if necessary
301  if(context->requestId < 0)
302  context->requestId = 0;
303 
304  //Save the value of the request-id field
305  context->informRequestId = message->requestId;
306 
307  //Make room for the message header at the beginning of the buffer
308  error = snmpComputeMessageOverhead(&context->response);
309  //Any error to report?
310  if(error)
311  return error;
312 
313  //Format the list of variable bindings
314  error = snmpWriteTrapVarBindingList(context, genericTrapType,
315  specificTrapCode, objectList, objectListSize);
316  //Any error to report?
317  if(error)
318  return error;
319 
320  //Format PDU header
321  error = snmpWritePduHeader(&context->response);
322  //Return status code
323  return error;
324 }
325 
326 
327 /**
328  * @brief Format SNMP GetRequest message
329  * @param[in] context Pointer to the SNMP agent context
330  * @param[in] version SNMP version identifier
331  * @return Error code
332  **/
333 
336 {
337  error_t error;
338 
339  //Clear the security profile
340  osMemset(&context->user, 0, sizeof(SnmpUserEntry));
341 
342  //Format GetRequest-PDU
343  error = snmpFormatGetRequestPdu(context, version);
344  //Any error to report?
345  if(error)
346  return error;
347 
348  //Format scopedPDU
349  error = snmpWriteScopedPdu(&context->response);
350  //Any error to report?
351  if(error)
352  return error;
353 
354  //Format SMNP message header
355  error = snmpWriteMessageHeader(&context->response);
356  //Any error to report?
357  if(error)
358  return error;
359 
360  //Successful processing
361  return NO_ERROR;
362 }
363 
364 
365 /**
366  * @brief Format GetRequest-PDU (engine ID discovery procedure)
367  * @param[in] context Pointer to the SNMP agent context
368  * @param[in] version SNMP version identifier
369  * @return Error code
370  **/
371 
373 {
374  error_t error;
376 
377  //Point to the SNMP message
378  message = &context->response;
379  //Initialize SNMP message
381 
382  //SNMP version identifier
383  message->version = version;
384 
385 #if (SNMP_V3_SUPPORT == ENABLED)
386  //SNMPv3 version?
387  if(version == SNMP_VERSION_3)
388  {
389  //Increment message identifier
390  message->msgId = context->msgId++;
391  //Wrap around if necessary
392  if(context->msgId < 0)
393  context->msgId = 0;
394 
395  //Save the value of the msgId field
396  context->informMsgId = message->msgId;
397 
398  //Maximum message size supported by the sender
399  message->msgMaxSize = SNMP_MAX_MSG_SIZE;
400 
401  //The reportable flag must always be set for request-type PDUs (refer
402  //to RFC 3412, section 6.4)
403  message->msgFlags |= SNMP_MSG_FLAG_REPORTABLE;
404 
405  //Security model used by the sender
406  message->msgSecurityModel = SNMP_SECURITY_MODEL_USM;
407  }
408 #endif
409 
410  //Prepare to send an GetRequest-PDU
411  message->pduType = SNMP_PDU_GET_REQUEST;
412 
413  //Increment request identifier
414  message->requestId = context->requestId++;
415  //Wrap around if necessary
416  if(context->requestId < 0)
417  context->requestId = 0;
418 
419  //Save the value of the request-id field
420  context->informRequestId = message->requestId;
421 
422  //Make room for the message header at the beginning of the buffer
423  error = snmpComputeMessageOverhead(&context->response);
424  //Any error to report?
425  if(error)
426  return error;
427 
428  //Format PDU header
429  error = snmpWritePduHeader(&context->response);
430  //Return status code
431  return error;
432 }
433 
434 
435 /**
436  * @brief Process GetResponse-PDU
437  * @param[in] context Pointer to the SNMP agent context
438  * @return Error code
439  **/
440 
442 {
444 
445  //Debug message
446  TRACE_INFO("Parsing GetResponse-PDU...\r\n");
447 
448  //Point to the incoming SNMP message
449  message = &context->request;
450 
451  //Total number of SNMP Get-Response PDUs which have been accepted and
452  //processed by the SNMP protocol entity
453  MIB2_SNMP_INC_COUNTER32(snmpInGetResponses, 1);
454 
455  //Check the error-status field
456  if(message->errorStatus == SNMP_ERROR_NONE)
457  {
458  //Compare the request-id against the expected identifier
459  if(message->requestId == context->informRequestId)
460  {
461  //The inform request has been acknowledged
462  osSetEvent(&context->informEvent);
463  }
464  }
465 
466  //Successful processing
467  return NO_ERROR;
468 }
469 
470 
471 /**
472  * @brief Process Report-PDU
473  * @param[in] context Pointer to the SNMP agent context
474  * @return Error code
475  **/
476 
478 {
479 #if (SNMP_V3_SUPPORT == ENABLED)
481 
482  //Debug message
483  TRACE_INFO("Parsing Report-PDU...\r\n");
484 
485  //Point to the incoming SNMP message
486  message = &context->request;
487 
488  //Sanity check
489  if(message->msgAuthEngineIdLen <= SNMP_MAX_CONTEXT_ENGINE_SIZE)
490  {
491  //Save the authoritative engine identifier
492  osMemcpy(context->informContextEngine, message->msgAuthEngineId,
493  message->msgAuthEngineIdLen);
494 
495  //Save the length of the engine identifier
496  context->informContextEngineLen = message->msgAuthEngineIdLen;
497 
498  //Save the msgAuthoritativeEngineBoots field
499  context->informEngineBoots = message->msgAuthEngineBoots;
500  //Save the msgAuthoritativeEngineTime field
501  context->informEngineTime = message->msgAuthEngineTime;
502 
503  //Check current state
504  if(context->informState == SNMP_AGENT_STATE_WAITING_REPORT)
505  {
506  //Valid context engine identifier?
507  if(context->informContextEngineLen > 0)
508  {
509  //The discovery process is complete
510  osSetEvent(&context->informEvent);
511  }
512  }
513  }
514 
515  //Successful processing
516  return NO_ERROR;
517 #else
518  //SNMPv3 is not supported
519  return ERROR_NOT_IMPLEMENTED;
520 #endif
521 }
522 
523 #endif
uint8_t message[]
Definition: chap.h:154
uint8_t version
Definition: coap_common.h:177
unsigned int uint_t
Definition: compiler_port.h:50
char char_t
Definition: compiler_port.h:48
Debugging facilities.
#define TRACE_WARNING(...)
Definition: debug.h:85
#define TRACE_INFO(...)
Definition: debug.h:95
error_t
Error codes.
Definition: error.h:43
@ ERROR_NOT_IMPLEMENTED
Definition: error.h:66
@ NO_ERROR
Success.
Definition: error.h:44
@ ERROR_UNKNOWN_USER_NAME
Definition: error.h:261
@ ERROR_INVALID_VERSION
Definition: error.h:118
MIB-II module.
#define MIB2_SNMP_INC_COUNTER32(name, value)
Definition: mib2_module.h:192
@ MIB_ROW_STATUS_ACTIVE
Definition: mib_common.h:103
TCP/IP stack core.
#define osMemset(p, value, length)
Definition: os_port.h:135
#define osMemcpy(dest, src, length)
Definition: os_port.h:141
#define osStrlen(s)
Definition: os_port.h:165
void osSetEvent(OsEvent *event)
Set the specified event object to the signaled state.
SNMP agent (Simple Network Management Protocol)
#define SnmpAgentContext
Definition: snmp_agent.h:36
error_t snmpFormatGetRequestPdu(SnmpAgentContext *context, SnmpVersion version)
Format GetRequest-PDU (engine ID discovery procedure)
error_t snmpFormatInformRequestPdu(SnmpAgentContext *context, SnmpVersion version, const char_t *userName, uint_t genericTrapType, uint_t specificTrapCode, const SnmpTrapObject *objectList, uint_t objectListSize)
Format InformRequest-PDU.
error_t snmpProcessReportPdu(SnmpAgentContext *context)
Process Report-PDU.
error_t snmpFormatGetRequestMessage(SnmpAgentContext *context, SnmpVersion version)
Format SNMP GetRequest message.
error_t snmpFormatInformRequestMessage(SnmpAgentContext *context, SnmpVersion version, const char_t *userName, uint_t genericTrapType, uint_t specificTrapCode, const SnmpTrapObject *objectList, uint_t objectListSize)
Format SNMP InformRequest message.
error_t snmpProcessGetResponsePdu(SnmpAgentContext *context)
Process GetResponse-PDU.
SNMP inform notifications.
@ SNMP_AGENT_STATE_WAITING_REPORT
void snmpInitMessage(SnmpMessage *message)
Initialize a SNMP message.
error_t snmpWriteScopedPdu(SnmpMessage *message)
Format scopedPDU.
error_t snmpComputeMessageOverhead(SnmpMessage *message)
Compute SNMP message overhead.
error_t snmpWritePduHeader(SnmpMessage *message)
Format PDU header.
error_t snmpWriteMessageHeader(SnmpMessage *message)
Format SNMP message header.
error_t snmpWriteTrapVarBindingList(SnmpAgentContext *context, uint_t genericTrapType, uint_t specificTrapCode, const SnmpTrapObject *objectList, uint_t objectListSize)
Format the variable binding list for Trap-PDU or SNMPv2-Trap-PDU.
Helper functions for SNMP agent.
error_t snmpLocalizeKey(SnmpAuthProtocol authProtocol, const uint8_t *engineId, size_t engineIdLen, SnmpKey *key, SnmpKey *localizedKey)
Key localization algorithm.
error_t snmpEncryptData(const SnmpUserEntry *user, SnmpMessage *message, uint64_t *salt)
Data encryption.
size_t snmpGetMacLength(SnmpAuthProtocol authProtocol)
Get the length of the truncated MAC for a given authentication protocol.
error_t snmpAuthOutgoingMessage(const SnmpUserEntry *user, SnmpMessage *message)
Authenticate outgoing SNMP message.
SnmpUserEntry * snmpFindUserEntry(SnmpAgentContext *context, const char_t *name, size_t length)
Search the user table for a given user name.
@ SNMP_AUTH_PROTOCOL_NONE
No authentication.
@ SNMP_SECURITY_MODEL_USM
User-based security model.
@ SNMP_PRIV_PROTOCOL_DES
DES-CBC.
@ SNMP_PRIV_PROTOCOL_NONE
No privacy.
@ SNMP_PRIV_PROTOCOL_AES
AES-128-CFB.
@ SNMP_MSG_FLAG_PRIV
@ SNMP_MSG_FLAG_REPORTABLE
@ SNMP_MSG_FLAG_AUTH
#define SNMP_MAX_CONTEXT_ENGINE_SIZE
Definition: snmp_common.h:67
@ SNMP_PDU_GET_REQUEST
Definition: snmp_common.h:150
@ SNMP_PDU_INFORM_REQUEST
Definition: snmp_common.h:156
SnmpVersion
SNMP version identifiers.
Definition: snmp_common.h:137
@ SNMP_VERSION_3
Definition: snmp_common.h:140
@ SNMP_VERSION_2C
Definition: snmp_common.h:139
#define SNMP_MAX_MSG_SIZE
Definition: snmp_common.h:60
@ SNMP_ERROR_NONE
Definition: snmp_common.h:184
SNMP message.
Object descriptor for trap notifications.
User table entry.
MibRowStatus status
Status of the user.