source: genesis/nodes/ipcalc.pl@ 4975

Last change on this file since 4975 was 4317, checked in by dirkx, 20 years ago

Fixing line types

  • Property svn:eol-style set to native
  • Property svn:executable set to *
File size: 27.6 KB
Line 
1#!/usr/bin/perl -w
2
3
4# IPv4 Calculator
5# Copyright (C) Krischan Jodies 2000 - 2004
6# krischan()jodies.de, http://jodies.de/ipcalc
7#
8# This program is free software; you can redistribute it and/or modify
9# it under the terms of the GNU General Public License as published by
10# the Free Software Foundation; either version 2 of the License, or
11# (at your option) any later version.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU General Public License for more details.
17#
18# You should have received a copy of the GNU General Public License
19# along with this program; if not, write to the Free Software
20# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21
22use strict;
23
24my $version = '0.38';
25
26my @class = qw (0 8 16 24 4 5 5);
27
28my $quads_color = "\033[34m"; # dotted quads, blue
29my $norml_color = "\033[m"; # normal, black
30#my $binry_color = "\033[1m\033[46m\033[37m"; # binary, yellow
31my $binry_color = "\033[37m"; # binary, yellow
32my $mask_color = "\033[31m"; # netmask, red
33my $class_color = "\033[35m"; # classbits, magenta
34my $subnt_color = "\033[0m\033[32m"; # subnet bits, green
35my $error_color = "\033[31m";
36my $sfont = "";
37my $break ="\n";
38
39my $color_old = "";
40my $color_actual = "";
41
42my $opt_text = 1;
43my $opt_html = 0;
44my $opt_color = 0;
45my $opt_print_bits = 1;
46my $opt_print_only_class = 0;
47my $opt_split = 0;
48my $opt_deaggregate = 0;
49my $opt_version = 0;
50my $opt_help = 0;
51my @opt_split_sizes;
52my @arguments;
53my $error = "";
54my $thirtytwobits = 4294967295; # for masking bitwise not on 64 bit arch
55
56main();
57exit;
58
59sub main
60{
61 my $address = -1;
62 my $address2 = -1;
63 my $network = -1;
64 my $mask1 = -1;
65 my $mask2 = -1;
66
67 if (! defined ($ARGV[0])) {
68 usage();
69 exit();
70 }
71
72 @ARGV = getopts();
73
74 if ($opt_help) {
75 help();
76 exit;
77 }
78
79 if ($opt_version) {
80 print "$version\n";
81 exit;
82 }
83
84#print "opt_html $opt_html\n";
85#print "opt_color $opt_color\n";
86#print "opt_print_bits $opt_print_bits\n";
87#print "opt_print_only_class $opt_print_only_class\n";
88#print "opt_deaggregate $opt_deaggregate\n";
89
90 if (! $opt_color) {
91 $quads_color = '';
92 $norml_color = '';
93 $binry_color = '';
94 $mask_color = '';
95 $class_color = '';
96 $subnt_color = '';
97 $sfont = '';
98 }
99
100 if ($opt_html) {
101 $quads_color = '<font color="#0000ff">' ;
102 $norml_color = '<font color="#000000">';
103 $binry_color = '<font color="#909090">';
104 $mask_color = '<font color="#ff0000">';
105 $class_color = '<font color="#009900">';
106 $subnt_color = '<font color="#663366">';
107 $sfont = '</font>';
108 $break = "<br>";
109 #$private = "(<a href=\"http://www.ietf.org/rfc/rfc1918.txt\">Private Internet</a>)";
110# print "<pre>\n";
111print << 'EOF';
112<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
113<html>
114<head>
115<meta HTTP-EQUIV="content-type" CONTENT="text/html; charset=UTF-8">
116<title>Bla</title>
117</head>
118<body>
119EOF
120 print "<!-- Version $version -->\n";
121 }
122
123# foreach (@arguments) {
124# print "arguments: $_\n";
125# }
126
127# foreach (@ARGV) {
128# print "ARGV: $_\n";
129# }
130
131 # get base address
132 if (defined $ARGV[0]) {
133 $address = argton($ARGV[0],0);
134 }
135 if ($address == -1) {
136 $error .= "INVALID ADDRESS: $ARGV[0]\n";
137 $address = argton("192.168.1.1");
138 }
139
140 # if deaggregate get last address
141 if ($opt_deaggregate) {
142 if (defined $ARGV[1]) {
143 $address2 = argton($ARGV[1],0);
144 }
145 if ($address2 == -1) {
146 $error .= "INVALID ADDRESS2: $ARGV[1]\n";
147 $address2 = argton("192.168.1.1");
148 }
149 }
150
151 if ($opt_deaggregate) {
152 if ($error) {
153 print "$error\n";
154 }
155 print "deaggregate ".ntoa($address) . " - " . ntoa($address2)."\n";
156 deaggregate($address,$address2);
157 exit;
158 }
159
160 # get netmasks
161 if (defined $ARGV[1]) {
162 $mask1 = argton($ARGV[1],1);
163 } else {
164 #get natural mask ***
165 $mask1 = argton(24);
166 }
167 if ($mask1 == -1) {
168 $error .= "INVALID MASK1: $ARGV[1]\n";
169 $mask1 = argton(24);
170 }
171
172 if (defined $ARGV[2]) {
173 $mask2 = argton($ARGV[2],1);
174 } else {
175 $mask2 = $mask1;
176 }
177 if ($mask2 == -1) {
178 $error .= "INVALID MASK2: $ARGV[2]\n";
179 $mask2 = argton(24);
180 }
181
182 if ($error) {
183 if ($opt_color) {
184 print set_color($error_color);
185 }
186 print "$error\n";
187 }
188
189# print "Address: ".ntoa($address)."\n";
190# print "mask1: ($mask1) ".ntoa($mask1)."\n";
191# print "mask2: ($mask2) ".ntoa($mask2)."\n";
192
193 html('<table border="0" cellspacing="0" cellpadding="0">');
194 html("\n");
195 printline ("Address", $address ,$mask1,$mask1,1);
196 printline ("Netmask", $mask1 ,$mask1,$mask1);
197 printline ("Wildcard", ~$mask1 ,$mask1,$mask1);
198 html("<tr>\n");
199 html('<td colspan="3"><tt>');
200 print "=>";
201 html("</tt></td>\n");
202 html("</tr>\n");
203 print "\n";
204
205 $network = $address & $mask1;
206 printnet($network,$mask1,$mask1);
207 html("</table>\n");
208 if ($opt_deaggregate) {
209 deaggregate();
210 }
211 if ($opt_split) {
212 split_network($network,$mask1,$mask2,@opt_split_sizes);
213 exit;
214 }
215 if ($mask1 < $mask2) {
216 print "Subnets after transition from /" . ntobitcountmask($mask1);
217 print " to /". ntobitcountmask($mask2) . "\n\n";
218 subnets($network,$mask1,$mask2);
219 }
220 if ($mask1 > $mask2) {
221 print "Supernet\n\n";
222 supernet($network,$mask1,$mask2);
223 }
224 if ($opt_html) {
225 print << 'EOF';
226 <p>
227 <a href="http://validator.w3.org/check/referer"><img border="0"
228 src="http://www.w3.org/Icons/valid-html401"
229 alt="Valid HTML 4.01!" height="31" width="88"></a>
230 </p>
231EOF
232 }
233 exit;
234
235}
236
237# ---------------------------------------------------------------------
238
239sub end {
240 if ($opt_html) {
241# print "\n</pre>\n";
242print "<html>\n";
243 }
244 exit;
245}
246
247sub supernet {
248 my ($network,$mask1,$mask2) = @_;
249 $network = $network & $mask2;
250 printline ("Netmask", $mask2 ,$mask2,$mask1,1);
251 printline ("Wildcard", ~$mask2 ,$mask2,$mask1);
252 print "\n";
253 printnet($network,$mask2,$mask1);
254}
255
256sub subnets
257{
258 my ($network,$mask1,$mask2) = @_;
259 my $subnet=0;
260 my $bitcountmask1 = ntobitcountmask($mask1);
261 my $bitcountmask2 = ntobitcountmask($mask2);
262
263 html('<table border="0" cellspacing="0" cellpadding="0">');
264 html("\n");
265 printline ("Netmask", $mask2 ,$mask2,$mask1,1);
266 printline ("Wildcard", ~$mask2 ,$mask2,$mask1);
267 html("</table>\n");
268
269 print "\n";
270
271 for ($subnet=0; $subnet < (1 << ($bitcountmask2-$bitcountmask1)); $subnet++)
272 {
273 my $net = $network | ($subnet << (32-$bitcountmask2));
274 print " ". ($subnet+1) .".\n";
275 html('<table border="0" cellspacing="0" cellpadding="0">');
276 html("\n");
277 printnet($net,$mask2,$mask1);
278 html("</table>\n");
279 if ($subnet >= 1000) {
280 print "... stopped at 1000 subnets ...$break";
281 last;
282 }
283 }
284 $subnet = (1 << ($bitcountmask2-$bitcountmask1));
285 my $hostn = ($network | ((~$mask2) & $thirtytwobits)) - $network - 1;
286 if ($hostn > -1) {
287 print "\nSubnets: $quads_color$subnet";
288 html('</font>');
289 print "$norml_color$break";
290 html('</font>');
291 }
292 if ($hostn < 1 ) {
293 $hostn = 1;
294 }
295 print "Hosts: $quads_color" . ($hostn * $subnet);
296 html('</font>');
297 print "$norml_color$break";
298 html('</font>');
299}
300
301sub getclass {
302 my $network = shift;
303 my $class = 1;
304# print "n $network bit ". (1 << (32-$class)) . " & " .
305 while (($network & (1 << (32-$class))) == (1 << (32-$class)) ) {
306 $class++;
307 if ($class > 5) {
308 return "invalid";
309 }
310 }
311 return chr($class+64);
312}
313
314sub printnet {
315 my ($network,$mask1,$mask2) = @_;
316 my $hmin;
317 my $hmax;
318 my $hostn;
319 my $mask;
320
321 my $broadcast = $network | ((~$mask1) & $thirtytwobits);
322
323 $hmin = $network + 1;
324 $hmax = $broadcast - 1;
325 $hostn = $hmax - $hmin + 1;
326 $mask = ntobitcountmask($mask1);
327 if ($mask == 31) {
328 $hmax = $broadcast;
329 $hmin = $network;
330 $hostn = 2;
331 }
332 if ($mask == 32) {
333 $hostn = 1;
334 }
335
336
337 #if ($hmax < $hmin) {
338 # $hmax = $hmin;
339 # $hostn = 1;
340 #}
341
342
343 #private...
344 #$p = 0;
345 #for ($i=0; $i<3; $i++) {
346 # if ( (&bintoint($hmax) <= $privmax[$i]) &&
347 # (&bintoint($hmin) >= $privmin[$i]) ) {
348 # $p = $i +1;
349 # last;
350 # }
351 #}
352
353 #if ($p) {
354 # $p = $private;
355 #} else {
356 # $p = '';
357 #}
358
359
360 if ($mask == 32) {
361 printline ("Hostroute", $network ,$mask1,$mask2,1);
362 } else {
363 printline ("Network", $network ,$mask1,$mask2,1);
364 printline ("HostMin", $hmin ,$mask1,$mask2);
365 printline ("HostMax", $hmax ,$mask1,$mask2);
366 printline ("Broadcast", $broadcast,$mask1,$mask2) if $mask < 31;
367 }
368# html("</table>\n");
369
370# html('<table border="0" cellspacing="0" cellpadding="0">');
371# html("\n");
372 html("<tr>\n");
373 html('<td valign="top"><tt>'); #label
374 print set_color($norml_color);
375 print "Hosts/Net: " ;
376 html("</font></tt></td>\n");
377 html('<td valign="top"><tt>');
378# printf $norml_color . "Hosts/Net: </tt>$quads_color%-22s",$hostn;
379# html("<td><tt>");
380 print set_color($quads_color);
381 printf "%-22s",$hostn;
382# printf "%-22s", (ntoa($address).$additional_info);
383 html("</font></tt></td>\n");
384 html("<td>"); #label
385 if ($opt_html) {
386#warn "HTML\n";
387 print wrap_html(30,get_description($network,$mask1));
388 } else {
389 print get_description($network,$mask1);
390 }
391 html("</font></td>\n");
392 html("</tr>\n");
393 html("\n");
394 text("\n");
395 text("\n");
396
397 ##printf "Class %s, ",getclass($network);
398 ##printf "%s",netblock($network,$mask1);
399# my ($label,$address,$mask1,$mask2,$classbitcolor_on,$is_netmask) = @_;
400# print $sfont . $norml_color;
401
402# print "$break\n";
403# exit;
404 return $hostn;
405}
406
407sub get_description
408{
409 my $network = shift;
410 my $mask = shift;
411 my @description;
412 my $field;
413 # class
414 if ($opt_color || $opt_html) {
415 $field = set_color($class_color) . "Class " . getclass($network);
416 if ($opt_html) {
417 $field .= '</font>';
418 }
419 $field .= set_color($norml_color);
420 push @description, $field
421# push @description, set_color($class_color) . "Class " .
422# getclass($network) . set_color($norml_color);
423 } else {
424 push @description, "Class " . getclass($network);
425 }
426 # netblock
427 my ($netblock_txt,$netblock_url) = split ",",netblock($network,$mask);
428 if (defined $netblock_txt) {
429 if ($opt_html) {
430 $netblock_txt = '<a href="' . $netblock_url . '">' .
431 $netblock_txt . '</a>';
432 }
433#warn "DESC: '$netblock_txt'";
434 push @description,$netblock_txt;
435 }
436 # /31
437 if (ntobitcountmask($mask) == 31) {
438 if ($opt_html) {
439 push @description,"<a href=\"http://www.ietf.org/rfc/rfc3021.txt\">PtP Link</a>";
440 } else {
441 push @description,"PtP Link RFC 3021";
442 }
443 }
444#$rfc3021 = "<a href=\"http://www.ietf.org/rfc/rfc3021.txt\">Point-to-Point
445#Link</a>";
446 return join ", ",@description;
447}
448
449sub printline
450{
451 my ($label,$address,$mask1,$mask2,$html_fillup) = @_;
452 $mask1 = ntobitcountmask($mask1);
453 $mask2 = ntobitcountmask($mask2);
454 my $line = "";
455 my $bit;
456 my $newbitcolor_on = 0;
457 my $toggle_newbitcolor = 0;
458 my $bit_color;
459 my $additional_info = '';
460 my $classbitcolor_on;
461 my $second_field;
462 if ($label eq 'Netmask') {
463 $additional_info = " = $mask1";
464 }
465 if ($label eq 'Network') {
466 $classbitcolor_on = 1;
467 $additional_info = "/$mask1";
468 }
469 if ($label eq 'Hostroute' && $mask1 == 32) {
470 $classbitcolor_on = 1;
471 }
472
473 html("<tr>\n");
474 html("<td><tt>");
475 #label
476 print set_color($norml_color);
477 if ($opt_html && $html_fillup) {
478 print "$label:";
479 print "&nbsp;" x (11 - length($label));
480 } else {
481 printf "%-11s","$label:";
482 }
483 html("</font></tt></td>\n");
484 #address
485 html("<td><tt>");
486 print set_color($quads_color);
487 #printf "%s-22s",(ntoa($address).$additional_info);
488
489 #printf "%s%-11s$sfont%s",set_color($norml_color),"$label:",set_color($quads_color);
490 $second_field = ntoa($address).$additional_info;
491 if ($opt_html && $html_fillup) {
492 print $second_field;
493 print "&nbsp;" x (21 - length($second_field));
494 } else {
495 printf "%-21s", (ntoa($address).$additional_info);
496 }
497 html("</font></tt></td>\n");
498
499
500 if ($opt_print_bits)
501 {
502 html("<td><tt>");
503 $bit_color = set_color($binry_color);
504 if ($label eq 'Netmask') {
505 $bit_color = set_color($mask_color);
506 }
507
508 if ($classbitcolor_on) {
509 $line .= set_color($class_color);
510 } else {
511 $line .= set_color($bit_color);
512 }
513 for (my $i=1;$i<33;$i++)
514 {
515 $bit = 0;
516 if (($address & (1 << 32-$i)) == (1 << 32-$i)) {
517 $bit = 1;
518 }
519 $line .= $bit;
520 if ($classbitcolor_on && $bit == 0) {
521 $classbitcolor_on = 0;
522 if ($newbitcolor_on) {
523 $line .= set_color($subnt_color);
524 } else {
525 $line .= set_color($bit_color);
526 }
527 }
528# print "$mask1 $i % 8 == " . (($i) % 8) . "\n";
529 if ($i % 8 == 0 && $i < 32) {
530 $line .= set_color($norml_color) . '.';
531 $line .= set_color('oldcolor');
532 }
533 if ($i == $mask1) {
534 $line .= " ";
535 }
536 if (($i == $mask1 || $i == $mask2) && $mask1 != $mask2) {
537 if ($newbitcolor_on) {
538 $newbitcolor_on = 0;
539 $line .= set_color($bit_color) if ! $classbitcolor_on;
540 } else {
541 $newbitcolor_on = 1;
542 $line .= set_color($subnt_color) if ! $classbitcolor_on;
543 }
544 }
545 }
546 $line .= set_color($norml_color);
547 print "$line";
548 html("</tt></font></td>\n");
549 }
550 html("</tr>\n");
551html("\n");
552text("\n");
553 #print $sfont . $break;
554}
555
556sub text
557{
558 my $str = shift;
559 if ($opt_text) {
560 print "$str";
561 }
562}
563
564sub html
565{
566 my $str = shift;
567 if ($opt_html) {
568 print "$str";
569 }
570}
571
572sub set_color
573{
574 my $new_color = shift;
575 my $return;
576 if ($new_color eq $color_old) {
577# print "SETCOLOR saved one dupe\n";
578 # $return = 'x';
579 $return = '';
580 }
581 if ($new_color eq 'oldcolor') {
582 $new_color = $color_old;
583 }
584 $color_old = $color_actual;
585 #$return .= "$color_actual" . "old";
586 $color_actual = $new_color;
587 #return $new_color;
588 $return .= $new_color;
589 return $return;
590}
591
592sub split_network
593{
594 my $network = shift;
595 my $mask1 = shift;
596 my $mask2 = shift;
597 my @sizes = @_;
598
599 my $first_address = $network;
600 my $broadcast = $network | ((~$mask1) & $thirtytwobits);
601 my @network;
602 my $i=0;
603 my @net;
604 my @mask;
605 my $needed_addresses = 0;
606 my $needed_size;
607 foreach (@sizes) {
608 $needed_size = round2powerof2($_+2);
609# printf "%3d -> %3d -> %3d\n",$_,$_+2,$needed_size;
610 push @network , $needed_size .":".$i++;
611 $needed_addresses += $needed_size;
612 }
613 @network = sort { ($b =~ /(.+):/)[0] <=> ($a =~ /(.+):/)[0] } @network;
614 foreach (@network) {
615 my ($size,$nr) = split ":",$_;
616 $net[$nr]= $network;
617 $mask[$nr]= (32-log($size)/log(2));
618 $network += $size;
619 }
620 $i = -1;
621 while ($i++ < $#sizes) {
622 printf "%d. Requested size: %d hosts\n", $i+1,$sizes[$i];
623 ###$mask = $mask[$i];
624 #$mark_newbits = 1;
625 ###print_netmask(bitcountmasktobin($mask[$i]),$mask);
626 printline("Netmask",bitcountmaskton($mask[$i]),bitcountmaskton($mask[$i]),$mask2);
627 printnet($net[$i],bitcountmaskton($mask[$i]),$mask2);
628 }
629
630 my $used_mask = 32-log(round2powerof2($needed_addresses))/log(2);
631 if ($used_mask < ntobitcountmask($mask1)) {
632 print "Network is too small\n";
633 }
634 print "Needed size: ". $needed_addresses . " addresses.\n";
635 print "Used network: ". ntoa($first_address) ."/$used_mask\n";
636 print "Unused:\n";
637 deaggregate($network,$broadcast);
638
639}
640
641sub round2powerof2 {
642 my $i=0;
643 while ($_[0] > ( 1 << $i)) {
644 $i++;
645 }
646 return 1 << $i;
647}
648
649# Deaggregate address range
650# expects: range: (dotted quads)start (dotted quads)end
651
652sub deaggregate
653{
654 my $start = shift;
655 my $end = shift;
656 my $base = $start;
657 my $step;
658 while ($base <= $end)
659 {
660 $step = 0;
661 while (($base | (1 << $step)) != $base) {
662 if (($base | (((~0) & $thirtytwobits) >> (31-$step))) > $end) {
663 last;
664 }
665 $step++;
666 }
667 print ntoa($base)."/" .(32-$step);
668 print "\n";
669 $base += 1 << $step;
670 }
671}
672
673sub getopts
674 # expects nothing
675 # returns @ARGV without options
676 # sets global opt variables
677
678 # -h --html
679 # -h without further opts -> help
680 # (workaround: can't change meaning of -h since this would
681 # break old cgi_wrapper scripts)
682 # --help
683 # -n --nocolor
684 # -v --version
685 # -c --class print natural class
686 # -s --split
687 # -b --nobinary
688 # -d --deaggregate
689{
690 my @arguments;
691 my $arg;
692 my $prefix;
693 my $nr_opts = 0;
694 my @tmp;
695
696 # opt_color defaults to 1 when connected to a terminal
697 if (-t STDOUT) {
698 $opt_color = 1;
699 }
700
701 while (has_opts()) {
702 $arg = shift @ARGV;
703 if ($arg =~ /^--(.+)/) {
704 $nr_opts += read_opt('--',$1);
705 }
706 elsif ($arg =~ /^-(.+)/) {
707 $nr_opts += read_opt('-',split //,$1);
708 }
709 else {
710 push @tmp, $arg;
711 }
712 }
713
714# foreach (@arguments) {
715# print "arg: $_\n";
716# }
717
718 foreach (@ARGV) {
719 push @tmp,$_;
720 }
721 # extract base address and netmasks and ranges
722 foreach (@tmp) {
723 if (/^(.+?)\/(.+)$/) {
724 push @arguments,$1;
725 push @arguments,$2;
726 }
727 elsif (/^(.+)\/$/) {
728 push @arguments,$1;
729 }
730 elsif (/^(.+)\-(.+)$/) {
731 push @arguments,$1;
732 push @arguments,$2;
733 $opt_deaggregate = 1;
734 }
735 elsif (/^\-$/) {
736 $opt_deaggregate = 1;
737 }
738 else {
739 push @arguments, $_;
740 }
741 }
742 if ($#arguments == 2 && $arguments[1] eq '-') {
743 @arguments = ($arguments[0],$arguments[2]);
744 $opt_deaggregate = 1;
745 }
746
747
748 # workaround for -h
749 if ($opt_html && $nr_opts == 1 && $#arguments == -1) {
750 $opt_help = 1;
751 }
752
753
754 if ($error) {
755 print "$error";
756 exit;
757 }
758 return @arguments;
759
760 sub read_opt {
761 my $prefix = shift;
762 my $opts_read = 0;
763 foreach my $opt (@_)
764 {
765 ++$opts_read;
766 if ($opt eq 'h' || $opt eq 'html') {
767 $opt_html = 1;
768 $opt_text = 0;
769 }
770 elsif ($opt eq 'help') {
771 $opt_help = 1;
772 }
773 elsif ($opt eq 'n' || $opt eq 'nocolor') {
774 $opt_color = 0;
775 }
776 elsif ($opt eq 'v' || $opt eq 'version') {
777 $opt_version = 1;
778 }
779 elsif ($opt eq 'b' || $opt eq 'nobinary') {
780 $opt_print_bits = 0;
781 }
782 elsif ($opt eq 'c' || $opt eq 'class') {
783 $opt_print_only_class = 1;
784 }
785 elsif ($opt eq 'r' || $opt eq 'range') {
786 $opt_deaggregate = 1;
787 }
788 elsif ($opt eq 's' || $opt eq 'split') {
789 $opt_split = 1;
790 while (defined $ARGV[0] && $ARGV[0] =~ /^\d+$/) {
791 push @opt_split_sizes, shift @ARGV;
792 }
793 if ($#opt_split_sizes < 0) {
794 $error .= "Argument for ". $prefix . $opt . " is missing or invalid \n";
795 }
796 }
797 else {
798 $error .= "Unknown option: " . $prefix . $opt . "\n";
799 --$opts_read;
800 }
801 }
802 return $opts_read;
803 }
804
805 sub has_opts {
806 foreach (@ARGV) {
807 return 1 if /^-/;
808 }
809 return 0;
810 }
811}
812
813
814# expects int width
815# string
816# returns wrapped string
817sub wrap_html
818{
819 my $width = shift;
820 my $str = shift;
821#warn "WRAP: '$str'\n";
822 my @str = split //,$str;
823 my $result;
824 my $current_pos = 0;
825 my $start = 0;
826 my $last_pos = 0;
827 my $line;
828 while ($current_pos < $#str)
829 {
830#warn "$current_pos\n";
831#warn "$current_pos: $str[$current_pos]\n";
832 # find next blank
833 while ($current_pos < $#str && $str[$current_pos] ne ' ') {
834 # ignore tags
835 if ($str[$current_pos] eq '<') {
836 $current_pos++;
837 while ($str[$current_pos] ne '>') {
838 $current_pos++;
839 }
840 }
841 $current_pos++;
842 }
843 # fits in one line?...
844 $line = substr($str,$start,$current_pos-$start);
845 $line =~ s/<.+?>//g;
846 if ( length($line) <= $width) {
847 # ... yes. keep position in mind and try next
848 $last_pos = $current_pos;
849 $current_pos++;
850 next;
851 } else {
852 # ...no. wrap at last position (if there was one,
853 # otherwise wrap here)
854 if ($last_pos ne $start) {
855 $current_pos = $last_pos;
856 }
857 $line = substr($str,$start,$current_pos-$start);
858 $current_pos++;
859 $start = $current_pos;
860 $last_pos = $start;
861 # no output if end of string is reached because
862 # rest of string is treated after this block
863 if ($current_pos < $#str) {
864#warn "RESULT+ '$line'\n";
865 $result .= "$line<br>";
866 }
867
868 }
869 }
870 $line = substr($str,$start,$current_pos-$start);
871 $result .= "$line";
872#warn "'return RESULT $result'\n";
873 return $result;
874}
875
876
877# gets network address as dq
878# returns string description,string url
879sub netblock
880{
881 my ($mynetwork_start,$mymask) = @_;
882 my $mynetwork_end = $mynetwork_start | ((~$mymask) & $thirtytwobits);
883 my %netblocks = ( "192.168.0.0/16" => "Private Internet,http://www.ietf.org/rfc/rfc1918.txt",
884 "172.16.0.0/12" => "Private Internet,http://www.ietf.org/rfc/rfc1918.txt",
885 "10.0.0.0/8" => "Private Internet,http://www.ietf.org/rfc/rfc1918.txt",
886 "169.254.0.0/16" => "APIPA,http://www.ietf.org/rfc/rfc3330.txt",
887 "127.0.0.0/8" => "Loopback,http://www.ietf.org/rfc/rfc1700.txt",
888 "224.0.0.0/4" => "Multicast,http://www.ietf.org/rfc/rfc3171.txt"
889 );
890 my $match = 0;
891 #
892 foreach (keys %netblocks) {
893 my ($network,$mask) = split "/",$_;
894 my $start = argton($network);
895 my $end = $start + (1 << (32-$mask)) -1;
896 # mynetwork starts within block
897 if ($mynetwork_start >= $start && $mynetwork_start <= $end) {
898 $match++;
899 }
900 # mynetwork ends within block
901 if ($mynetwork_end >= $start && $mynetwork_end <= $end) {
902 $match++;
903 }
904 # block is part of mynetwork
905 if ($start > $mynetwork_start && $end < $mynetwork_end) {
906 $match = 1;
907 }
908 if ($match == 1) {
909 return "In Part ".$netblocks{$_};
910 }
911 if ($match == 2) {
912 return $netblocks{$_};
913 }
914 }
915 return "";
916}
917
918# ------- converter ---------------------------------------------
919
920sub bitcountmaskton
921{
922 my $bitcountmask = shift;
923 my $n;
924 for (my $i=0;$i<$bitcountmask;$i++) {
925 $n |= 1 << (31-$i);
926 }
927 return $n;
928}
929
930sub argton
931# expects 1. an address as dotted decimals, bit-count-mask, or hex
932# 2. netmask flag. if set -> check netmask and negate wildcard
933# masks
934# returns integer or -1 if invalid
935{
936 my $arg = shift;
937 my $netmask_flag = shift;
938
939 my $i = 24;
940 my $n = 0;
941
942 # dotted decimals
943 if ($arg =~ /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/) {
944 my @decimals = ($1,$2,$3,$4);
945 foreach (@decimals) {
946 if ($_ > 255 || $_ < 0) {
947 return -1;
948 }
949 $n += $_ << $i;
950 $i -= 8;
951 }
952 if ($netmask_flag) {
953 return validate_netmask($n);
954 }
955 return $n;
956 }
957
958 # bit-count-mask (24 or /24)
959 $arg =~ s/^\/(\d+)$/$1/;
960 if ($arg =~ /^\d{1,2}$/) {
961 if ($arg < 1 || $arg > 32) {
962 return -1;
963 }
964 for ($i=0;$i<$arg;$i++) {
965 $n |= 1 << (31-$i);
966 }
967 return $n;
968 }
969
970 # hex
971 if ($arg =~ /^[0-9A-Fa-f]{8}$/ ||
972 $arg =~ /^0x[0-9A-Fa-f]{8}$/ ) {
973 if ($netmask_flag) {
974 return validate_netmask(hex($arg));
975 }
976 return hex($arg);
977 }
978
979 # invalid
980 return -1;
981
982 sub validate_netmask
983 {
984 my $mask = shift;
985 my $saw_zero = 0;
986 # negate wildcard
987 if (($mask & (1 << 31)) == 0) {
988 print "WILDCARD\n";
989 $mask = ~$mask;
990 }
991 # find ones following zeros
992 for (my $i=0;$i<32;$i++) {
993 if (($mask & (1 << (31-$i))) == 0) {
994 $saw_zero = 1;
995 } else {
996 if ($saw_zero) {
997 print "INVALID NETMASK\n";
998 return -1;
999 }
1000 }
1001 }
1002 return $mask;
1003 }
1004}
1005
1006sub ntoa
1007{
1008 return join ".",unpack("CCCC",pack("N",shift));
1009}
1010
1011sub ntobitcountmask
1012# expects integer
1013# returns bitcountmask
1014{
1015 my $mask = shift;
1016 my $bitcountmask = 0;
1017 # find first zero
1018 while ( ($mask & (1 << (31-$bitcountmask))) != 0 ) {
1019 if ($bitcountmask > 31) {
1020 last;
1021 }
1022 $bitcountmask++;
1023 }
1024 return $bitcountmask;
1025}
1026
1027# ------- documentation ----------------------------------------
1028
1029sub usage {
1030 print << "EOF";
1031Usage: ipcalc [options] <ADDRESS>[[/]<NETMASK>] [NETMASK]
1032
1033ipcalc takes an IP address and netmask and calculates the resulting broadcast,
1034network, Cisco wildcard mask, and host range. By giving a second netmask, you
1035can design sub- and supernetworks. It is also intended to be a teaching tool
1036and presents the results as easy-to-understand binary values.
1037
1038 -n --nocolor Don't display ANSI color codes.
1039 -b --nobinary Suppress the bitwise output.
1040 -c --class Just print bit-count-mask of given address.
1041 -h --html Display results as HTML (not finished in this version).
1042 -v --version Print Version.
1043 -s --split n1 n2 n3
1044 Split into networks of size n1, n2, n3.
1045 -r --range Deaggregate address range.
1046 --help Longer help text.
1047
1048Examples:
1049
1050ipcalc 192.168.0.1/24
1051ipcalc 192.168.0.1/255.255.128.0
1052ipcalc 192.168.0.1 255.255.128.0 255.255.192.0
1053ipcalc 192.168.0.1 0.0.63.255
1054
1055
1056ipcalc <ADDRESS1> - <ADDRESS2> deaggregate address range
1057
1058ipcalc <ADDRESS>/<NETMASK> --s a b c
1059 split network to subnets
1060 where a b c fits in.
1061
1062! New HTML support not yet finished.
1063
1064ipcalc $version
1065EOF
1066}
1067
1068sub help {
1069 print << "EOF";
1070
1071IP Calculator $version
1072
1073Enter your netmask(s) in CIDR notation (/25) or dotted decimals (255.255.255.0).
1074Inverse netmask are recognized. If you mmit the netmask, ipcalc uses the default
1075netmask for the class of your network.
1076
1077Look at the space between the bits of the addresses: The bits before it are
1078the network part of the address, the bits after it are the host part. You can
1079see two simple facts: In a network address all host bits are zero, in a
1080broadcast address they are all set.
1081
1082The class of your network is determined by its first bits.
1083
1084If your network is a private internet according to RFC 1918 this is remarked.
1085When displaying subnets the new bits in the network part of the netmask are
1086marked in a different color.
1087
1088The wildcard is the inverse netmask as used for access control lists in Cisco
1089routers. You can also enter netmasks in wildcard notation.
1090
1091Do you want to split your network into subnets? Enter the address and netmask
1092of your original network and play with the second netmask until the result
1093matches your needs.
1094
1095
1096Questions? Comments? Drop me a mail...
1097krischan at jodies.de
1098http://jodies.de/ipcalc
1099
1100Thanks for your nice ideas and help to make this tool more useful:
1101
1102Hermann J. Beckers hj.beckers()kreis-steinfurt.de
1103Kevin Ivory ki()sernet.de
1104Frank Quotschalla gutschy()netzwerkinfo.de
1105Sven Anderson sven()anderson.de
1106Scott Davis sdavis()austin-texas.net
1107Denis A. Hainsworth denis()ans.net
1108Steve Kent stevek()onshore.com
1109Igor Zozulya izozulya()yahoo.com
1110Lutz Pressler lp()sernet.de
1111
1112EOF
1113usage();
1114exit;
1115}
Note: See TracBrowser for help on using the repository browser.