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

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