jablonka.czprosek.czf

sedlo

Subversion Repositories:
[/] [branches/] [sedlo.pl] - Blame information for rev 14

 

Line No. Rev Author Line
11simandl#!/usr/bin/perl
2# author : Petr Simandl www.simandl.cz
3# rewrite to Perl: Jan Spitalnik www.spitalnik.net
4# release date : 29/08/2004
5# name : sedlo
6# description : dynamic side routing tables tool
7# license : GPL
8 
9 
10# TODO:
11# 2) add logging :-) create some convenience fnc that can store either to STDOUT or file
12# 3) pridat podporu pro deamonize - default time je 5minut
13 
14use strict;
15use LWP::UserAgent;
16use Fcntl qw(:DEFAULT :flock);
17use Getopt::Long;
18use Pod::Usage;
19use Storable;
20 
21# Main config files
22my($sedlo_config) = "/etc/sedlo.conf";
23my($sedlo_spool_dir) = "/var/cache/sedlo/";
24my($sedlo_spool_config) = "sedlo.conf";
25# iproute2 config files
26my($rttables_config) = "/etc/iproute2/rt_tables";
27my($rttables_min) = 110;
28my($rttables_max) = 200;
29my($ip_nodef) = "10.0.0.0/8";
30# Logging
31# 0 = no logging, 1 = basic logging, 2 = extensive logging
32my($debug_level) = 0;
33 
34#################################################
35############# DON'T MODIFY BELOW ################
36#################################################
37 
38# define version
39use constant VERSION => "0.0.3";
40 
41# define error codes
42use constant E_MISSING_CMD => -1; # missing command
43use constant E_MISSING_CFG => -2; # missing configuration file
44use constant E_MISSING_MCNF => -3; # missing mcnf stanza in main config. file
45use constant E_NONET => -4; # Problems with network communication
46use constant E_CREATSPOOL => -5; # Problems creating spool directory
47use constant E_INVSTANZA => -6; # Invalid stanza in config. file
48use constant E_MISC => -7; # Other kind of error
49use constant E_OK => 0; # Everything is OK
50use constant TRUE => 1; # All's OK
51use constant FALSE => 0; # Baaad
52 
53# Commands
54my($IP_CMD) = get_command("ip");
55 
56# Check config files
57check_configs($sedlo_config, $rttables_config);
58 
59# Where is main cfg file?
60my($main_cfg_url) = get_main_cfg_url($sedlo_config);
61 
62# Parse command line
63my($opt_show_version) = 0;
64my($opt_show_help) = 0;
65my($opt_show_report) = 0;
66my($opt_no_get_cfg) = 0;
67my($opt_info1) = 0;
68my($opt_info2) = 0;
69 
70Getopt::Long::Configure("bundling");
71GetOptions('version|v' => \$opt_show_version,
72 'help|h' => \$opt_show_help,
73 'report|r' => \$opt_show_report,
74 'nogetcfg|n' => \$opt_no_get_cfg,
75 'info1|1' => \$opt_info1,
76 'info2|2' => \$opt_info2);
77 
78parse_cmd_line();
79 
80# create spool directory
81create_spool_dir($sedlo_spool_dir);
82 
83if(not $opt_no_get_cfg)
84{
85 # Download main config. file
86 get_main_config();
87}
88 
89# Merge main config with local config
90merge_main_and_local_config();
91 
92# Remove duplicates from spool config file
93remove_dups_from_config();
94 
95# Create routing tables
96create_tables();
97 
98# Fill routing tables
99fill_tables();
100 
101# Fill routing rules
102fill_rules();
103 
104################### Util Functions ##########################
105 
106# Name: fill_rules()
107# Desc: Fills routing tables with rules
108# Param: none
109sub fill_rules()
110{
111 my(%IPs) = ret_IPs();
112 my(@iGW_array);
113 my(@cmd_out);
114 my($IP);
115 my($iGW);
116 
117 foreach $IP (keys(%IPs))
118 {
119 @iGW_array = @{ Storable::thaw($IPs{$IP}) };
120 my($we_added_this_entry) = FALSE;
121 
122 foreach $iGW (@iGW_array)
123 {
124 if($iGW =~ /^$/)
125 {
126 next;
127 }
128 
129 # Check for existing entries
130 @cmd_out = `$IP_CMD route ls table $iGW 2>&1`;
131 if($! == 0 && $we_added_this_entry == FALSE)
132 {
133 my($has_entry) = TRUE;
134 
135 # Remove any existing entries
136 while($has_entry)
137 {
138 @cmd_out = `$IP_CMD rule ls 2>&1`;
139 my($tmp_line);
140 
141 $has_entry = FALSE;
142 
143 foreach $tmp_line (@cmd_out)
144 {
145 if($tmp_line =~ /$IP/)
146 {
147 $has_entry = TRUE;
148 }
149 }
150 
151 if($has_entry)
152 {
153 if($debug_level > 1)
154 {
155 print "Info: Removing old rules for $IP.\n";
156 }
157 
158 @cmd_out = `$IP_CMD rule del from $IP 2>&1`;
159 @cmd_out = `$IP_CMD rule del from $IP to $ip_nodef 2>&1`;
160 }
161 }
162 
163 if ($debug_level > 1)
164 {
165 print "Info: Creating new rules to send $IP to table $iGW.\n";
166 }
167 
168 # Add new rules
169 @cmd_out = `$IP_CMD rule add from $IP lookup $iGW 2>&1`;
170 @cmd_out = `$IP_CMD rule add from $IP to $ip_nodef lookup main 2>&1`;
171 $we_added_this_entry = TRUE;
172 }
173 else
174 {
175 if($debug_level > 1)
176 {
177 if($we_added_this_entry)
178 {
179 print "Info: iGW '$iGW' with lower priority!\n";
180 }
181 else
182 {
183 print "Info: Non-existant table!\n";
184 }
185 }
186 }
187 }
188 
189 }
190}
191 
192# Name: ret_IPs()
193# Desc: Returns hash of IP containing array of iGWs as value.
194# Param: none
195sub ret_IPs()
196{
197 open(SEDLO_SPOOL_CONFIG, "< $sedlo_spool_dir$sedlo_spool_config")
198 or die("Error: Can't open $sedlo_spool_dir$sedlo_spool_config!\n");
199 flock(SEDLO_SPOOL_CONFIG, LOCK_SH);
200 
201 my($line);
202 my(%IPs);
203 
204 while($line = <SEDLO_SPOOL_CONFIG>)
205 {
206 chomp($line);
207 if($line =~ m/^ip\s+/)
208 {
209 my(@iGWs) = split(/\s/, $line);
210 
211 # Remove ^ip
212 shift(@iGWs);
213 
214 # Store IP address
215 my($IP) = $iGWs[0];
216 
217 # Remove IP address
218 shift(@iGWs);
219 
220 # Remove name of subnet
221 shift(@iGWs);
222 
223 $IPs{$IP} = Storable::freeze(\@iGWs);
224 }
225 }
226 
227 close(SEDLO_SPOOL_CONFIG);
228 
229 return %IPs;
230}
231 
232# Name: fill_tables()
233# Desc: Fills routing tables
234# Param: none
235sub fill_tables()
236{
237 my(%iGWs) = get_iGWs_with_IPs();
238 my($iGW);
239 my($iGW_IP);
240 my($iGW_name);
241 my($iGW_via);
242 my(@cmd_tmp);
243 
244 foreach $iGW (keys %iGWs)
245 {
246 $iGW_IP = $iGW;
247 $iGW_name = $iGWs{$iGW};
248 
249 @cmd_tmp = `$IP_CMD route ls 2>&1`;
250 
251 my($line);
252 foreach $line (@cmd_tmp)
253 {
254 if($line =~ /$iGW_IP\s+via\s+(\w+)/)
255 {
256 $iGW_via = $1;
257 }
258 }
259 
260 @cmd_tmp = `$IP_CMD route ls table $iGW_name 2>&1`;
261 if($! != 0)
262 {
263 @cmd_tmp=`$IP_CMD route flush table $iGW_name 2>&1`;
264 }
265 
266 if(not defined $iGW_via)
267 {
268 if($debug_level > 1)
269 {
270 print "Info: Route not found for iGW $iGW_name - leaving table empty.\n";
271 }
272 }
273 else
274 {
275 @cmd_tmp=`$IP_CMD route add 0.0.0.0/1 via $iGW_via table $iGW_name 2>&1`;
276 @cmd_tmp=`$IP_CMD route add 128.0.0.0/1 via $iGW_via table $iGW_name 2>&1`;
277 
278 if($debug_level > 1)
279 {
280 print "Info: Table filled for iGW $iGW_name.\n";
281 }
282 }
283 }
284}
285 
286# Name: create_tables()
287# Desc: Creates routing tables
288# Parm: none
289sub create_tables()
290{
291 my(@iGWs) = get_iGWs();
292 my($iGW);
293 
294 foreach $iGW (@iGWs)
295 {
296 if(not ret_rttb_config_entry($iGW, 2))
297 {
298 my($count) = $rttables_max;
299 
300 while($count != $rttables_min)
301 {
302 if(not ret_rttb_config_entry($count, 1))
303 {
304 open(RTTABLES_CONFIG, ">> $rttables_config") or die("Error: Can't open $rttables_config!\n");
305 flock(RTTABLES_CONFIG, LOCK_SH);
306 
307 print RTTABLES_CONFIG "$count\t$iGW\n";
308 
309 close(RTTABLES_CONFIG);
310 
311 $count = $rttables_min;
312 next;
313 }
314 
315 $count--;
316 }
317 }
318 else
319 {
320 if($debug_level > 1)
321 {
322 print "Table found for $iGW, no action taken.\n";
323 }
324 }
325 }
326}
327 
328# Name: ret_rttb_config_entry()
329# Desc: Returns true/false if found routing table config entry for given IP/Hostname
330# Second parameter says whether you want to match against cost (1) or against
331# name of the table (2)
332# Parm: string, int
333sub ret_rttb_config_entry()
334{
335 my($entry, $type) = @_;
336 
337 open(RTTABLES_CONFIG, "< $rttables_config") or die("Error: Can't open $rttables_config!\n");
338 flock(RTTABLES_CONFIG, LOCK_SH);
339 
340 my($line);
341 while($line = <RTTABLES_CONFIG>)
342 {
343 chomp($line);
344 # Cost
345 if($type == 1)
346 {
347 if($line =~ /^$entry\s+\w+/)
348 {
349 return TRUE;
350 }
351 }
352 # Name of table
353 elsif($type == 2)
354 {
355 if($line =~ /^\w+\s+$entry/)
356 {
357 return TRUE;
358 }
359 }
360 else
361 {
362 print "Error: Invalid argument to ret_rttb_config_entry()!\n";
363 exit(E_MISC);
364 }
365 }
366 
367 close(RTTABLES_CONFIG);
368 return FALSE;
369}
370 
371# Name: get_iGWs()
372# Desc: Returns iGWs from config. file
373# Parm: none
374sub get_iGWs()
375{
376 my(@iGWs);
377 
378 open(SEDLO_SPOOL_CONFIG, "< $sedlo_spool_dir$sedlo_spool_config")
379 or die("Error: Can't open $sedlo_spool_dir$sedlo_spool_config!\n");
380 flock(SEDLO_SPOOL_CONFIG, LOCK_SH);
381 
382 my($line);
383 while($line = <SEDLO_SPOOL_CONFIG>)
384 {
385 chomp($line);
386 if($line =~ /^igw\s+(.*)\s+(.*)/)
387 {
388 push(@iGWs, $2);
389 }
390 }
391 
392 close(SEDLO_SPOOL_CONFIG);
393 
394 return @iGWs;
395}
396 
397# Name: get_iGWs_with_IPs()
398# Desc: Returns iGWs with IPs from config. file
399# Parm: none
400sub get_iGWs_with_IPs()
401{
402 my(%iGWs);
403 
404 open(SEDLO_SPOOL_CONFIG, "< $sedlo_spool_dir$sedlo_spool_config")
405 or die("Error: Can't open $sedlo_spool_dir$sedlo_spool_config!\n");
406 flock(SEDLO_SPOOL_CONFIG, LOCK_SH);
407 
408 my($line);
409 while($line = <SEDLO_SPOOL_CONFIG>)
410 {
411 chomp($line);
412 if($line =~ /^igw\s+(.*)\s+(.*)/)
413 {
414 $iGWs{$1} = $2;
415 }
416 }
417 
418 close(SEDLO_SPOOL_CONFIG);
419 
420 return %iGWs;
421}
422 
423# Name: parse_cmd_line()
424# Desc: Parses command line options
425# Param: none
426sub parse_cmd_line()
427{
428 if($opt_show_version)
429 {
430 print "$0 ".VERSION."\n";
431 exit(E_OK);
432 }
433 elsif($opt_show_help)
434 {
435 show_help();
436 exit(E_OK);
437 }
438 elsif($opt_show_report)
439 {
440 show_report();
441 exit(E_OK);
442 }
443 elsif($opt_info1)
444 {
445 $debug_level = 1;
446 }
447 elsif($opt_info2)
448 {
449 $debug_level = 2;
450 }
451}
452 
453# Name: show_report()
454# Desc: Prints routing tables and rules
455# Param: none
456sub show_report()
457{
458 print "##### SEDLO #####\n";
459 print "Version:".VERSION."\n";
460 print "local config: $sedlo_config\n";
461 print "main config: $main_cfg_url\n";
462 
463 print "##### TABLES #####\n";
464 open(RTTABLES, "< $rttables_config") or die("Can't open $rttables_config!\n");
465 flock(RTTABLES, LOCK_SH);
466 my($line);
467 while($line = <RTTABLES>)
468 {
469 print $line;
470 }
471 close(RTTABLES);
472 
473 print "##### RULES #####\n";
474 my(@cmd_out) = `$IP_CMD rule ls 2>&1`;
475 foreach $line (@cmd_out)
476 {
477 print $line;
478 }
479}
480 
481# Name: show_help()
482# Desc: Prints help screen
483# Param: none
484sub show_help()
485{
486 pod2usage(1);
487 
488}
489 
490# Name: create_spool_dir()
491# Desc: Creates spool dir if not present
492# Param: string
493sub create_spool_dir()
494{
495 my($spool_dir) = @_;
496 
497 if(not -e $spool_dir)
498 {
499 if(not mkdir($spool_dir))
500 {
501 print "Error: Can't create spool dir in $spool_dir!\n";
502 exit(E_CREATSPOOL);
503 }
504 }
505}
506 
507# Name: get_main_config()
508# Desc: Downloads configuration file
509# Param: string
510sub get_main_config()
511{
512 my($user_agent) = LWP::UserAgent->new;
513 my($request);
514 my($response);
515 
516 $user_agent->agent("Sedlo/".VERSION);
517 $request = HTTP::Request->new(GET => $main_cfg_url);
518 $response = $user_agent->request($request);
519 
520 if($response->is_error)
521 {
522 print "Error: Can't download $main_cfg_url!\n";
523 exit(E_NONET);
524 }
525 
526 # sedlo.config.main
527 open(SEDLO_CONFIG_MAIN, ">$sedlo_spool_dir$sedlo_spool_config.main")
528 or die("Error: Can't open $sedlo_spool_dir$sedlo_spool_config.main");
529 flock(SEDLO_CONFIG_MAIN, LOCK_EX);
530 print SEDLO_CONFIG_MAIN $response->content;
531 close(SEDLO_CONFIG_MAIN);
532}
533 
534# Name: merge_main_and_local_config()
535# Desc: Merges main and local configuration files
536# Param: none
537sub merge_main_and_local_config()
538{
539 my($file);
540 my(@config_files) = ($sedlo_config, $sedlo_spool_dir.$sedlo_spool_config.".main");
541 
542 open(SEDLO_SPOOL_CONFIG, ">>$sedlo_spool_dir$sedlo_spool_config")
543 or die("Error: Can't open $sedlo_spool_dir$sedlo_spool_config!\n");
544 flock(SEDLO_SPOOL_CONFIG, LOCK_EX);
545 
546 foreach $file (@config_files)
547 {
548 open(DATA, "< $file") or die("Error: Can't open $file!\n");
549 flock(DATA, LOCK_SH);
550 
551 my($line);
552 
553 while($line = <DATA>)
554 {
555 if($line =~ /^mcnf\s+/)
556 {
557 print SEDLO_SPOOL_CONFIG "$line";
558 }
559 elsif($line =~ /^igw\s+/)
560 {
561 print SEDLO_SPOOL_CONFIG "$line";
562 }
563 elsif($line =~ /^ip\s+/)
564 {
565 print SEDLO_SPOOL_CONFIG "$line";
566 }
567 elsif($line =~ /^\#/ or $line =~ /^\s+$/ or $line =~ /^$/)
568 {
569 next;
570 }
571 else
572 {
573 print "Error: Invalid stanza in $file!\n";
574 print "Trace -->$line<--\n";
575 exit(E_INVSTANZA);
576 }
577 }
578 
579 close(DATA);
580 }
581 
582 close(SEDLO_SPOOL_CONFIG);
583}
584 
585# Name: remove_dups_from_config
586# Desc: Removes duplicate lines from spool config
587# Param: none
588sub remove_dups_from_config()
589{
590 # Now lets dump duplicate entries in $SEDLO_SPOOL_CONFIG
591 open(SEDLO_SPOOL_CONFIG, "< $sedlo_spool_dir$sedlo_spool_config")
592 or die("Error: Can't open $sedlo_spool_dir$sedlo_spool_config!\n");
593 flock(SEDLO_SPOOL_CONFIG, LOCK_SH);
594 
595 my($line);
596 my(%uniq);
597 
598 while($line = <SEDLO_SPOOL_CONFIG>)
599 {
600 if($line !~ /^\#/)
601 {
602 $uniq{$line} = TRUE;
603 }
604 }
605 
606 close(SEDLO_SPOOL_CONFIG);
607 
608 open(SEDLO_SPOOL_CONFIG, "> $sedlo_spool_dir$sedlo_spool_config")
609 or die("Error: Can't open $sedlo_spool_dir$sedlo_spool_config!\n");
610 flock(SEDLO_SPOOL_CONFIG, LOCK_SH);
611 
612 print SEDLO_SPOOL_CONFIG "# generated file\n";
613 print SEDLO_SPOOL_CONFIG keys(%uniq);
614 
615 close(SEDLO_SPOOL_CONFIG);
616}
617 
618# Name: get_main_cfg_url()
619# Desc: Returns URL of main config file
620# Param: string
621sub get_main_cfg_url()
622{
623 my($sedlo_conf) = @_;
624 my($line);
625 my($cfg_file);
626 
627 open(SEDLO_CFG, "< $sedlo_conf") or die("Error: Can't open $sedlo_conf!\n");
628 while($line = <SEDLO_CFG>)
629 {
630 if($line =~ /^mcnf\s+(.*)/)
631 {
632 $cfg_file = $1;
633 }
634 }
635 
636 if (not defined $cfg_file)
637 {
638 print "Error: Can't find 'mcnf' stanza in config file!\n";
639 exit(E_MISSING_MCNF);
640 }
641 else
642 {
643 return $cfg_file;
644 }
645}
646 
647# Name: check_configs()
648# Desc: Check for existance of configuration files
649# Param: string, string
650sub check_configs()
651{
652 my($sedlo_config, $rttables_config) = @_;
653 
654 if (not -e $sedlo_config)
655 {
656 print "Error: Missing configuration file '$sedlo_config'!\n";
657 exit(E_MISSING_CFG);
658 }
659 
660 if (not -e $rttables_config)
661 {
662 print "Error: Missing configuration file '$rttables_config'!\n";
663 exit(E_MISSING_CFG);
664 }
665}
666 
667# Name: get_command()
668# Desc: Returns path for specified command
669# Param: string
670sub get_command()
671{
672 my($command) = @_;
673 my($path) = "PATH=/bin:/sbin:/usr/bin:/usr/sbin:usr/local/bin:/usr/local/sbin";
674 my($cmd_path) = `$path which $command`;
675 
676 chomp($cmd_path);
677 
678 if ($cmd_path =~ /not found/)
679 {
680 print "Error: can't find '$command' in \$PATH!\n";
681 exit(E_MISSING_CMD);
682 }
683 return $cmd_path;
684}
685 
686 
687__END__
688 
689=head1 NAME
690 
691sedlo - Program na nastavovani default routy podle danych pravidel
692 
693=head1 SYNOPSIS
694 
695sedlo [options]
696 
697=head1 OPTIONS
698 
699=over 8
700 
701=item B<--version, -v>
702 
703Vypise verzi programu
704 
705=item B<--help, -h>
706 
707Vypise tuto napovedu
708 
709=item B<--report, -r>
710 
711Vypise prehled pravidel a tabulek
712 
713=item B<--nogetcfg, -n>
714 
715Zajisti ze se nedude znovu nacitat konfigurace a pouzije se predchozi z cache
716 
717=item B<--info1, -1>
718 
719Nastavi upovidanost na 1 (malo).
720 
721=item B<--info2, -2>
722 
723Nastavi upovidanost na 2 (hodne).
724 
725=back
726 
727=head1 DESCRIPTIOn
728 
729B<Sedlo> stahne hlavni seznam a spolecne s lokalnim seznamem pravidel nastavi default routy
730pro jednotlive podsite tak, jak jsou zaznamenany v konfiguracnim souboru. Jeho hlavni vyuziti
731je v prostredi, kde je vice ISP a pouziva se dynamicke routovani (OSPF).
732 
733(This documentation is crap ;-) If you feel adventurous send patches :-)
734 
735=cut

Powered by WebSVN 2.2.1