sgw: do not clear egress data in the read race
[olsrd.git] / src / egressFile.c
1 #ifdef __linux__
2
3 #include "egressFile.h"
4
5 /* Plugin includes */
6
7 /* OLSRD includes */
8 #include "olsr_cfg.h"
9 #include "gateway_costs.h"
10 #include "gateway.h"
11 #include "scheduler.h"
12 #include "ipcalc.h"
13 #include "log.h"
14
15 /* System includes */
16 #include <unistd.h>
17 #include <fcntl.h>
18 #include <regex.h>
19 #include <sys/stat.h>
20 #include <assert.h>
21 #include <net/if.h>
22
23 /** the maximum length of a line that is read from the file */
24 #define LINE_LENGTH 256
25
26 /** the weights for the cost calculation */
27 static struct costs_weights gw_costs_weights_storage;
28 static struct costs_weights * gw_costs_weights = NULL;
29
30 /** regular expression describing a comment */
31 static const char * regexComment = "^([[:space:]]*|[[:space:]#]+.*)$";
32
33 /**
34  * regular expression describing an egress line.
35  *
36  * # interface=uplink (Kbps),downlink (Kbps),path cost,gateway
37  */
38 static const char * regexEgress = "^[[:space:]]*" //
39         "([^[:space:]=]+)"                     /* 01: interface, mandatory, can NOT be empty */
40         "[[:space:]]*=[[:space:]]*"            /* --: field separator */
41         "([[:digit:]]*)"                       /* 02: uplink, mandatory, can be empty */
42         "[[:space:]]*,[[:space:]]*"            /* --: field separator */
43         "([[:digit:]]*)"                       /* 03: downlink, mandatory, can be empty */
44         "("                                    /* 04: (the rest is optional) */
45         "[[:space:]]*,[[:space:]]*"            /* --: field separator */
46         "([[:digit:]]*)"                       /* 05: path cost, optional, can be empty */
47         "("                                    /* 06: (the rest is optional) */
48         "[[:space:]]*,[[:space:]]*"            /* --: field separator */
49         "(|([[:digit:]\\.:]+)/([[:digit:]]+))" /* 07: network, optional, can be empty, 07=ip/x 08=ip 09=x */
50         "("                                    /* 10: (the rest is optional) */
51         "[[:space:]]*,[[:space:]]*"            /* --: field separator */
52         "([[:digit:]\\.:]*)"                   /* 11: gateway, optional, can be empty */
53         ")?"                                   /* 10 */
54         ")?"                                   /* 06 */
55         ")?"                                   /* 04 */
56         "[[:space:]]*$";
57
58 /** the number of matches in regexEgress */
59 #define REGEX_EGRESS_LINE_MATCH_COUNT (1 /* 00 */ + 11)
60
61 /** the compiled regular expression describing a comment */
62 static regex_t compiledRegexComment;
63
64 /** the compiled regular expression describing an egress line */
65 static regex_t compiledRegexEgress;
66
67 /** true when the file reader has been started */
68 static bool started = false;
69
70 /** type to hold the cached stat result */
71 typedef struct _CachedStat {
72 #if defined(__linux__) && !defined(__ANDROID__)
73   struct timespec timeStamp; /* Time of last modification (full resolution) */
74 #else
75   time_t timeStamp; /* Time of last modification (second resolution) */
76 #endif
77 } CachedStat;
78
79 /** the cached stat result */
80 static CachedStat cachedStat;
81
82 /** the malloc-ed buffer in which to store a line read from the file */
83 static char * line = NULL;
84
85 /* forward declaration */
86 static bool readEgressFile(const char * fileName);
87
88 /*
89  * Error Reporting
90  */
91
92 /** the maximum length of an error report */
93 #define ERROR_LENGTH 1024
94
95 /** true when errors have been reported, used to reduce error reports */
96 static bool reportedErrors = false;
97
98 /**
99  * Report an error.
100  *
101  * @param useErrno
102  * when true then errno is used in the error message; the error reason is also
103  * reported.
104  * @param lineNo
105  * the line number of the caller
106  * @param format
107  * a pointer to the format string
108  * @param ...
109  * arguments to the format string
110  */
111 __attribute__ ((format(printf, 3, 4)))
112 static void egressFileError(bool useErrno, int lineNo, const char *format, ...) {
113   char str[ERROR_LENGTH];
114   char *strErr = NULL;
115
116   if (reportedErrors) {
117     return;
118   }
119
120   if (useErrno) {
121     strErr = strerror(errno);
122   }
123
124   if ((format == NULL ) || (*format == '\0')) {
125     olsr_syslog(OLSR_LOG_ERR, "%s@%d: %s\n", __FILE__, lineNo, useErrno ? strErr : "Unknown error");
126   } else {
127     va_list arglist;
128
129     va_start(arglist, format);
130     vsnprintf(str, sizeof(str), format, arglist);
131     va_end(arglist);
132
133     str[sizeof(str) - 1] = '\0'; /* Ensures null termination */
134
135     if (useErrno) {
136       olsr_syslog(OLSR_LOG_ERR, "%s@%d: %s: %s\n", __FILE__, lineNo, str, strErr);
137     } else {
138       olsr_syslog(OLSR_LOG_ERR, "%s@%d: %s\n", __FILE__, lineNo, str);
139     }
140   }
141 }
142
143 /*
144  * Helpers
145  */
146
147 #ifdef __ANDROID__
148 static ssize_t getline(char **lineptr, size_t *n, FILE *stream)
149 {
150     char *ptr;
151     size_t len;
152
153     ptr = fgetln(stream, n);
154
155     if (ptr == NULL) {
156         return -1;
157     }
158
159     /* Free the original ptr */
160     if (*lineptr != NULL) free(*lineptr);
161
162     /* Add one more space for '\0' */
163     len = n[0] + 1;
164
165     /* Update the length */
166     n[0] = len;
167
168     /* Allocate a new buffer */
169     *lineptr = malloc(len);
170
171     /* Copy over the string */
172     memcpy(*lineptr, ptr, len-1);
173
174     /* Write the NULL character */
175     (*lineptr)[len-1] = '\0';
176
177     /* Return the length of the new buffer */
178     return len;
179 }
180 #endif
181
182 /**
183  * Read an (olsr_ip_addr) IP address from a string:
184  * First tries to parse the value as an IPv4 address, and if not successful
185  * tries to parse it as an IPv6 address.
186  *
187  * @param str
188  * The string to convert to an (olsr_ip_addr) IP address
189  * @param dst
190  * A pointer to the location where to store the (olsr_ip_addr) IP address upon
191  * successful conversion. Not touched when errors are reported.
192  * @param dstSet
193  * A pointer to the location where to store the flag that signals whether the
194  * IP address is set. Not touched when errors are reported.
195  * @param dstIpVersion
196  * A pointer to the location where to store the IP version of the IP address.
197  * Not touched when errors are reported.
198  *
199  * @return
200  * - true on success
201  * - false otherwise
202  */
203 static bool readIPAddress(const char * str, union olsr_ip_addr * dst, bool * dstSet, int * dstIpVersion) {
204   int conversion;
205   union olsr_ip_addr ip;
206   int ip_version;
207
208   assert(str);
209   assert(dst);
210   assert(dstSet);
211   assert(dstIpVersion);
212
213   /* try IPv4 first */
214   ip_version = AF_INET;
215   memset(&ip, 0, sizeof(ip));
216   conversion = inet_pton(ip_version, str, &ip.v4);
217
218   if (conversion != 1) {
219     /* now try IPv6: IPv4 conversion was not successful */
220     ip_version = AF_INET6;
221     memset(&ip, 0, sizeof(ip));
222     conversion = inet_pton(ip_version, str, &ip.v6);
223   }
224
225   if (conversion != 1) {
226     return false;
227   }
228
229   *dst = ip;
230   *dstSet = true;
231   *dstIpVersion = ip_version;
232   return true;
233 }
234
235 /**
236  * Read an unsigned long long number from a value string.
237  * An empty string results in a value of zero.
238  *
239  * @param value
240  * The string to convert to a number
241  * @param valueNumber
242  * A pointer to the location where to store the number upon successful conversion.
243  * Not touched when errors are reported.
244  *
245  * @return
246  * - true on success
247  * - false otherwise
248  */
249 static bool readULL(const char * value, unsigned long long * valueNumber) {
250   char * endPtr = NULL;
251   unsigned long valueNew;
252
253   assert(value);
254   assert(valueNumber);
255
256   if (!value || !strlen(value)) {
257     *valueNumber = 0;
258     return true;
259   }
260
261   errno = 0;
262   valueNew = strtoull(value, &endPtr, 10);
263
264   if (!((endPtr != value) && (*value != '\0') && (*endPtr == '\0')) || (errno == ERANGE)) {
265     /* invalid conversion */
266     return false;
267   }
268
269   *valueNumber = valueNew;
270   return true;
271 }
272
273 /**
274  * Strip EOL characters from the end of a string
275  *
276  * @param str the string to strip
277  * @param length the length of the string
278  */
279 static void stripEols(char * str, ssize_t length) {
280   ssize_t len = length;
281   while ((len > 0) && ((str[len - 1] == '\n') || (str[len - 1] == '\r'))) {
282     len--;
283   }
284   str[len] = '\0';
285 }
286
287 /**
288  * Find an egress interface in the configuration
289  *
290  * @param name the name of the egress interface
291  * @return the pointer to the egress interface, NULL when not found
292  */
293 struct sgw_egress_if * findEgressInterface(char * name) {
294   if (name && (name[0] != '\0')) {
295     struct sgw_egress_if * egress_if = olsr_cnf->smart_gw_egress_interfaces;
296     while (egress_if) {
297       if (!strcmp(egress_if->name, name)) {
298         return egress_if;
299       }
300       egress_if = egress_if->next;
301     }
302   }
303
304   return NULL ;
305 }
306
307 /**
308  * Find an egress interface in the configuration by if_index
309  *
310  * @param if_index the index of the egress interface
311  * @return the pointer to the egress interface, NULL when not found
312  */
313 struct sgw_egress_if * findEgressInterfaceByIndex(int if_index) {
314   if (if_index > 0) {
315     struct sgw_egress_if * egress_if = olsr_cnf->smart_gw_egress_interfaces;
316     while (egress_if) {
317       if (egress_if->if_index == if_index) {
318         return egress_if;
319       }
320       egress_if = egress_if->next;
321     }
322   }
323
324   return NULL ;
325 }
326
327 /**
328  * Calculate the costs from the bandwidth parameters
329  *
330  * @param bw the bandwidth parameters
331  * @param up true when the interface is up
332  * @return true when the costs changed
333  */
334 bool egressBwCalculateCosts(struct egress_if_bw * bw, bool up) {
335   if (!gw_costs_weights) {
336     gw_costs_weights_storage.WexitU = olsr_cnf->smart_gw_weight_exitlink_up;
337     gw_costs_weights_storage.WexitD = olsr_cnf->smart_gw_weight_exitlink_down;
338     gw_costs_weights_storage.Wetx = olsr_cnf->smart_gw_weight_etx;
339     gw_costs_weights_storage.Detx = olsr_cnf->smart_gw_divider_etx;
340     gw_costs_weights = &gw_costs_weights_storage;
341   }
342
343   {
344     int64_t costsPrevious = bw->costs;
345     bw->costs = gw_costs_weigh(up, gw_costs_weights_storage, bw->path_cost, olsr_cnf->smart_gw_path_max_cost_etx_max, bw->egressUk, bw->egressDk);
346     return (costsPrevious != bw->costs);
347   }
348 }
349
350 /**
351  * Clear the bandwidth parameters
352  * @param bw the bandwidth parameters
353  * @param up true when the interface is up
354  */
355 void egressBwClear(struct egress_if_bw * bw, bool up) {
356   bw->egressUk = 0;
357   bw->egressDk = 0;
358   bw->path_cost = 0;
359   memset(&bw->network, 0, sizeof(bw->network));
360   memset(&bw->gateway, 0, sizeof(bw->gateway));
361
362   bw->networkSet = false;
363   bw->gatewaySet = false;
364
365   egressBwCalculateCosts(bw, up);
366 }
367
368 /*
369  * Timer
370  */
371
372 /** the timer for polling the egress file for changes */
373 static struct timer_entry *egress_file_timer;
374
375 /**
376  * Timer callback to read the egress file
377  *
378  * @param unused unused
379  */
380 static void egress_file_timer_callback(void *unused __attribute__ ((unused))) {
381   if (readEgressFile(olsr_cnf->smart_gw_egress_file)) {
382     doRoutesMultiGw(true, false, GW_MULTI_CHANGE_PHASE_RUNTIME);
383   }
384 }
385
386 /*
387  * Life Cycle
388  */
389
390 /**
391  * Initialises the egress file reader
392  *
393  * @return
394  * - true upon success
395  * - false otherwise
396  */
397 bool startEgressFile(void) {
398   int r;
399
400   if (started) {
401     return true;
402   }
403
404   line = malloc(LINE_LENGTH);
405   if (!line) {
406     egressFileError(false, __LINE__, "Could not allocate a line buffer");
407     return false;
408   }
409   *line = '\0';
410
411   r = regcomp(&compiledRegexComment, regexComment, REG_EXTENDED);
412   if (r) {
413     regerror(r, &compiledRegexComment, line, LINE_LENGTH);
414     egressFileError(false, __LINE__, "Could not compile regex \"%s\" (%d = %s)", regexComment, r, line);
415
416     free(line);
417     line = NULL;
418     return false;
419   }
420
421   r = regcomp(&compiledRegexEgress, regexEgress, REG_EXTENDED);
422   if (r) {
423     regerror(r, &compiledRegexEgress, line, LINE_LENGTH);
424     egressFileError(false, __LINE__, "Could not compile regex \"%s\" (%d = %s)", regexEgress, r, line);
425
426     regfree(&compiledRegexComment);
427     free(line);
428     line = NULL;
429     return false;
430   }
431
432   memset(&cachedStat.timeStamp, 0, sizeof(cachedStat.timeStamp));
433
434   readEgressFile(olsr_cnf->smart_gw_egress_file);
435
436   olsr_set_timer(&egress_file_timer, olsr_cnf->smart_gw_egress_file_period, 0, true, &egress_file_timer_callback, NULL, NULL);
437
438   started = true;
439   return true;
440 }
441
442 /**
443  * Cleans up the egress file reader.
444  */
445 void stopEgressFile(void) {
446   if (started) {
447     olsr_stop_timer(egress_file_timer);
448     egress_file_timer = NULL;
449
450     regfree(&compiledRegexEgress);
451     regfree(&compiledRegexComment);
452     free(line);
453
454     started = false;
455   }
456 }
457
458 /*
459  * File Reader
460  */
461
462 /** the buffer with regex matches */
463 static regmatch_t pmatch[REGEX_EGRESS_LINE_MATCH_COUNT];
464
465 static void readEgressFileClear(void) {
466   struct sgw_egress_if * egress_if = olsr_cnf->smart_gw_egress_interfaces;
467   while (egress_if) {
468     egress_if->bwPrevious = egress_if->bwCurrent;
469     egress_if->bwCostsChanged = false;
470     egress_if->bwNetworkChanged = false;
471     egress_if->bwGatewayChanged = false;
472     egress_if->bwChanged = false;
473
474     egress_if->inEgressFile = false;
475
476     egress_if = egress_if->next;
477   }
478 }
479
480 /**
481  * Read the egress file
482  *
483  * @param fileName the filename
484  * @return true to indicate changes (any egress_if->bwChanged is true)
485  */
486 static bool readEgressFile(const char * fileName) {
487   struct stat statBuf;
488   FILE * fp = NULL;
489   void * mtim;
490   unsigned int lineNumber = 0;
491
492   bool changed = false;
493   ssize_t length = -1;
494   bool reportedErrorsLocal = false;
495   const char * filepath = !fileName ? DEF_GW_EGRESS_FILE : fileName;
496   size_t line_length = LINE_LENGTH;
497
498   if (stat(filepath, &statBuf)) {
499     /* could not stat the file */
500     memset(&cachedStat.timeStamp, 0, sizeof(cachedStat.timeStamp));
501     readEgressFileClear();
502     goto outerror;
503   }
504
505 #if defined(__linux__) && !defined(__ANDROID__)
506   mtim = &statBuf.st_mtim;
507 #else
508   mtim = &statBuf.st_mtime;
509 #endif
510
511   if (!memcmp(&cachedStat.timeStamp, mtim, sizeof(cachedStat.timeStamp))) {
512     /* file did not change since last read */
513     goto out;
514   }
515
516   fp = fopen(filepath, "r");
517   if (!fp) {
518     /* could not open the file */
519     goto out;
520   }
521
522   memcpy(&cachedStat.timeStamp, mtim, sizeof(cachedStat.timeStamp));
523
524   /* copy 'current' egress interfaces into 'previous' field */
525   readEgressFileClear();
526
527   while ((length = getline(&line, &line_length, fp)) != -1) {
528     struct sgw_egress_if * egress_if = NULL;
529     unsigned long long uplink = DEF_EGRESS_UPLINK_KBPS;
530     unsigned long long downlink = DEF_EGRESS_DOWNLINK_KBPS;
531     unsigned long long pathCosts = DEF_EGRESS_PATH_COSTS;
532     struct olsr_ip_prefix network;
533     union olsr_ip_addr gateway;
534     bool networkSet = false;
535     bool gatewaySet = false;
536     int networkIpVersion = AF_INET;
537     int gatewayIpVersion = AF_INET;
538
539     lineNumber++;
540
541     if (!regexec(&compiledRegexComment, line, 0, NULL, 0)) {
542       /* the line is a comment */
543       continue;
544     }
545
546     memset(&network, 0, sizeof(network));
547     memset(&gateway, 0, sizeof(gateway));
548
549     stripEols(line, length);
550
551     memset(pmatch, 0, sizeof(pmatch));
552     if (regexec(&compiledRegexEgress, line, REGEX_EGRESS_LINE_MATCH_COUNT, pmatch, 0)) {
553       egressFileError(false, __LINE__, "Egress speed file line %d uses invalid syntax: line is ignored (%s)", lineNumber, line);
554       reportedErrorsLocal = true;
555       continue;
556     }
557
558     /* iface: mandatory presence, guaranteed through regex match */
559     {
560       regoff_t len = pmatch[1].rm_eo - pmatch[1].rm_so;
561       char * ifaceString = &line[pmatch[1].rm_so];
562       line[pmatch[1].rm_eo] = '\0';
563
564       if (len > IFNAMSIZ) {
565         /* interface name is too long */
566         egressFileError(false, __LINE__, "Egress speed file line %d: interface \"%s\" is too long: line is ignored", lineNumber, ifaceString);
567         reportedErrorsLocal = true;
568         continue;
569       }
570
571       egress_if = findEgressInterface(ifaceString);
572       if (!egress_if) {
573         /* not a known egress interface */
574         egressFileError(false, __LINE__, "Egress speed file line %d: interface \"%s\" is not a configured egress interface: line is ignored", lineNumber,
575             ifaceString);
576         reportedErrorsLocal = true;
577         continue;
578       }
579     }
580     assert(egress_if);
581
582     /* uplink: mandatory presence, guaranteed through regex match */
583     {
584       regoff_t len = pmatch[2].rm_eo - pmatch[2].rm_so;
585       char * uplinkString = &line[pmatch[2].rm_so];
586       line[pmatch[2].rm_eo] = '\0';
587
588       if ((len > 0) && !readULL(uplinkString, &uplink)) {
589         egressFileError(false, __LINE__, "Egress speed file line %d: uplink bandwidth \"%s\" is not a valid number: line is ignored", lineNumber, uplinkString);
590         reportedErrorsLocal = true;
591         continue;
592       }
593     }
594     uplink = MIN(uplink, MAX_SMARTGW_SPEED);
595
596     /* downlink: mandatory presence, guaranteed through regex match */
597     {
598       regoff_t len = pmatch[3].rm_eo - pmatch[3].rm_so;
599       char * downlinkString = &line[pmatch[3].rm_so];
600       line[pmatch[3].rm_eo] = '\0';
601
602       if ((len > 0) && !readULL(downlinkString, &downlink)) {
603         egressFileError(false, __LINE__, "Egress speed file line %d: downlink bandwidth \"%s\" is not a valid number: line is ignored", lineNumber,
604             downlinkString);
605         reportedErrorsLocal = true;
606         continue;
607       }
608     }
609     downlink = MIN(downlink, MAX_SMARTGW_SPEED);
610
611     /* path costs: optional presence */
612     if (pmatch[5].rm_so != -1) {
613       regoff_t len = pmatch[5].rm_eo - pmatch[5].rm_so;
614       char * pathCostsString = &line[pmatch[5].rm_so];
615       line[pmatch[5].rm_eo] = '\0';
616
617       if ((len > 0) && !readULL(pathCostsString, &pathCosts)) {
618         egressFileError(false, __LINE__, "Egress speed file line %d: path costs \"%s\" is not a valid number: line is ignored", lineNumber, pathCostsString);
619         reportedErrorsLocal = true;
620         continue;
621       }
622     }
623     pathCosts = MIN(pathCosts, UINT32_MAX);
624
625     /* network: optional presence */
626     if ((pmatch[7].rm_so != -1) && ((pmatch[7].rm_eo - pmatch[7].rm_so) > 0)) {
627       /* network is present: guarantees IP and prefix presence */
628       unsigned long long prefix_len;
629       char * networkString = &line[pmatch[8].rm_so];
630       char * prefixlenString = &line[pmatch[9].rm_so];
631       line[pmatch[8].rm_eo] = '\0';
632       line[pmatch[9].rm_eo] = '\0';
633
634       if (!readIPAddress(networkString, &network.prefix, &networkSet, &networkIpVersion)) {
635         egressFileError(false, __LINE__, "Egress speed file line %d: network IP address \"%s\" is not a valid IP address: line is ignored", lineNumber,
636             networkString);
637         reportedErrorsLocal = true;
638         continue;
639       }
640
641       if (!readULL(prefixlenString, &prefix_len)) {
642         egressFileError(false, __LINE__, "Egress speed file line %d: network prefix \"%s\" is not a valid number: line is ignored", lineNumber,
643             prefixlenString);
644         reportedErrorsLocal = true;
645         continue;
646       }
647
648       if (prefix_len > ((networkIpVersion == AF_INET) ? 32 : 128)) {
649         egressFileError(false, __LINE__, "Egress speed file line %d: network prefix \"%s\" is not in the range [0, %d]: line is ignored", lineNumber,
650             prefixlenString, ((networkIpVersion == AF_INET) ? 32 : 128));
651         reportedErrorsLocal = true;
652         continue;
653       }
654
655       network.prefix_len = prefix_len;
656     }
657
658     /* gateway: optional presence */
659     if (pmatch[11].rm_so != -1) {
660       regoff_t len = pmatch[11].rm_eo - pmatch[11].rm_so;
661       char * gatewayString = &line[pmatch[11].rm_so];
662       line[pmatch[11].rm_eo] = '\0';
663
664       if ((len > 0) && !readIPAddress(gatewayString, &gateway, &gatewaySet, &gatewayIpVersion)) {
665         egressFileError(false, __LINE__, "Egress speed file line %d: gateway IP address \"%s\" is not a valid IP address: line is ignored", lineNumber,
666             gatewayString);
667         reportedErrorsLocal = true;
668         continue;
669       }
670     }
671
672     /* check all IP versions are the same */
673     if ((networkSet && gatewaySet) && (networkIpVersion != gatewayIpVersion)) {
674       egressFileError(false, __LINE__, "Egress speed file line %d: network and gateway IP addresses must be of the same IP version: line is ignored",
675           lineNumber);
676       reportedErrorsLocal = true;
677       continue;
678     }
679
680     /* check no IPv6 */
681     if ((networkSet && networkIpVersion == AF_INET6) || //
682         (gatewaySet && gatewayIpVersion == AF_INET6)) {
683       egressFileError(false, __LINE__, "Egress speed file line %d: network and gateway IP addresses must not be IPv6 addresses: line is ignored", lineNumber);
684       reportedErrorsLocal = true;
685       continue;
686     }
687
688     /* ensure network is masked by netmask */
689     if (networkSet) {
690       /* assumes IPv4 */
691       in_addr_t mask = (network.prefix_len == 0) ? 0 : (~0U << (32 - network.prefix_len));
692       uint32_t masked = ntohl(network.prefix.v4.s_addr) & mask;
693       network.prefix.v4.s_addr = htonl(masked);
694     }
695
696     if (!uplink || !downlink) {
697       egressBwClear(&egress_if->bwCurrent, egress_if->upCurrent);
698     } else {
699       egress_if->bwCurrent.egressUk = uplink;
700       egress_if->bwCurrent.egressDk = downlink;
701       egress_if->bwCurrent.path_cost = pathCosts;
702       egress_if->bwCurrent.network = network;
703       egress_if->bwCurrent.gateway = gateway;
704
705       egress_if->bwCurrent.networkSet = networkSet;
706       egress_if->bwCurrent.gatewaySet = gatewaySet;
707
708       egressBwCalculateCosts(&egress_if->bwCurrent, egress_if->upCurrent);
709     }
710
711     egress_if->inEgressFile = true;
712   }
713
714   fclose(fp);
715   fp = NULL;
716
717   reportedErrors = reportedErrorsLocal;
718
719   outerror:
720
721   /* clear absent egress interfaces and setup 'changed' status */
722   {
723     struct sgw_egress_if * egress_if = olsr_cnf->smart_gw_egress_interfaces;
724     while (egress_if) {
725       if (!egress_if->inEgressFile) {
726         egressBwClear(&egress_if->bwCurrent, egress_if->upCurrent);
727       }
728
729       egress_if->bwCostsChanged = egressBwCostsChanged(egress_if);
730       egress_if->bwNetworkChanged = egressBwNetworkChanged(egress_if);
731       egress_if->bwGatewayChanged = egressBwGatewayChanged(egress_if);
732       egress_if->bwChanged = egressBwChanged(egress_if);
733       if (egress_if->bwChanged) {
734         changed = true;
735       }
736
737       egress_if = egress_if->next;
738     }
739   }
740
741   out: if (fp) {
742     fclose(fp);
743   }
744   return changed;
745 }
746
747 #endif /* __linux__ */