Mercurial > routeflapper
comparison 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 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:48d06780cf77 |
---|---|
1 /* | |
2 | |
3 Copyright (c) 2007 Carl Byington - 510 Software Group, released under | |
4 the GPL version 3 or any later version at your choice available at | |
5 http://www.gnu.org/licenses/gpl-3.0.txt | |
6 | |
7 */ | |
8 | |
9 #include "includes.h" | |
10 #include <fcntl.h> | |
11 #include <sys/socket.h> | |
12 #include <netinet/in.h> | |
13 #include <arpa/inet.h> | |
14 #include <netdb.h> | |
15 #include <limits.h> | |
16 #include <set> | |
17 #include <vector> | |
18 #include <map> | |
19 #include <stdint.h> | |
20 #include <stdlib.h> | |
21 #include <time.h> | |
22 | |
23 char *token_announce; | |
24 char *token_file; | |
25 char *token_include; | |
26 char *token_index_ip; | |
27 char *token_index_length; | |
28 char *token_index_path; | |
29 char *token_index_value; | |
30 char *token_ip; | |
31 char *token_lbrace; | |
32 char *token_path; | |
33 char *token_rbrace; | |
34 char *token_reset; | |
35 char *token_semi; | |
36 char *token_slash; | |
37 char *token_withdraw; | |
38 | |
39 const int training = 100; // need 100 hours uptime before using the statistics | |
40 const float origin_threshold = 2.9; // a bit less than 1 + decay + decay^2 | |
41 const float adjacent_threshold = 2.9; // "" | |
42 const float decay = 0.99; // hourly exponential decay | |
43 const float threshold = 0.01; // when counts have decayed below threshold, discard the item | |
44 const int ancient_smtp = 3*3600;// suspicious smtp connections over 3 hours old are ignored | |
45 | |
46 string_set all_strings; // owns all the strings, only modified by the config loader thread | |
47 const int maxlen = 1000; // used for snprintf buffers | |
48 typedef pair<int,int> adjacent; | |
49 typedef vector<int> aspath; | |
50 typedef map<int,float> o_history; // as origin history | |
51 typedef map<adjacent,float> a_history; // as adjacency history | |
52 typedef set<adjacent> a_set; // set of current adjacency pairs | |
53 typedef map<uint32_t,time_t> m_connect; // smtp connections | |
54 | |
55 class route_prefix { | |
56 uint32_t prefix_value; | |
57 bool announced; | |
58 bool trusted; // cannot be suspicious | |
59 aspath path; | |
60 o_history origin_history; | |
61 m_connect smtp_connections; | |
62 public: | |
63 route_prefix(uint32_t value); | |
64 void add_route(aspath path_, a_history &adj_history); | |
65 void remove_route(int prefix_length); | |
66 uint32_t prefix() const { return prefix_value; }; | |
67 bool active() const { return announced; }; | |
68 adjacent aspair(int i) const { return adjacent(path[i], path[i+1]); }; | |
69 bool empty() const { return !announced && origin_history.empty(); }; | |
70 void update_history(a_set &adj_set); | |
71 suspicion suspicious(a_history &adj_history, bool debug = false, int prefix_length = 0, uint32_t ip = 0); | |
72 void record_smtp(uint32_t ip); | |
73 string name(int length) const; | |
74 }; | |
75 | |
76 struct ltrouteprefix { | |
77 bool operator()(const route_prefix* r1, const route_prefix* r2) const { | |
78 return r1->prefix() < r2->prefix(); | |
79 } | |
80 }; | |
81 | |
82 typedef set<route_prefix*, ltrouteprefix> route_prefixes; | |
83 | |
84 class RIB { | |
85 pthread_mutex_t rib_mutex; | |
86 int uptime; | |
87 bool stable; | |
88 route_prefixes prefixes[33]; // /0 to /32 | |
89 a_history history; | |
90 aspath path; | |
91 public: | |
92 RIB(); | |
93 void set_path(aspath path_) {path = path_;}; | |
94 void add_route(int prefix_length, uint32_t prefix_value); | |
95 void remove_route(int prefix_length, uint32_t prefix_value); | |
96 void update_history(); | |
97 suspicion suspicious(route_prefix *r, bool debug = false, int prefix_length = 0, uint32_t ip = 0); | |
98 suspicion suspicious(uint32_t ip); | |
99 void clear(); | |
100 void reset(); | |
101 }; | |
102 | |
103 | |
104 RIB route_base; | |
105 | |
106 const uint32_t masks[33] = { | |
107 0x00000000, | |
108 0x80000000, | |
109 0xc0000000, | |
110 0xe0000000, | |
111 0xf0000000, | |
112 0xf8000000, | |
113 0xfc000000, | |
114 0xfe000000, | |
115 0xff000000, | |
116 0xff800000, | |
117 0xffc00000, | |
118 0xffe00000, | |
119 0xfff00000, | |
120 0xfff80000, | |
121 0xfffc0000, | |
122 0xfffe0000, | |
123 0xffff0000, | |
124 0xffff8000, | |
125 0xffffc000, | |
126 0xffffe000, | |
127 0xfffff000, | |
128 0xfffff800, | |
129 0xfffffc00, | |
130 0xfffffe00, | |
131 0xffffff00, | |
132 0xffffff80, | |
133 0xffffffc0, | |
134 0xffffffe0, | |
135 0xfffffff0, | |
136 0xfffffff8, | |
137 0xfffffffc, | |
138 0xfffffffe, | |
139 0xffffffff}; | |
140 | |
141 | |
142 //////////////////////////////////////////////// | |
143 // | |
144 char *suspicious_name(suspicion s) | |
145 { | |
146 char *ss = ""; | |
147 switch (s) { | |
148 case suspicious_none: ss = "none"; break; | |
149 case suspicious_origin: ss = "origin"; break; | |
150 case suspicious_adjacency: ss = "adjacency"; break; | |
151 default: break; | |
152 } | |
153 return ss; | |
154 } | |
155 | |
156 | |
157 //////////////////////////////////////////////// | |
158 // | |
159 route_prefix::route_prefix(uint32_t value) | |
160 { | |
161 prefix_value = value; | |
162 announced = false; | |
163 trusted = false; | |
164 } | |
165 | |
166 | |
167 void route_prefix::add_route(aspath path_, a_history &adj_history) | |
168 { | |
169 suspicion s = suspicious(adj_history); | |
170 trusted = announced && (s == suspicious_none); | |
171 announced = true; | |
172 path = path_; | |
173 } | |
174 | |
175 | |
176 void route_prefix::remove_route(int prefix_length) | |
177 { | |
178 if (announced) { | |
179 for (m_connect::iterator i = smtp_connections.begin(); i != smtp_connections.end(); i++) { | |
180 const uint32_t &ip = (*i).first; | |
181 time_t &t = (*i).second; | |
182 uint32_t nip = htonl(ip); | |
183 char buf[maxlen]; | |
184 char adr[sizeof "255.255.255.255 "]; | |
185 adr[0] = '\0'; | |
186 inet_ntop(AF_INET, (const u_char *)&nip, adr, sizeof(adr)); | |
187 char ctbuf[maxlen]; | |
188 ctime_r(&t, ctbuf); | |
189 int ii = strlen(ctbuf); | |
190 if (ii > 1) ctbuf[ii-1] = '\0'; // remove trailing newline | |
191 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); | |
192 my_syslog(buf); | |
193 } | |
194 } | |
195 announced = false; | |
196 smtp_connections.clear(); | |
197 } | |
198 | |
199 | |
200 void route_prefix::update_history(a_set &adj_set) | |
201 { | |
202 // decay origin history | |
203 for (o_history::iterator i = origin_history.begin(); i != origin_history.end(); i++) { | |
204 float &count = (*i).second; | |
205 count *= decay; | |
206 } | |
207 if (announced) { | |
208 // remove old suspicious smtp connections | |
209 time_t cutoff = time(NULL) - ancient_smtp; | |
210 for (m_connect::iterator i = smtp_connections.begin(); i != smtp_connections.end(); ) { | |
211 time_t &t = (*i).second; | |
212 if (t > cutoff) i++; | |
213 else smtp_connections.erase(i++); | |
214 } | |
215 | |
216 // update origin history | |
217 int n = path.size() - 1; | |
218 int origin = path[n]; | |
219 o_history::iterator j = origin_history.find(origin); | |
220 if (j == origin_history.end()) { | |
221 origin_history[origin] = 1.0; | |
222 } | |
223 else { | |
224 float &count = (*j).second; | |
225 count++; | |
226 } | |
227 // update current adjacency set | |
228 for (int k=0; k<n; k++) { | |
229 adj_set.insert(aspair(k)); | |
230 } | |
231 } | |
232 | |
233 // remove origin history entries below the threshold | |
234 for (o_history::iterator i = origin_history.begin(); i != origin_history.end();) { | |
235 float &count = (*i).second; | |
236 if (count > threshold) i++; | |
237 else origin_history.erase(i++); | |
238 } | |
239 } | |
240 | |
241 | |
242 suspicion route_prefix::suspicious(a_history &adj_history, bool debug, int prefix_length, uint32_t ip) | |
243 { | |
244 if (!announced || trusted) return suspicious_none; | |
245 debug &= (debug_syslog > 2); | |
246 | |
247 // check origin stability | |
248 int n = path.size() - 1; | |
249 int origin = path[n]; | |
250 o_history::const_iterator o = origin_history.find(origin); | |
251 if (o == origin_history.end()) { | |
252 if (debug) { | |
253 char buf[maxlen]; | |
254 snprintf(buf, sizeof(buf), "debug suspicious origin %d missing count %s", | |
255 origin, name(prefix_length).c_str()); | |
256 my_syslog(buf); | |
257 } | |
258 record_smtp(ip); | |
259 return suspicious_origin; | |
260 } | |
261 const float &count = (*o).second; | |
262 if (count < origin_threshold) { | |
263 if (debug) { | |
264 char buf[maxlen]; | |
265 snprintf(buf, sizeof(buf), "debug suspicious origin %d count %5.2f vs %5.2f %s", | |
266 origin, count, origin_threshold, name(prefix_length).c_str()); | |
267 my_syslog(buf); | |
268 } | |
269 record_smtp(ip); | |
270 return suspicious_origin; | |
271 } | |
272 | |
273 // check as adjacency stability | |
274 for (int k=0; k<n; k++) { | |
275 adjacent aa = aspair(k); | |
276 a_history::iterator a = adj_history.find(aa); | |
277 if (a == adj_history.end()) { | |
278 if (debug) { | |
279 char buf[maxlen]; | |
280 snprintf(buf, sizeof(buf), "debug suspicious adjacency (%d,%d) missing count %s", | |
281 aa.first, aa.second, name(prefix_length).c_str()); | |
282 my_syslog(buf); | |
283 } | |
284 record_smtp(ip); | |
285 return suspicious_adjacency; | |
286 } | |
287 float &count = (*a).second; | |
288 if (count < adjacent_threshold) { | |
289 if (debug) { | |
290 char buf[maxlen]; | |
291 snprintf(buf, sizeof(buf), "debug suspicious adjacency (%d,%d) count %5.2f vs %5.2f %s", | |
292 aa.first, aa.second, count, adjacent_threshold, name(prefix_length).c_str()); | |
293 my_syslog(buf); | |
294 } | |
295 record_smtp(ip); | |
296 return suspicious_adjacency; | |
297 } | |
298 } | |
299 return suspicious_none; | |
300 } | |
301 | |
302 | |
303 void route_prefix::record_smtp(uint32_t ip) | |
304 { | |
305 if (ip) smtp_connections[ip] = time(NULL); | |
306 } | |
307 | |
308 | |
309 string route_prefix::name(int length) const | |
310 { | |
311 char buf[maxlen]; | |
312 char adr[sizeof "255.255.255.255 "]; | |
313 adr[0] = '\0'; | |
314 uint32_t nip = htonl(prefix_value); | |
315 inet_ntop(AF_INET, (const u_char *)&nip, adr, sizeof(adr)); | |
316 snprintf(buf, sizeof(buf), "%s/%d", adr, length); | |
317 int n = path.size(); | |
318 for (int i=0; i<n; i++) { | |
319 snprintf(adr, sizeof(adr), " %d", path[i]); | |
320 strncat(buf, adr, max(0, maxlen-1-(int)strlen(adr))); | |
321 } | |
322 return string(buf); | |
323 } | |
324 | |
325 | |
326 //////////////////////////////////////////////// | |
327 // | |
328 | |
329 RIB::RIB() | |
330 { | |
331 pthread_mutex_init(&rib_mutex, 0); | |
332 uptime = 0; | |
333 stable = false; | |
334 } | |
335 | |
336 | |
337 void RIB::add_route(int prefix_length, uint32_t prefix_value) | |
338 { | |
339 if (prefix_length < 0) return; | |
340 if (prefix_length > 32) return; | |
341 pthread_mutex_lock(&rib_mutex); | |
342 prefix_value &= masks[prefix_length]; | |
343 route_prefix rr(prefix_value); | |
344 route_prefixes &p = prefixes[prefix_length]; | |
345 route_prefixes::iterator i = p.find(&rr); | |
346 route_prefix *r = NULL; | |
347 if (i == p.end()) { | |
348 // new prefix | |
349 r = new route_prefix(prefix_value); | |
350 p.insert(r); | |
351 } | |
352 else { | |
353 // existing prefix | |
354 r = *i; | |
355 } | |
356 r->add_route(path, history); | |
357 suspicion s; | |
358 if (debug_syslog > 2) s = suspicious(r); | |
359 if ((debug_syslog > 2) && (s != suspicious_none) && (prefix_length < 22)) { | |
360 char buf[maxlen]; | |
361 snprintf(buf, sizeof(buf), "add suspicious %s route %s", suspicious_name(s), r->name(prefix_length).c_str()); | |
362 my_syslog(buf); | |
363 } | |
364 else if (debug_syslog > 3) { | |
365 char buf[maxlen]; | |
366 snprintf(buf, sizeof(buf), "add route %s", r->name(prefix_length).c_str()); | |
367 my_syslog(buf); | |
368 } | |
369 pthread_mutex_unlock(&rib_mutex); | |
370 } | |
371 | |
372 | |
373 void RIB::remove_route(int prefix_length, uint32_t prefix_value) | |
374 { | |
375 if (prefix_length < 0) return; | |
376 if (prefix_length > 32) return; | |
377 pthread_mutex_lock(&rib_mutex); | |
378 uint32_t mask = masks[prefix_length]; | |
379 prefix_value &= mask; | |
380 route_prefix r(prefix_value); | |
381 route_prefixes &p = prefixes[prefix_length]; | |
382 route_prefixes::iterator i = p.find(&r); | |
383 if (i != p.end()) { | |
384 // existing prefix | |
385 route_prefix* r = *i; | |
386 if (debug_syslog > 3) { | |
387 char buf[maxlen]; | |
388 snprintf(buf, sizeof(buf), "remove route %s", r->name(prefix_length).c_str()); | |
389 my_syslog(buf); | |
390 } | |
391 r->remove_route(prefix_length); | |
392 if (r->empty()) { | |
393 p.erase(r); | |
394 delete r; | |
395 }; | |
396 } | |
397 pthread_mutex_unlock(&rib_mutex); | |
398 } | |
399 | |
400 | |
401 void RIB::update_history() | |
402 { | |
403 pthread_mutex_lock(&rib_mutex); | |
404 a_set adj_set; | |
405 uptime++; | |
406 stable = (uptime > training); | |
407 int total = 0; | |
408 int inactive = 0; | |
409 int suspic = 0; | |
410 for (int i=0; i<=32; i++) { | |
411 bool debug = true; // show first suspicious prefix | |
412 route_prefixes &p = prefixes[i]; | |
413 for (route_prefixes::iterator j = p.begin(); j != p.end();) { | |
414 route_prefix *r = *j; | |
415 r->update_history(adj_set); | |
416 total++; | |
417 if (r->active()) { | |
418 if (suspicious(r, debug, i) != suspicious_none) { | |
419 suspic++; | |
420 debug = false; | |
421 } | |
422 } | |
423 else { | |
424 inactive++; | |
425 } | |
426 if (r->empty()) { | |
427 p.erase(j++); | |
428 delete r; | |
429 } | |
430 else j++; | |
431 } | |
432 } | |
433 if (debug_syslog > 2) { | |
434 char buf[maxlen]; | |
435 snprintf(buf, sizeof(buf), "total %d inactive %d suspicious %d", total, inactive, suspic); | |
436 my_syslog(buf); | |
437 } | |
438 // decay adjacency history | |
439 for (a_history::iterator i = history.begin(); i != history.end(); i++) { | |
440 float &count = (*i).second; | |
441 count *= decay; | |
442 } | |
443 // update adjacency history from the current adjacency set | |
444 for (a_set::iterator i = adj_set.begin(); i != adj_set.end(); i++) { | |
445 a_history::iterator a = history.find(*i); | |
446 if (a == history.end()) { | |
447 // new adjacency | |
448 history[*i] = 1.0; | |
449 } | |
450 else { | |
451 float &count = (*a).second; | |
452 count++; | |
453 } | |
454 } | |
455 // remove adjacency history entries below the threshold | |
456 for (a_history::iterator i = history.begin(); i != history.end();) { | |
457 float &count = (*i).second; | |
458 if (count > threshold) i++; | |
459 else history.erase(i++); | |
460 } | |
461 pthread_mutex_unlock(&rib_mutex); | |
462 } | |
463 | |
464 | |
465 suspicion RIB::suspicious(route_prefix *r, bool debug, int prefix_length, uint32_t ip) | |
466 { | |
467 if (!stable) return suspicious_none; | |
468 if (!r) return suspicious_none; | |
469 return r->suspicious(history, debug, prefix_length, ip); | |
470 } | |
471 | |
472 | |
473 suspicion RIB::suspicious(uint32_t ip) | |
474 { | |
475 if (!stable) return suspicious_none; | |
476 suspicion rc = suspicious_none; | |
477 route_prefix *r1 = NULL; | |
478 int pl; | |
479 pthread_mutex_lock(&rib_mutex); | |
480 for (int i=32; i>=0; i--) { | |
481 route_prefixes &p = prefixes[i]; | |
482 uint32_t network = ip & masks[i]; | |
483 route_prefix r(network); | |
484 route_prefixes::iterator j = p.find(&r); | |
485 if (j != p.end()) { | |
486 // existing prefix | |
487 route_prefix* r = *j; | |
488 if (r->active()) { | |
489 r1 = r; | |
490 pl = i; | |
491 break; | |
492 } | |
493 } | |
494 } | |
495 rc = suspicious(r1, true, pl, ip); | |
496 pthread_mutex_unlock(&rib_mutex); | |
497 return rc; | |
498 } | |
499 | |
500 | |
501 void RIB::clear() | |
502 { | |
503 pthread_mutex_lock(&rib_mutex); | |
504 for (int i=0; i<=32; i++) { | |
505 route_prefixes &p = prefixes[i]; | |
506 for (route_prefixes::iterator j = p.begin(); j != p.end();) { | |
507 route_prefix *r = *j; | |
508 p.erase(j++); | |
509 delete r; | |
510 } | |
511 } | |
512 history.clear(); | |
513 pthread_mutex_unlock(&rib_mutex); | |
514 } | |
515 | |
516 | |
517 void RIB::reset() | |
518 { | |
519 pthread_mutex_lock(&rib_mutex); | |
520 for (int i=0; i<=32; i++) { | |
521 route_prefixes &p = prefixes[i]; | |
522 for (route_prefixes::iterator j = p.begin(); j != p.end(); j++) { | |
523 route_prefix *r = *j; | |
524 r->remove_route(i); | |
525 } | |
526 } | |
527 pthread_mutex_unlock(&rib_mutex); | |
528 } | |
529 | |
530 | |
531 //////////////////////////////////////////////// | |
532 // | |
533 CONFIG::CONFIG() { | |
534 reference_count = 0; | |
535 generation = 0; | |
536 load_time = 0; | |
537 } | |
538 | |
539 | |
540 CONFIG::~CONFIG() { | |
541 for (routeconfig_list::iterator i=routeconfigs.begin(); i!=routeconfigs.end(); i++) { | |
542 ROUTECONFIG *c = *i; | |
543 delete c; | |
544 } | |
545 } | |
546 | |
547 | |
548 void CONFIG::dump() { | |
549 for (routeconfig_list::iterator i=routeconfigs.begin(); i!=routeconfigs.end(); i++) { | |
550 ROUTECONFIG *c = *i; | |
551 c->dump(0); | |
552 } | |
553 } | |
554 | |
555 | |
556 void CONFIG::read() { | |
557 while (true) { | |
558 bool have = false; | |
559 for (routeconfig_list::iterator i=routeconfigs.begin(); i!=routeconfigs.end(); i++) { | |
560 ROUTECONFIGP c = *i; | |
561 have |= c->read(*this); | |
562 } | |
563 if (!have) break; | |
564 } | |
565 } | |
566 | |
567 | |
568 | |
569 //////////////////////////////////////////////// | |
570 // | |
571 ROUTECONFIG::ROUTECONFIG(TOKEN &tok, char *file_name_) { | |
572 tokp = &tok; | |
573 file_name = file_name_; | |
574 open(true); | |
575 } | |
576 | |
577 | |
578 ROUTECONFIG::~ROUTECONFIG() { | |
579 close(); | |
580 for (pattern_list::iterator i=patterns.begin(); i!=patterns.end(); i++) { | |
581 PATTERN *p = *i; | |
582 delete p; | |
583 } | |
584 } | |
585 | |
586 | |
587 void ROUTECONFIG::open(bool msg) { | |
588 fd = ::open(file_name, O_RDONLY); | |
589 len = 0; | |
590 if (fd == -1) { | |
591 if (msg) { | |
592 char buf[maxlen]; | |
593 snprintf(buf, sizeof(buf), "syslog file %s not readable", file_name); | |
594 tokp->token_error(buf); | |
595 } | |
596 } | |
597 else { | |
598 if (debug_syslog > 1) { | |
599 snprintf(buf, sizeof(buf), "syslog file %s opened", file_name); | |
600 my_syslog(buf); | |
601 } | |
602 if (msg) lseek(fd, 0, SEEK_END); | |
603 if (fstat(fd, &openfdstat)) { | |
604 close(); | |
605 snprintf(buf, sizeof(buf), "syslog file %s cannot stat after open", file_name); | |
606 tokp->token_error(buf); | |
607 } | |
608 // specify that this fd gets closed on exec, so that selinux | |
609 // won't complain | |
610 int oldflags = fcntl(fd, F_GETFD, 0); | |
611 if (oldflags >= 0) { | |
612 fcntl(fd, F_SETFD, oldflags | FD_CLOEXEC); | |
613 } | |
614 } | |
615 } | |
616 | |
617 | |
618 bool ROUTECONFIG::read(CONFIG &con) { | |
619 if (failed()) { | |
620 open(false); | |
621 if (failed()) return false; | |
622 } | |
623 int n = ::read(fd, buf+len, buflen-len); | |
624 bool have = (n > 0); | |
625 if (have) { | |
626 len += n; | |
627 while (true) { | |
628 char *p = (char*)memchr(buf, '\n', len); | |
629 if (!p) break; | |
630 n = p-buf; | |
631 *p = '\0'; | |
632 process(con); // process null terminated string | |
633 len -= n+1; | |
634 memmove(buf, p+1, len); | |
635 } | |
636 // no <lf> in a full buffer | |
637 if (len == buflen) len = 0; | |
638 } | |
639 else { | |
640 // check for file close | |
641 struct stat filenamest; | |
642 if (0 == stat(file_name, &filenamest)) { | |
643 if ((filenamest.st_dev != openfdstat.st_dev) || | |
644 (filenamest.st_ino != openfdstat.st_ino)) { | |
645 close(); | |
646 } | |
647 } | |
648 else { | |
649 // filename no longer exists | |
650 close(); | |
651 } | |
652 } | |
653 return have; | |
654 } | |
655 | |
656 | |
657 void ROUTECONFIG::close() { | |
658 if (debug_syslog > 1) { | |
659 snprintf(buf, sizeof(buf), "syslog file %s closed", file_name); | |
660 my_syslog(buf); | |
661 } | |
662 if (fd != -1) ::close(fd); | |
663 fd = -1; | |
664 } | |
665 | |
666 | |
667 void ROUTECONFIG::add_pattern(PATTERNP pat) { | |
668 patterns.push_back(pat); | |
669 } | |
670 | |
671 | |
672 void ROUTECONFIG::process(CONFIG &con) { | |
673 int pi=0; | |
674 for (pattern_list::iterator i=patterns.begin(); i!=patterns.end(); i++) { | |
675 PATTERN *p = *i; | |
676 if (p->process(buf, con, file_name, pi)) break; | |
677 pi++; | |
678 } | |
679 } | |
680 | |
681 | |
682 void ROUTECONFIG::dump(int level) { | |
683 char indent[maxlen]; | |
684 int i = min(maxlen-1, level*4); | |
685 memset(indent, ' ', i); | |
686 indent[i] = '\0'; | |
687 printf("%s file \"%s\" {\n", indent, file_name); | |
688 for (pattern_list::iterator i=patterns.begin(); i!=patterns.end(); i++) { | |
689 PATTERN *p = *i; | |
690 p->dump(level+1); | |
691 } | |
692 printf("%s }; \n", indent); | |
693 } | |
694 | |
695 | |
696 //////////////////////////////////////////////// | |
697 // | |
698 int ip_address(char *have); | |
699 int ip_address(char *have) { | |
700 int ipaddr = 0; | |
701 in_addr ip; | |
702 if (inet_aton(have, &ip)) ipaddr = ip.s_addr; | |
703 else { | |
704 struct hostent *host = gethostbyname(have); | |
705 if (host && host->h_addrtype == AF_INET) memcpy(&ipaddr, host->h_addr, sizeof(ipaddr)); | |
706 } | |
707 return ntohl(ipaddr); | |
708 } | |
709 | |
710 | |
711 //////////////////////////////////////////////// | |
712 // | |
713 | |
714 PATTERN::PATTERN(TOKEN &tok, pattern_style style_, char *pattern_, int index1_, int index2_) | |
715 { | |
716 style = style_; | |
717 pattern = pattern_; | |
718 index1 = index1_; | |
719 index2 = index2_; | |
720 if (pattern) { | |
721 int rc = regcomp(&re, pattern, REG_ICASE | REG_EXTENDED); | |
722 if (rc) { | |
723 char bu[maxlen]; | |
724 regerror(rc, &re, bu, maxlen); | |
725 char buf[maxlen]; | |
726 snprintf(buf, sizeof(buf), "pattern %s not valid - %s", pattern, bu); | |
727 tok.token_error(buf); | |
728 pattern = NULL; | |
729 } | |
730 } | |
731 } | |
732 | |
733 | |
734 PATTERN::~PATTERN() { | |
735 regfree(&re); | |
736 } | |
737 | |
738 | |
739 bool PATTERN::process(char *buf, CONFIG &con, char *file_name, int pattern_index) | |
740 { | |
741 if (pattern) { | |
742 const int nmatch = max(index1, index2) + 1; | |
743 regmatch_t match[nmatch]; | |
744 if (0 == regexec(&re, buf, nmatch, match, 0)) { | |
745 int sp1 = match[index1].rm_so; | |
746 int ep1 = match[index1].rm_eo; | |
747 int sp2 = match[index2].rm_so; | |
748 int ep2 = match[index2].rm_eo; | |
749 if ((sp1 != -1) && (sp2 != -1)) { | |
750 if (debug_syslog > 13) { | |
751 my_syslog(buf); // show lines with matches | |
752 } | |
753 buf[ep1] = '\0'; | |
754 buf[ep2] = '\0'; | |
755 uint32_t ip; | |
756 int pl; | |
757 suspicion s; | |
758 switch (style) { | |
759 case style_reset: | |
760 route_base.reset(); | |
761 break; | |
762 case style_path: | |
763 { | |
764 aspath path; | |
765 char *p = buf+sp1; | |
766 char *e; | |
767 long l; | |
768 while (true) { | |
769 l = strtol(p, &e, 10); | |
770 if (e == p) break; | |
771 p = e; | |
772 path.push_back((int)l); | |
773 } | |
774 route_base.set_path(path); | |
775 } | |
776 break; | |
777 case style_announce: | |
778 ip = ip_address(buf+sp1); | |
779 pl = atoi(buf+sp2); | |
780 if (ip) route_base.add_route(pl, ip); | |
781 break; | |
782 case style_withdraw: | |
783 ip = ip_address(buf+sp1); | |
784 pl = atoi(buf+sp2); | |
785 if (ip) route_base.remove_route(pl, ip); | |
786 break; | |
787 case style_ip: | |
788 ip = ip_address(buf+sp1); | |
789 s = route_base.suspicious(ip); | |
790 if (s != suspicious_none) { | |
791 char adr[sizeof "255.255.255.255 "]; | |
792 adr[0] = '\0'; | |
793 uint32_t nip = htonl(ip); | |
794 inet_ntop(AF_INET, (const u_char *)&nip, adr, sizeof(adr)); | |
795 char buf[maxlen]; | |
796 snprintf(buf, sizeof(buf), "*** suspicious %s ip %s", suspicious_name(s), adr); | |
797 my_syslog(buf); | |
798 } | |
799 break; | |
800 } | |
801 return true; | |
802 } | |
803 } | |
804 } | |
805 return false; | |
806 } | |
807 | |
808 | |
809 void PATTERN::dump(int level, int index, char *token) | |
810 { | |
811 char indent[maxlen]; | |
812 int i = min(maxlen-1, level*4); | |
813 memset(indent, ' ', i); | |
814 indent[i] = '\0'; | |
815 printf("%s %s %d; \n", indent, token, index); | |
816 } | |
817 | |
818 | |
819 void PATTERN::dump(int level) | |
820 { | |
821 char indent[maxlen]; | |
822 int i = min(maxlen-1, level*4); | |
823 memset(indent, ' ', i); | |
824 indent[i] = '\0'; | |
825 switch (style) { | |
826 case style_reset: | |
827 printf("%s %s \"%s\" { \n", indent, token_reset, pattern); | |
828 break; | |
829 case style_path: | |
830 printf("%s %s \"%s\" { \n", indent, token_path, pattern); | |
831 dump(level+1, index1, token_index_path); | |
832 break; | |
833 case style_announce: | |
834 printf("%s %s \"%s\" { \n", indent, token_announce, pattern); | |
835 dump(level+1, index1, token_index_value); | |
836 dump(level+1, index2, token_index_length); | |
837 break; | |
838 case style_withdraw: | |
839 printf("%s %s \"%s\" { \n", indent, token_withdraw, pattern); | |
840 dump(level+1, index1, token_index_value); | |
841 dump(level+1, index2, token_index_length); | |
842 break; | |
843 case style_ip: | |
844 printf("%s %s \"%s\" { \n", indent, token_ip, pattern); | |
845 dump(level+1, index1, token_index_ip); | |
846 break; | |
847 } | |
848 printf("%s }; \n", indent); | |
849 } | |
850 | |
851 | |
852 //////////////////////////////////////////////// | |
853 // helper to discard the strings held by a string_set | |
854 // | |
855 void discard(string_set &s) { | |
856 for (string_set::iterator i=s.begin(); i!=s.end(); i++) { | |
857 free(*i); | |
858 } | |
859 s.clear(); | |
860 } | |
861 | |
862 | |
863 //////////////////////////////////////////////// | |
864 // helper to register a string in a string set | |
865 // | |
866 char* register_string(string_set &s, char *name) { | |
867 string_set::iterator i = s.find(name); | |
868 if (i != s.end()) return *i; | |
869 char *x = strdup(name); | |
870 s.insert(x); | |
871 return x; | |
872 } | |
873 | |
874 | |
875 //////////////////////////////////////////////// | |
876 // register a global string | |
877 // | |
878 char* register_string(char *name) { | |
879 return register_string(all_strings, name); | |
880 } | |
881 | |
882 | |
883 //////////////////////////////////////////////// | |
884 // clear all global strings, helper for valgrind checking | |
885 // | |
886 void clear_strings() { | |
887 discard(all_strings); | |
888 } | |
889 | |
890 | |
891 //////////////////////////////////////////////// | |
892 // clear the rib, helper for valgrind checking | |
893 // | |
894 void clear_rib() { | |
895 route_base.clear(); | |
896 } | |
897 | |
898 | |
899 //////////////////////////////////////////////// | |
900 // | |
901 bool tsa(TOKEN &tok, char *token); | |
902 bool tsa(TOKEN &tok, char *token) { | |
903 char *have = tok.next(); | |
904 if (have == token) return true; | |
905 tok.token_error(token, have); | |
906 return false; | |
907 } | |
908 | |
909 | |
910 //////////////////////////////////////////////// | |
911 // | |
912 bool parse_path(TOKEN &tok, ROUTECONFIG &con, char *tokk, pattern_style style); | |
913 bool parse_path(TOKEN &tok, ROUTECONFIG &con, char *tokk, pattern_style style) { | |
914 char *pattern = tok.next(); | |
915 int index = 0; | |
916 if (!tsa(tok, token_lbrace)) return false; | |
917 while (true) { | |
918 char *have = tok.next(); | |
919 if (!have) break; | |
920 if (have == token_rbrace) break; | |
921 if (have == tokk) { | |
922 index = tok.nextint(); | |
923 if (!tsa(tok, token_semi)) return false; | |
924 } | |
925 else return false; | |
926 } | |
927 if (!tsa(tok, token_semi)) return false; | |
928 PATTERNP p = new PATTERN(tok, style, pattern, index, 0); | |
929 con.add_pattern(p); | |
930 return true; | |
931 } | |
932 | |
933 | |
934 bool parse_announce_withdraw(TOKEN &tok, ROUTECONFIG &con, pattern_style style); | |
935 bool parse_announce_withdraw(TOKEN &tok, ROUTECONFIG &con, pattern_style style) { | |
936 char *pattern = tok.next(); | |
937 int index1 = 0; | |
938 int index2 = 0; | |
939 if (!tsa(tok, token_lbrace)) return false; | |
940 while (true) { | |
941 char *have = tok.next(); | |
942 if (!have) break; | |
943 if (have == token_rbrace) break; | |
944 if (have == token_index_value) { | |
945 index1 = tok.nextint(); | |
946 if (!tsa(tok, token_semi)) return false; | |
947 } | |
948 else if (have == token_index_length) { | |
949 index2 = tok.nextint(); | |
950 if (!tsa(tok, token_semi)) return false; | |
951 } | |
952 else return false; | |
953 } | |
954 if (!tsa(tok, token_semi)) return false; | |
955 PATTERNP p = new PATTERN(tok, style, pattern, index1, index2); | |
956 con.add_pattern(p); | |
957 return true; | |
958 } | |
959 | |
960 | |
961 bool parse_routeconfig(TOKEN &tok, CONFIG &dc); | |
962 bool parse_routeconfig(TOKEN &tok, CONFIG &dc) { | |
963 char *name = tok.next(); | |
964 if (!tsa(tok, token_lbrace)) return false; | |
965 ROUTECONFIGP con = new ROUTECONFIG(tok, name); | |
966 if (con->failed()) { | |
967 delete con; | |
968 return false; | |
969 } | |
970 dc.add_routeconfig(con); | |
971 while (true) { | |
972 char *have = tok.next(); | |
973 if (!have) break; | |
974 if (have == token_rbrace) break; | |
975 if (have == token_reset) { | |
976 if (!parse_path(tok, *con, NULL, style_reset)) return false; | |
977 } | |
978 else if (have == token_path) { | |
979 if (!parse_path(tok, *con, token_index_path, style_path)) return false; | |
980 } | |
981 else if (have == token_ip) { | |
982 if (!parse_path(tok, *con, token_index_ip, style_ip)) return false; | |
983 } | |
984 else if (have == token_announce) { | |
985 if (!parse_announce_withdraw(tok, *con, style_announce)) return false; | |
986 } | |
987 else if (have == token_withdraw) { | |
988 if (!parse_announce_withdraw(tok, *con, style_withdraw)) return false; | |
989 } | |
990 else { | |
991 tok.token_error("path/announce/withdraw", have); | |
992 return false; | |
993 } | |
994 } | |
995 if (!tsa(tok, token_semi)) return false; | |
996 return true; | |
997 } | |
998 | |
999 | |
1000 //////////////////////////////////////////////// | |
1001 // parse a config file | |
1002 // | |
1003 bool load_conf(CONFIG &dc, char *fn) { | |
1004 int count = 0; | |
1005 TOKEN tok(fn, &dc.config_files); | |
1006 while (true) { | |
1007 char *have = tok.next(); | |
1008 if (!have) break; | |
1009 if (have == token_file) { | |
1010 if (!parse_routeconfig(tok, dc)) return false; | |
1011 count++; | |
1012 } | |
1013 else { | |
1014 tok.token_error("file", have); | |
1015 return false; | |
1016 } | |
1017 } | |
1018 tok.token_error("load_conf() found %d syslog files in %s", count, fn); | |
1019 return (!dc.routeconfigs.empty()); | |
1020 } | |
1021 | |
1022 | |
1023 //////////////////////////////////////////////// | |
1024 // | |
1025 void routing_hourly_update() { | |
1026 route_base.update_history(); | |
1027 } | |
1028 | |
1029 | |
1030 //////////////////////////////////////////////// | |
1031 // init the tokens | |
1032 // | |
1033 void token_init() { | |
1034 token_announce = register_string("announce"); | |
1035 token_file = register_string("file"); | |
1036 token_include = register_string("include"); | |
1037 token_index_ip = register_string("index_ip"); | |
1038 token_index_length = register_string("index_length"); | |
1039 token_index_path = register_string("index_path"); | |
1040 token_index_value = register_string("index_value"); | |
1041 token_ip = register_string("ip"); | |
1042 token_lbrace = register_string("{"); | |
1043 token_path = register_string("path"); | |
1044 token_rbrace = register_string("}"); | |
1045 token_reset = register_string("reset"); | |
1046 token_semi = register_string(";"); | |
1047 token_slash = register_string("/"); | |
1048 token_withdraw = register_string("withdraw"); | |
1049 } |