Mercurial > syslog2iptables
diff src/syslogconfig.cpp @ 3:8fe310e5cd44
initial coding
author | carl |
---|---|
date | Sun, 27 Nov 2005 21:12:01 -0800 |
parents | 6e88da080f08 |
children | 2737ab01659a |
line wrap: on
line diff
--- a/src/syslogconfig.cpp Thu Nov 24 10:31:09 2005 -0800 +++ b/src/syslogconfig.cpp Sun Nov 27 21:12:01 2005 -0800 @@ -20,22 +20,179 @@ #include "includes.h" #include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> static char* syslogconfig_version="$Id$"; -char *token_cisco; +char *token_bucket; char *token_file; +char *token_ignore; char *token_include; +char *token_index; char *token_lbrace; -char *token_parser; +char *token_pattern; char *token_rbrace; char *token_semi; -char *token_ssh; +char *token_slash; +char *token_threshold; + +struct ltint +{ + bool operator()(const int s1, const int s2) const + { + return (unsigned)s1 < (unsigned)s2; + } +}; string_set all_strings; // owns all the strings, only modified by the config loader thread const int maxlen = 1000; // used for snprintf buffers +typedef map<int, int, ltint> ip_buckets; + +class IPR { + ip_buckets violations; +public: + void add(int ip, int bucket, CONFIG &con); + void changed(CONFIG &con); + void leak(int amount, CONFIG &con); +}; + +IPR recorder; + + +//////////////////////////////////////////////// +// +void IPR::add(int ip, int bucket, CONFIG &con) { + if (con.looking(ip)) { + ip_buckets::iterator i = violations.find(ip); + if (i == violations.end()) violations[ip] = bucket; + else { + (*i).second += bucket; + if ((*i).second > con.get_threshold()) changed(con); + } + } +} + + +void IPR::leak(int amount, CONFIG &con) { + bool ch = false; + for (ip_buckets::iterator i=violations.begin(); i!=violations.end(); ) { + int ip = (*i).first; + int n = (*i).second; + in_addr ad; + ad.s_addr = htonl(ip); + char buf[maxlen]; + snprintf(buf, maxlen, "leak %s with %d count", inet_ntoa(ad), n); + my_syslog(buf); + if (n <= amount) { + ch = true; + violations.erase(i++); + } + else { + (*i).second -= amount; + i++; + } + } + if (ch) changed(con); +} + + +void IPR::changed(CONFIG &con) { + my_syslog("**** dump"); + for (ip_buckets::iterator i=violations.begin(); i!=violations.end(); i++) { + int ip = (*i).first; + int n = (*i).second; + in_addr ad; + ad.s_addr = htonl(ip); + char buf[maxlen]; + snprintf(buf, maxlen, "%s with %d count", inet_ntoa(ad), n); + my_syslog(buf); + } +} +//////////////////////////////////////////////// +// +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, char *pattern_, int index_, int bucket_) { + pattern = pattern_; + index = index_; + bucket = bucket_; + 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) { + if (pattern) { + const int nmatch = index+1; + regmatch_t match[nmatch]; + if (0 == regexec(&re, buf, nmatch, match, 0)) { + int s = match[index].rm_so; + int e = match[index].rm_eo; + // char bu[maxlen]; + // snprintf(bu, maxlen, "re match from %d to %d", s, e); + // my_syslog(bu); + if (s != -1) { + my_syslog(buf); + buf[e] = '\0'; + int ip = ip_address(buf+s); + if (ip) { + my_syslog(buf+s); + recorder.add(ip, bucket, con); + } + return true; + } + } + } + return false; +} + + +void PATTERN::dump(int level) { + char indent[maxlen]; + int i = min(maxlen-1, level*4); + memset(indent, ' ', i); + indent[i] = '\0'; + printf("%s pattern \"%s\" {; \n", indent, pattern); + printf("%s index %d; \n", indent, index); + printf("%s bucket %d; \n", indent, bucket); + printf("%s }; \n", indent); +} + + +//////////////////////////////////////////////// +// CONFIG::CONFIG() { reference_count = 0; generation = 0; @@ -48,6 +205,7 @@ SYSLOGCONFIG *c = *i; delete c; } + ignore.clear(); } @@ -56,7 +214,23 @@ } +void CONFIG::add_pair(IPPAIR pair) { + ignore.push_back(pair); +} + + void CONFIG::dump() { + printf(" threshold %d; \n\n", threshold); + + printf(" ignore { \n"); + for (ippair_list::iterator i=ignore.begin(); i!=ignore.end(); i++) { + IPPAIR &p = *i; + in_addr ip; + ip.s_addr = htonl(p.first); + printf(" %s/%d; \n", inet_ntoa(ip), p.cidr); + } + printf(" }; \n\n"); + for (syslogconfig_list::iterator i=syslogconfigs.begin(); i!=syslogconfigs.end(); i++) { SYSLOGCONFIGP c = *i; c->dump(0); @@ -65,16 +239,36 @@ void CONFIG::read() { + while (true) { + bool have = false; for (syslogconfig_list::iterator i=syslogconfigs.begin(); i!=syslogconfigs.end(); i++) { SYSLOGCONFIGP c = *i; - c->read(); + have |= c->read(*this); + } + break; //!! + if (!have) break; } } -SYSLOGCONFIG::SYSLOGCONFIG(TOKEN &tok, char *file_name_, parser_style parser_) { +void CONFIG::sleep(int duration) { + ::sleep(duration); + recorder.leak(duration, *this); +} + + +bool CONFIG::looking(int ip) { + for (ippair_list::iterator i=ignore.begin(); i!=ignore.end(); i++) { + IPPAIR &p = *i; + if ((p.first <= ip) && (ip <= p.last)) return false; + } + return true; +} + +//////////////////////////////////////////////// +// +SYSLOGCONFIG::SYSLOGCONFIG(TOKEN &tok, char *file_name_) { file_name = file_name_; - parser = parser_; fd = open(file_name, O_RDONLY); len = 0; if (fd == -1) { @@ -83,7 +277,7 @@ tok.token_error(buf); } else { - lseek(fd, 0, SEEK_END); + //lseek(fd, 0, SEEK_END); //!! } } @@ -91,31 +285,45 @@ SYSLOGCONFIG::~SYSLOGCONFIG() { if (fd != -1) close(fd); fd = -1; + for (pattern_list::iterator i=patterns.begin(); i!=patterns.end(); i++) { + PATTERN *p = *i; + delete p; + } } -void SYSLOGCONFIG::read() { - if (failed()) return; - int n = ::read(fd, buf, buflen-len); - if (n > 0) { +void SYSLOGCONFIG::add_pattern(PATTERNP pat) { + patterns.push_back(pat); +} + + +bool SYSLOGCONFIG::read(CONFIG &con) { + 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(); // process null terminated string + 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; } + return have; } -void SYSLOGCONFIG::process() { - my_syslog(buf); +void SYSLOGCONFIG::process(CONFIG &con) { + for (pattern_list::iterator i=patterns.begin(); i!=patterns.end(); i++) { + PATTERN *p = *i; + if (p->process(buf, con)) break; + } } @@ -126,13 +334,9 @@ indent[i] = '\0'; char buf[maxlen]; printf("%s file \"%s\" {\n", indent, file_name); - switch (parser) { - case cisco: - printf("%s parser cisco; \n", indent); - break; - case ssh: - printf("%s parser ssh; \n", indent); - break; + for (pattern_list::iterator i=patterns.begin(); i!=patterns.end(); i++) { + PATTERN *p = *i; + p->dump(level); } printf("%s }; \n", indent); } @@ -182,38 +386,128 @@ //////////////////////////////////////////////// // -bool parse_syslogconfig(TOKEN &tok, CONFIG &dc); -bool parse_syslogconfig(TOKEN &tok, CONFIG &dc) { - char *name = tok.next(); - parser_style parser; +bool parse_pattern(TOKEN &tok, SYSLOGCONFIG &con); +bool parse_pattern(TOKEN &tok, SYSLOGCONFIG &con) { + char *pat = tok.next(); + int ind, buc; if (!tsa(tok, token_lbrace)) return false; while (true) { char *have = tok.next(); - if (have == token_parser) { + if (!have) break; + if (have == token_rbrace) break; + if (have == token_index) { have = tok.next(); - if (have == token_cisco) parser = cisco; - else if (have == token_ssh) parser = ssh; - else { - tok.token_error("cisco/ssh", have); - return false; - } + ind = atoi(have); if (!tsa(tok, token_semi)) return false; } - else if (have == token_rbrace) { - break; // done + else if (have == token_bucket) { + have = tok.next(); + buc = atoi(have); + if (!tsa(tok, token_semi)) return false; } else { - tok.token_error("file keyword", have); + tok.token_error("index/bucket", have); return false; } } if (!tsa(tok, token_semi)) return false; - SYSLOGCONFIGP con = new SYSLOGCONFIG(tok, name, parser); + PATTERNP patt = new PATTERN(tok, pat, ind, buc); + con.add_pattern(patt); + return true; +} + + +//////////////////////////////////////////////// +// +bool parse_ignore(TOKEN &tok, CONFIG &dc); +bool parse_ignore(TOKEN &tok, CONFIG &dc) { + if (!tsa(tok, token_lbrace)) return false; + while (true) { + char *have = tok.next(); + if (!have) break; + if (have == token_rbrace) break; + int ipaddr = ip_address(have); + if (ipaddr == 0) { + tok.token_error("ip address", have); + return false; + } + if (!tsa(tok, token_slash)) return false; + have = tok.next(); + int mask = atoi(have); + if ((mask < 8) || (mask > 32)) { + tok.token_error("cidr 8..32 value", have); + return false; + } + if (!tsa(tok, token_semi)) return false; + IPPAIR pair; + const int masks[33] = {0xffffffff, // 0 + 0x7fffffff, // 1 + 0x3fffffff, // 2 + 0x1fffffff, // 3 + 0x0fffffff, // 4 + 0x07ffffff, // 5 + 0x03ffffff, // 6 + 0x01ffffff, // 7 + 0x00ffffff, // 8 + 0x007fffff, // 9 + 0x003fffff, // 10 + 0x001fffff, // 11 + 0x000fffff, // 12 + 0x0007ffff, // 13 + 0x0003ffff, // 14 + 0x0001ffff, // 15 + 0x0000ffff, // 16 + 0x00007fff, // 17 + 0x00003fff, // 18 + 0x00001fff, // 19 + 0x00000fff, // 20 + 0x000007ff, // 21 + 0x000003ff, // 22 + 0x000001ff, // 23 + 0x000000ff, // 24 + 0x0000007f, // 25 + 0x0000003f, // 26 + 0x0000001f, // 27 + 0x0000000f, // 28 + 0x00000007, // 29 + 0x00000003, // 30 + 0x00000001, // 31 + 0x00000000}; // 32 + pair.first = ipaddr; + pair.last = ipaddr | masks[mask]; + pair.cidr = mask; + dc.add_pair(pair); + } + if (!tsa(tok, token_semi)) return false; + return true; +} + + +//////////////////////////////////////////////// +// +bool parse_syslogconfig(TOKEN &tok, CONFIG &dc); +bool parse_syslogconfig(TOKEN &tok, CONFIG &dc) { + char *name = tok.next(); + if (!tsa(tok, token_lbrace)) return false; + SYSLOGCONFIGP con = new SYSLOGCONFIG(tok, name); if (con->failed()) { delete con; return false; } dc.add_syslogconfig(con); + while (true) { + char *have = tok.next(); + if (!have) break; + if (have == token_rbrace) break; + if (have == token_pattern) { + if (!parse_pattern(tok, *con)) return false; + } + else { + tok.token_error("pattern", have); + return false; + } + } + if (!tsa(tok, token_semi)) return false; return true; } @@ -227,15 +521,20 @@ while (true) { char *have = tok.next(); if (!have) break; - if (have == token_file) { - if (!parse_syslogconfig(tok, dc)) { - tok.token_error("load_conf() failed to parse syslogconfig"); - return false; + if (have == token_threshold) { + have = tok.next(); + dc.set_threshold(atoi(have)); + if (!tsa(tok, token_semi)) return false; } - else count++; + else if (have == token_ignore) { + if (!parse_ignore(tok, dc)) return false; + } + else if (have == token_file) { + if (!parse_syslogconfig(tok, dc)) return false; + count++; } else { - tok.token_error(token_file, have); + tok.token_error("threshold/ignore/file", have); return false; } } @@ -248,12 +547,15 @@ // init the tokens // void token_init() { - token_cisco = register_string("cisco"); + token_bucket = register_string("bucket"); token_file = register_string("file"); + token_ignore = register_string("ignore"); token_include = register_string("include"); + token_index = register_string("index"); token_lbrace = register_string("{"); - token_parser = register_string("parser"); + token_pattern = register_string("pattern"); token_rbrace = register_string("}"); token_semi = register_string(";"); - token_ssh = register_string("ssh"); + token_slash = register_string("/"); + token_threshold = register_string("threshold"); }