Mercurial > routeflapper
diff src/routeconfig.cpp @ 0:48d06780cf77
initial version
author | Carl Byington <carl@five-ten-sg.com> |
---|---|
date | Tue, 13 May 2008 14:03:10 -0700 |
parents | |
children | bb3f804f13a0 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/routeconfig.cpp Tue May 13 14:03:10 2008 -0700 @@ -0,0 +1,1049 @@ +/* + +Copyright (c) 2007 Carl Byington - 510 Software Group, released under +the GPL version 3 or any later version at your choice available at +http://www.gnu.org/licenses/gpl-3.0.txt + +*/ + +#include "includes.h" +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <limits.h> +#include <set> +#include <vector> +#include <map> +#include <stdint.h> +#include <stdlib.h> +#include <time.h> + +char *token_announce; +char *token_file; +char *token_include; +char *token_index_ip; +char *token_index_length; +char *token_index_path; +char *token_index_value; +char *token_ip; +char *token_lbrace; +char *token_path; +char *token_rbrace; +char *token_reset; +char *token_semi; +char *token_slash; +char *token_withdraw; + +const int training = 100; // need 100 hours uptime before using the statistics +const float origin_threshold = 2.9; // a bit less than 1 + decay + decay^2 +const float adjacent_threshold = 2.9; // "" +const float decay = 0.99; // hourly exponential decay +const float threshold = 0.01; // when counts have decayed below threshold, discard the item +const int ancient_smtp = 3*3600;// suspicious smtp connections over 3 hours old are ignored + +string_set all_strings; // owns all the strings, only modified by the config loader thread +const int maxlen = 1000; // used for snprintf buffers +typedef pair<int,int> adjacent; +typedef vector<int> aspath; +typedef map<int,float> o_history; // as origin history +typedef map<adjacent,float> a_history; // as adjacency history +typedef set<adjacent> a_set; // set of current adjacency pairs +typedef map<uint32_t,time_t> m_connect; // smtp connections + +class route_prefix { + uint32_t prefix_value; + bool announced; + bool trusted; // cannot be suspicious + aspath path; + o_history origin_history; + m_connect smtp_connections; +public: + route_prefix(uint32_t value); + void add_route(aspath path_, a_history &adj_history); + void remove_route(int prefix_length); + uint32_t prefix() const { return prefix_value; }; + bool active() const { return announced; }; + adjacent aspair(int i) const { return adjacent(path[i], path[i+1]); }; + bool empty() const { return !announced && origin_history.empty(); }; + void update_history(a_set &adj_set); + suspicion suspicious(a_history &adj_history, bool debug = false, int prefix_length = 0, uint32_t ip = 0); + void record_smtp(uint32_t ip); + string name(int length) const; +}; + +struct ltrouteprefix { + bool operator()(const route_prefix* r1, const route_prefix* r2) const { + return r1->prefix() < r2->prefix(); + } +}; + +typedef set<route_prefix*, ltrouteprefix> route_prefixes; + +class RIB { + pthread_mutex_t rib_mutex; + int uptime; + bool stable; + route_prefixes prefixes[33]; // /0 to /32 + a_history history; + aspath path; +public: + RIB(); + void set_path(aspath path_) {path = path_;}; + void add_route(int prefix_length, uint32_t prefix_value); + void remove_route(int prefix_length, uint32_t prefix_value); + void update_history(); + suspicion suspicious(route_prefix *r, bool debug = false, int prefix_length = 0, uint32_t ip = 0); + suspicion suspicious(uint32_t ip); + void clear(); + void reset(); +}; + + +RIB route_base; + +const uint32_t masks[33] = { + 0x00000000, + 0x80000000, + 0xc0000000, + 0xe0000000, + 0xf0000000, + 0xf8000000, + 0xfc000000, + 0xfe000000, + 0xff000000, + 0xff800000, + 0xffc00000, + 0xffe00000, + 0xfff00000, + 0xfff80000, + 0xfffc0000, + 0xfffe0000, + 0xffff0000, + 0xffff8000, + 0xffffc000, + 0xffffe000, + 0xfffff000, + 0xfffff800, + 0xfffffc00, + 0xfffffe00, + 0xffffff00, + 0xffffff80, + 0xffffffc0, + 0xffffffe0, + 0xfffffff0, + 0xfffffff8, + 0xfffffffc, + 0xfffffffe, + 0xffffffff}; + + +//////////////////////////////////////////////// +// +char *suspicious_name(suspicion s) +{ + char *ss = ""; + switch (s) { + case suspicious_none: ss = "none"; break; + case suspicious_origin: ss = "origin"; break; + case suspicious_adjacency: ss = "adjacency"; break; + default: break; + } + return ss; +} + + +//////////////////////////////////////////////// +// +route_prefix::route_prefix(uint32_t value) +{ + prefix_value = value; + announced = false; + trusted = false; +} + + +void route_prefix::add_route(aspath path_, a_history &adj_history) +{ + suspicion s = suspicious(adj_history); + trusted = announced && (s == suspicious_none); + announced = true; + path = path_; +} + + +void route_prefix::remove_route(int prefix_length) +{ + if (announced) { + for (m_connect::iterator i = smtp_connections.begin(); i != smtp_connections.end(); i++) { + const uint32_t &ip = (*i).first; + time_t &t = (*i).second; + uint32_t nip = htonl(ip); + char buf[maxlen]; + char adr[sizeof "255.255.255.255 "]; + adr[0] = '\0'; + inet_ntop(AF_INET, (const u_char *)&nip, adr, sizeof(adr)); + char ctbuf[maxlen]; + ctime_r(&t, ctbuf); + int ii = strlen(ctbuf); + if (ii > 1) ctbuf[ii-1] = '\0'; // remove trailing newline + snprintf(buf, sizeof(buf), "*** smtp connection at %s from %s via prefix %s/%d being removed", ctbuf, adr, name(prefix_length).c_str(), prefix_length); + my_syslog(buf); + } + } + announced = false; + smtp_connections.clear(); +} + + +void route_prefix::update_history(a_set &adj_set) +{ + // decay origin history + for (o_history::iterator i = origin_history.begin(); i != origin_history.end(); i++) { + float &count = (*i).second; + count *= decay; + } + if (announced) { + // remove old suspicious smtp connections + time_t cutoff = time(NULL) - ancient_smtp; + for (m_connect::iterator i = smtp_connections.begin(); i != smtp_connections.end(); ) { + time_t &t = (*i).second; + if (t > cutoff) i++; + else smtp_connections.erase(i++); + } + + // update origin history + int n = path.size() - 1; + int origin = path[n]; + o_history::iterator j = origin_history.find(origin); + if (j == origin_history.end()) { + origin_history[origin] = 1.0; + } + else { + float &count = (*j).second; + count++; + } + // update current adjacency set + for (int k=0; k<n; k++) { + adj_set.insert(aspair(k)); + } + } + + // remove origin history entries below the threshold + for (o_history::iterator i = origin_history.begin(); i != origin_history.end();) { + float &count = (*i).second; + if (count > threshold) i++; + else origin_history.erase(i++); + } +} + + +suspicion route_prefix::suspicious(a_history &adj_history, bool debug, int prefix_length, uint32_t ip) +{ + if (!announced || trusted) return suspicious_none; + debug &= (debug_syslog > 2); + + // check origin stability + int n = path.size() - 1; + int origin = path[n]; + o_history::const_iterator o = origin_history.find(origin); + if (o == origin_history.end()) { + if (debug) { + char buf[maxlen]; + snprintf(buf, sizeof(buf), "debug suspicious origin %d missing count %s", + origin, name(prefix_length).c_str()); + my_syslog(buf); + } + record_smtp(ip); + return suspicious_origin; + } + const float &count = (*o).second; + if (count < origin_threshold) { + if (debug) { + char buf[maxlen]; + snprintf(buf, sizeof(buf), "debug suspicious origin %d count %5.2f vs %5.2f %s", + origin, count, origin_threshold, name(prefix_length).c_str()); + my_syslog(buf); + } + record_smtp(ip); + return suspicious_origin; + } + + // check as adjacency stability + for (int k=0; k<n; k++) { + adjacent aa = aspair(k); + a_history::iterator a = adj_history.find(aa); + if (a == adj_history.end()) { + if (debug) { + char buf[maxlen]; + snprintf(buf, sizeof(buf), "debug suspicious adjacency (%d,%d) missing count %s", + aa.first, aa.second, name(prefix_length).c_str()); + my_syslog(buf); + } + record_smtp(ip); + return suspicious_adjacency; + } + float &count = (*a).second; + if (count < adjacent_threshold) { + if (debug) { + char buf[maxlen]; + snprintf(buf, sizeof(buf), "debug suspicious adjacency (%d,%d) count %5.2f vs %5.2f %s", + aa.first, aa.second, count, adjacent_threshold, name(prefix_length).c_str()); + my_syslog(buf); + } + record_smtp(ip); + return suspicious_adjacency; + } + } + return suspicious_none; +} + + +void route_prefix::record_smtp(uint32_t ip) +{ + if (ip) smtp_connections[ip] = time(NULL); +} + + +string route_prefix::name(int length) const +{ + char buf[maxlen]; + char adr[sizeof "255.255.255.255 "]; + adr[0] = '\0'; + uint32_t nip = htonl(prefix_value); + inet_ntop(AF_INET, (const u_char *)&nip, adr, sizeof(adr)); + snprintf(buf, sizeof(buf), "%s/%d", adr, length); + int n = path.size(); + for (int i=0; i<n; i++) { + snprintf(adr, sizeof(adr), " %d", path[i]); + strncat(buf, adr, max(0, maxlen-1-(int)strlen(adr))); + } + return string(buf); +} + + +//////////////////////////////////////////////// +// + +RIB::RIB() +{ + pthread_mutex_init(&rib_mutex, 0); + uptime = 0; + stable = false; +} + + +void RIB::add_route(int prefix_length, uint32_t prefix_value) +{ + if (prefix_length < 0) return; + if (prefix_length > 32) return; + pthread_mutex_lock(&rib_mutex); + prefix_value &= masks[prefix_length]; + route_prefix rr(prefix_value); + route_prefixes &p = prefixes[prefix_length]; + route_prefixes::iterator i = p.find(&rr); + route_prefix *r = NULL; + if (i == p.end()) { + // new prefix + r = new route_prefix(prefix_value); + p.insert(r); + } + else { + // existing prefix + r = *i; + } + r->add_route(path, history); + suspicion s; + if (debug_syslog > 2) s = suspicious(r); + if ((debug_syslog > 2) && (s != suspicious_none) && (prefix_length < 22)) { + char buf[maxlen]; + snprintf(buf, sizeof(buf), "add suspicious %s route %s", suspicious_name(s), r->name(prefix_length).c_str()); + my_syslog(buf); + } + else if (debug_syslog > 3) { + char buf[maxlen]; + snprintf(buf, sizeof(buf), "add route %s", r->name(prefix_length).c_str()); + my_syslog(buf); + } + pthread_mutex_unlock(&rib_mutex); +} + + +void RIB::remove_route(int prefix_length, uint32_t prefix_value) +{ + if (prefix_length < 0) return; + if (prefix_length > 32) return; + pthread_mutex_lock(&rib_mutex); + uint32_t mask = masks[prefix_length]; + prefix_value &= mask; + route_prefix r(prefix_value); + route_prefixes &p = prefixes[prefix_length]; + route_prefixes::iterator i = p.find(&r); + if (i != p.end()) { + // existing prefix + route_prefix* r = *i; + if (debug_syslog > 3) { + char buf[maxlen]; + snprintf(buf, sizeof(buf), "remove route %s", r->name(prefix_length).c_str()); + my_syslog(buf); + } + r->remove_route(prefix_length); + if (r->empty()) { + p.erase(r); + delete r; + }; + } + pthread_mutex_unlock(&rib_mutex); +} + + +void RIB::update_history() +{ + pthread_mutex_lock(&rib_mutex); + a_set adj_set; + uptime++; + stable = (uptime > training); + int total = 0; + int inactive = 0; + int suspic = 0; + for (int i=0; i<=32; i++) { + bool debug = true; // show first suspicious prefix + route_prefixes &p = prefixes[i]; + for (route_prefixes::iterator j = p.begin(); j != p.end();) { + route_prefix *r = *j; + r->update_history(adj_set); + total++; + if (r->active()) { + if (suspicious(r, debug, i) != suspicious_none) { + suspic++; + debug = false; + } + } + else { + inactive++; + } + if (r->empty()) { + p.erase(j++); + delete r; + } + else j++; + } + } + if (debug_syslog > 2) { + char buf[maxlen]; + snprintf(buf, sizeof(buf), "total %d inactive %d suspicious %d", total, inactive, suspic); + my_syslog(buf); + } + // decay adjacency history + for (a_history::iterator i = history.begin(); i != history.end(); i++) { + float &count = (*i).second; + count *= decay; + } + // update adjacency history from the current adjacency set + for (a_set::iterator i = adj_set.begin(); i != adj_set.end(); i++) { + a_history::iterator a = history.find(*i); + if (a == history.end()) { + // new adjacency + history[*i] = 1.0; + } + else { + float &count = (*a).second; + count++; + } + } + // remove adjacency history entries below the threshold + for (a_history::iterator i = history.begin(); i != history.end();) { + float &count = (*i).second; + if (count > threshold) i++; + else history.erase(i++); + } + pthread_mutex_unlock(&rib_mutex); +} + + +suspicion RIB::suspicious(route_prefix *r, bool debug, int prefix_length, uint32_t ip) +{ + if (!stable) return suspicious_none; + if (!r) return suspicious_none; + return r->suspicious(history, debug, prefix_length, ip); +} + + +suspicion RIB::suspicious(uint32_t ip) +{ + if (!stable) return suspicious_none; + suspicion rc = suspicious_none; + route_prefix *r1 = NULL; + int pl; + pthread_mutex_lock(&rib_mutex); + for (int i=32; i>=0; i--) { + route_prefixes &p = prefixes[i]; + uint32_t network = ip & masks[i]; + route_prefix r(network); + route_prefixes::iterator j = p.find(&r); + if (j != p.end()) { + // existing prefix + route_prefix* r = *j; + if (r->active()) { + r1 = r; + pl = i; + break; + } + } + } + rc = suspicious(r1, true, pl, ip); + pthread_mutex_unlock(&rib_mutex); + return rc; +} + + +void RIB::clear() +{ + pthread_mutex_lock(&rib_mutex); + for (int i=0; i<=32; i++) { + route_prefixes &p = prefixes[i]; + for (route_prefixes::iterator j = p.begin(); j != p.end();) { + route_prefix *r = *j; + p.erase(j++); + delete r; + } + } + history.clear(); + pthread_mutex_unlock(&rib_mutex); +} + + +void RIB::reset() +{ + pthread_mutex_lock(&rib_mutex); + for (int i=0; i<=32; i++) { + route_prefixes &p = prefixes[i]; + for (route_prefixes::iterator j = p.begin(); j != p.end(); j++) { + route_prefix *r = *j; + r->remove_route(i); + } + } + pthread_mutex_unlock(&rib_mutex); +} + + +//////////////////////////////////////////////// +// +CONFIG::CONFIG() { + reference_count = 0; + generation = 0; + load_time = 0; +} + + +CONFIG::~CONFIG() { + for (routeconfig_list::iterator i=routeconfigs.begin(); i!=routeconfigs.end(); i++) { + ROUTECONFIG *c = *i; + delete c; + } +} + + +void CONFIG::dump() { + for (routeconfig_list::iterator i=routeconfigs.begin(); i!=routeconfigs.end(); i++) { + ROUTECONFIG *c = *i; + c->dump(0); + } +} + + +void CONFIG::read() { + while (true) { + bool have = false; + for (routeconfig_list::iterator i=routeconfigs.begin(); i!=routeconfigs.end(); i++) { + ROUTECONFIGP c = *i; + have |= c->read(*this); + } + if (!have) break; + } +} + + + +//////////////////////////////////////////////// +// +ROUTECONFIG::ROUTECONFIG(TOKEN &tok, char *file_name_) { + tokp = &tok; + file_name = file_name_; + open(true); +} + + +ROUTECONFIG::~ROUTECONFIG() { + close(); + for (pattern_list::iterator i=patterns.begin(); i!=patterns.end(); i++) { + PATTERN *p = *i; + delete p; + } +} + + +void ROUTECONFIG::open(bool msg) { + fd = ::open(file_name, O_RDONLY); + len = 0; + if (fd == -1) { + if (msg) { + char buf[maxlen]; + snprintf(buf, sizeof(buf), "syslog file %s not readable", file_name); + tokp->token_error(buf); + } + } + else { + if (debug_syslog > 1) { + snprintf(buf, sizeof(buf), "syslog file %s opened", file_name); + my_syslog(buf); + } + if (msg) lseek(fd, 0, SEEK_END); + if (fstat(fd, &openfdstat)) { + close(); + snprintf(buf, sizeof(buf), "syslog file %s cannot stat after open", file_name); + tokp->token_error(buf); + } + // specify that this fd gets closed on exec, so that selinux + // won't complain + int oldflags = fcntl(fd, F_GETFD, 0); + if (oldflags >= 0) { + fcntl(fd, F_SETFD, oldflags | FD_CLOEXEC); + } + } +} + + +bool ROUTECONFIG::read(CONFIG &con) { + if (failed()) { + open(false); + if (failed()) return false; + } + int n = ::read(fd, buf+len, buflen-len); + bool have = (n > 0); + if (have) { + len += n; + while (true) { + char *p = (char*)memchr(buf, '\n', len); + if (!p) break; + n = p-buf; + *p = '\0'; + process(con); // process null terminated string + len -= n+1; + memmove(buf, p+1, len); + } + // no <lf> in a full buffer + if (len == buflen) len = 0; + } + else { + // check for file close + struct stat filenamest; + if (0 == stat(file_name, &filenamest)) { + if ((filenamest.st_dev != openfdstat.st_dev) || + (filenamest.st_ino != openfdstat.st_ino)) { + close(); + } + } + else { + // filename no longer exists + close(); + } + } + return have; +} + + +void ROUTECONFIG::close() { + if (debug_syslog > 1) { + snprintf(buf, sizeof(buf), "syslog file %s closed", file_name); + my_syslog(buf); + } + if (fd != -1) ::close(fd); + fd = -1; +} + + +void ROUTECONFIG::add_pattern(PATTERNP pat) { + patterns.push_back(pat); +} + + +void ROUTECONFIG::process(CONFIG &con) { + int pi=0; + for (pattern_list::iterator i=patterns.begin(); i!=patterns.end(); i++) { + PATTERN *p = *i; + if (p->process(buf, con, file_name, pi)) break; + pi++; + } +} + + +void ROUTECONFIG::dump(int level) { + char indent[maxlen]; + int i = min(maxlen-1, level*4); + memset(indent, ' ', i); + indent[i] = '\0'; + printf("%s file \"%s\" {\n", indent, file_name); + for (pattern_list::iterator i=patterns.begin(); i!=patterns.end(); i++) { + PATTERN *p = *i; + p->dump(level+1); + } + printf("%s }; \n", indent); +} + + +//////////////////////////////////////////////// +// +int ip_address(char *have); +int ip_address(char *have) { + int ipaddr = 0; + in_addr ip; + if (inet_aton(have, &ip)) ipaddr = ip.s_addr; + else { + struct hostent *host = gethostbyname(have); + if (host && host->h_addrtype == AF_INET) memcpy(&ipaddr, host->h_addr, sizeof(ipaddr)); + } + return ntohl(ipaddr); +} + + +//////////////////////////////////////////////// +// + +PATTERN::PATTERN(TOKEN &tok, pattern_style style_, char *pattern_, int index1_, int index2_) +{ + style = style_; + pattern = pattern_; + index1 = index1_; + index2 = index2_; + if (pattern) { + int rc = regcomp(&re, pattern, REG_ICASE | REG_EXTENDED); + if (rc) { + char bu[maxlen]; + regerror(rc, &re, bu, maxlen); + char buf[maxlen]; + snprintf(buf, sizeof(buf), "pattern %s not valid - %s", pattern, bu); + tok.token_error(buf); + pattern = NULL; + } + } +} + + +PATTERN::~PATTERN() { + regfree(&re); +} + + +bool PATTERN::process(char *buf, CONFIG &con, char *file_name, int pattern_index) +{ + if (pattern) { + const int nmatch = max(index1, index2) + 1; + regmatch_t match[nmatch]; + if (0 == regexec(&re, buf, nmatch, match, 0)) { + int sp1 = match[index1].rm_so; + int ep1 = match[index1].rm_eo; + int sp2 = match[index2].rm_so; + int ep2 = match[index2].rm_eo; + if ((sp1 != -1) && (sp2 != -1)) { + if (debug_syslog > 13) { + my_syslog(buf); // show lines with matches + } + buf[ep1] = '\0'; + buf[ep2] = '\0'; + uint32_t ip; + int pl; + suspicion s; + switch (style) { + case style_reset: + route_base.reset(); + break; + case style_path: + { + aspath path; + char *p = buf+sp1; + char *e; + long l; + while (true) { + l = strtol(p, &e, 10); + if (e == p) break; + p = e; + path.push_back((int)l); + } + route_base.set_path(path); + } + break; + case style_announce: + ip = ip_address(buf+sp1); + pl = atoi(buf+sp2); + if (ip) route_base.add_route(pl, ip); + break; + case style_withdraw: + ip = ip_address(buf+sp1); + pl = atoi(buf+sp2); + if (ip) route_base.remove_route(pl, ip); + break; + case style_ip: + ip = ip_address(buf+sp1); + s = route_base.suspicious(ip); + if (s != suspicious_none) { + char adr[sizeof "255.255.255.255 "]; + adr[0] = '\0'; + uint32_t nip = htonl(ip); + inet_ntop(AF_INET, (const u_char *)&nip, adr, sizeof(adr)); + char buf[maxlen]; + snprintf(buf, sizeof(buf), "*** suspicious %s ip %s", suspicious_name(s), adr); + my_syslog(buf); + } + break; + } + return true; + } + } + } + return false; +} + + +void PATTERN::dump(int level, int index, char *token) +{ + char indent[maxlen]; + int i = min(maxlen-1, level*4); + memset(indent, ' ', i); + indent[i] = '\0'; + printf("%s %s %d; \n", indent, token, index); +} + + +void PATTERN::dump(int level) +{ + char indent[maxlen]; + int i = min(maxlen-1, level*4); + memset(indent, ' ', i); + indent[i] = '\0'; + switch (style) { + case style_reset: + printf("%s %s \"%s\" { \n", indent, token_reset, pattern); + break; + case style_path: + printf("%s %s \"%s\" { \n", indent, token_path, pattern); + dump(level+1, index1, token_index_path); + break; + case style_announce: + printf("%s %s \"%s\" { \n", indent, token_announce, pattern); + dump(level+1, index1, token_index_value); + dump(level+1, index2, token_index_length); + break; + case style_withdraw: + printf("%s %s \"%s\" { \n", indent, token_withdraw, pattern); + dump(level+1, index1, token_index_value); + dump(level+1, index2, token_index_length); + break; + case style_ip: + printf("%s %s \"%s\" { \n", indent, token_ip, pattern); + dump(level+1, index1, token_index_ip); + break; + } + printf("%s }; \n", indent); +} + + +//////////////////////////////////////////////// +// helper to discard the strings held by a string_set +// +void discard(string_set &s) { + for (string_set::iterator i=s.begin(); i!=s.end(); i++) { + free(*i); + } + s.clear(); +} + + +//////////////////////////////////////////////// +// helper to register a string in a string set +// +char* register_string(string_set &s, char *name) { + string_set::iterator i = s.find(name); + if (i != s.end()) return *i; + char *x = strdup(name); + s.insert(x); + return x; +} + + +//////////////////////////////////////////////// +// register a global string +// +char* register_string(char *name) { + return register_string(all_strings, name); +} + + +//////////////////////////////////////////////// +// clear all global strings, helper for valgrind checking +// +void clear_strings() { + discard(all_strings); +} + + +//////////////////////////////////////////////// +// clear the rib, helper for valgrind checking +// +void clear_rib() { + route_base.clear(); +} + + +//////////////////////////////////////////////// +// +bool tsa(TOKEN &tok, char *token); +bool tsa(TOKEN &tok, char *token) { + char *have = tok.next(); + if (have == token) return true; + tok.token_error(token, have); + return false; +} + + +//////////////////////////////////////////////// +// +bool parse_path(TOKEN &tok, ROUTECONFIG &con, char *tokk, pattern_style style); +bool parse_path(TOKEN &tok, ROUTECONFIG &con, char *tokk, pattern_style style) { + char *pattern = tok.next(); + int index = 0; + if (!tsa(tok, token_lbrace)) return false; + while (true) { + char *have = tok.next(); + if (!have) break; + if (have == token_rbrace) break; + if (have == tokk) { + index = tok.nextint(); + if (!tsa(tok, token_semi)) return false; + } + else return false; + } + if (!tsa(tok, token_semi)) return false; + PATTERNP p = new PATTERN(tok, style, pattern, index, 0); + con.add_pattern(p); + return true; +} + + +bool parse_announce_withdraw(TOKEN &tok, ROUTECONFIG &con, pattern_style style); +bool parse_announce_withdraw(TOKEN &tok, ROUTECONFIG &con, pattern_style style) { + char *pattern = tok.next(); + int index1 = 0; + int index2 = 0; + if (!tsa(tok, token_lbrace)) return false; + while (true) { + char *have = tok.next(); + if (!have) break; + if (have == token_rbrace) break; + if (have == token_index_value) { + index1 = tok.nextint(); + if (!tsa(tok, token_semi)) return false; + } + else if (have == token_index_length) { + index2 = tok.nextint(); + if (!tsa(tok, token_semi)) return false; + } + else return false; + } + if (!tsa(tok, token_semi)) return false; + PATTERNP p = new PATTERN(tok, style, pattern, index1, index2); + con.add_pattern(p); + return true; +} + + +bool parse_routeconfig(TOKEN &tok, CONFIG &dc); +bool parse_routeconfig(TOKEN &tok, CONFIG &dc) { + char *name = tok.next(); + if (!tsa(tok, token_lbrace)) return false; + ROUTECONFIGP con = new ROUTECONFIG(tok, name); + if (con->failed()) { + delete con; + return false; + } + dc.add_routeconfig(con); + while (true) { + char *have = tok.next(); + if (!have) break; + if (have == token_rbrace) break; + if (have == token_reset) { + if (!parse_path(tok, *con, NULL, style_reset)) return false; + } + else if (have == token_path) { + if (!parse_path(tok, *con, token_index_path, style_path)) return false; + } + else if (have == token_ip) { + if (!parse_path(tok, *con, token_index_ip, style_ip)) return false; + } + else if (have == token_announce) { + if (!parse_announce_withdraw(tok, *con, style_announce)) return false; + } + else if (have == token_withdraw) { + if (!parse_announce_withdraw(tok, *con, style_withdraw)) return false; + } + else { + tok.token_error("path/announce/withdraw", have); + return false; + } + } + if (!tsa(tok, token_semi)) return false; + return true; +} + + +//////////////////////////////////////////////// +// parse a config file +// +bool load_conf(CONFIG &dc, char *fn) { + int count = 0; + TOKEN tok(fn, &dc.config_files); + while (true) { + char *have = tok.next(); + if (!have) break; + if (have == token_file) { + if (!parse_routeconfig(tok, dc)) return false; + count++; + } + else { + tok.token_error("file", have); + return false; + } + } + tok.token_error("load_conf() found %d syslog files in %s", count, fn); + return (!dc.routeconfigs.empty()); +} + + +//////////////////////////////////////////////// +// +void routing_hourly_update() { + route_base.update_history(); +} + + +//////////////////////////////////////////////// +// init the tokens +// +void token_init() { + token_announce = register_string("announce"); + token_file = register_string("file"); + token_include = register_string("include"); + token_index_ip = register_string("index_ip"); + token_index_length = register_string("index_length"); + token_index_path = register_string("index_path"); + token_index_value = register_string("index_value"); + token_ip = register_string("ip"); + token_lbrace = register_string("{"); + token_path = register_string("path"); + token_rbrace = register_string("}"); + token_reset = register_string("reset"); + token_semi = register_string(";"); + token_slash = register_string("/"); + token_withdraw = register_string("withdraw"); +}