Report community / route expiration times in UTC
authorJennifer Richards <jennifer@painless-security.com>
Thu, 26 Apr 2018 19:56:10 +0000 (15:56 -0400)
committerJennifer Richards <jennifer@painless-security.com>
Thu, 26 Apr 2018 19:59:05 +0000 (15:59 -0400)
  * add utility methods for timespec math
  * add method to convert between CLOCK_*

common/tr_comm.c
common/tr_comm_encoders.c
common/tr_util.c
include/tr_comm.h
include/tr_util.h
include/trp_route.h
trp/trp_route.c
trp/trp_route_encoders.c
trp/trps.c

index f8f91df..9f173c6 100644 (file)
 #include <tr_rp.h>
 #include <tr_idp.h>
 #include <tr_name_internal.h>
+#include <trp_internal.h>
 #include <tr_comm.h>
 #include <tr_debug.h>
-
+#include <tr_util.h>
 
 static int tr_comm_destructor(void *obj)
 {
@@ -1017,6 +1018,18 @@ struct timespec *tr_comm_memb_get_expiry(TR_COMM_MEMB *memb)
   return memb->expiry;
 }
 
+/**
+ * Get the expiration according to the realtime clock
+ *
+ * @param memb
+ * @param result space to store the result
+ * @return pointer to the result, or null on error
+ */
+struct timespec *tr_comm_memb_get_expiry_realtime(TR_COMM_MEMB *memb, struct timespec *result)
+{
+  return tr_clock_convert(TRP_CLOCK, memb->expiry, CLOCK_REALTIME, result);
+}
+
 int tr_comm_memb_is_expired(TR_COMM_MEMB *memb, struct timespec *curtime)
 {
   tr_debug("tr_comm_memb_is_expired: (cur->tv_sec>memb->expiry->tv_sec)=(%u > %u)=%s",
index 2655fe5..798c636 100644 (file)
 
 static json_t *expiry_to_json_string(TR_COMM_MEMB *memb)
 {
-  struct timespec ts_zero = {0, 0};
+  struct timespec ts = {0}; /* initialization to zero is important */
   char *s = NULL;
   json_t *jstr = NULL;
 
-  if (tr_cmp_timespec(tr_comm_memb_get_expiry(memb), &ts_zero) == 0) {
-    s = strdup("");
-  } else {
-    s = timespec_to_str(tr_comm_memb_get_expiry(memb));
-  }
+  if (tr_cmp_timespec(tr_comm_memb_get_expiry(memb), &ts) > 0) {
+    if (tr_comm_memb_get_expiry_realtime(memb, &ts) == NULL)
+      s = strdup("error");
+    else
+      s = timespec_to_str(&ts);
 
-  if (s) {
-    jstr = json_string(s);
-    free(s);
+    if (s) {
+      jstr = json_string(s);
+      free(s);
+    }
   }
 
   return jstr;
index ef85776..f45ca2b 100644 (file)
@@ -51,9 +51,16 @@ void tr_bin_to_hex(const unsigned char * bin, size_t bin_len,
   }
 }
 
-/* Returns 0 if ts1==ts2, <0 if ts1<ts2, >= if ts1>ts2.
- * Assumes that tv_nsec <= 1e9. */
-int tr_cmp_timespec(struct timespec *ts1, struct timespec *ts2)
+/**
+ * Compare two timespecs
+ *
+ * Assumes tv_nsec <= 1e9
+ *
+ * @param ts1
+ * @param ts2
+ * @return 0 if ts1==ts2, -1 if ts1<ts2, 1 if ts1>ts2.
+ */
+int tr_cmp_timespec(const struct timespec *ts1, const struct timespec *ts2)
 {
   if (ts1->tv_sec > ts2->tv_sec)
     return 1;
@@ -73,26 +80,132 @@ int tr_cmp_timespec(struct timespec *ts1, struct timespec *ts2)
 }
 
 /**
+ * Compute ts1 + ts2
+ *
+ * @param ts1
+ * @param ts2
+ * @param sum ts1 + ts2
+ * @return 0 on success, nonzero on error
+ */
+int tr_add_timespec(const struct timespec *ts1, const struct timespec *ts2, struct timespec *sum)
+{
+  const time_t ONE_BILLION = 1000000000;
+
+  if (!ts1 || !ts2 || !sum)
+    return -1;
+
+  /* would be nice to do range checking, but I don't know a portable way to get the
+   * max value of a time_t. Figure that nsec <= 1e9 and seconds are unlikely to go off
+   * too close to infinity, so overflow is unlikely */
+  sum->tv_nsec = ts1->tv_nsec + ts2->tv_nsec;
+  sum->tv_sec = ts1->tv_sec + ts2->tv_sec;
+
+  /* make sure that we have no more than a second worth of nsec */
+  while (sum->tv_nsec >= ONE_BILLION) {
+    sum->tv_nsec -= ONE_BILLION;
+    sum->tv_sec += 1;
+  }
+
+  return 0;
+}
+
+/**
+ * Compute ts1 - ts2
+ *
+ * Allows negative results, which will be represented as a negative tv_sec.
+ * The returned tv_nsec is always positive and less than 1e9.
+ *
+ * (The value represented is tv_sec + tv_nsec/1e9 seconds - this is a little
+ * counterintuitive when tv_sec is positive. E.g., -1.5 seconds is represented
+ * as {-2, 500000000).)
+ *
+ * @param ts1
+ * @param ts2
+ * @param diff ts1 - ts2
+ * @return 0 on success, nonzero on error
+ */
+int tr_sub_timespec(const struct timespec *ts1, const struct timespec *ts2, struct timespec *diff)
+{
+  const time_t ONE_BILLION = 1000000000;
+  struct timespec ts1_copy = {0};
+  struct timespec ts2_copy = {0};
+  
+  if (!ts1 || !ts2 || !diff)
+    return -1;
+
+  ts1_copy = *ts1;
+  ts2_copy = *ts2;
+  
+  while (ts2_copy.tv_nsec > ts1_copy.tv_nsec) {
+    /* Reduce ts2 by one second worth of nsec, balanced by removing a second
+     * from ts1. Repeat until ts2->tv_nsec <= ts1->tv_nsec. */
+    ts2_copy.tv_nsec -= ONE_BILLION;
+    ts1_copy.tv_sec -= 1;
+  }
+
+  diff->tv_nsec = ts1_copy.tv_nsec - ts2_copy.tv_nsec; /* >= 0 */
+  diff->tv_sec = ts1_copy.tv_sec - ts2_copy.tv_sec; /* sign indeterminate */
+
+  /* make sure we have no more than 1 sec worth of nsec */
+  while (diff->tv_nsec > ONE_BILLION) {
+    diff->tv_nsec -= ONE_BILLION;
+    diff->tv_sec += 1;
+  }
+
+  return 0;
+}
+
+/**
  * Convert a struct timespec to a string representation
  * @param ts
  * @return
  */
-char *timespec_to_str(struct timespec *ts)
+char *timespec_to_str(const struct timespec *ts)
 {
   struct tm tm;
   char *s=NULL;
 
-  if (localtime_r(&(ts->tv_sec), &tm)==NULL)
+  if (gmtime_r(&(ts->tv_sec), &tm)==NULL)
     return NULL;
 
   s=malloc(40); /* long enough to contain strftime result */
   if (s==NULL)
     return NULL;
 
-  if (strftime(s, 40, "%F %T", &tm)==0) {
+  if (strftime(s, 40, "%F %T UTC", &tm)==0) {
     free(s);
     return NULL;
   }
   return s;
 }
 
+/**
+ * Convert a time from one clock to another
+ *
+ * Because this involves reading each clock, it is not exact.
+ *
+ * @param from clock to convert from
+ * @param when time to convert, measured on the 'from' clock
+ * @param to clock to convert to
+ * @param dst destination, measured on the 'to' clock
+ * @return dst or null on error
+ */
+struct timespec *tr_clock_convert(clockid_t from, const struct timespec *when,
+                                  clockid_t to, struct timespec *dst)
+{
+  struct timespec now_from = {0};
+  struct timespec diff = {0}; /* difference between when and now_from */
+  struct timespec now_to = {0};
+
+  if ((clock_gettime(from, &now_from) != 0)
+      || (clock_gettime(to, &now_to) != 0)) {
+    return NULL;
+  }
+  if (tr_sub_timespec(when, &now_from, &diff) != 0) {
+    return NULL;
+  }
+  if (tr_add_timespec(&now_to, &diff, dst) != 0) {
+    return NULL;
+  }
+  return dst;
+}
\ No newline at end of file
index 6228c1f..c65d82a 100644 (file)
@@ -155,6 +155,7 @@ void tr_comm_memb_set_interval(TR_COMM_MEMB *memb, unsigned int interval);
 unsigned int tr_comm_memb_get_interval(TR_COMM_MEMB *memb);
 void tr_comm_memb_set_expiry(TR_COMM_MEMB *memb, struct timespec *time);
 struct timespec *tr_comm_memb_get_expiry(TR_COMM_MEMB *memb);
+struct timespec *tr_comm_memb_get_expiry_realtime(TR_COMM_MEMB *memb, struct timespec *result);
 int tr_comm_memb_is_expired(TR_COMM_MEMB *memb, struct timespec *curtime);
 void tr_comm_memb_set_triggered(TR_COMM_MEMB *memb, int trig);
 int tr_comm_memb_is_triggered(TR_COMM_MEMB *memb);
index bed0482..9bf7d8e 100644 (file)
 /* NB, tr_bin_to_hex() is also prototyped in trust_router/tr_dh.h */
 TR_EXPORT void tr_bin_to_hex(const unsigned char * bin, size_t binlen,
                              char * hex_out, size_t hex_len);
-TR_EXPORT int tr_cmp_timespec(struct timespec *ts1, struct timespec *ts2);
-char *timespec_to_str(struct timespec *ts);
+TR_EXPORT int tr_cmp_timespec(const struct timespec *ts1, const struct timespec *ts2);
+int tr_add_timespec(const struct timespec *ts1, const struct timespec *ts2, struct timespec *sum);
+int tr_sub_timespec(const struct timespec *ts1_copy, const struct timespec *ts2_copy, struct timespec *diff);
+char *timespec_to_str(const struct timespec *ts);
+struct timespec *tr_clock_convert(clockid_t from, const struct timespec *when,
+                                  clockid_t to, struct timespec *dst);
 
 #endif /* TR_UTIL_H */
index d159861..aff0ba0 100644 (file)
@@ -78,6 +78,7 @@ void trp_route_set_interval(TRP_ROUTE *entry, int interval);
 int trp_route_get_interval(TRP_ROUTE *entry);
 void trp_route_set_expiry(TRP_ROUTE *entry, struct timespec *exp);
 struct timespec *trp_route_get_expiry(TRP_ROUTE *entry);
+struct timespec *trp_route_get_expiry_realtime(TRP_ROUTE *comm, struct timespec *result);
 void trp_route_set_local(TRP_ROUTE *entry, int local);
 int trp_route_is_local(TRP_ROUTE *entry);
 void trp_route_set_triggered(TRP_ROUTE *entry, int trig);
index 1334032..6178d97 100644 (file)
@@ -45,6 +45,7 @@
 #include <tr_debug.h>
 #include <trust_router/trp.h>
 #include <trust_router/tid.h>
+#include <tr_util.h>
 
 
 /* Note: be careful mixing talloc with glib. */
@@ -225,6 +226,18 @@ struct timespec *trp_route_get_expiry(TRP_ROUTE *entry)
   return entry->expiry;
 }
 
+/**
+ * Get the expiration according to the realtime clock
+ *
+ * @param entry
+ * @param result space to store the result
+ * @return pointer to the result, or null on error
+ */
+struct timespec *trp_route_get_expiry_realtime(TRP_ROUTE *entry, struct timespec *result)
+{
+  return tr_clock_convert(TRP_CLOCK, entry->expiry, CLOCK_REALTIME, result);
+}
+
 void trp_route_set_local(TRP_ROUTE *entry, int local)
 {
   entry->local=local;
index 8e1809b..c954a81 100644 (file)
@@ -84,12 +84,15 @@ char *trp_route_to_str(TALLOC_CTX *mem_ctx, TRP_ROUTE *entry, const char *sep)
 /* helper */
 static json_t *expiry_to_json_string(TRP_ROUTE *route)
 {
-  struct timespec ts_zero = {0, 0};
+  struct timespec ts = {0}; /* initialization to zero is important */
   char *s = NULL;
   json_t *jstr = NULL;
 
-  if (tr_cmp_timespec(trp_route_get_expiry(route), &ts_zero) > 0) {
-    s = timespec_to_str(trp_route_get_expiry(route));
+  if (tr_cmp_timespec(trp_route_get_expiry(route), &ts) > 0) {
+    if (trp_route_get_expiry_realtime(route, &ts) == NULL)
+      s = strdup("error");
+    else
+      s = timespec_to_str(&ts);
 
     if (s) {
       jstr = json_string(s);
index 573f0a3..f2d3b9d 100644 (file)
@@ -1179,6 +1179,7 @@ TRP_RC trps_sweep_ctable(TRPS_INSTANCE *trps)
 {
   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
   struct timespec sweep_time={0,0};
+  struct timespec tmp = {0};
   TR_COMM_MEMB *memb=NULL;
   TR_COMM_ITER *iter=NULL;
   TRP_RC rc=TRP_ERROR;
@@ -1211,7 +1212,7 @@ TRP_RC trps_sweep_ctable(TRPS_INSTANCE *trps)
                  tr_comm_memb_get_realm_id(memb)->len, tr_comm_memb_get_realm_id(memb)->buf,
                  tr_comm_get_id(tr_comm_memb_get_comm(memb))->len, tr_comm_get_id(tr_comm_memb_get_comm(memb))->buf,
                  tr_comm_memb_get_origin(memb)->len, tr_comm_memb_get_origin(memb)->buf,
-                 timespec_to_str(tr_comm_memb_get_expiry(memb)));
+                 timespec_to_str(tr_comm_memb_get_expiry_realtime(memb, &tmp)));
         tr_comm_table_remove_memb(trps->ctable, memb);
         tr_comm_memb_free(memb);
       } else {
@@ -1219,8 +1220,8 @@ TRP_RC trps_sweep_ctable(TRPS_INSTANCE *trps)
         tr_comm_memb_expire(memb);
         trps_compute_expiry(trps, tr_comm_memb_get_interval(memb), tr_comm_memb_get_expiry(memb));
         tr_debug("trps_sweep_ctable: community membership expired at %s, resetting expiry to %s (%.*s in %.*s, origin %.*s).",
-                 timespec_to_str(&sweep_time),
-                 timespec_to_str(tr_comm_memb_get_expiry(memb)),
+                 timespec_to_str(tr_clock_convert(TRP_CLOCK, &sweep_time, CLOCK_REALTIME, &tmp)),
+                 timespec_to_str(tr_comm_memb_get_expiry_realtime(memb, &tmp)),
                  tr_comm_memb_get_realm_id(memb)->len, tr_comm_memb_get_realm_id(memb)->buf,
                  tr_comm_get_id(tr_comm_memb_get_comm(memb))->len, tr_comm_get_id(tr_comm_memb_get_comm(memb))->buf,
                  tr_comm_memb_get_origin(memb)->len, tr_comm_memb_get_origin(memb)->buf);