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 
00049 /*
00050  * Using this as a global had been derived from the original CMU code.
00051  * It is very ugly (shiffer), but may require some effort to transform
00052  * it into something local.
00053  */
00054 static uint8_t *packet_end;
00055 
00056 static void SetVariable(CONST uint8_t * var_val, uint8_t var_val_type, uint8_t * statP, size_t statLen)
00057 {
00058     size_t buffersize = 1000;
00059 
00060     switch (var_val_type) {
00061     case ASN_INTEGER:
00062     case ASN_COUNTER:
00063     case ASN_GAUGE:
00064     case ASN_TIMETICKS:
00065         AsnIntegerParse(var_val, &buffersize, &var_val_type, (long *) statP);
00066         break;
00067     case ASN_OCTET_STR:
00068     case ASN_IPADDRESS:
00069     case ASN_OPAQUE:
00070         AsnOctetStringParse(var_val, &buffersize, &var_val_type, statP, &statLen);
00071         break;
00072     case ASN_OBJECT_ID:
00073         AsnOidParse(var_val, &buffersize, &var_val_type, (OID *) statP, &statLen);
00074         break;
00075     }
00076 }
00077 
00095 static int SnmpVarListParse(SNMP_SESSION * sess, CONST uint8_t * data, size_t length, uint8_t * out_data, size_t out_length,
00096                             long *index, int msgtype, int action)
00097 {
00098     OID var_name[MAX_OID_LEN];
00099     size_t var_name_len;
00100     size_t var_val_len;
00101     uint8_t var_val_type;
00102     uint8_t *var_val;
00103     uint8_t statType;
00104     uint8_t *statP;
00105     size_t statLen;
00106     uint16_t acl;
00107     int exact, err;
00108     WMETHOD *wmethod;
00109     uint8_t *headerP;
00110     uint8_t *var_list_start;
00111     size_t dummyLen;
00112     int noSuchObject = 0;
00113 
00114     exact = (msgtype != SNMP_MSG_GETNEXT);
00115     /* Check if the list starts with a sequence header and get its length. */
00116     if ((data = AsnSequenceParse(data, &length, ASN_SEQUENCE | ASN_CONSTRUCTOR)) == NULL) {
00117         return SNMP_PARSE_ERROR;
00118     }
00119 
00120     /* Build ASN header. */
00121     headerP = out_data;
00122     if ((out_data = AsnSequenceBuild(out_data, &out_length, ASN_SEQUENCE | ASN_CONSTRUCTOR, 0)) == NULL) {
00123         return SNMP_BUILD_ERROR;
00124     }
00125     var_list_start = out_data;
00126 
00127     *index = 1;
00128     while (length > 0) {
00129         /* Get name and ASN1 encoded value of the next variable. */
00130         var_name_len = MAX_OID_LEN;
00131         if ((data = SnmpVarParse(data, &length, var_name, &var_name_len, &var_val_type, &var_val, &var_val_len)) == NULL) {
00132             return SNMP_PARSE_ERROR;
00133         }
00134 
00135         /* Now attempt to retrieve the variable on the local entity. */
00136         statP = SnmpMibFind(var_name, &var_name_len, &statType, &statLen, &acl, exact, &wmethod, &noSuchObject);
00137 
00138         /* Check access. */
00139         if (msgtype == SNMP_MSG_SET) {
00140             /* Make sure we have write access. */
00141             if (acl != ACL_RWRITE) {
00142                 return sess->sess_version == SNMP_VERSION_1 ? SNMP_ERR_NOSUCHNAME : SNMP_ERR_NOTWRITABLE;
00143             }
00144             if (wmethod == NULL) {
00145                 if (statP == NULL) {
00146                     return sess->sess_version == SNMP_VERSION_1 ? SNMP_ERR_NOSUCHNAME : SNMP_ERR_NOCREATION;
00147                 }
00148                 /* Check if the type and value is consistent with this entity's variable. */
00149                 if (var_val_len > statLen || var_val_type != statType) {
00150                     return sess->sess_version == SNMP_VERSION_1 ? SNMP_ERR_BADVALUE : SNMP_ERR_WRONGTYPE;
00151                 }
00152                 /* Actually do the set if necessary. */
00153                 if (action == SNMP_ACT_COMMIT) {
00154                     SetVariable(var_val, var_val_type, statP, statLen);
00155                 }
00156             } else {
00157                 err = (*wmethod) (action, var_val, var_val_type, var_val_len, var_name, var_name_len);
00158 
00159                 /*
00160                  * Map the SNMPv2 error codes to SNMPv1 error codes (RFC 2089).
00161                  */
00162                 if (err && sess->sess_version == SNMP_VERSION_1) {
00163                     switch (err) {
00164                     case SNMP_ERR_WRONGVALUE:
00165                     case SNMP_ERR_WRONGENCODING:
00166                     case SNMP_ERR_WRONGTYPE:
00167                     case SNMP_ERR_WRONGLENGTH:
00168                     case SNMP_ERR_INCONSISTENTVALUE:
00169                         err = SNMP_ERR_BADVALUE;
00170                         break;
00171                     case SNMP_ERR_NOACCESS:
00172                     case SNMP_ERR_NOTWRITABLE:
00173                     case SNMP_ERR_NOCREATION:
00174                     case SNMP_ERR_INCONSISTENTNAME:
00175                     case SNMP_ERR_AUTHORIZATIONERROR:
00176                         err = SNMP_ERR_NOSUCHNAME;
00177                         break;
00178                     default:
00179                         err = SNMP_ERR_GENERR;
00180                         break;
00181                     }
00182                     return err;
00183                 }
00184             }
00185         } else {
00186             /* Retrieve the value and place it into the outgoing packet. */
00187             if (statP == NULL) {
00188                 statLen = 0;
00189                 if (exact) {
00190                     if (noSuchObject) {
00191                         statType = SNMP_NOSUCHOBJECT;
00192                     } else {
00193                         statType = SNMP_NOSUCHINSTANCE;
00194                     }
00195                 } else {
00196                     statType = SNMP_ENDOFMIBVIEW;
00197                 }
00198             }
00199             out_data = SnmpVarBuild(out_data, &out_length, var_name, var_name_len, statType, statP, statLen);
00200             if (out_data == NULL) {
00201                 return SNMP_ERR_TOOBIG;
00202             }
00203         }
00204         (*index)++;
00205     }
00206 
00207     if (msgtype != SNMP_MSG_SET) {
00208         /*
00209          * Save a pointer to the end of the packet and
00210          * rebuild header with the actual lengths
00211          */
00212         packet_end = out_data;
00213         dummyLen = packet_end - var_list_start;
00214         if (AsnSequenceBuild(headerP, &dummyLen, ASN_SEQUENCE | ASN_CONSTRUCTOR, dummyLen) == NULL) {
00215             return SNMP_ERR_TOOBIG;
00216         }
00217     }
00218     *index = 0;
00219 
00220     return 0;
00221 }
00222 
00232 static int SnmpCreateIdentical(SNMP_SESSION * sess, CONST uint8_t * snmp_in, uint8_t * snmp_out, size_t snmp_length, long errstat,
00233                                long errindex)
00234 {
00235     uint8_t *data;
00236     uint8_t type;
00237     long dummy;
00238     size_t length;
00239     size_t headerLength;
00240     uint8_t *headerPtr;
00241     CONST uint8_t *reqidPtr;
00242     uint8_t *errstatPtr;
00243     uint8_t *errindexPtr;
00244     CONST uint8_t *varListPtr;
00245 
00246     /* Copy packet contents. */
00247     memcpy(snmp_out, snmp_in, snmp_length);
00248     length = snmp_length;
00249     if ((headerPtr = (uint8_t *) SnmpAuthParse(snmp_out, &length, sess->sess_id, &sess->sess_id_len, &dummy)) == NULL) {
00250         return -1;
00251     }
00252     sess->sess_id[sess->sess_id_len] = 0;
00253 
00254     if ((reqidPtr = AsnHeaderParse(headerPtr, &length, (uint8_t *) & dummy)) == NULL) {
00255         return -1;
00256     }
00257     headerLength = length;
00258 
00259     /* Request id. */
00260     if ((errstatPtr = (uint8_t *) AsnIntegerParse(reqidPtr, &length, &type, &dummy)) == NULL) {
00261         return -1;
00262     }
00263     /* Error status. */
00264     if ((errindexPtr = (uint8_t *) AsnIntegerParse(errstatPtr, &length, &type, &dummy)) == NULL) {
00265         return -1;
00266     }
00267     /* Error index. */
00268     if ((varListPtr = AsnIntegerParse(errindexPtr, &length, &type, &dummy)) == NULL) {
00269         return -1;
00270     }
00271 
00272     if ((data = AsnHeaderBuild(headerPtr, &headerLength, SNMP_MSG_RESPONSE, headerLength)) == NULL) {
00273         return -1;
00274     }
00275     length = snmp_length;
00276     type = (uint8_t) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER);
00277     if ((data = AsnIntegerBuild(errstatPtr, &length, type, &errstat)) != errindexPtr) {
00278         return -1;
00279     }
00280     if ((data = AsnIntegerBuild(errindexPtr, &length, type, &errindex)) != varListPtr) {
00281         return -1;
00282     }
00283     packet_end = snmp_out + snmp_length;
00284 
00285     return 0;
00286 }
00287 
00302 int SnmpAgentProcessRequest(SNMP_SESSION * sess, CONST uint8_t * in_data, size_t in_len, uint8_t * out_data, size_t * out_len)
00303 {
00304     long zero = 0;
00305     uint8_t msgtype;
00306     uint8_t type;
00307     long reqid;
00308     long errstat;
00309     long errindex;
00310     long dummyindex;
00311     uint8_t *out_auth;
00312     uint8_t *out_header;
00313     uint8_t *out_reqid;
00314     CONST uint8_t *data;
00315     size_t len;
00316 
00317     SnmpStatsInc(SNMP_STAT_INPKTS);
00318 
00319     /* Get version and community from the packet header. */
00320     len = in_len;
00321     sess->sess_id_len = sizeof(sess->sess_id) - 1;
00322     if ((data = SnmpAuthParse(in_data, &len, sess->sess_id, &sess->sess_id_len, &sess->sess_version)) == NULL) {
00323         SnmpStatsInc(SNMP_STAT_INASNPARSEERRS);
00324         return -1;
00325     }
00326 
00327     /* Check authentication. */
00328     if (sess->sess_version == SNMP_VERSION_1 || sess->sess_version == SNMP_VERSION_2C) {
00329         if (SnmpCommunityFind((char *) sess->sess_id, &sess->sess_read_view, &sess->sess_write_view)) {
00330             /* TODO: Create SNMPv2 report. */
00331             SnmpStatsInc(SNMP_STAT_INBADCOMMUNITYNAMES);
00332             return -1;
00333         }
00334     } else {
00335         /* Unsupported SNMP version. */
00336         SnmpStatsInc(SNMP_STAT_INBADVERSIONS);
00337         return -1;
00338     }
00339 
00340     /* Parse request header and check type. */
00341     if ((data = AsnHeaderParse(data, &len, &msgtype)) == NULL) {
00342         SnmpStatsInc(SNMP_STAT_INASNPARSEERRS);
00343         return -1;
00344     }
00345     if (msgtype == SNMP_MSG_GETBULK) {
00346         /* SNMPv2 bulk requests are not yet supported. */
00347         return -1;
00348     } else if (msgtype != SNMP_MSG_GET && msgtype != SNMP_MSG_GETNEXT && msgtype != SNMP_MSG_SET) {
00349         /* Bad request type. */
00350         return -1;
00351     }
00352 
00353     /* Parse request ID. */
00354     if ((data = AsnIntegerParse(data, &len, &type, &reqid)) == NULL) {
00355         SnmpStatsInc(SNMP_STAT_INASNPARSEERRS);
00356         return -1;
00357     }
00358     /* Parse error status. */
00359     if ((data = AsnIntegerParse(data, &len, &type, &errstat)) == NULL) {
00360         SnmpStatsInc(SNMP_STAT_INASNPARSEERRS);
00361         return -1;
00362     }
00363     /* Parse error index. */
00364     if ((data = AsnIntegerParse(data, &len, &type, &errindex)) == NULL) {
00365         SnmpStatsInc(SNMP_STAT_INASNPARSEERRS);
00366         return -1;
00367     }
00368 
00369     /*
00370      * Now start cobbling together what is known about the output packet. 
00371      * The final lengths are not known now, so they will have to be 
00372      * recomputed later.
00373      */
00374     out_auth = out_data;
00375     if ((out_header = SnmpAuthBuild(sess, out_auth, out_len, 0)) == NULL) {
00376         return -1;
00377     }
00378     if ((out_reqid = AsnSequenceBuild(out_header, out_len, SNMP_MSG_RESPONSE, 0)) == NULL) {
00379         return -1;
00380     }
00381     /* Return identical request ID. */
00382     type = (uint8_t) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER);
00383     if ((out_data = AsnIntegerBuild(out_reqid, out_len, type, &reqid)) == NULL) {
00384         return -1;
00385     }
00386     /* Assume that error status will be zero. */
00387     if ((out_data = AsnIntegerBuild(out_data, out_len, type, &zero)) == NULL) {
00388         return -1;
00389     }
00390     /* Assume that error index will be zero. */
00391     if ((out_data = AsnIntegerBuild(out_data, out_len, type, &zero)) == NULL) {
00392         return -1;
00393     }
00394 
00395     /*
00396      * Walk through the list of variables and retrieve each one, 
00397      * placing its value in the output packet.
00398      *
00399      * TODO: Handle bulk requests.
00400      */
00401     errstat = SnmpVarListParse(sess, data, len, out_data, *out_len, &errindex, msgtype, SNMP_ACT_RESERVE1);
00402 
00403     /*
00404      * Sets require 3 to 4 passes through the var_op_list. The first two 
00405      * passes verify that all types, lengths, and values are valid and 
00406      * may reserve resources and the third does the set and a fourth 
00407      * executes any actions. Then the identical GET RESPONSE packet is
00408      * returned.
00409      *
00410      * If either of the first two passes returns an error, another pass 
00411      * is made so that any reserved resources can be freed.
00412      */
00413     if (msgtype == SNMP_MSG_SET) {
00414         if (errstat == 0) {
00415             errstat = SnmpVarListParse(sess, data, len, out_data, *out_len, &errindex, msgtype, SNMP_ACT_RESERVE2);
00416         }
00417         if (errstat == 0) {
00418             errstat = SnmpVarListParse(sess, data, len, out_data, *out_len, &dummyindex, msgtype, SNMP_ACT_COMMIT);
00419             SnmpVarListParse(sess, data, len, out_data, *out_len, &dummyindex, msgtype, errstat ? SNMP_ACT_FREE : SNMP_ACT_ACTION);
00420             if (SnmpCreateIdentical(sess, in_data, out_auth, in_len, 0L, 0L)) {
00421                 return -1;
00422             }
00423             *out_len = packet_end - out_auth;
00424             return 0;
00425         } else {
00426             SnmpVarListParse(sess, data, len, out_data, *out_len, &dummyindex, msgtype, SNMP_ACT_FREE);
00427         }
00428     }
00429 
00430     if (errstat) {
00431         /* Create an error response. */
00432         if (SnmpCreateIdentical(sess, in_data, out_auth, in_len, errstat, errindex)) {
00433             return -1;
00434         }
00435         *out_len = packet_end - out_auth;
00436         return 0;
00437     }
00438 
00439     /* 
00440      * Re-encode the headers with the real lengths.
00441      */
00442     *out_len = packet_end - out_header;
00443     out_data = AsnSequenceBuild(out_header, out_len, SNMP_MSG_RESPONSE, packet_end - out_reqid);
00444     if (out_data != out_reqid) {
00445         return -1;
00446     }
00447     *out_len = packet_end - out_auth;
00448     out_data = SnmpAuthBuild(sess, out_auth, out_len, packet_end - out_header);
00449     *out_len = packet_end - out_auth;
00450 
00451     return 0;
00452 }
00453 
00463 int SnmpAgent(UDPSOCKET * sock)
00464 {
00465     int rc = -1;
00466     uint32_t raddr;
00467     uint16_t rport;
00468     size_t out_len;
00469     uint8_t *in_data = malloc(SNMP_MAX_LEN);
00470     uint8_t *out_data = malloc(SNMP_MAX_LEN);
00471     SNMP_SESSION *sess = malloc(sizeof(SNMP_SESSION));
00472 
00473     if (in_data && out_data && sess) {
00474         for (;;) {
00475             rc = NutUdpReceiveFrom(sock, &raddr, &rport, in_data, SNMP_MAX_LEN, 0);
00476             out_len = SNMP_MAX_LEN;
00477             memset(sess, 0, sizeof(SNMP_SESSION));
00478             if (SnmpAgentProcessRequest(sess, in_data, (size_t) rc, out_data, &out_len) == 0) {
00479                 if (NutUdpSendTo(sock, raddr, rport, out_data, out_len) == 0) {
00480                     SnmpStatsInc(SNMP_STAT_OUTPKTS);
00481                 }
00482             }
00483         }
00484     } else {
00485         rc = -1;
00486     }
00487     if (in_data) {
00488         free(in_data);
00489     }
00490     if (out_data) {
00491         free(out_data);
00492     }
00493     if (sess) {
00494         free(sess);
00495     }
00496     return rc;
00497 }

© 2000-2007 by egnite Software GmbH - visit http://www.ethernut.de/