net-snmp 5.7
lcd_time.c
00001 /*
00002  * lcd_time.c
00003  *
00004  * XXX  Should etimelist entries with <0,0> time tuples be timed out?
00005  * XXX  Need a routine to free the memory?  (Perhaps at shutdown?)
00006  */
00007 
00008 #include <net-snmp/net-snmp-config.h>
00009 #include <net-snmp/net-snmp-features.h>
00010 
00011 #include <sys/types.h>
00012 #include <stdio.h>
00013 #ifdef HAVE_STDLIB_H
00014 #include <stdlib.h>
00015 #endif
00016 #if HAVE_STRING_H
00017 #include <string.h>
00018 #else
00019 #include <strings.h>
00020 #endif
00021 #if TIME_WITH_SYS_TIME
00022 # include <sys/time.h>
00023 # include <time.h>
00024 #else
00025 # if HAVE_SYS_TIME_H
00026 #  include <sys/time.h>
00027 # else
00028 #  include <time.h>
00029 # endif
00030 #endif
00031 #ifdef HAVE_NETINET_IN_H
00032 #include <netinet/in.h>
00033 #endif
00034 
00035 #if HAVE_UNISTD_H
00036 #include <unistd.h>
00037 #endif
00038 #if HAVE_DMALLOC_H
00039 #include <dmalloc.h>
00040 #endif
00041 
00042 #include <net-snmp/types.h>
00043 #include <net-snmp/output_api.h>
00044 #include <net-snmp/utilities.h>
00045 
00046 #include <net-snmp/library/snmp_api.h>
00047 #include <net-snmp/library/callback.h>
00048 #include <net-snmp/library/snmp_secmod.h>
00049 #include <net-snmp/library/snmpusm.h>
00050 #include <net-snmp/library/lcd_time.h>
00051 #include <net-snmp/library/scapi.h>
00052 #include <net-snmp/library/snmpv3.h>
00053 
00054 #include <net-snmp/library/transform_oids.h>
00055 
00056 netsnmp_feature_child_of(usm_support, libnetsnmp)
00057 netsnmp_feature_child_of(usm_lcd_time, usm_support)
00058 
00059 #ifndef NETSNMP_FEATURE_REMOVE_USM_LCD_TIME
00060 
00061 /*
00062  * Global static hashlist to contain Enginetime entries.
00063  *
00064  * New records are prepended to the appropriate list at the hash index.
00065  */
00066 static Enginetime etimelist[ETIMELIST_SIZE];
00067 
00068 
00069 
00070 
00071 /*******************************************************************-o-******
00072  * get_enginetime
00073  *
00074  * Parameters:
00075  *      *engineID
00076  *       engineID_len
00077  *      *engineboot
00078  *      *engine_time
00079  *      
00080  * Returns:
00081  *      SNMPERR_SUCCESS         Success -- when a record for engineID is found.
00082  *      SNMPERR_GENERR          Otherwise.
00083  *
00084  *
00085  * Lookup engineID and return the recorded values for the
00086  * <engine_time, engineboot> tuple adjusted to reflect the estimated time
00087  * at the engine in question.
00088  *
00089  * Special case: if engineID is NULL or if engineID_len is 0 then
00090  * the time tuple is returned immediately as zero.
00091  *
00092  * XXX  What if timediff wraps?  >shrug<
00093  * XXX  Then: you need to increment the boots value.  Now.  Detecting
00094  *            this is another matter.
00095  */
00096 int
00097 get_enginetime(const u_char * engineID,
00098                u_int engineID_len,
00099                u_int * engineboot,
00100                u_int * engine_time, u_int authenticated)
00101 {
00102     int             rval = SNMPERR_SUCCESS;
00103     int             timediff = 0;
00104     Enginetime      e = NULL;
00105 
00106 
00107 
00108     /*
00109      * Sanity check.
00110      */
00111     if (!engine_time || !engineboot) {
00112         QUITFUN(SNMPERR_GENERR, get_enginetime_quit);
00113     }
00114 
00115 
00116     /*
00117      * Compute estimated current engine_time tuple at engineID if
00118      * a record is cached for it.
00119      */
00120     *engine_time = *engineboot = 0;
00121 
00122     if (!engineID || (engineID_len <= 0)) {
00123         QUITFUN(SNMPERR_GENERR, get_enginetime_quit);
00124     }
00125 
00126     if (!(e = search_enginetime_list(engineID, engineID_len))) {
00127         QUITFUN(SNMPERR_GENERR, get_enginetime_quit);
00128     }
00129 #ifdef LCD_TIME_SYNC_OPT
00130     if (!authenticated || e->authenticatedFlag) {
00131 #endif
00132         *engine_time = e->engineTime;
00133         *engineboot = e->engineBoot;
00134 
00135        timediff = (int) (snmpv3_local_snmpEngineTime() - e->lastReceivedEngineTime);
00136 
00137 #ifdef LCD_TIME_SYNC_OPT
00138     }
00139 #endif
00140 
00141     if (timediff > (int) (ENGINETIME_MAX - *engine_time)) {
00142         *engine_time = (timediff - (ENGINETIME_MAX - *engine_time));
00143 
00144         /*
00145          * FIX -- move this check up... should not change anything
00146          * * if engineboot is already locked.  ???
00147          */
00148         if (*engineboot < ENGINEBOOT_MAX) {
00149             *engineboot += 1;
00150         }
00151 
00152     } else {
00153         *engine_time += timediff;
00154     }
00155 
00156     DEBUGMSGTL(("lcd_get_enginetime", "engineID "));
00157     DEBUGMSGHEX(("lcd_get_enginetime", engineID, engineID_len));
00158     DEBUGMSG(("lcd_get_enginetime", ": boots=%d, time=%d\n", *engineboot,
00159               *engine_time));
00160 
00161   get_enginetime_quit:
00162     return rval;
00163 
00164 }                               /* end get_enginetime() */
00165 
00166 /*******************************************************************-o-******
00167  * get_enginetime
00168  *
00169  * Parameters:
00170  *      *engineID
00171  *       engineID_len
00172  *      *engineboot
00173  *      *engine_time
00174  *      
00175  * Returns:
00176  *      SNMPERR_SUCCESS         Success -- when a record for engineID is found.
00177  *      SNMPERR_GENERR          Otherwise.
00178  *
00179  *
00180  * Lookup engineID and return the recorded values for the
00181  * <engine_time, engineboot> tuple adjusted to reflect the estimated time
00182  * at the engine in question.
00183  *
00184  * Special case: if engineID is NULL or if engineID_len is 0 then
00185  * the time tuple is returned immediately as zero.
00186  *
00187  * XXX  What if timediff wraps?  >shrug<
00188  * XXX  Then: you need to increment the boots value.  Now.  Detecting
00189  *            this is another matter.
00190  */
00191 int
00192 get_enginetime_ex(u_char * engineID,
00193                   u_int engineID_len,
00194                   u_int * engineboot,
00195                   u_int * engine_time,
00196                   u_int * last_engine_time, u_int authenticated)
00197 {
00198     int             rval = SNMPERR_SUCCESS;
00199     int             timediff = 0;
00200     Enginetime      e = NULL;
00201 
00202 
00203 
00204     /*
00205      * Sanity check.
00206      */
00207     if (!engine_time || !engineboot || !last_engine_time) {
00208         QUITFUN(SNMPERR_GENERR, get_enginetime_ex_quit);
00209     }
00210 
00211 
00212     /*
00213      * Compute estimated current engine_time tuple at engineID if
00214      * a record is cached for it.
00215      */
00216     *last_engine_time = *engine_time = *engineboot = 0;
00217 
00218     if (!engineID || (engineID_len <= 0)) {
00219         QUITFUN(SNMPERR_GENERR, get_enginetime_ex_quit);
00220     }
00221 
00222     if (!(e = search_enginetime_list(engineID, engineID_len))) {
00223         QUITFUN(SNMPERR_GENERR, get_enginetime_ex_quit);
00224     }
00225 #ifdef LCD_TIME_SYNC_OPT
00226     if (!authenticated || e->authenticatedFlag) {
00227 #endif
00228         *last_engine_time = *engine_time = e->engineTime;
00229         *engineboot = e->engineBoot;
00230 
00231        timediff = (int) (snmpv3_local_snmpEngineTime() - e->lastReceivedEngineTime);
00232 
00233 #ifdef LCD_TIME_SYNC_OPT
00234     }
00235 #endif
00236 
00237     if (timediff > (int) (ENGINETIME_MAX - *engine_time)) {
00238         *engine_time = (timediff - (ENGINETIME_MAX - *engine_time));
00239 
00240         /*
00241          * FIX -- move this check up... should not change anything
00242          * * if engineboot is already locked.  ???
00243          */
00244         if (*engineboot < ENGINEBOOT_MAX) {
00245             *engineboot += 1;
00246         }
00247 
00248     } else {
00249         *engine_time += timediff;
00250     }
00251 
00252     DEBUGMSGTL(("lcd_get_enginetime_ex", "engineID "));
00253     DEBUGMSGHEX(("lcd_get_enginetime_ex", engineID, engineID_len));
00254     DEBUGMSG(("lcd_get_enginetime_ex", ": boots=%d, time=%d\n",
00255               *engineboot, *engine_time));
00256 
00257   get_enginetime_ex_quit:
00258     return rval;
00259 
00260 }                               /* end get_enginetime_ex() */
00261 
00262 
00263 void free_enginetime(unsigned char *engineID, size_t engineID_len)
00264 {
00265     Enginetime      e = NULL;
00266     int             rval = 0;
00267 
00268     rval = hash_engineID(engineID, engineID_len);
00269     if (rval < 0)
00270         return;
00271 
00272     e = etimelist[rval];
00273 
00274     while (e != NULL) {
00275         etimelist[rval] = e->next;
00276         SNMP_FREE(e->engineID);
00277         SNMP_FREE(e);
00278         e = etimelist[rval];
00279     }
00280 
00281 }
00282 
00283 /*******************************************************************-o-****
00284 **
00285  * free_etimelist
00286  *
00287  * Parameters:
00288  *   None
00289  *      
00290  * Returns:
00291  *   void
00292  *
00293  *
00294  * Free all of the memory used by entries in the etimelist.
00295  *
00296  */
00297 void free_etimelist(void)
00298 {
00299      int index = 0;
00300      Enginetime e = NULL;
00301      Enginetime nextE = NULL;
00302 
00303      for( ; index < ETIMELIST_SIZE; ++index)
00304      {
00305            e = etimelist[index];
00306 
00307            while(e != NULL)
00308            {
00309                  nextE = e->next;
00310                  SNMP_FREE(e->engineID);
00311                  SNMP_FREE(e);
00312                  e = nextE;
00313            }
00314 
00315            etimelist[index] = NULL;
00316      }
00317      return;
00318 }
00319 
00320 /*******************************************************************-o-******
00321  * set_enginetime
00322  *
00323  * Parameters:
00324  *      *engineID
00325  *       engineID_len
00326  *       engineboot
00327  *       engine_time
00328  *      
00329  * Returns:
00330  *      SNMPERR_SUCCESS         Success.
00331  *      SNMPERR_GENERR          Otherwise.
00332  *
00333  *
00334  * Lookup engineID and store the given <engine_time, engineboot> tuple
00335  * and then stamp the record with a consistent source of local time.
00336  * If the engineID record does not exist, create one.
00337  *
00338  * Special case: engineID is NULL or engineID_len is 0 defines an engineID
00339  * that is "always set."
00340  *
00341  * XXX  "Current time within the local engine" == time(NULL)...
00342  */
00343 int
00344 set_enginetime(const u_char * engineID,
00345                u_int engineID_len,
00346                u_int engineboot, u_int engine_time, u_int authenticated)
00347 {
00348     int             rval = SNMPERR_SUCCESS, iindex;
00349     Enginetime      e = NULL;
00350 
00351 
00352 
00353     /*
00354      * Sanity check.
00355      */
00356     if (!engineID || (engineID_len <= 0)) {
00357         return rval;
00358     }
00359 
00360 
00361     /*
00362      * Store the given <engine_time, engineboot> tuple in the record
00363      * for engineID.  Create a new record if necessary.
00364      */
00365     if (!(e = search_enginetime_list(engineID, engineID_len))) {
00366         if ((iindex = hash_engineID(engineID, engineID_len)) < 0) {
00367             QUITFUN(SNMPERR_GENERR, set_enginetime_quit);
00368         }
00369 
00370         e = (Enginetime) calloc(1, sizeof(*e));
00371 
00372         e->next = etimelist[iindex];
00373         etimelist[iindex] = e;
00374 
00375         e->engineID = (u_char *) calloc(1, engineID_len);
00376         memcpy(e->engineID, engineID, engineID_len);
00377 
00378         e->engineID_len = engineID_len;
00379     }
00380 #ifdef LCD_TIME_SYNC_OPT
00381     if (authenticated || !e->authenticatedFlag) {
00382         e->authenticatedFlag = authenticated;
00383 #else
00384     if (authenticated) {
00385 #endif
00386         e->engineTime = engine_time;
00387         e->engineBoot = engineboot;
00388         e->lastReceivedEngineTime = snmpv3_local_snmpEngineTime();
00389     }
00390 
00391     e = NULL;                   /* Indicates a successful update. */
00392 
00393     DEBUGMSGTL(("lcd_set_enginetime", "engineID "));
00394     DEBUGMSGHEX(("lcd_set_enginetime", engineID, engineID_len));
00395     DEBUGMSG(("lcd_set_enginetime", ": boots=%d, time=%d\n", engineboot,
00396               engine_time));
00397 
00398   set_enginetime_quit:
00399     SNMP_FREE(e);
00400 
00401     return rval;
00402 
00403 }                               /* end set_enginetime() */
00404 
00405 
00406 
00407 
00408 /*******************************************************************-o-******
00409  * search_enginetime_list
00410  *
00411  * Parameters:
00412  *      *engineID
00413  *       engineID_len
00414  *      
00415  * Returns:
00416  *      Pointer to a etimelist record with engineID <engineID>  -OR-
00417  *      NULL if no record exists.
00418  *
00419  *
00420  * Search etimelist for an entry with engineID.
00421  *
00422  * ASSUMES that no engineID will have more than one record in the list.
00423  */
00424 Enginetime
00425 search_enginetime_list(const u_char * engineID, u_int engineID_len)
00426 {
00427     int             rval = SNMPERR_SUCCESS;
00428     Enginetime      e = NULL;
00429 
00430 
00431     /*
00432      * Sanity check.
00433      */
00434     if (!engineID || (engineID_len <= 0)) {
00435         QUITFUN(SNMPERR_GENERR, search_enginetime_list_quit);
00436     }
00437 
00438 
00439     /*
00440      * Find the entry for engineID if there be one.
00441      */
00442     rval = hash_engineID(engineID, engineID_len);
00443     if (rval < 0) {
00444         QUITFUN(SNMPERR_GENERR, search_enginetime_list_quit);
00445     }
00446     e = etimelist[rval];
00447 
00448     for ( /*EMPTY*/; e; e = e->next) {
00449         if ((engineID_len == e->engineID_len)
00450             && !memcmp(e->engineID, engineID, engineID_len)) {
00451             break;
00452         }
00453     }
00454 
00455 
00456   search_enginetime_list_quit:
00457     return e;
00458 
00459 }                               /* end search_enginetime_list() */
00460 
00461 
00462 
00463 
00464 
00465 /*******************************************************************-o-******
00466  * hash_engineID
00467  *
00468  * Parameters:
00469  *      *engineID
00470  *       engineID_len
00471  *      
00472  * Returns:
00473  *      >0                      etimelist index for this engineID.
00474  *      SNMPERR_GENERR          Error.
00475  *      
00476  * 
00477  * Use a cheap hash to build an index into the etimelist.  Method is 
00478  * to hash the engineID, then split the hash into u_int's and add them up
00479  * and modulo the size of the list.
00480  *
00481  */
00482 int
00483 hash_engineID(const u_char * engineID, u_int engineID_len)
00484 {
00485     int             rval = SNMPERR_GENERR;
00486     size_t          buf_len = SNMP_MAXBUF;
00487     u_int           additive = 0;
00488     u_char         *bufp, buf[SNMP_MAXBUF];
00489     void           *context = NULL;
00490 
00491 
00492 
00493     /*
00494      * Sanity check.
00495      */
00496     if (!engineID || (engineID_len <= 0)) {
00497         QUITFUN(SNMPERR_GENERR, hash_engineID_quit);
00498     }
00499 
00500 
00501     /*
00502      * Hash engineID into a list index.
00503      */
00504 #ifndef NETSNMP_DISABLE_MD5
00505     rval = sc_hash(usmHMACMD5AuthProtocol,
00506                    sizeof(usmHMACMD5AuthProtocol) / sizeof(oid),
00507                    engineID, engineID_len, buf, &buf_len);
00508 #else
00509     rval = sc_hash(usmHMACSHA1AuthProtocol,
00510                    sizeof(usmHMACSHA1AuthProtocol) / sizeof(oid),
00511                    engineID, engineID_len, buf, &buf_len);
00512 #endif
00513     QUITFUN(rval, hash_engineID_quit);
00514 
00515     for (bufp = buf; (bufp - buf) < (int) buf_len; bufp += 4) {
00516         additive += (u_int) * bufp;
00517     }
00518 
00519   hash_engineID_quit:
00520     SNMP_FREE(context);
00521     memset(buf, 0, SNMP_MAXBUF);
00522 
00523     return (rval < 0) ? rval : (int)(additive % ETIMELIST_SIZE);
00524 
00525 }                               /* end hash_engineID() */
00526 
00527 
00528 
00529 
00530 #ifdef NETSNMP_ENABLE_TESTING_CODE
00531 /*******************************************************************-o-******
00532  * dump_etimelist_entry
00533  *
00534  * Parameters:
00535  *      e
00536  *      count
00537  */
00538 void
00539 dump_etimelist_entry(Enginetime e, int count)
00540 {
00541     u_int           buflen;
00542     char            tabs[SNMP_MAXBUF], *t = tabs, *s;
00543 
00544 
00545 
00546     count += 1;
00547     while (count--) {
00548         t += sprintf(t, "  ");
00549     }
00550 
00551 
00552     buflen = e->engineID_len;
00553 #ifdef NETSNMP_ENABLE_TESTING_CODE
00554     if (!(s = dump_snmpEngineID(e->engineID, &buflen))) {
00555 #endif
00556         binary_to_hex(e->engineID, e->engineID_len, &s);
00557 #ifdef NETSNMP_ENABLE_TESTING_CODE
00558     }
00559 #endif
00560 
00561     DEBUGMSGTL(("dump_etimelist", "%s\n", tabs));
00562     DEBUGMSGTL(("dump_etimelist", "%s%s (len=%d) <%d,%d>\n", tabs,
00563                 s, e->engineID_len, e->engineTime, e->engineBoot));
00564     DEBUGMSGTL(("dump_etimelist", "%s%ld (%ld)", tabs,
00565                 e->lastReceivedEngineTime,
00566                 snmpv3_local_snmpEngineTime() - e->lastReceivedEngineTime));
00567 
00568     SNMP_FREE(s);
00569 
00570 }                               /* end dump_etimelist_entry() */
00571 
00572 
00573 
00574 
00575 /*******************************************************************-o-******
00576  * dump_etimelist
00577  */
00578 void
00579 dump_etimelist(void)
00580 {
00581     int             iindex = -1, count = 0;
00582     Enginetime      e;
00583 
00584 
00585 
00586     DEBUGMSGTL(("dump_etimelist", "\n"));
00587 
00588     while (++iindex < ETIMELIST_SIZE) {
00589         DEBUGMSG(("dump_etimelist", "[%d]", iindex));
00590 
00591         count = 0;
00592         e = etimelist[iindex];
00593 
00594         while (e) {
00595             dump_etimelist_entry(e, count++);
00596             e = e->next;
00597         }
00598 
00599         if (count > 0) {
00600             DEBUGMSG(("dump_etimelist", "\n"));
00601         }
00602     }                           /* endwhile */
00603 
00604     DEBUGMSG(("dump_etimelist", "\n"));
00605 
00606 }                               /* end dump_etimelist() */
00607 #endif                          /* NETSNMP_ENABLE_TESTING_CODE */
00608 #endif /* NETSNMP_FEATURE_REMOVE_USM_LCD_TIME */