sgw: add egress file reader
authorFerry Huberts <ferry.huberts@pelagic.nl>
Fri, 31 Oct 2014 11:26:05 +0000 (12:26 +0100)
committerFerry Huberts <ferry.huberts@pelagic.nl>
Wed, 12 Nov 2014 10:45:25 +0000 (11:45 +0100)
Signed-off-by: Ferry Huberts <ferry.huberts@pelagic.nl>
src/egressFile.c [new file with mode: 0644]
src/egressFile.h [new file with mode: 0644]
src/egressTypes.h
src/gateway.c

diff --git a/src/egressFile.c b/src/egressFile.c
new file mode 100644 (file)
index 0000000..daee98d
--- /dev/null
@@ -0,0 +1,670 @@
+#ifdef __linux__
+
+#include "egressFile.h"
+
+/* Plugin includes */
+
+/* OLSRD includes */
+#include "olsr_cfg.h"
+#include "gateway_costs.h"
+#include "scheduler.h"
+#include "ipcalc.h"
+#include "log.h"
+
+/* System includes */
+#include <unistd.h>
+#include <fcntl.h>
+#include <regex.h>
+#include <sys/stat.h>
+#include <assert.h>
+#include <net/if.h>
+
+/** the weights for the cost calculation */
+static struct costs_weights gw_costs_weights;
+
+/** regular expression describing a comment */
+static const char * regexComment = "^([[:space:]]*|[[:space:]#]+.*)$";
+
+/**
+ * regular expression describing an egress line.
+ *
+ * # interface=uplink (Kbps),downlink (Kbps),path cost,gateway
+ */
+static const char * regexEgress = "^[[:space:]]*" //
+        "([^[:space:]=]+)"                     /* 01: interface, mandatory, can NOT be empty */
+        "[[:space:]]*=[[:space:]]*"            /* --: field separator */
+        "([[:digit:]]*)"                       /* 02: uplink, mandatory, can be empty */
+        "[[:space:]]*,[[:space:]]*"            /* --: field separator */
+        "([[:digit:]]*)"                       /* 03: downlink, mandatory, can be empty */
+        "("                                    /* 04: (the rest is optional) */
+        "[[:space:]]*,[[:space:]]*"            /* --: field separator */
+        "([[:digit:]]*)"                       /* 05: path cost, optional, can be empty */
+        "("                                    /* 06: (the rest is optional) */
+        "[[:space:]]*,[[:space:]]*"            /* --: field separator */
+        "(|([[:digit:]\\.:]+)/([[:digit:]]+))" /* 07: network, optional, can be empty, 07=ip/x 08=ip 09=x */
+        "("                                    /* 10: (the rest is optional) */
+        "[[:space:]]*,[[:space:]]*"            /* --: field separator */
+        "([[:digit:]\\.:]*)"                   /* 11: gateway, optional, can be empty */
+        ")?"                                   /* 10 */
+        ")?"                                   /* 06 */
+        ")?"                                   /* 04 */
+        "[[:space:]]*$";
+
+/** the number of matches in regexEgress */
+#define REGEX_EGRESS_LINE_MATCH_COUNT (1 /* 00 */ + 11)
+
+/** the compiled regular expression describing a comment */
+static regex_t compiledRegexComment;
+
+/** the compiled regular expression describing an egress line */
+static regex_t compiledRegexEgress;
+
+/** true when the file reader has been started */
+static bool started = false;
+
+/** the cached stat result */
+static struct timespec cachedStat;
+
+/** the malloc-ed buffer in which to store a line read from the file */
+static char * line = NULL;
+
+/** the maximum length of a line that is read from the file */
+static size_t line_length = 256;
+
+/* forward declaration */
+static bool readEgressFile(char * fileName);
+
+/*
+ * Error Reporting
+ */
+
+/** the maximum length of an error report */
+#define ERROR_LENGTH 1024
+
+/** true when errors have been reported, used to reduce error reports */
+static bool reportedErrors = false;
+
+/**
+ * Report an error.
+ *
+ * @param useErrno
+ * when true then errno is used in the error message; the error reason is also
+ * reported.
+ * @param lineNo
+ * the line number of the caller
+ * @param format
+ * a pointer to the format string
+ * @param ...
+ * arguments to the format string
+ */
+__attribute__ ((format(printf, 3, 4)))
+static void egressFileError(bool useErrno, int lineNo, const char *format, ...) {
+  char str[ERROR_LENGTH];
+  char *strErr = NULL;
+
+  if (reportedErrors) {
+    return;
+  }
+
+  if (useErrno) {
+    strErr = strerror(errno);
+  }
+
+  if ((format == NULL ) || (*format == '\0')) {
+    olsr_syslog(OLSR_LOG_ERR, "%s@%d: %s\n", __FILE__, lineNo, useErrno ? strErr : "Unknown error");
+  } else {
+    va_list arglist;
+
+    va_start(arglist, format);
+    vsnprintf(str, sizeof(str), format, arglist);
+    va_end(arglist);
+
+    str[sizeof(str) - 1] = '\0'; /* Ensures null termination */
+
+    if (useErrno) {
+      olsr_syslog(OLSR_LOG_ERR, "%s@%d: %s: %s\n", __FILE__, lineNo, str, strErr);
+    } else {
+      olsr_syslog(OLSR_LOG_ERR, "%s@%d: %s\n", __FILE__, lineNo, str);
+    }
+  }
+}
+
+/*
+ * Helpers
+ */
+
+/**
+ * Read an (olsr_ip_addr) IP address from a string:
+ * First tries to parse the value as an IPv4 address, and if not successful
+ * tries to parse it as an IPv6 address.
+ *
+ * @param str
+ * The string to convert to an (olsr_ip_addr) IP address
+ * @param dst
+ * A pointer to the location where to store the (olsr_ip_addr) IP address upon
+ * successful conversion. Not touched when errors are reported.
+ * @param dstSet
+ * A pointer to the location where to store the flag that signals whether the
+ * IP address is set. Not touched when errors are reported.
+ * @param dstIpVersion
+ * A pointer to the location where to store the IP version of the IP address.
+ * Not touched when errors are reported.
+ *
+ * @return
+ * - true on success
+ * - false otherwise
+ */
+static bool readIPAddress(const char * str, union olsr_ip_addr * dst, bool * dstSet, int * dstIpVersion) {
+  int conversion;
+  union olsr_ip_addr ip;
+  int ip_version;
+
+  assert(str);
+  assert(dst);
+  assert(dstSet);
+  assert(dstIpVersion);
+
+  /* try IPv4 first */
+  ip_version = AF_INET;
+  memset(&ip, 0, sizeof(ip));
+  conversion = inet_pton(ip_version, str, &ip.v4);
+
+  if (conversion != 1) {
+    /* now try IPv6: IPv4 conversion was not successful */
+    ip_version = AF_INET6;
+    memset(&ip, 0, sizeof(ip));
+    conversion = inet_pton(ip_version, str, &ip.v6);
+  }
+
+  if (conversion != 1) {
+    return false;
+  }
+
+  *dst = ip;
+  *dstSet = true;
+  *dstIpVersion = ip_version;
+  return true;
+}
+
+/**
+ * Read an unsigned long long number from a value string.
+ * An empty string results in a value of zero.
+ *
+ * @param value
+ * The string to convert to a number
+ * @param valueNumber
+ * A pointer to the location where to store the number upon successful conversion.
+ * Not touched when errors are reported.
+ *
+ * @return
+ * - true on success
+ * - false otherwise
+ */
+static bool readULL(const char * value, unsigned long long * valueNumber) {
+  char * endPtr = NULL;
+  unsigned long valueNew;
+
+  assert(value);
+  assert(valueNumber);
+
+  if (!value || !strlen(value)) {
+    *valueNumber = 0;
+    return true;
+  }
+
+  errno = 0;
+  valueNew = strtoull(value, &endPtr, 10);
+
+  if (!((endPtr != value) && (*value != '\0') && (*endPtr == '\0')) || (errno == ERANGE)) {
+    /* invalid conversion */
+    return false;
+  }
+
+  *valueNumber = valueNew;
+  return true;
+}
+
+/**
+ * Strip EOL characters from the end of a string
+ *
+ * @param str the string to strip
+ * @param length the length of the string
+ */
+static void stripEols(char * str, ssize_t length) {
+  ssize_t len = length;
+  while ((len > 0) && ((str[len - 1] == '\n') || (str[len - 1] == '\r'))) {
+    len--;
+  }
+  str[len] = '\0';
+}
+
+/**
+ * Find an egress interface in the configuration
+ *
+ * @param name the name of the egress interface
+ * @return the pointer to the egress interface, NULL when not found
+ */
+struct sgw_egress_if * findEgressInterface(char * name) {
+  if (name && (name[0] != '\0')) {
+    struct sgw_egress_if * egress_if = olsr_cnf->smart_gw_egress_interfaces;
+    while (egress_if) {
+      if (!strcmp(egress_if->name, name)) {
+        return egress_if;
+      }
+      egress_if = egress_if->next;
+    }
+  }
+
+  return NULL ;
+}
+
+/**
+ * Calculate the costs from the bandwidth parameters
+ *
+ * @param bw the bandwidth parameters
+ * @param up true when the interface is up
+ * @return true when the costs changed
+ */
+bool egressBwCalculateCosts(struct egress_if_bw * bw, bool up) {
+  int64_t costsPrevious = bw->costs;
+  bw->costs = gw_costs_weigh(up, gw_costs_weights, bw->path_cost, bw->egressUk, bw->egressDk);
+  return (costsPrevious != bw->costs);
+}
+
+/**
+ * Clear the bandwidth parameters
+ * @param bw the bandwidth parameters
+ * @param up true when the interface is up
+ */
+void egressBwClear(struct egress_if_bw * bw, bool up) {
+  bw->egressUk = 0;
+  bw->egressDk = 0;
+  bw->path_cost = UINT32_MAX;
+  memset(&bw->network, 0, sizeof(bw->network));
+  memset(&bw->gateway, 0, sizeof(bw->gateway));
+
+  bw->networkSet = false;
+  bw->gatewaySet = false;
+
+  egressBwCalculateCosts(bw, up);
+}
+
+/*
+ * Timer
+ */
+
+/** the timer for polling the egress file for changes */
+static struct timer_entry *egress_file_timer;
+
+/**
+ * Timer callback to read the egress file
+ *
+ * @param unused unused
+ */
+static void egress_file_timer_callback(void *unused __attribute__ ((unused))) {
+  if (readEgressFile(olsr_cnf->smart_gw_egress_file)) {
+    // FIXME process changes
+  }
+}
+
+/*
+ * Life Cycle
+ */
+
+/**
+ * Initialises the egress file reader
+ *
+ * @return
+ * - true upon success
+ * - false otherwise
+ */
+bool startEgressFile(void) {
+  int r;
+
+  if (started) {
+    return true;
+  }
+
+  line = malloc(line_length);
+  if (!line) {
+    egressFileError(false, __LINE__, "Could not allocate a line buffer");
+    return false;
+  }
+  *line = '\0';
+
+  r = regcomp(&compiledRegexComment, regexComment, REG_EXTENDED);
+  if (r) {
+    regerror(r, &compiledRegexComment, line, line_length);
+    egressFileError(false, __LINE__, "Could not compile regex \"%s\" (%d = %s)", regexComment, r, line);
+
+    free(line);
+    line = NULL;
+    return false;
+  }
+
+  r = regcomp(&compiledRegexEgress, regexEgress, REG_EXTENDED);
+  if (r) {
+    regerror(r, &compiledRegexEgress, line, line_length);
+    egressFileError(false, __LINE__, "Could not compile regex \"%s\" (%d = %s)", regexEgress, r, line);
+
+    regfree(&compiledRegexComment);
+    free(line);
+    line = NULL;
+    return false;
+  }
+
+  gw_costs_weights.WexitU = olsr_cnf->smart_gw_weight_exitlink_up;
+  gw_costs_weights.WexitD = olsr_cnf->smart_gw_weight_exitlink_down;
+  gw_costs_weights.Wetx = olsr_cnf->smart_gw_weight_etx;
+  gw_costs_weights.Detx = olsr_cnf->smart_gw_divider_etx;
+
+  cachedStat.tv_sec = -1;
+  cachedStat.tv_nsec = -1;
+
+  readEgressFile(olsr_cnf->smart_gw_egress_file);
+
+  olsr_set_timer(&egress_file_timer, olsr_cnf->smart_gw_egress_file_period, 0, true, &egress_file_timer_callback, NULL, NULL);
+
+  started = true;
+  return true;
+}
+
+/**
+ * Cleans up the egress file reader.
+ */
+void stopEgressFile(void) {
+  if (started) {
+    olsr_stop_timer(egress_file_timer);
+    egress_file_timer = NULL;
+
+    regfree(&compiledRegexEgress);
+    regfree(&compiledRegexComment);
+    free(line);
+
+    started = false;
+  }
+}
+
+/*
+ * File Reader
+ */
+
+/** the buffer with regex matches */
+static regmatch_t pmatch[REGEX_EGRESS_LINE_MATCH_COUNT];
+
+/**
+ * Read the egress file
+ *
+ * @param fileName the filename
+ * @return true to indicate changes (any egress_if->bwChanged is true)
+ */
+static bool readEgressFile(char * fileName) {
+  bool changed = false;
+
+  int fd;
+  FILE * fp = NULL;
+  struct stat statBuf;
+  unsigned int lineNumber = 0;
+  ssize_t length = -1;
+  bool reportedErrorsLocal = false;
+
+  fd = open(!fileName ? DEF_GW_EGRESS_FILE : fileName, O_RDONLY);
+  if (fd < 0) {
+    /* could not access the file */
+    goto out;
+  }
+
+  if (fstat(fd, &statBuf)) {
+    /* could not stat the file */
+    goto out;
+  }
+
+  if (!memcmp(&cachedStat, &statBuf.st_mtim, sizeof(cachedStat))) {
+    /* file did not change since last read */
+    goto out;
+  }
+
+  fp = fdopen(fd, "r");
+  if (!fp) {
+    /* could not open the file */
+    goto out;
+  }
+
+  /* copy 'current' egress interfaces into 'previous' field */
+  {
+    struct sgw_egress_if * egress_if = olsr_cnf->smart_gw_egress_interfaces;
+    while (egress_if) {
+      egress_if->bwPrevious = egress_if->bwCurrent;
+      egress_if->bwCostsChanged = false;
+      egress_if->bwNetworkChanged = false;
+      egress_if->bwGatewayChanged = false;
+      egress_if->bwChanged = false;
+
+      egress_if->inEgressFile = false;
+
+      egress_if = egress_if->next;
+    }
+  }
+
+  while ((length = getline(&line, &line_length, fp)) != -1) {
+    struct sgw_egress_if * egress_if = NULL;
+    unsigned long long uplink = DEF_EGRESS_UPLINK_KBPS;
+    unsigned long long downlink = DEF_EGRESS_DOWNLINK_KBPS;
+    unsigned long long pathCosts = DEF_EGRESS_PATH_COSTS;
+    struct olsr_ip_prefix network;
+    union olsr_ip_addr gateway;
+    bool networkSet = false;
+    bool gatewaySet = false;
+    int networkIpVersion = AF_INET;
+    int gatewayIpVersion = AF_INET;
+
+    lineNumber++;
+
+    if (!regexec(&compiledRegexComment, line, 0, NULL, 0)) {
+      /* the line is a comment */
+      continue;
+    }
+
+    memset(&network, 0, sizeof(network));
+    memset(&gateway, 0, sizeof(gateway));
+
+    stripEols(line, length);
+
+    memset(pmatch, 0, sizeof(pmatch));
+    if (regexec(&compiledRegexEgress, line, REGEX_EGRESS_LINE_MATCH_COUNT, pmatch, 0)) {
+      egressFileError(false, __LINE__, "Egress speed file line %d uses invalid syntax: line is ignored (%s)", lineNumber, line);
+      reportedErrorsLocal = true;
+      continue;
+    }
+
+    /* iface: mandatory presence, guaranteed through regex match */
+    {
+      regoff_t len = pmatch[1].rm_eo - pmatch[1].rm_so;
+      char * ifaceString = &line[pmatch[1].rm_so];
+      line[pmatch[1].rm_eo] = '\0';
+
+      if (len > IFNAMSIZ) {
+        /* interface name is too long */
+        egressFileError(false, __LINE__, "Egress speed file line %d: interface \"%s\" is too long: line is ignored", lineNumber, ifaceString);
+        reportedErrorsLocal = true;
+        continue;
+      }
+
+      egress_if = findEgressInterface(ifaceString);
+      if (!egress_if) {
+        /* not a known egress interface */
+        egressFileError(false, __LINE__, "Egress speed file line %d: interface \"%s\" is not a configured egress interface: line is ignored", lineNumber,
+            ifaceString);
+        reportedErrorsLocal = true;
+        continue;
+      }
+    }
+    assert(egress_if);
+
+    /* uplink: mandatory presence, guaranteed through regex match */
+    {
+      regoff_t len = pmatch[2].rm_eo - pmatch[2].rm_so;
+      char * uplinkString = &line[pmatch[2].rm_so];
+      line[pmatch[2].rm_eo] = '\0';
+
+      if ((len > 0) && !readULL(uplinkString, &uplink)) {
+        egressFileError(false, __LINE__, "Egress speed file line %d: uplink bandwidth \"%s\" is not a valid number: line is ignored", lineNumber, uplinkString);
+        reportedErrorsLocal = true;
+        continue;
+      }
+    }
+    uplink = MIN(uplink, MAX_SMARTGW_SPEED);
+
+    /* downlink: mandatory presence, guaranteed through regex match */
+    {
+      regoff_t len = pmatch[3].rm_eo - pmatch[3].rm_so;
+      char * downlinkString = &line[pmatch[3].rm_so];
+      line[pmatch[3].rm_eo] = '\0';
+
+      if ((len > 0) && !readULL(downlinkString, &downlink)) {
+        egressFileError(false, __LINE__, "Egress speed file line %d: downlink bandwidth \"%s\" is not a valid number: line is ignored", lineNumber,
+            downlinkString);
+        reportedErrorsLocal = true;
+        continue;
+      }
+    }
+    downlink = MIN(downlink, MAX_SMARTGW_SPEED);
+
+    /* path costs: optional presence */
+    if (pmatch[5].rm_so != -1) {
+      regoff_t len = pmatch[5].rm_eo - pmatch[5].rm_so;
+      char * pathCostsString = &line[pmatch[5].rm_so];
+      line[pmatch[5].rm_eo] = '\0';
+
+      if ((len > 0) && !readULL(pathCostsString, &pathCosts)) {
+        egressFileError(false, __LINE__, "Egress speed file line %d: path costs \"%s\" is not a valid number: line is ignored", lineNumber, pathCostsString);
+        reportedErrorsLocal = true;
+        continue;
+      }
+    }
+    pathCosts = MIN(pathCosts, UINT32_MAX);
+
+    /* network: optional presence */
+    if ((pmatch[7].rm_so != -1) && ((pmatch[7].rm_eo - pmatch[7].rm_so) > 0)) {
+      /* network is present: guarantees IP and prefix presence */
+      unsigned long long prefix_len;
+      char * networkString = &line[pmatch[8].rm_so];
+      char * prefixlenString = &line[pmatch[9].rm_so];
+      line[pmatch[8].rm_eo] = '\0';
+      line[pmatch[9].rm_eo] = '\0';
+
+      if (!readIPAddress(networkString, &network.prefix, &networkSet, &networkIpVersion)) {
+        egressFileError(false, __LINE__, "Egress speed file line %d: network IP address \"%s\" is not a valid IP address: line is ignored", lineNumber,
+            networkString);
+        reportedErrorsLocal = true;
+        continue;
+      }
+
+      if (!readULL(prefixlenString, &prefix_len)) {
+        egressFileError(false, __LINE__, "Egress speed file line %d: network prefix \"%s\" is not a valid number: line is ignored", lineNumber,
+            prefixlenString);
+        reportedErrorsLocal = true;
+        continue;
+      }
+
+      if (prefix_len > ((networkIpVersion == AF_INET) ? 32 : 128)) {
+        egressFileError(false, __LINE__, "Egress speed file line %d: network prefix \"%s\" is not in the range [0, %d]: line is ignored", lineNumber,
+            prefixlenString, ((networkIpVersion == AF_INET) ? 32 : 128));
+        reportedErrorsLocal = true;
+        continue;
+      }
+
+      network.prefix_len = prefix_len;
+    }
+
+    /* gateway: optional presence */
+    if (pmatch[11].rm_so != -1) {
+      regoff_t len = pmatch[11].rm_eo - pmatch[11].rm_so;
+      char * gatewayString = &line[pmatch[11].rm_so];
+      line[pmatch[11].rm_eo] = '\0';
+
+      if ((len > 0) && !readIPAddress(gatewayString, &gateway, &gatewaySet, &gatewayIpVersion)) {
+        egressFileError(false, __LINE__, "Egress speed file line %d: gateway IP address \"%s\" is not a valid IP address: line is ignored", lineNumber,
+            gatewayString);
+        reportedErrorsLocal = true;
+        continue;
+      }
+    }
+
+    /* check all IP versions are the same */
+    if ((networkSet && gatewaySet) && (networkIpVersion != gatewayIpVersion)) {
+      egressFileError(false, __LINE__, "Egress speed file line %d: network and gateway IP addresses must be of the same IP version: line is ignored",
+          lineNumber);
+      reportedErrorsLocal = true;
+      continue;
+    }
+
+    /* check no IPv6 */
+    if ((networkSet && networkIpVersion == AF_INET6) || //
+        (gatewaySet && gatewayIpVersion == AF_INET6)) {
+      egressFileError(false, __LINE__, "Egress speed file line %d: network and gateway IP addresses must not be IPv6 addresses: line is ignored", lineNumber);
+      reportedErrorsLocal = true;
+      continue;
+    }
+
+    /* ensure network is masked by netmask */
+    if (networkSet) {
+      /* assumes IPv4 */
+      in_addr_t mask = (network.prefix_len == 0) ? 0 : (~0U << (32 - network.prefix_len));
+      network.prefix.v4.s_addr = htonl(ntohl(network.prefix.v4.s_addr) & mask);
+    }
+
+    if (!uplink || !downlink) {
+      egressBwClear(&egress_if->bwCurrent, false);
+    } else {
+      egress_if->bwCurrent.egressUk = uplink;
+      egress_if->bwCurrent.egressDk = downlink;
+      egress_if->bwCurrent.path_cost = pathCosts;
+      egress_if->bwCurrent.network = network;
+      egress_if->bwCurrent.gateway = gateway;
+
+      egress_if->bwCurrent.networkSet = networkSet;
+      egress_if->bwCurrent.gatewaySet = gatewaySet;
+
+      egressBwCalculateCosts(&egress_if->bwCurrent, true);
+    }
+
+    egress_if->inEgressFile = true;
+  }
+
+  fclose(fp);
+  fp = NULL;
+
+  cachedStat = statBuf.st_mtim;
+
+  reportedErrors = reportedErrorsLocal;
+
+  /* clear absent egress interfaces and setup 'changed' status */
+  {
+    struct sgw_egress_if * egress_if = olsr_cnf->smart_gw_egress_interfaces;
+    while (egress_if) {
+      if (!egress_if->inEgressFile) {
+        egressBwClear(&egress_if->bwCurrent, false);
+      }
+
+      egress_if->bwCostsChanged = egressBwCostsChanged(egress_if);
+      egress_if->bwNetworkChanged = egressBwNetworkChanged(egress_if);
+      egress_if->bwGatewayChanged = egressBwGatewayChanged(egress_if);
+      egress_if->bwChanged = egressBwChanged(egress_if);
+      if (egress_if->bwChanged) {
+        changed = true;
+      }
+
+      egress_if = egress_if->next;
+    }
+  }
+
+  out: if (fd >= 0) {
+    close(fd);
+    fd = -1;
+  }
+
+  return changed;
+}
+
+#endif /* __linux__ */
diff --git a/src/egressFile.h b/src/egressFile.h
new file mode 100644 (file)
index 0000000..56b8810
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef EGRESSFILE_H
+#define EGRESSFILE_H
+
+#ifdef __linux__
+
+/* Plugin includes */
+
+/* OLSRD includes */
+#include "egressTypes.h"
+
+/* System includes */
+#include <stdbool.h>
+#include <stdint.h>
+
+#define DEF_EGRESS_UPLINK_KBPS       0
+#define DEF_EGRESS_DOWNLINK_KBPS     0
+#define DEF_EGRESS_PATH_COSTS        0
+
+bool startEgressFile(void);
+void stopEgressFile(void);
+
+struct sgw_egress_if * findEgressInterface(char * name);
+
+bool egressBwCalculateCosts(struct egress_if_bw * bw, bool up);
+void egressBwClear(struct egress_if_bw * egress_if, bool up);
+
+#endif /* __linux__ */
+
+#endif /* EGRESSFILE_H */
index eb2e506..11652bf 100644 (file)
@@ -6,10 +6,31 @@
 /* Plugin includes */
 
 /* OLSRD includes */
+#include "olsr_types.h"
 
 /* System includes */
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+
+struct egress_if_bw {
+  /* in the egress file */
+  uint32_t egressUk; /**< in Kbps, [0, MAX_SMARTGW_SPEED] */
+  uint32_t egressDk; /**< in Kbps, [0, MAX_SMARTGW_SPEED] */
+  uint32_t path_cost; /**<         [0, UINT32_MAX]        */
+  struct olsr_ip_prefix network;
+  union olsr_ip_addr gateway;
+
+  /* derived from network, gateway */
+  bool networkSet;
+  bool gatewaySet;
+
+  /* calculated from egressUk, egressDk, path_costs */
+  int64_t costs;
+};
 
 struct sgw_egress_if {
+  /* configured through the SmartGatewayEgressInterfaces configuration parameter */
   char *name;
 
   /* configured through the SmartGatewayTablesOffset and SmartGatewayRulesOffset configuration parameters */
@@ -17,9 +38,44 @@ struct sgw_egress_if {
   uint8_t ruleNr; /**< IP rule number */
   uint8_t bypassRuleNr; /**< bypass IP rule number */
 
+
+  /* configured through the readEgressFile function */
+  struct egress_if_bw bwPrevious;
+  struct egress_if_bw bwCurrent;
+  bool bwCostsChanged;
+  bool bwNetworkChanged;
+  bool bwGatewayChanged;
+  bool bwChanged; /* covers bwCostsChanged, bwNetworkChanged and bwGatewayChanged */
+
+  /* state for the readEgressFile function */
+  bool inEgressFile;
+
+  /* next in the list */
   struct sgw_egress_if *next;
 };
 
+static inline bool egressBwCostsChanged(struct sgw_egress_if * egress_if) {
+  return (egress_if->bwPrevious.costs != egress_if->bwCurrent.costs);
+}
+
+static inline bool egressBwNetworkChanged(struct sgw_egress_if * egress_if) {
+  return //
+    (egress_if->bwPrevious.networkSet != egress_if->bwCurrent.networkSet) || //
+    (egress_if->bwCurrent.networkSet && //
+      memcmp(&egress_if->bwPrevious.network, &egress_if->bwCurrent.network, sizeof(egress_if->bwCurrent.network)));
+}
+
+static inline bool egressBwGatewayChanged(struct sgw_egress_if * egress_if) {
+  return //
+    (egress_if->bwPrevious.gatewaySet != egress_if->bwCurrent.gatewaySet) || //
+    (egress_if->bwCurrent.gatewaySet && //
+      memcmp(&egress_if->bwPrevious.gateway, &egress_if->bwCurrent.gateway, sizeof(egress_if->bwCurrent.gateway)));
+}
+
+static inline bool egressBwChanged(struct sgw_egress_if * egress_if) {
+  return egress_if->bwCostsChanged || egress_if->bwNetworkChanged || egress_if->bwGatewayChanged;
+}
+
 #endif /* __linux__ */
 
 #endif /* EGRESSTYPES_H */
index f8403dc..e89f7ab 100644 (file)
@@ -23,6 +23,7 @@
 #include "gateway_list.h"
 #include "gateway.h"
 #include "egressTypes.h"
+#include "egressFile.h"
 
 #include <assert.h>
 #include <net/if.h>
@@ -656,6 +657,23 @@ int olsr_startup_gateways(void) {
     return 0;
   }
 
+  /* Initialise the egress interfaces */
+  {
+    struct sgw_egress_if * egress_if = olsr_cnf->smart_gw_egress_interfaces;
+    while (egress_if) {
+      egressBwClear(&egress_if->bwPrevious, false);
+      egressBwClear(&egress_if->bwCurrent, false);
+      egress_if->bwCostsChanged = egressBwCostsChanged(egress_if);
+      egress_if->bwNetworkChanged = egressBwNetworkChanged(egress_if);
+      egress_if->bwGatewayChanged = egressBwGatewayChanged(egress_if);
+      egress_if->bwChanged = egressBwChanged(egress_if);
+
+      egress_if->inEgressFile = false;
+
+      egress_if = egress_if->next;
+    }
+  }
+
   ok = ok && multiGwRulesGeneric(true);
   ok = ok && multiGwRulesSgwServerTunnel(true);
   ok = ok && multiGwRulesOlsrInterfaces(true);
@@ -667,6 +685,9 @@ int olsr_startup_gateways(void) {
     return 1;
   }
 
+  startEgressFile();
+  // FIXME process changes (always: initial setup)
+
   olsr_add_ifchange_handler(smartgw_tunnel_monitor);
 
   if (olsr_cnf->smart_gw_takedown_percentage > 0) {
@@ -694,6 +715,24 @@ void olsr_shutdown_gateways(void) {
 
   olsr_remove_ifchange_handler(smartgw_tunnel_monitor);
 
+  stopEgressFile();
+  {
+    struct sgw_egress_if * egress_if = olsr_cnf->smart_gw_egress_interfaces;
+    while (egress_if) {
+      egress_if->bwPrevious = egress_if->bwCurrent;
+      egressBwClear(&egress_if->bwCurrent, false);
+      egress_if->bwCostsChanged = egressBwCostsChanged(egress_if);
+      egress_if->bwNetworkChanged = egressBwNetworkChanged(egress_if);
+      egress_if->bwGatewayChanged = egressBwGatewayChanged(egress_if);
+      egress_if->bwChanged = egressBwChanged(egress_if);
+
+      egress_if->inEgressFile = false;
+
+      egress_if = egress_if->next;
+    }
+  }
+  // FIXME process changes (always: shutdown)
+
   (void)multiGwRulesSgwTunnels(false);
   (void)multiGwRulesEgressInterfaces(false);
   (void)multiGwRulesOlsrInterfaces(false);