info: add support for caching and use it
authorFerry Huberts <ferry.huberts@pelagic.nl>
Thu, 7 Apr 2016 18:44:16 +0000 (20:44 +0200)
committerFerry Huberts <ferry.huberts@pelagic.nl>
Thu, 7 Apr 2016 20:18:16 +0000 (22:18 +0200)
Signed-off-by: Ferry Huberts <ferry.huberts@pelagic.nl>
lib/info/README_INFO
lib/info/info_types.h
lib/info/olsrd_info.c
lib/info/olsrd_info.h
lib/jsoninfo/src/olsrd_plugin.c
lib/txtinfo/src/olsrd_plugin.c

index 614bc43..783d8cf 100644 (file)
@@ -74,6 +74,16 @@ LoadPlugin "an_info_plugin.so.0.0"
   # Set to true to only listen on IPv6 addresses when running in IPv6 mode.
   # Default: false
   # PlParam "ipv6only"       "false"
+
+  # The time (in milliseconds) after which cached information times out.
+  # A negative value or a value of zero effectively disables caching.
+  # Caching is on by default since it provides some protection again DoS
+  # attacks, therefore it is strongly recommended to keep caching switched
+  # on by configuring a positive value here.
+  # Note: startup information (version, config and plugins) is cached forever
+  #       by default.
+  # Default: 1000
+  # PlParam "cachetimeout"       "1000"
 }
 
 
index 27dee16..fa7c561 100644 (file)
@@ -48,6 +48,8 @@
 
 #include "common/autobuf.h"
 
+#define CACHE_TIMEOUT_DEFAULT 1000
+
 typedef struct {
     union olsr_ip_addr accept_ip;
     union olsr_ip_addr listen_ip;
@@ -55,6 +57,7 @@ typedef struct {
     bool http_headers;
     bool allow_localhost;
     bool ipv6_only;
+    long cache_timeout;
 } info_plugin_config_t;
 
 #define INFO_PLUGIN_CONFIG_PLUGIN_PARAMETERS(config) \
@@ -63,7 +66,8 @@ typedef struct {
   { .name = "listen", .set_plugin_parameter = &set_plugin_ipaddress, .data = &config.listen_ip }, \
   { .name = "httpheaders", .set_plugin_parameter = &set_plugin_boolean, .data = &config.http_headers }, \
   { .name = "allowlocalhost", .set_plugin_parameter = &set_plugin_boolean, .data = &config.allow_localhost }, \
-  { .name = "ipv6only", .set_plugin_parameter = &set_plugin_boolean, .data = &config.ipv6_only }
+  { .name = "ipv6only", .set_plugin_parameter = &set_plugin_boolean, .data = &config.ipv6_only },\
+  { .name = "cachetimeout", .set_plugin_parameter = &set_plugin_long, .data = &config.cache_timeout }
 
 /* these provide all of the runtime status info */
 #define SIW_NEIGHBORS                    0x00000001ULL
@@ -93,6 +97,7 @@ typedef struct {
 
 typedef void (*init_plugin)(const char *plugin_name);
 typedef bool (*command_matcher)(const char *str, unsigned long long siw);
+typedef long (*cache_timeout_func)(info_plugin_config_t *plugin_config, unsigned long long siw);
 typedef const char * (*mime_type)(unsigned int send_what);
 typedef void (*output_start_end)(struct autobuf *abuf);
 typedef void (*printer_error)(struct autobuf *abuf, unsigned int status, const char * req, bool http_headers);
@@ -102,6 +107,7 @@ typedef struct {
     bool supportsCompositeCommands;
     init_plugin init;
     command_matcher is_command;
+    cache_timeout_func cache_timeout;
     mime_type determine_mime_type;
     output_start_end output_start;
     output_start_end output_end;
@@ -122,6 +128,96 @@ typedef struct {
     printer_generic plugins;
 } info_plugin_functions_t;
 
+struct info_cache_entry_t {
+    long long timestamp;
+    struct autobuf buf;
+};
+
+struct info_cache_t {
+    struct info_cache_entry_t neighbors;
+    struct info_cache_entry_t links;
+    struct info_cache_entry_t routes;
+    struct info_cache_entry_t hna;
+    struct info_cache_entry_t mid;
+    struct info_cache_entry_t topology;
+    struct info_cache_entry_t gateways;
+    struct info_cache_entry_t interfaces;
+    struct info_cache_entry_t twohop;
+    struct info_cache_entry_t sgw;
+
+    struct info_cache_entry_t version;
+    struct info_cache_entry_t config;
+    struct info_cache_entry_t plugins;
+};
+
+static INLINE struct info_cache_entry_t * info_cache_get_entry(struct info_cache_t * cache, unsigned long long siw) {
+  struct info_cache_entry_t * r = NULL;
+
+  if (!cache) {
+    return r;
+  }
+
+  switch (siw) {
+    case SIW_NEIGHBORS:
+      r = &cache->neighbors;
+      break;
+
+    case SIW_LINKS:
+      r = &cache->links;
+      break;
+
+    case SIW_ROUTES:
+      r = &cache->routes;
+      break;
+
+    case SIW_HNA:
+      r = &cache->hna;
+      break;
+
+    case SIW_MID:
+      r = &cache->mid;
+      break;
+
+    case SIW_TOPOLOGY:
+      r = &cache->topology;
+      break;
+
+    case SIW_GATEWAYS:
+      r = &cache->gateways;
+      break;
+
+    case SIW_INTERFACES:
+      r = &cache->interfaces;
+      break;
+
+    case SIW_2HOP:
+      r = &cache->twohop;
+      break;
+
+    case SIW_SGW:
+      r = &cache->sgw;
+      break;
+
+    case SIW_VERSION:
+      r = &cache->version;
+      break;
+
+    case SIW_CONFIG:
+      r = &cache->config;
+      break;
+
+    case SIW_PLUGINS:
+      r = &cache->plugins;
+      break;
+
+    default:
+      /* not cached */
+      break;
+  }
+
+  return r;
+}
+
 static INLINE void info_plugin_config_init(info_plugin_config_t *config, unsigned short port) {
   assert(config);
 
@@ -137,6 +233,7 @@ static INLINE void info_plugin_config_init(info_plugin_config_t *config, unsigne
   config->http_headers = true;
   config->allow_localhost = false;
   config->ipv6_only = false;
+  config->cache_timeout = CACHE_TIMEOUT_DEFAULT;
 }
 
 #endif /* _OLSRD_LIB_INFO_INFO_TYPES_H_ */
index ec54db2..a076b1d 100644 (file)
@@ -92,6 +92,8 @@ static info_plugin_outbuffer_t outbuffer;
 
 static struct timer_entry *writetimer_entry = NULL;
 
+static struct info_cache_t info_cache;
+
 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
 
 static char * skipMultipleSlashes(char * requ) {
@@ -135,6 +137,61 @@ static unsigned long long SIW_ENTRIES_ALL[] = {
     SIW_OLSRD_CONF //
     };
 
+long cache_timeout_generic(info_plugin_config_t *plugin_config __attribute__((unused)), unsigned long long siw) {
+  switch (siw) {
+    case SIW_NEIGHBORS:
+    case SIW_LINKS:
+    case SIW_ROUTES:
+    case SIW_HNA:
+    case SIW_MID:
+    case SIW_TOPOLOGY:
+    case SIW_GATEWAYS:
+    case SIW_INTERFACES:
+    case SIW_2HOP:
+    case SIW_SGW:
+      return plugin_config->cache_timeout;
+
+    case SIW_VERSION:
+    case SIW_CONFIG:
+    case SIW_PLUGINS:
+      return LONG_MAX;
+
+    default:
+      /* not cached */
+      return false;
+  }
+}
+
+static void info_plugin_cache_init(bool init) {
+  unsigned int i;
+
+  if (!functions->cache_timeout) {
+    return;
+  }
+
+  for (i = 0; i < ARRAY_SIZE(SIW_ENTRIES_ALL); ++i) {
+    unsigned long long siw = SIW_ENTRIES_ALL[i];
+    struct info_cache_entry_t * entry;
+
+    if (functions->cache_timeout(config, siw) <= 0) {
+      continue;
+    }
+
+    entry = info_cache_get_entry(&info_cache, siw);
+    if (!entry) {
+      continue;
+    }
+
+    if (init) {
+      entry->timestamp = 0;
+      abuf_init(&entry->buf, AUTOBUFCHUNK);
+    } else {
+      abuf_free(&entry->buf);
+      entry->timestamp = 0;
+    }
+  }
+}
+
 static unsigned int determine_single_action(char *requ) {
   unsigned int i;
 
@@ -292,6 +349,7 @@ typedef struct {
 static void send_info_from_table(struct autobuf *abuf, unsigned int send_what, SiwLookupTableEntry *funcs, unsigned int funcsSize, unsigned int *outputLength) {
   unsigned int i;
   unsigned int preLength;
+  cache_timeout_func cache_timeout_f = functions->cache_timeout;
 
   if (functions->output_start) {
     functions->output_start(abuf);
@@ -300,10 +358,33 @@ static void send_info_from_table(struct autobuf *abuf, unsigned int send_what, S
   preLength = abuf->len;
 
   for (i = 0; i < funcsSize; i++) {
-    if (send_what & funcs[i].siw) {
+    unsigned long long siw = funcs[i].siw;
+    if (send_what & siw) {
       printer_generic func = funcs[i].func;
       if (func) {
-        func(abuf);
+        long cache_timeout = 0;
+        struct info_cache_entry_t *cache_entry = NULL;
+
+        if (cache_timeout_f) {
+          cache_timeout = cache_timeout_f(config, siw);
+          cache_entry = (cache_timeout <= 0) ? NULL : info_cache_get_entry(&info_cache, siw);
+        }
+
+        if (!cache_entry) {
+            func(abuf);
+        } else {
+          long long now = olsr_times();
+          long long age = abs(now - cache_entry->timestamp);
+          if (!cache_entry->timestamp || (age >= cache_timeout)) {
+            /* cache is never used before or cache is too old */
+            cache_entry->buf.buf[0] = '\0';
+            cache_entry->buf.len = 0;
+            cache_entry->timestamp = now;
+            func(&cache_entry->buf);
+          }
+
+          abuf_concat(abuf, &cache_entry->buf);
+        }
       }
     }
   }
@@ -698,6 +779,8 @@ int info_plugin_init(const char * plugin_name, info_plugin_functions_t *plugin_f
     functions->init(name);
   }
 
+  info_plugin_cache_init(true);
+
   plugin_ipc_init();
   return 1;
 }
@@ -722,4 +805,6 @@ void info_plugin_exit(void) {
     }
   }
   outbuffer.count = 0;
+
+  info_plugin_cache_init(false);
 }
index 8016549..8cd9b30 100644 (file)
@@ -46,5 +46,6 @@
 
 int info_plugin_init(const char * plugin_name, info_plugin_functions_t *plugin_functions, info_plugin_config_t *plugin_config);
 void info_plugin_exit(void);
+long cache_timeout_generic(info_plugin_config_t *plugin_config, unsigned long long siw);
 
 #endif /* _OLSRD_LIB_INFO_OLSRD_INFO_H_ */
index d8391bf..3ae3480 100644 (file)
@@ -58,6 +58,7 @@ static info_plugin_functions_t functions = { //
         .supportsCompositeCommands = true, //
         .init = plugin_init, //
         .is_command = isCommand, //
+        .cache_timeout = cache_timeout_generic, //
         .determine_mime_type = determine_mime_type, //
         .output_start = output_start, //
         .output_end = output_end, //
index f9a04b1..24e55de 100644 (file)
@@ -58,6 +58,7 @@ static info_plugin_functions_t functions = { //
         .supportsCompositeCommands = true, //
         .init = NULL, //
         .is_command = isCommand, //
+        .cache_timeout = cache_timeout_generic, //
         .determine_mime_type = NULL, //
         .output_start = NULL, //
         .output_end = NULL, //