snmp_agent.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  1. /*
  2. * Copyright 1998-2007 by egnite Software GmbH
  3. * Copyright 1988, 1989, 1991, 1992 by Carnegie Mellon University
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions
  7. * are met:
  8. *
  9. * 1. Redistributions of source code must retain the above copyright
  10. * notice, this list of conditions and the following disclaimer.
  11. * 2. Redistributions in binary form must reproduce the above copyright
  12. * notice, this list of conditions and the following disclaimer in the
  13. * documentation and/or other materials provided with the distribution.
  14. * 3. Neither the name of the copyright holders nor the names of
  15. * contributors may be used to endorse or promote products derived
  16. * from this software without specific prior written permission.
  17. *
  18. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  19. * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  20. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  21. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  22. * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  23. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  24. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  25. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  26. * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  27. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
  28. * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  29. * SUCH DAMAGE.
  30. *
  31. * For additional information see http://www.ethernut.de/
  32. */
  33. #include <sys/types.h>
  34. #include <stdlib.h>
  35. #include <string.h>
  36. #ifndef WIN32
  37. #include <memdebug.h>
  38. #include <arpa/inet.h>
  39. #endif
  40. #include <pro/snmp_config.h>
  41. #include <pro/snmp.h>
  42. #include <pro/snmp_api.h>
  43. #include <pro/snmp_auth.h>
  44. #include <pro/snmp_mib.h>
  45. #include <pro/snmp_agent.h>
  46. /*!
  47. * \addtogroup xgSNMP
  48. */
  49. /*@{*/
  50. /*
  51. * Using this as a global had been derived from the original CMU code.
  52. * It is very ugly (shiffer), but may require some effort to transform
  53. * it into something local.
  54. */
  55. static uint8_t *packet_end;
  56. static void SetVariable(const uint8_t * var_val, uint8_t var_val_type, uint8_t * statP, size_t statLen)
  57. {
  58. size_t buffersize = 1000;
  59. switch (var_val_type) {
  60. case ASN_INTEGER:
  61. case ASN_COUNTER:
  62. case ASN_GAUGE:
  63. case ASN_TIMETICKS:
  64. AsnIntegerParse(var_val, &buffersize, &var_val_type, (long *) statP);
  65. break;
  66. case ASN_OCTET_STR:
  67. case ASN_IPADDRESS:
  68. case ASN_OPAQUE:
  69. AsnOctetStringParse(var_val, &buffersize, &var_val_type, statP, &statLen);
  70. break;
  71. case ASN_OBJECT_ID:
  72. AsnOidParse(var_val, &buffersize, &var_val_type, (OID *) statP, &statLen);
  73. break;
  74. }
  75. }
  76. /*!
  77. * \brief Parse a list of variables.
  78. *
  79. * \param data Pointer to the start of the list.
  80. * \param length Contains the number of valid bytes following the
  81. * start of the list.
  82. * \param out_data Pointer to the output buffer.
  83. * \param out_length Number of bytes available in the output buffer.
  84. * \param index Error index.
  85. * \param msgtype Type of the incoming packet.
  86. * \param action Action to perform, either SNMP_ACT_RESERVE1,
  87. * SNMP_ACT_RESERVE2, SNMP_ACT_COMMIT, SNMP_ACT_ACTION
  88. * or SNMP_ACT_FREE.
  89. *
  90. * \return 0 on success. Otherwise an error code is returned.
  91. *
  92. */
  93. static int SnmpVarListParse(SNMP_SESSION * sess, const uint8_t * data, size_t length, uint8_t * out_data, size_t out_length,
  94. long *index, int msgtype, int action)
  95. {
  96. OID var_name[MAX_OID_LEN];
  97. size_t var_name_len;
  98. size_t var_val_len;
  99. uint8_t var_val_type;
  100. uint8_t *var_val;
  101. uint8_t statType;
  102. uint8_t *statP;
  103. size_t statLen;
  104. uint16_t acl;
  105. int exact, err;
  106. WMETHOD *wmethod;
  107. uint8_t *headerP;
  108. uint8_t *var_list_start;
  109. size_t dummyLen;
  110. int noSuchObject = 0;
  111. exact = (msgtype != SNMP_MSG_GETNEXT);
  112. /* Check if the list starts with a sequence header and get its length. */
  113. if ((data = AsnSequenceParse(data, &length, ASN_SEQUENCE | ASN_CONSTRUCTOR)) == NULL) {
  114. return SNMP_PARSE_ERROR;
  115. }
  116. /* Build ASN header. */
  117. headerP = out_data;
  118. if ((out_data = AsnSequenceBuild(out_data, &out_length, ASN_SEQUENCE | ASN_CONSTRUCTOR, 0)) == NULL) {
  119. return SNMP_BUILD_ERROR;
  120. }
  121. var_list_start = out_data;
  122. *index = 1;
  123. while (length > 0) {
  124. /* Get name and ASN1 encoded value of the next variable. */
  125. var_name_len = MAX_OID_LEN;
  126. if ((data = SnmpVarParse(data, &length, var_name, &var_name_len, &var_val_type, &var_val, &var_val_len)) == NULL) {
  127. return SNMP_PARSE_ERROR;
  128. }
  129. /* Now attempt to retrieve the variable on the local entity. */
  130. statP = SnmpMibFind(var_name, &var_name_len, &statType, &statLen, &acl, exact, &wmethod, &noSuchObject);
  131. /* Check access. */
  132. if (msgtype == SNMP_MSG_SET) {
  133. /* Make sure we have write access. */
  134. if (acl != ACL_RWRITE) {
  135. return sess->sess_version == SNMP_VERSION_1 ? SNMP_ERR_NOSUCHNAME : SNMP_ERR_NOTWRITABLE;
  136. }
  137. if (wmethod == NULL) {
  138. if (statP == NULL) {
  139. return sess->sess_version == SNMP_VERSION_1 ? SNMP_ERR_NOSUCHNAME : SNMP_ERR_NOCREATION;
  140. }
  141. /* Check if the type and value is consistent with this entity's variable. */
  142. if (var_val_len > statLen || var_val_type != statType) {
  143. return sess->sess_version == SNMP_VERSION_1 ? SNMP_ERR_BADVALUE : SNMP_ERR_WRONGTYPE;
  144. }
  145. /* Actually do the set if necessary. */
  146. if (action == SNMP_ACT_COMMIT) {
  147. SetVariable(var_val, var_val_type, statP, statLen);
  148. }
  149. } else {
  150. err = (*wmethod) (action, var_val, var_val_type, var_val_len, var_name, var_name_len);
  151. /*
  152. * Map the SNMPv2 error codes to SNMPv1 error codes (RFC 2089).
  153. */
  154. if (err && sess->sess_version == SNMP_VERSION_1) {
  155. switch (err) {
  156. case SNMP_ERR_WRONGVALUE:
  157. case SNMP_ERR_WRONGENCODING:
  158. case SNMP_ERR_WRONGTYPE:
  159. case SNMP_ERR_WRONGLENGTH:
  160. case SNMP_ERR_INCONSISTENTVALUE:
  161. err = SNMP_ERR_BADVALUE;
  162. break;
  163. case SNMP_ERR_NOACCESS:
  164. case SNMP_ERR_NOTWRITABLE:
  165. case SNMP_ERR_NOCREATION:
  166. case SNMP_ERR_INCONSISTENTNAME:
  167. case SNMP_ERR_AUTHORIZATIONERROR:
  168. err = SNMP_ERR_NOSUCHNAME;
  169. break;
  170. default:
  171. err = SNMP_ERR_GENERR;
  172. break;
  173. }
  174. return err;
  175. }
  176. }
  177. } else {
  178. /* Retrieve the value and place it into the outgoing packet. */
  179. if (statP == NULL) {
  180. statLen = 0;
  181. if (exact) {
  182. if (noSuchObject) {
  183. statType = SNMP_NOSUCHOBJECT;
  184. } else {
  185. statType = SNMP_NOSUCHINSTANCE;
  186. }
  187. } else {
  188. statType = SNMP_ENDOFMIBVIEW;
  189. }
  190. }
  191. out_data = SnmpVarBuild(out_data, &out_length, var_name, var_name_len, statType, statP, statLen);
  192. if (out_data == NULL) {
  193. return SNMP_ERR_TOOBIG;
  194. }
  195. }
  196. (*index)++;
  197. }
  198. if (msgtype != SNMP_MSG_SET) {
  199. /*
  200. * Save a pointer to the end of the packet and
  201. * rebuild header with the actual lengths
  202. */
  203. packet_end = out_data;
  204. dummyLen = packet_end - var_list_start;
  205. if (AsnSequenceBuild(headerP, &dummyLen, ASN_SEQUENCE | ASN_CONSTRUCTOR, dummyLen) == NULL) {
  206. return SNMP_ERR_TOOBIG;
  207. }
  208. }
  209. *index = 0;
  210. return 0;
  211. }
  212. /*!
  213. * \brief Clone input packet.
  214. *
  215. * Creates a packet identical to the input packet, except for the error
  216. * status and the error index which are set according to the specified
  217. * parameters.
  218. *
  219. * \return 0 upon success and -1 upon failure.
  220. */
  221. static int SnmpCreateIdentical(SNMP_SESSION * sess, const uint8_t * snmp_in, uint8_t * snmp_out, size_t snmp_length, long errstat,
  222. long errindex)
  223. {
  224. uint8_t *data;
  225. uint8_t type;
  226. long dummy;
  227. size_t length;
  228. size_t headerLength;
  229. uint8_t *headerPtr;
  230. const uint8_t *reqidPtr;
  231. uint8_t *errstatPtr;
  232. uint8_t *errindexPtr;
  233. const uint8_t *varListPtr;
  234. /* Copy packet contents. */
  235. memcpy(snmp_out, snmp_in, snmp_length);
  236. length = snmp_length;
  237. if ((headerPtr = (uint8_t *) SnmpAuthParse(snmp_out, &length, sess->sess_id, &sess->sess_id_len, &dummy)) == NULL) {
  238. return -1;
  239. }
  240. sess->sess_id[sess->sess_id_len] = 0;
  241. if ((reqidPtr = AsnHeaderParse(headerPtr, &length, (uint8_t *) & dummy)) == NULL) {
  242. return -1;
  243. }
  244. headerLength = length;
  245. /* Request id. */
  246. if ((errstatPtr = (uint8_t *) AsnIntegerParse(reqidPtr, &length, &type, &dummy)) == NULL) {
  247. return -1;
  248. }
  249. /* Error status. */
  250. if ((errindexPtr = (uint8_t *) AsnIntegerParse(errstatPtr, &length, &type, &dummy)) == NULL) {
  251. return -1;
  252. }
  253. /* Error index. */
  254. if ((varListPtr = AsnIntegerParse(errindexPtr, &length, &type, &dummy)) == NULL) {
  255. return -1;
  256. }
  257. if ((data = AsnHeaderBuild(headerPtr, &headerLength, SNMP_MSG_RESPONSE, headerLength)) == NULL) {
  258. return -1;
  259. }
  260. length = snmp_length;
  261. type = (uint8_t) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER);
  262. if ((data = AsnIntegerBuild(errstatPtr, &length, type, &errstat)) != errindexPtr) {
  263. return -1;
  264. }
  265. if ((data = AsnIntegerBuild(errindexPtr, &length, type, &errindex)) != varListPtr) {
  266. return -1;
  267. }
  268. packet_end = snmp_out + snmp_length;
  269. return 0;
  270. }
  271. /*!
  272. * \brief Parse incoming and create outgoing packet.
  273. *
  274. * \param in_data Pointer to the incoming packet.
  275. * \param in_len Number of valid bytes in the incoming packet.
  276. * \param out_data Pointer to a buffer for the outgoing packet.
  277. * \param out_len Pointer to the variable that receives the number of
  278. * bytes in the outgoing packet.
  279. * \param out_len Pointer to a variable which contains the size of the
  280. * output buffer on entry. On exit, it is returned
  281. * as the number of valid bytes in the output buffer.
  282. *
  283. * \return 0 upon success and -1 upon failure.
  284. */
  285. int SnmpAgentProcessRequest(SNMP_SESSION * sess, const uint8_t * in_data, size_t in_len, uint8_t * out_data, size_t * out_len)
  286. {
  287. long zero = 0;
  288. uint8_t msgtype;
  289. uint8_t type;
  290. long reqid;
  291. long errstat;
  292. long errindex;
  293. long dummyindex;
  294. uint8_t *out_auth;
  295. uint8_t *out_header;
  296. uint8_t *out_reqid;
  297. const uint8_t *data;
  298. size_t len;
  299. SnmpStatsInc(SNMP_STAT_INPKTS);
  300. /* Get version and community from the packet header. */
  301. len = in_len;
  302. sess->sess_id_len = sizeof(sess->sess_id) - 1;
  303. if ((data = SnmpAuthParse(in_data, &len, sess->sess_id, &sess->sess_id_len, &sess->sess_version)) == NULL) {
  304. SnmpStatsInc(SNMP_STAT_INASNPARSEERRS);
  305. return -1;
  306. }
  307. /* Check authentication. */
  308. if (sess->sess_version == SNMP_VERSION_1 || sess->sess_version == SNMP_VERSION_2C) {
  309. if (SnmpCommunityFind((char *) sess->sess_id, &sess->sess_read_view, &sess->sess_write_view)) {
  310. /* TODO: Create SNMPv2 report. */
  311. SnmpStatsInc(SNMP_STAT_INBADCOMMUNITYNAMES);
  312. return -1;
  313. }
  314. } else {
  315. /* Unsupported SNMP version. */
  316. SnmpStatsInc(SNMP_STAT_INBADVERSIONS);
  317. return -1;
  318. }
  319. /* Parse request header and check type. */
  320. if ((data = AsnHeaderParse(data, &len, &msgtype)) == NULL) {
  321. SnmpStatsInc(SNMP_STAT_INASNPARSEERRS);
  322. return -1;
  323. }
  324. if (msgtype == SNMP_MSG_GETBULK) {
  325. /* SNMPv2 bulk requests are not yet supported. */
  326. return -1;
  327. } else if (msgtype != SNMP_MSG_GET && msgtype != SNMP_MSG_GETNEXT && msgtype != SNMP_MSG_SET) {
  328. /* Bad request type. */
  329. return -1;
  330. }
  331. /* Parse request ID. */
  332. if ((data = AsnIntegerParse(data, &len, &type, &reqid)) == NULL) {
  333. SnmpStatsInc(SNMP_STAT_INASNPARSEERRS);
  334. return -1;
  335. }
  336. /* Parse error status. */
  337. if ((data = AsnIntegerParse(data, &len, &type, &errstat)) == NULL) {
  338. SnmpStatsInc(SNMP_STAT_INASNPARSEERRS);
  339. return -1;
  340. }
  341. /* Parse error index. */
  342. if ((data = AsnIntegerParse(data, &len, &type, &errindex)) == NULL) {
  343. SnmpStatsInc(SNMP_STAT_INASNPARSEERRS);
  344. return -1;
  345. }
  346. /*
  347. * Now start cobbling together what is known about the output packet.
  348. * The final lengths are not known now, so they will have to be
  349. * recomputed later.
  350. */
  351. out_auth = out_data;
  352. if ((out_header = SnmpAuthBuild(sess, out_auth, out_len, 0)) == NULL) {
  353. return -1;
  354. }
  355. if ((out_reqid = AsnSequenceBuild(out_header, out_len, SNMP_MSG_RESPONSE, 0)) == NULL) {
  356. return -1;
  357. }
  358. /* Return identical request ID. */
  359. type = (uint8_t) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER);
  360. if ((out_data = AsnIntegerBuild(out_reqid, out_len, type, &reqid)) == NULL) {
  361. return -1;
  362. }
  363. /* Assume that error status will be zero. */
  364. if ((out_data = AsnIntegerBuild(out_data, out_len, type, &zero)) == NULL) {
  365. return -1;
  366. }
  367. /* Assume that error index will be zero. */
  368. if ((out_data = AsnIntegerBuild(out_data, out_len, type, &zero)) == NULL) {
  369. return -1;
  370. }
  371. /*
  372. * Walk through the list of variables and retrieve each one,
  373. * placing its value in the output packet.
  374. *
  375. * TODO: Handle bulk requests.
  376. */
  377. errstat = SnmpVarListParse(sess, data, len, out_data, *out_len, &errindex, msgtype, SNMP_ACT_RESERVE1);
  378. /*
  379. * Sets require 3 to 4 passes through the var_op_list. The first two
  380. * passes verify that all types, lengths, and values are valid and
  381. * may reserve resources and the third does the set and a fourth
  382. * executes any actions. Then the identical GET RESPONSE packet is
  383. * returned.
  384. *
  385. * If either of the first two passes returns an error, another pass
  386. * is made so that any reserved resources can be freed.
  387. */
  388. if (msgtype == SNMP_MSG_SET) {
  389. if (errstat == 0) {
  390. errstat = SnmpVarListParse(sess, data, len, out_data, *out_len, &errindex, msgtype, SNMP_ACT_RESERVE2);
  391. }
  392. if (errstat == 0) {
  393. errstat = SnmpVarListParse(sess, data, len, out_data, *out_len, &dummyindex, msgtype, SNMP_ACT_COMMIT);
  394. SnmpVarListParse(sess, data, len, out_data, *out_len, &dummyindex, msgtype, errstat ? SNMP_ACT_FREE : SNMP_ACT_ACTION);
  395. if (SnmpCreateIdentical(sess, in_data, out_auth, in_len, 0L, 0L)) {
  396. return -1;
  397. }
  398. *out_len = packet_end - out_auth;
  399. return 0;
  400. } else {
  401. SnmpVarListParse(sess, data, len, out_data, *out_len, &dummyindex, msgtype, SNMP_ACT_FREE);
  402. }
  403. }
  404. if (errstat) {
  405. /* Create an error response. */
  406. if (SnmpCreateIdentical(sess, in_data, out_auth, in_len, errstat, errindex)) {
  407. return -1;
  408. }
  409. *out_len = packet_end - out_auth;
  410. return 0;
  411. }
  412. /*
  413. * Re-encode the headers with the real lengths.
  414. */
  415. *out_len = packet_end - out_header;
  416. out_data = AsnSequenceBuild(out_header, out_len, SNMP_MSG_RESPONSE, packet_end - out_reqid);
  417. if (out_data != out_reqid) {
  418. return -1;
  419. }
  420. *out_len = packet_end - out_auth;
  421. out_data = SnmpAuthBuild(sess, out_auth, out_len, packet_end - out_header);
  422. *out_len = packet_end - out_auth;
  423. return 0;
  424. }
  425. /*!
  426. * \brief Run SNMP agent.
  427. *
  428. * Normally runs in an endless loop, which is only left in case of an error.
  429. *
  430. * \param sock UDP socket to use.
  431. *
  432. * \return Always -1.
  433. */
  434. int SnmpAgent(SOCKET sock)
  435. {
  436. int rc = -1;
  437. size_t out_len;
  438. uint8_t *in_data = malloc(SNMP_MAX_LEN);
  439. uint8_t *out_data = malloc(SNMP_MAX_LEN);
  440. SNMP_SESSION *sess = malloc(sizeof(SNMP_SESSION));
  441. #ifdef WIN32
  442. struct sockaddr_in from;
  443. int fromlen;
  444. #else
  445. uint32_t raddr;
  446. uint16_t rport;
  447. #endif
  448. if (in_data && out_data && sess) {
  449. for (;;) {
  450. #ifdef WIN32
  451. fromlen = sizeof(struct sockaddr);
  452. rc = recvfrom(sock, (char *) in_data, SNMP_MAX_LEN, 0, (struct sockaddr *)&from, &fromlen);
  453. #else
  454. rc = NutUdpReceiveFrom(sock, &raddr, &rport, in_data, SNMP_MAX_LEN, 0);
  455. #endif
  456. if (rc < 0) {
  457. break;
  458. }
  459. out_len = SNMP_MAX_LEN;
  460. memset(sess, 0, sizeof(SNMP_SESSION));
  461. if (SnmpAgentProcessRequest(sess, in_data, (size_t) rc, out_data, &out_len) == 0) {
  462. #ifdef WIN32
  463. if (sendto(sock, (char *) out_data, (int)out_len, 0, (struct sockaddr *)&from, sizeof(from)) >= 0) {
  464. #else
  465. if (NutUdpSendTo(sock, raddr, rport, out_data, out_len) == 0) {
  466. #endif
  467. SnmpStatsInc(SNMP_STAT_OUTPKTS);
  468. }
  469. }
  470. }
  471. } else {
  472. rc = -1;
  473. }
  474. if (in_data) {
  475. free(in_data);
  476. }
  477. if (out_data) {
  478. free(out_data);
  479. }
  480. if (sess) {
  481. free(sess);
  482. }
  483. return rc;
  484. }
  485. /*@}*/