comparison src/syslogconfig.cpp @ 51:206448c00b55 stable-1-0-12

Allow multiple contexts with independent add/remove commands.
author Carl Byington <carl@five-ten-sg.com>
date Sat, 24 Jan 2009 15:52:20 -0800
parents ba0259c9e411
children b45dddebe8fc
comparison
equal deleted inserted replaced
50:75361069c6ef 51:206448c00b55
14 #include <netdb.h> 14 #include <netdb.h>
15 #include <limits.h> 15 #include <limits.h>
16 16
17 const char *token_add; 17 const char *token_add;
18 const char *token_bucket; 18 const char *token_bucket;
19 const char *token_context;
19 const char *token_file; 20 const char *token_file;
20 const char *token_ignore; 21 const char *token_ignore;
21 const char *token_include; 22 const char *token_include;
22 const char *token_index; 23 const char *token_index;
23 const char *token_lbrace; 24 const char *token_lbrace;
26 const char *token_rbrace; 27 const char *token_rbrace;
27 const char *token_remove; 28 const char *token_remove;
28 const char *token_semi; 29 const char *token_semi;
29 const char *token_slash; 30 const char *token_slash;
30 const char *token_threshold; 31 const char *token_threshold;
31 32 string_set all_strings;// owns all the strings, only modified by the config loader thread
32 struct ltint 33 recorder_map recorders; // all the recorders are named
33 {
34 bool operator()(const int s1, const int s2) const
35 {
36 return (unsigned)s1 < (unsigned)s2;
37 }
38 };
39
40 struct bucket {
41 int count;
42 bool latch; // true iff ever count>threshold
43 };
44
45 string_set all_strings; // owns all the strings, only modified by the config loader thread
46 const int maxlen = 1000; // used for snprintf buffers 34 const int maxlen = 1000; // used for snprintf buffers
47 typedef map<int, bucket, ltint> ip_buckets; 35
48 36
49 class IPR { 37 ////////////////////////////////////////////////
50 ip_buckets violations; 38 //
51 public: 39
52 void add(int ip, int amount, CONFIG &con, const char *file_name, int pattern_index, const char *message); 40 IPR::IPR() {
53 void leak(int amount, CONFIG &con); 41 reference_count = 0;
54 void free_all(CONFIG &con); 42 }
55 void update(int ip, bool added, const char *file_name, int pattern_index, const char *message); 43
56 void changed(CONFIG &con, int ip, bool added); 44 IPR* IPR::find(const char* name) {
57 }; 45 recorder_map::iterator m = recorders.find(name);
58 46 if (m == recorders.end()) recorders[name] = new IPR;
59 IPR recorder; 47 recorders[name]->reference(1);
60 48 return recorders[name];
61 49 }
62 //////////////////////////////////////////////// 50
63 // 51
64 void IPR::add(int ip, int amount, CONFIG &con, const char *file_name, int pattern_index, const char *message) { 52 void IPR::release(const char* name) {
53 recorder_map::iterator m = recorders.find(name);
54 IPR* i = (*m).second;
55 int r = i->reference(-1);
56 if (r == 0) {
57 delete i;
58 recorders.erase(m);
59 }
60 }
61
62
63 void IPR::add(int ip, int amount, CONTEXT &con, const char *file_name, int pattern_index, const char *message) {
65 if (con.looking(ip)) { 64 if (con.looking(ip)) {
66 ip_buckets::iterator i = violations.find(ip); 65 ip_buckets::iterator i = violations.find(ip);
67 if (i == violations.end()) { 66 if (i == violations.end()) {
68 bucket b; 67 bucket b;
69 b.count = amount; 68 b.count = amount;
89 } 88 }
90 } 89 }
91 } 90 }
92 91
93 92
94 void IPR::leak(int amount, CONFIG &con) { 93 void IPR::leak(int amount, CONTEXT &con) {
95 for (ip_buckets::iterator i=violations.begin(); i!=violations.end(); ) { 94 for (ip_buckets::iterator i=violations.begin(); i!=violations.end(); ) {
96 int ip = (*i).first; 95 int ip = (*i).first;
97 bucket &b = (*i).second; 96 bucket &b = (*i).second;
98 if (b.count <= amount) { 97 if (b.count <= amount) {
99 if (b.latch) { 98 if (b.latch) {
108 } 107 }
109 } 108 }
110 } 109 }
111 110
112 111
113 void IPR::free_all(CONFIG &con) { 112 void IPR::free_all(CONTEXT &con) {
114 if (debug_syslog > 2) { 113 if (debug_syslog > 2) {
115 my_syslog("syslog2iptables shutting down"); 114 my_syslog("syslog2iptables shutting down");
116 } 115 }
117 for (ip_buckets::iterator i=violations.begin(); i!=violations.end(); i++) { 116 for (ip_buckets::iterator i=violations.begin(); i!=violations.end(); i++) {
118 int ip = (*i).first; 117 int ip = (*i).first;
139 my_syslog(buf); 138 my_syslog(buf);
140 } 139 }
141 } 140 }
142 141
143 142
144 void IPR::changed(CONFIG &con, int ip, bool added) { 143 void IPR::changed(CONTEXT &con, int ip, bool added) {
145 int t = con.get_threshold(); 144 int t = con.get_threshold();
146 char buf[maxlen]; 145 char buf[maxlen];
147 if (added) { 146 if (added) {
148 bucket &b = violations[ip]; 147 bucket &b = violations[ip];
149 if (con.looking(ip) && (b.count > t)) { 148 if (con.looking(ip) && (b.count > t)) {
201 PATTERN::~PATTERN() { 200 PATTERN::~PATTERN() {
202 regfree(&re); 201 regfree(&re);
203 } 202 }
204 203
205 204
206 bool PATTERN::process(char *buf, CONFIG &con, const char *file_name, int pattern_index) { 205 bool PATTERN::process(char *buf, CONTEXT &con, const char *file_name, int pattern_index) {
207 if (pattern) { 206 if (pattern) {
208 const int nmatch = index+1; 207 const int nmatch = index+1;
209 regmatch_t match[nmatch]; 208 regmatch_t match[nmatch];
210 if (0 == regexec(&re, buf, nmatch, match, 0)) { 209 if (0 == regexec(&re, buf, nmatch, match, 0)) {
211 int s = match[index].rm_so; 210 int s = match[index].rm_so;
215 my_syslog(buf); // show lines with matches 214 my_syslog(buf); // show lines with matches
216 } 215 }
217 buf[e] = '\0'; 216 buf[e] = '\0';
218 int ip = ip_address(buf+s); 217 int ip = ip_address(buf+s);
219 if (ip) { 218 if (ip) {
220 recorder.add(ip, amount, con, file_name, pattern_index, message); 219 con.recorder->add(ip, amount, con, file_name, pattern_index, message);
221 } 220 }
222 return true; 221 return true;
223 } 222 }
224 } 223 }
225 } 224 }
240 } 239 }
241 240
242 241
243 //////////////////////////////////////////////// 242 ////////////////////////////////////////////////
244 // 243 //
245 CONFIG::CONFIG() { 244 CONTEXT::CONTEXT(const char *nam) {
246 reference_count = 0; 245 name = nam;
247 generation = 0;
248 load_time = 0;
249 threshold = 500; 246 threshold = 500;
250 add_command = "/sbin/iptables -I INPUT --src %s --jump DROP"; 247 add_command = "/sbin/iptables -I INPUT --src %s --jump DROP";
251 remove_command = "/sbin/iptables -D INPUT --src %s --jump DROP"; 248 remove_command = "/sbin/iptables -D INPUT --src %s --jump DROP";
252 } 249 recorder = IPR::find(name);
253 250 }
254 251
255 CONFIG::~CONFIG() { 252
253 ////////////////////////////////////////////////
254 //
255 CONTEXT::~CONTEXT() {
256 ignore.clear();
256 for (syslogconfig_list::iterator i=syslogconfigs.begin(); i!=syslogconfigs.end(); i++) { 257 for (syslogconfig_list::iterator i=syslogconfigs.begin(); i!=syslogconfigs.end(); i++) {
257 SYSLOGCONFIG *c = *i; 258 SYSLOGCONFIG *c = *i;
258 delete c; 259 delete c;
259 } 260 }
260 ignore.clear(); 261 IPR::release(name);
261 } 262 }
262 263
263 264
264 void CONFIG::add_syslogconfig(SYSLOGCONFIGP con) { 265 void CONTEXT::add_syslogconfig(SYSLOGCONFIGP con) {
265 syslogconfigs.push_back(con); 266 syslogconfigs.push_back(con);
266 } 267 }
267 268
268 269
269 void CONFIG::add_pair(IPPAIR pair) { 270 void CONTEXT::add_pair(IPPAIR pair) {
270 ignore.push_back(pair); 271 ignore.push_back(pair);
271 } 272 }
272 273
273 274
274 void CONFIG::dump() { 275 void CONTEXT::dump() {
275 printf(" threshold %d; \n\n", threshold); 276 string indents(" ");
276 277 const char *indent = indents.c_str();
277 printf(" add_command \"%s\"; \n", add_command); 278
278 printf(" remove_command \"%s\"; \n\n", remove_command); 279 printf("context %s {\n", name);
279 280 printf("%s threshold %d; \n\n", indent, threshold);
280 printf(" ignore { \n"); 281
282 printf("%s add_command \"%s\"; \n", indent, add_command);
283 printf("%s remove_command \"%s\"; \n\n", indent, remove_command);
284
285 printf("%s ignore { \n", indent);
281 for (ippair_list::iterator i=ignore.begin(); i!=ignore.end(); i++) { 286 for (ippair_list::iterator i=ignore.begin(); i!=ignore.end(); i++) {
282 IPPAIR &p = *i; 287 IPPAIR &p = *i;
283 in_addr ip; 288 in_addr ip;
284 ip.s_addr = htonl(p.first); 289 ip.s_addr = htonl(p.first);
285 printf(" %s/%d; \n", inet_ntoa(ip), p.cidr); 290 printf("%s %s/%d; \n", indent, inet_ntoa(ip), p.cidr);
286 } 291 }
287 printf(" }; \n\n"); 292 printf("%s }; \n\n", indent);
288 293
289 for (syslogconfig_list::iterator i=syslogconfigs.begin(); i!=syslogconfigs.end(); i++) { 294 for (syslogconfig_list::iterator i=syslogconfigs.begin(); i!=syslogconfigs.end(); i++) {
290 SYSLOGCONFIGP c = *i; 295 SYSLOGCONFIGP c = *i;
291 c->dump(0); 296 c->dump(1);
292 } 297 }
293 } 298 printf("}; \n\n");
294 299 }
295 300
296 void CONFIG::read() { 301
302 void CONTEXT::read(CONFIG &con) {
297 while (true) { 303 while (true) {
298 bool have = false; 304 bool have = false;
299 for (syslogconfig_list::iterator i=syslogconfigs.begin(); i!=syslogconfigs.end(); i++) { 305 for (syslogconfig_list::iterator i=syslogconfigs.begin(); i!=syslogconfigs.end(); i++) {
300 SYSLOGCONFIGP c = *i; 306 SYSLOGCONFIGP c = *i;
301 have |= c->read(*this); 307 have |= c->read(*this);
303 if (!have) break; 309 if (!have) break;
304 } 310 }
305 } 311 }
306 312
307 313
314 void CONTEXT::free_all() {
315 recorder->free_all(*this);
316 }
317
318
319 void CONTEXT::leak(int delta) {
320 recorder->leak(delta, *this);
321
322 }
323
324
325 bool CONTEXT::looking(int ip) {
326 for (ippair_list::iterator i=ignore.begin(); i!=ignore.end(); i++) {
327 IPPAIR &p = *i;
328 if ((p.first <= ip) && (ip <= p.last)) return false;
329 }
330 return true;
331 }
332
333 ////////////////////////////////////////////////
334 //
335 CONFIG::CONFIG() {
336 reference_count = 0;
337 generation = 0;
338 load_time = 0;
339 }
340
341
342 CONFIG::~CONFIG() {
343 for (context_list::iterator i=contexts.begin(); i!=contexts.end(); i++) {
344 CONTEXT *c = *i;
345 delete c;
346 }
347 }
348
349
350 void CONFIG::dump() {
351 for (context_list::iterator i=contexts.begin(); i!=contexts.end(); i++) {
352 CONTEXTP c = *i;
353 c->dump();
354 }
355 }
356
357
358 void CONFIG::read() {
359 for (context_list::iterator i=contexts.begin(); i!=contexts.end(); i++) {
360 CONTEXT *c = *i;
361 c->read(*this);
362 }
363 }
364
365
308 void CONFIG::sleep(int duration, time_t &previous) { 366 void CONFIG::sleep(int duration, time_t &previous) {
309 ::sleep(duration); 367 ::sleep(duration);
310 time_t now = time(NULL); 368 time_t now = time(NULL);
311 recorder.leak(now-previous, *this); 369 for (context_list::iterator i=contexts.begin(); i!=contexts.end(); i++) {
370 CONTEXT *c = *i;
371 c->leak(now-previous);
372 }
312 previous = now; 373 previous = now;
313 } 374 }
314 375
315 376
316 void CONFIG::free_all() { 377 void CONFIG::free_all() {
317 recorder.free_all(*this); 378 for (context_list::iterator i=contexts.begin(); i!=contexts.end(); i++) {
318 } 379 CONTEXT *c = *i;
319 380 c->free_all();
320 bool CONFIG::looking(int ip) { 381 }
321 for (ippair_list::iterator i=ignore.begin(); i!=ignore.end(); i++) { 382 }
322 IPPAIR &p = *i; 383
323 if ((p.first <= ip) && (ip <= p.last)) return false;
324 }
325 return true;
326 }
327 384
328 //////////////////////////////////////////////// 385 ////////////////////////////////////////////////
329 // 386 //
330 SYSLOGCONFIG::SYSLOGCONFIG(TOKEN &tok, const char *file_name_) { 387 SYSLOGCONFIG::SYSLOGCONFIG(TOKEN &tok, const char *file_name_) {
331 tokp = &tok; 388 tokp = &tok;
372 } 429 }
373 } 430 }
374 } 431 }
375 432
376 433
377 bool SYSLOGCONFIG::read(CONFIG &con) { 434 bool SYSLOGCONFIG::read(CONTEXT &con) {
378 if (failed()) { 435 if (failed()) {
379 open(false); 436 open(false);
380 if (failed()) return false; 437 if (failed()) return false;
381 } 438 }
382 int n = ::read(fd, buf+len, buflen-len); 439 int n = ::read(fd, buf+len, buflen-len);
426 void SYSLOGCONFIG::add_pattern(PATTERNP pat) { 483 void SYSLOGCONFIG::add_pattern(PATTERNP pat) {
427 patterns.push_back(pat); 484 patterns.push_back(pat);
428 } 485 }
429 486
430 487
431 void SYSLOGCONFIG::process(CONFIG &con) { 488 void SYSLOGCONFIG::process(CONTEXT &con) {
432 int pi=0; 489 int pi=0;
433 for (pattern_list::iterator i=patterns.begin(); i!=patterns.end(); i++) { 490 for (pattern_list::iterator i=patterns.begin(); i!=patterns.end(); i++) {
434 PATTERN *p = *i; 491 PATTERN *p = *i;
435 if (p->process(buf, con, file_name, pi)) break; 492 if (p->process(buf, con, file_name, pi)) break;
436 pi++; 493 pi++;
502 } 559 }
503 560
504 561
505 //////////////////////////////////////////////// 562 ////////////////////////////////////////////////
506 // 563 //
507 bool parse_pattern(TOKEN &tok, SYSLOGCONFIG &con); 564 bool parse_pattern(TOKEN &tok, SYSLOGCONFIG &con, CONTEXT &me);
508 bool parse_pattern(TOKEN &tok, SYSLOGCONFIG &con) { 565 bool parse_pattern(TOKEN &tok, SYSLOGCONFIG &con, CONTEXT &me) {
509 const char *pat = tok.next(); 566 const char *pat = tok.next();
510 int ind = 0; 567 int ind = 0;
511 int buc = 0; 568 int buc = 0;
512 const char *msg = NULL; 569 const char *msg = NULL;
513 if (!tsa(tok, token_lbrace)) return false; 570 if (!tsa(tok, token_lbrace)) return false;
541 } 598 }
542 599
543 600
544 //////////////////////////////////////////////// 601 ////////////////////////////////////////////////
545 // 602 //
546 bool parse_ignore(TOKEN &tok, CONFIG &dc); 603 bool parse_ignore(TOKEN &tok, CONFIG &dc, CONTEXT &me);
547 bool parse_ignore(TOKEN &tok, CONFIG &dc) { 604 bool parse_ignore(TOKEN &tok, CONFIG &dc, CONTEXT &me) {
548 if (!tsa(tok, token_lbrace)) return false; 605 if (!tsa(tok, token_lbrace)) return false;
549 while (true) { 606 while (true) {
550 const char *have = tok.next(); 607 const char *have = tok.next();
551 if (!have) break; 608 if (!have) break;
552 if (have == token_rbrace) break; 609 if (have == token_rbrace) break;
598 0x00000001, // 31 655 0x00000001, // 31
599 0x00000000}; // 32 656 0x00000000}; // 32
600 pair.first = ipaddr; 657 pair.first = ipaddr;
601 pair.last = ipaddr | masks[mask]; 658 pair.last = ipaddr | masks[mask];
602 pair.cidr = mask; 659 pair.cidr = mask;
603 dc.add_pair(pair); 660 me.add_pair(pair);
604 } 661 }
605 if (!tsa(tok, token_semi)) return false; 662 if (!tsa(tok, token_semi)) return false;
606 return true; 663 return true;
607 } 664 }
608 665
609 666
610 //////////////////////////////////////////////// 667 ////////////////////////////////////////////////
611 // 668 //
612 bool parse_syslogconfig(TOKEN &tok, CONFIG &dc); 669 bool parse_syslogconfig(TOKEN &tok, CONFIG &dc, CONTEXT &me);
613 bool parse_syslogconfig(TOKEN &tok, CONFIG &dc) { 670 bool parse_syslogconfig(TOKEN &tok, CONFIG &dc, CONTEXT &me) {
614 const char *name = tok.next(); 671 const char *name = tok.next();
615 if (!tsa(tok, token_lbrace)) return false; 672 if (!tsa(tok, token_lbrace)) return false;
616 SYSLOGCONFIGP con = new SYSLOGCONFIG(tok, name); 673 SYSLOGCONFIGP con = new SYSLOGCONFIG(tok, name);
617 if (con->failed()) { 674 if (con->failed()) {
618 delete con; 675 delete con;
619 return false; 676 return false;
620 } 677 }
621 dc.add_syslogconfig(con); 678 me.add_syslogconfig(con);
622 while (true) { 679 while (true) {
623 const char *have = tok.next(); 680 const char *have = tok.next();
624 if (!have) break; 681 if (!have) break;
625 if (have == token_rbrace) break; 682 if (have == token_rbrace) break;
626 if (have == token_pattern) { 683 if (have == token_pattern) {
627 if (!parse_pattern(tok, *con)) return false; 684 if (!parse_pattern(tok, *con, me)) return false;
628 } 685 }
629 else { 686 else {
630 tok.token_error("pattern", have); 687 tok.token_error("pattern", have);
631 return false; 688 return false;
632 } 689 }
633 } 690 }
634 if (!tsa(tok, token_semi)) return false; 691 if (!tsa(tok, token_semi)) return false;
692 return true;
693 }
694
695
696 ////////////////////////////////////////////////
697 //
698 bool parse_context(TOKEN &tok, CONFIG &dc, CONTEXTP parent);
699 bool parse_context(TOKEN &tok, CONFIG &dc, CONTEXTP parent) {
700 const char *name = tok.next();
701 if (!tsa(tok, token_lbrace)) return false;
702 CONTEXTP con = new CONTEXT(name);
703
704 while (true) {
705 const char *have = tok.next();
706 if (!have) break;
707 if (have == token_rbrace) break; // done
708 if (have == token_threshold) {
709 have = tok.next();
710 con->set_threshold(atoi(have));
711 if (!tsa(tok, token_semi)) return false;
712 }
713 else if (have == token_ignore) {
714 if (!parse_ignore(tok, dc, *con)) return false;
715 }
716 else if (have == token_add) {
717 have = tok.next();
718 con->set_add(have);
719 if (!tsa(tok, token_semi)) return false;
720 }
721 else if (have == token_remove) {
722 have = tok.next();
723 con->set_remove(have);
724 if (!tsa(tok, token_semi)) return false;
725 }
726 else if (have == token_file) {
727 if (!parse_syslogconfig(tok, dc, *con)) return false;
728 }
729 else {
730 tok.token_error("threshold/ignore/add_command/remove_command/file", have);
731 return false;
732 }
733 }
734 if (!tsa(tok, token_semi)) {
735 delete con;
736 return false;
737 }
738 dc.add_context(con);
635 return true; 739 return true;
636 } 740 }
637 741
638 742
639 //////////////////////////////////////////////// 743 ////////////////////////////////////////////////
643 int count = 0; 747 int count = 0;
644 TOKEN tok(fn, &dc.config_files); 748 TOKEN tok(fn, &dc.config_files);
645 while (true) { 749 while (true) {
646 const char *have = tok.next(); 750 const char *have = tok.next();
647 if (!have) break; 751 if (!have) break;
648 if (have == token_threshold) { 752 if (have == token_context) {
649 have = tok.next(); 753 if (!parse_context(tok, dc, NULL)) {
650 dc.set_threshold(atoi(have)); 754 tok.token_error("load_conf() failed to parse context");
651 if (!tsa(tok, token_semi)) return false; 755 return false;
652 } 756 }
653 else if (have == token_ignore) { 757 else count++;
654 if (!parse_ignore(tok, dc)) return false;
655 }
656 else if (have == token_add) {
657 have = tok.next();
658 dc.set_add(have);
659 if (!tsa(tok, token_semi)) return false;
660 }
661 else if (have == token_remove) {
662 have = tok.next();
663 dc.set_remove(have);
664 if (!tsa(tok, token_semi)) return false;
665 }
666 else if (have == token_file) {
667 if (!parse_syslogconfig(tok, dc)) return false;
668 count++;
669 } 758 }
670 else { 759 else {
671 tok.token_error("threshold/ignore/add_command/remove_command/file", have); 760 tok.token_error(token_context, have);
672 return false; 761 return false;
673 } 762 }
674 } 763 }
675 tok.token_error("load_conf() found %d syslog files in %s", count, fn); 764 tok.token_error("load_conf() found %d contexts in %s", count, fn);
676 return (!dc.syslogconfigs.empty()); 765 return (!dc.contexts.empty());
677 } 766 }
678 767
679 768
680 //////////////////////////////////////////////// 769 ////////////////////////////////////////////////
681 // init the tokens 770 // init the tokens
682 // 771 //
683 void token_init() { 772 void token_init() {
684 token_add = register_string("add_command"); 773 token_add = register_string("add_command");
685 token_bucket = register_string("bucket"); 774 token_bucket = register_string("bucket");
775 token_context = register_string("context");
686 token_file = register_string("file"); 776 token_file = register_string("file");
687 token_ignore = register_string("ignore"); 777 token_ignore = register_string("ignore");
688 token_include = register_string("include"); 778 token_include = register_string("include");
689 token_index = register_string("index"); 779 token_index = register_string("index");
690 token_lbrace = register_string("{"); 780 token_lbrace = register_string("{");