Advertisement
savsanta

ipcalc

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