Nut/OS  4.10.3
API Reference
snmp_agent.c
Go to the documentation of this file.
00001 /*
00002  * Copyright 1998-2007 by egnite Software GmbH
00003  * Copyright 1988, 1989, 1991, 1992 by Carnegie Mellon University
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions
00007  * are met:
00008  *
00009  * 1. Redistributions of source code must retain the above copyright
00010  *    notice, this list of conditions and the following disclaimer.
00011  * 2. Redistributions in binary form must reproduce the above copyright
00012  *    notice, this list of conditions and the following disclaimer in the
00013  *    documentation and/or other materials provided with the distribution.
00014  * 3. Neither the name of the copyright holders nor the names of
00015  *    contributors may be used to endorse or promote products derived
00016  *    from this software without specific prior written permission.
00017  *
00018  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00019  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00020  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00021  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00022  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00023  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00024  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
00025  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
00026  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
00027  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
00028  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00029  * SUCH DAMAGE.
00030  *
00031  * For additional information see http://www.ethernut.de/
00032  */
00033 
00034 #include <sys/types.h>
00035 
00036 #include <stdlib.h>
00037 #include <string.h>
00038 #include <memdebug.h>
00039 
00040 #include <arpa/inet.h>
00041 
00042 #include <pro/snmp_config.h>
00043 #include <pro/snmp.h>
00044 #include <pro/snmp_api.h>
00045 #include <pro/snmp_auth.h>
00046 #include <pro/snmp_mib.h>
00047 #include <pro/snmp_agent.h>
00048 
00053 
00054 /*
00055  * Using this as a global had been derived from the original CMU code.
00056  * It is very ugly (shiffer), but may require some effort to transform
00057  * it into something local.
00058  */
00059 static uint8_t *packet_end;
00060 
00061 static void SetVariable(CONST uint8_t * var_val, uint8_t var_val_type, uint8_t * statP, size_t statLen)
00062 {
00063     size_t buffersize = 1000;
00064 
00065     switch (var_val_type) {
00066     case ASN_INTEGER:
00067     case ASN_COUNTER:
00068     case ASN_GAUGE:
00069     case ASN_TIMETICKS:
00070         AsnIntegerParse(var_val, &buffersize, &var_val_type, (long *) statP);
00071         break;
00072     case ASN_OCTET_STR:
00073     case ASN_IPADDRESS:
00074     case ASN_OPAQUE:
00075         AsnOctetStringParse(var_val, &buffersize, &var_val_type, statP, &statLen);
00076         break;
00077     case ASN_OBJECT_ID:
00078         AsnOidParse(var_val, &buffersize, &var_val_type, (OID *) statP, &statLen);
00079         break;
00080     }
00081 }
00082 
00100 static int SnmpVarListParse(SNMP_SESSION * sess, CONST uint8_t * data, size_t length, uint8_t * out_data, size_t out_length,
00101                             long *index, int msgtype, int action)
00102 {
00103     OID var_name[MAX_OID_LEN];
00104     size_t var_name_len;
00105     size_t var_val_len;
00106     uint8_t var_val_type;
00107     uint8_t *var_val;
00108     uint8_t statType;
00109     uint8_t *statP;
00110     size_t statLen;
00111     uint16_t acl;
00112     int exact, err;
00113     WMETHOD *wmethod;
00114     uint8_t *headerP;
00115     uint8_t *var_list_start;
00116     size_t dummyLen;
00117     int noSuchObject = 0;
00118 
00119     exact = (msgtype != SNMP_MSG_GETNEXT);
00120     /* Check if the list starts with a sequence header and get its length. */
00121     if ((data = AsnSequenceParse(data, &length, ASN_SEQUENCE | ASN_CONSTRUCTOR)) == NULL) {
00122         return SNMP_PARSE_ERROR;
00123     }
00124 
00125     /* Build ASN header. */
00126     headerP = out_data;
00127     if ((out_data = AsnSequenceBuild(out_data, &out_length, ASN_SEQUENCE | ASN_CONSTRUCTOR, 0)) == NULL) {
00128         return SNMP_BUILD_ERROR;
00129     }
00130     var_list_start = out_data;
00131 
00132     *index = 1;
00133     while (length > 0) {
00134         /* Get name and ASN1 encoded value of the next variable. */
00135         var_name_len = MAX_OID_LEN;
00136         if ((data = SnmpVarParse(data, &length, var_name, &var_name_len, &var_val_type, &var_val, &var_val_len)) == NULL) {
00137             return SNMP_PARSE_ERROR;
00138         }
00139 
00140         /* Now attempt to retrieve the variable on the local entity. */
00141         statP = SnmpMibFind(var_name, &var_name_len, &statType, &statLen, &acl, exact, &wmethod, &noSuchObject);
00142 
00143         /* Check access. */
00144         if (msgtype == SNMP_MSG_SET) {
00145             /* Make sure we have write access. */
00146             if (acl != ACL_RWRITE) {
00147                 return sess->sess_version == SNMP_VERSION_1 ? SNMP_ERR_NOSUCHNAME : SNMP_ERR_NOTWRITABLE;
00148             }
00149             if (wmethod == NULL) {
00150                 if (statP == NULL) {
00151                     return sess->sess_version == SNMP_VERSION_1 ? SNMP_ERR_NOSUCHNAME : SNMP_ERR_NOCREATION;
00152                 }
00153                 /* Check if the type and value is consistent with this entity's variable. */
00154                 if (var_val_len > statLen || var_val_type != statType) {
00155                     return sess->sess_version == SNMP_VERSION_1 ? SNMP_ERR_BADVALUE : SNMP_ERR_WRONGTYPE;
00156                 }
00157                 /* Actually do the set if necessary. */
00158                 if (action == SNMP_ACT_COMMIT) {
00159                     SetVariable(var_val, var_val_type, statP, statLen);
00160                 }
00161             } else {
00162                 err = (*wmethod) (action, var_val, var_val_type, var_val_len, var_name, var_name_len);
00163 
00164                 /*
00165                  * Map the SNMPv2 error codes to SNMPv1 error codes (RFC 2089).
00166                  */
00167                 if (err && sess->sess_version == SNMP_VERSION_1) {
00168                     switch (err) {
00169                     case SNMP_ERR_WRONGVALUE:
00170                     case SNMP_ERR_WRONGENCODING:
00171                     case SNMP_ERR_WRONGTYPE:
00172                     case SNMP_ERR_WRONGLENGTH:
00173                     case SNMP_ERR_INCONSISTENTVALUE:
00174                         err = SNMP_ERR_BADVALUE;
00175                         break;
00176                     case SNMP_ERR_NOACCESS:
00177                     case SNMP_ERR_NOTWRITABLE:
00178                     case SNMP_ERR_NOCREATION:
00179                     case SNMP_ERR_INCONSISTENTNAME:
00180                     case SNMP_ERR_AUTHORIZATIONERROR:
00181                         err = SNMP_ERR_NOSUCHNAME;
00182                         break;
00183                     default:
00184                         err = SNMP_ERR_GENERR;
00185                         break;
00186                     }
00187                     return err;
00188                 }
00189             }
00190         } else {
00191             /* Retrieve the value and place it into the outgoing packet. */
00192             if (statP == NULL) {
00193                 statLen = 0;
00194                 if (exact) {
00195                     if (noSuchObject) {
00196                         statType = SNMP_NOSUCHOBJECT;
00197                     } else {
00198                         statType = SNMP_NOSUCHINSTANCE;
00199                     }
00200                 } else {
00201                     statType = SNMP_ENDOFMIBVIEW;
00202                 }
00203             }
00204             out_data = SnmpVarBuild(out_data, &out_length, var_name, var_name_len, statType, statP, statLen);
00205             if (out_data == NULL) {
00206                 return SNMP_ERR_TOOBIG;
00207             }
00208         }
00209         (*index)++;
00210     }
00211 
00212     if (msgtype != SNMP_MSG_SET) {
00213         /*
00214          * Save a pointer to the end of the packet and
00215          * rebuild header with the actual lengths
00216          */
00217         packet_end = out_data;
00218         dummyLen = packet_end - var_list_start;
00219         if (AsnSequenceBuild(headerP, &dummyLen, ASN_SEQUENCE | ASN_CONSTRUCTOR, dummyLen) == NULL) {
00220             return SNMP_ERR_TOOBIG;
00221         }
00222     }
00223     *index = 0;
00224 
00225     return 0;
00226 }
00227 
00237 static int SnmpCreateIdentical(SNMP_SESSION * sess, CONST uint8_t * snmp_in, uint8_t * snmp_out, size_t snmp_length, long errstat,
00238                                long errindex)
00239 {
00240     uint8_t *data;
00241     uint8_t type;
00242     long dummy;
00243     size_t length;
00244     size_t headerLength;
00245     uint8_t *headerPtr;
00246     CONST uint8_t *reqidPtr;
00247     uint8_t *errstatPtr;
00248     uint8_t *errindexPtr;
00249     CONST uint8_t *varListPtr;
00250 
00251     /* Copy packet contents. */
00252     memcpy(snmp_out, snmp_in, snmp_length);
00253     length = snmp_length;
00254     if ((headerPtr = (uint8_t *) SnmpAuthParse(snmp_out, &length, sess->sess_id, &sess->sess_id_len, &dummy)) == NULL) {
00255         return -1;
00256     }
00257     sess->sess_id[sess->sess_id_len] = 0;
00258 
00259     if ((reqidPtr = AsnHeaderParse(headerPtr, &length, (uint8_t *) & dummy)) == NULL) {
00260         return -1;
00261     }
00262     headerLength = length;
00263 
00264     /* Request id. */
00265     if ((errstatPtr = (uint8_t *) AsnIntegerParse(reqidPtr, &length, &type, &dummy)) == NULL) {
00266         return -1;
00267     }
00268     /* Error status. */
00269     if ((errindexPtr = (uint8_t *) AsnIntegerParse(errstatPtr, &length, &type, &dummy)) == NULL) {
00270         return -1;
00271     }
00272     /* Error index. */
00273     if ((varListPtr = AsnIntegerParse(errindexPtr, &length, &type, &dummy)) == NULL) {
00274         return -1;
00275     }
00276 
00277     if ((data = AsnHeaderBuild(headerPtr, &headerLength, SNMP_MSG_RESPONSE, headerLength)) == NULL) {
00278         return -1;
00279     }
00280     length = snmp_length;
00281     type = (uint8_t) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER);
00282     if ((data = AsnIntegerBuild(errstatPtr, &length, type, &errstat)) != errindexPtr) {
00283         return -1;
00284     }
00285     if ((data = AsnIntegerBuild(errindexPtr, &length, type, &errindex)) != varListPtr) {
00286         return -1;
00287     }
00288     packet_end = snmp_out + snmp_length;
00289 
00290     return 0;
00291 }
00292 
00307 int SnmpAgentProcessRequest(SNMP_SESSION * sess, CONST uint8_t * in_data, size_t in_len, uint8_t * out_data, size_t * out_len)
00308 {
00309     long zero = 0;
00310     uint8_t msgtype;
00311     uint8_t type;
00312     long reqid;
00313     long errstat;
00314     long errindex;
00315     long dummyindex;
00316     uint8_t *out_auth;
00317     uint8_t *out_header;
00318     uint8_t *out_reqid;
00319     CONST uint8_t *data;
00320     size_t len;
00321 
00322     SnmpStatsInc(SNMP_STAT_INPKTS);
00323 
00324     /* Get version and community from the packet header. */
00325     len = in_len;
00326     sess->sess_id_len = sizeof(sess->sess_id) - 1;
00327     if ((data = SnmpAuthParse(in_data, &len, sess->sess_id, &sess->sess_id_len, &sess->sess_version)) == NULL) {
00328         SnmpStatsInc(SNMP_STAT_INASNPARSEERRS);
00329         return -1;
00330     }
00331 
00332     /* Check authentication. */
00333     if (sess->sess_version == SNMP_VERSION_1 || sess->sess_version == SNMP_VERSION_2C) {
00334         if (SnmpCommunityFind((char *) sess->sess_id, &sess->sess_read_view, &sess->sess_write_view)) {
00335             /* TODO: Create SNMPv2 report. */
00336             SnmpStatsInc(SNMP_STAT_INBADCOMMUNITYNAMES);
00337             return -1;
00338         }
00339     } else {
00340         /* Unsupported SNMP version. */
00341         SnmpStatsInc(SNMP_STAT_INBADVERSIONS);
00342         return -1;
00343     }
00344 
00345     /* Parse request header and check type. */
00346     if ((data = AsnHeaderParse(data, &len, &msgtype)) == NULL) {
00347         SnmpStatsInc(SNMP_STAT_INASNPARSEERRS);
00348         return -1;
00349     }
00350     if (msgtype == SNMP_MSG_GETBULK) {
00351         /* SNMPv2 bulk requests are not yet supported. */
00352         return -1;
00353     } else if (msgtype != SNMP_MSG_GET && msgtype != SNMP_MSG_GETNEXT && msgtype != SNMP_MSG_SET) {
00354         /* Bad request type. */
00355         return -1;
00356     }
00357 
00358     /* Parse request ID. */
00359     if ((data = AsnIntegerParse(data, &len, &type, &reqid)) == NULL) {
00360         SnmpStatsInc(SNMP_STAT_INASNPARSEERRS);
00361         return -1;
00362     }
00363     /* Parse error status. */
00364     if ((data = AsnIntegerParse(data, &len, &type, &errstat)) == NULL) {
00365         SnmpStatsInc(SNMP_STAT_INASNPARSEERRS);
00366         return -1;
00367     }
00368     /* Parse error index. */
00369     if ((data = AsnIntegerParse(data, &len, &type, &errindex)) == NULL) {
00370         SnmpStatsInc(SNMP_STAT_INASNPARSEERRS);
00371         return -1;
00372     }
00373 
00374     /*
00375      * Now start cobbling together what is known about the output packet. 
00376      * The final lengths are not known now, so they will have to be 
00377      * recomputed later.
00378      */
00379     out_auth = out_data;
00380     if ((out_header = SnmpAuthBuild(sess, out_auth, out_len, 0)) == NULL) {
00381         return -1;
00382     }
00383     if ((out_reqid = AsnSequenceBuild(out_header, out_len, SNMP_MSG_RESPONSE, 0)) == NULL) {
00384         return -1;
00385     }
00386     /* Return identical request ID. */
00387     type = (uint8_t) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER);
00388     if ((out_data = AsnIntegerBuild(out_reqid, out_len, type, &reqid)) == NULL) {
00389         return -1;
00390     }
00391     /* Assume that error status will be zero. */
00392     if ((out_data = AsnIntegerBuild(out_data, out_len, type, &zero)) == NULL) {
00393         return -1;
00394     }
00395     /* Assume that error index will be zero. */
00396     if ((out_data = AsnIntegerBuild(out_data, out_len, type, &zero)) == NULL) {
00397         return -1;
00398     }
00399 
00400     /*
00401      * Walk through the list of variables and retrieve each one, 
00402      * placing its value in the output packet.
00403      *
00404      * TODO: Handle bulk requests.
00405      */
00406     errstat = SnmpVarListParse(sess, data, len, out_data, *out_len, &errindex, msgtype, SNMP_ACT_RESERVE1);
00407 
00408     /*
00409      * Sets require 3 to 4 passes through the var_op_list. The first two 
00410      * passes verify that all types, lengths, and values are valid and 
00411      * may reserve resources and the third does the set and a fourth 
00412      * executes any actions. Then the identical GET RESPONSE packet is
00413      * returned.
00414      *
00415      * If either of the first two passes returns an error, another pass 
00416      * is made so that any reserved resources can be freed.
00417      */
00418     if (msgtype == SNMP_MSG_SET) {
00419         if (errstat == 0) {
00420             errstat = SnmpVarListParse(sess, data, len, out_data, *out_len, &errindex, msgtype, SNMP_ACT_RESERVE2);
00421         }
00422         if (errstat == 0) {
00423             errstat = SnmpVarListParse(sess, data, len, out_data, *out_len, &dummyindex, msgtype, SNMP_ACT_COMMIT);
00424             SnmpVarListParse(sess, data, len, out_data, *out_len, &dummyindex, msgtype, errstat ? SNMP_ACT_FREE : SNMP_ACT_ACTION);
00425             if (SnmpCreateIdentical(sess, in_data, out_auth, in_len, 0L, 0L)) {
00426                 return -1;
00427             }
00428             *out_len = packet_end - out_auth;
00429             return 0;
00430         } else {
00431             SnmpVarListParse(sess, data, len, out_data, *out_len, &dummyindex, msgtype, SNMP_ACT_FREE);
00432         }
00433     }
00434 
00435     if (errstat) {
00436         /* Create an error response. */
00437         if (SnmpCreateIdentical(sess, in_data, out_auth, in_len, errstat, errindex)) {
00438             return -1;
00439         }
00440         *out_len = packet_end - out_auth;
00441         return 0;
00442     }
00443 
00444     /* 
00445      * Re-encode the headers with the real lengths.
00446      */
00447     *out_len = packet_end - out_header;
00448     out_data = AsnSequenceBuild(out_header, out_len, SNMP_MSG_RESPONSE, packet_end - out_reqid);
00449     if (out_data != out_reqid) {
00450         return -1;
00451     }
00452     *out_len = packet_end - out_auth;
00453     out_data = SnmpAuthBuild(sess, out_auth, out_len, packet_end - out_header);
00454     *out_len = packet_end - out_auth;
00455 
00456     return 0;
00457 }
00458 
00468 int SnmpAgent(UDPSOCKET * sock)
00469 {
00470     int rc = -1;
00471     uint32_t raddr;
00472     uint16_t rport;
00473     size_t out_len;
00474     uint8_t *in_data = malloc(SNMP_MAX_LEN);
00475     uint8_t *out_data = malloc(SNMP_MAX_LEN);
00476     SNMP_SESSION *sess = malloc(sizeof(SNMP_SESSION));
00477 
00478     if (in_data && out_data && sess) {
00479         for (;;) {
00480             rc = NutUdpReceiveFrom(sock, &raddr, &rport, in_data, SNMP_MAX_LEN, 0);
00481             if (rc < 0) {
00482                 break;
00483             }
00484             out_len = SNMP_MAX_LEN;
00485             memset(sess, 0, sizeof(SNMP_SESSION));
00486             if (SnmpAgentProcessRequest(sess, in_data, (size_t) rc, out_data, &out_len) == 0) {
00487                 if (NutUdpSendTo(sock, raddr, rport, out_data, out_len) == 0) {
00488                     SnmpStatsInc(SNMP_STAT_OUTPKTS);
00489                 }
00490             }
00491         }
00492     } else {
00493         rc = -1;
00494     }
00495     if (in_data) {
00496         free(in_data);
00497     }
00498     if (out_data) {
00499         free(out_data);
00500     }
00501     if (sess) {
00502         free(sess);
00503     }
00504     return rc;
00505 }
00506