# net2net.pl
#
# Copyright (c) 1993, 1994, 1995, 1996, 1997, 1998, 1999 by RIPE NCC
#
# All Rights Reserved
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose and without fee is hereby granted,
# provided that the above copyright notice appear in all copies and that
# both that copyright notice and this permission notice appear in
# supporting documentation, and that the name of the author not be
# used in advertising or publicity pertaining to distribution of the
# software without specific, written prior permission.
#
# THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
# AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
# AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
# $Id: net2net.pl,v 2.9 1999/10/06 16:41:14 marek Exp $
#
#	$RCSfile: net2net.pl,v $
#	$Revision: 2.9 $
#	$Author: marek $
#	$Date: 1999/10/06 16:41:14 $
#

require "defines.pl";
require "syslog.pl";
require "misc.pl";
require "dpr.pl";

# 
# The quad2int & int2quad routines are completely rewritten
# since there were problems with perl5.
#
# It seems that the shift operations converted the unsigned int
# to a signed int, that was not very convenient for IP numbers ;-)
# So I rewrote everything to use normal /*+- arithmic :-(
# Speed will not be a problem since we are dealing with an interpreted
# language...
#
# David K. 950908

#
# replaced complicated regexp to something more understandable.
# int2quad and quad2int can be done simply with pack/unpack
#
# Chris

#
# quad quad2int(int, option)
#
# option=0   - don't log errors
# option=1   - log errors

sub quad2int {
  my($string, $log) = @_;

  my(@bytes) = split(/\./, $string);

  # check it looks like an ipv4 address.
  # there should be four parts, each being all digits with a value less
  # than 256
  #
  if (@bytes == 4 && scalar(grep {/^\d+$/ && $_<256} @bytes) == 4) {
    return unpack(N,pack(C4,@bytes));
  }

  &syslog("ERRLOG", "quad2int: illegal ip number [$string]") if ($log);
       	   
  return(-1);
}

#
# int int2quad(quad)
#
# given (long) integer returns a string representing it as an IP address style
# dotted quad.

sub int2quad {
  use strict;

  my($int)=@_;

  # nasty hack to make perl treat the integer as a numeric rather than a 
  # string. (see also iprightzeromask in misc.pl)
  #
  # just do a simple arithmetic operation on it - decrement then increment
  # unless it's zero.

  if ($int == 0) {
    $int++; $int--;
  } else {
    $int--; $int++;
  }

  return join('.', unpack('C4',pack('N',$int)));
}

# convert range in form 'a.b.c.d - a.b.255.255'
# to list of prefixes in form 'n/p' where n is 32 bit int IP address
# p is prefix.

sub range2prefixes {
   
    local(@prefixes)=();
    
    local($ip1,$ip2)=split(/ \- /, $_[0], 2);
    $ip1=&quad2int($ip1,0);
    $ip2=&quad2int($ip2,0);
   
   
    do {
   
      # get smallest useful prefix length based on size of range
      local($bits) = 32-int(log($ip2-$ip1+1)*$ONEDIVLOG2+$TINYLOGVALUE);

      {
	use integer;       # for the following bitwise &

	# find smallest prefix that masks all bits of IP address
	until ($ip1 == ($ip1 & $masks[$bits])) {
	  $bits++;
	}
      }
   
      push(@prefixes, $ip1."/".$bits);
       
      # add on block we've just found
      $ip1 += 2**(32-$bits); 

      # stop when we've reached the top end of range
    } until (($ip1-1) == $ip2);

   
    return @prefixes;
}


sub completeip {
    local($range)=@_;
    
    # clean up the range
    
    $range=~ s/\.*$//;
    
    # complete if needed
    
    local($nrofdots)= $range=~ s/\./\./g;
    
    &dpr( "range $range $nrofdots\n");
    
    $range=join("", $range, (".0") x (3 - $nrofdots)) if ($nrofdots<3);
    
    # remove leading zeros and check if we have a valid ip
    
    &dpr( $range, "\n");
    
    local($int);
    
    if (($int=&quad2int($range,0))>=0) {
       
       &dpr( $int, "\n");
       
       return (&int2quad($int),$int);
       
    }
    
    # nothing valid found
    
    return ("",-1);
    
}


sub normalizerange {
    local($range,$type)=@_;
    
    local($ip1);
    
    &dpr("normalizerange - type: $type range: $range\n") if ($range=~ /\-/);
    
    if ($range=~ /^(\d+)(\.[\.\d]*)? *([\/\-]) *([\d\.]+)*$/) {
       
      # Reject 0/8, 10/8, 127/8 & 224/4 networks

       return ("", $O_INVALIDPREFIX) if (($1=~ /^0*1?0|127$/) || ($1>223)); 
       
       if ($3 eq "\/") {
       
          local($len)=$4;
       
          ($range,$ip1)=&completeip($1.$2);
       
          return ("", $O_INVALIDIP) if ($ip1<0);
       
          &dpr("normalizerange - mask: ", ~$masks[$len], " prefix: $range len: $len\n");
       
          if (($len=~ /^0*([12]?[\d]|3[012])$/) && 
              (!($ip1 & (~$masks[$len])))) {

             return ($range." - ".&int2quad($ip1+(2**(32-$len)-1)), $O_OK);
       
          }
       
          return ("", $O_INVALIDPREFIX);
          
       }
       elsif ($3 eq "\-") {
       
          local($torange)=$4;
       
          ($range,$ip1)=&completeip($1.$2);
       
          local($ip2);
       
          &dpr("normalizerange - type $type from: $range to: $torange\n");
       
          ($torange,$ip2)=&completeip($torange);
       
          &dpr("normalizerange - type $type from: $range to: $torange\n");
       
          return ("", $O_INVALIDRANGE) if ($ip2<$ip1);
          return ("", $O_INVALIDIP) if (($ip1<0) || ($ip2<0));
       
          if ($torange=~ /^(\d+)\.\d+\.\d+\.0$/ && $ip2 != $ip1) {
          
             if ($1>=192) {
                
                &dpr("normalizerange - type $type from: $range to: $torange\n");
                
                $torange=~ s/\.0$/\.255/;
                
                &dpr("normalizerange - type $type from: $range to: $torange\n");
             
             }
             elsif ($1>=128) {
          
                $torange=~ s/\.0\.0$/\.255\.255/;
              
             }
             else {
             
                $torange=~ s/\.0\.0\.0$/\.255\.255\.255/;
             
             }
          
          }
       
          return ($range." - ".$torange, $O_OK);
       
       }
    
    }
    elsif ($range=~ /^(\d+)(\.[\.\d]*)?$/) {
       
       return ("", $O_INVALIDIP) if (($1>223) || ($1=~ /^0*1?0|127$/));
       
       ($range,$ip1)=&completeip($range);
       
       return ("", $O_INVALIDIP) if ($ip1<0);
       
       &dpr("normalizerange - type $type range: $range\n");
       
       # note that the order in the comparisons is important
       # *and* the order of the if's is important !!!
       
       if (($type ne "in") || ($range!~ /\.0$/)) {
          
          return ($range." - ".$range, $O_OK);
          
       }
       elsif (($range=~ /^(\d+)(\.\d+\.\d+)\.0$/) &&
           ($1>=192)) {
          
          return ($1.$2.".0 - ".$1.$2.".255", $O_OK);
          
       }
       elsif (($range=~ /^(\d+)(\.\d+)\.0\.0$/) &&
              ($1>=128)) {
          
          return ($1.$2.".0.0 - ".$1.$2.".255.255", $O_OK);
          
          
       }
       elsif ($range=~ /^(\d+)\.0\.0\.0$/) {
             
          return ($1.".0.0.0 - ".$1.".255.255.255", $O_OK);
          
       }
       
    }
    
    # nothing useful found
    
    return ("", $O_SYNTAXERROR);
        
}


# net2net.pl 
#
# net2net ($net_rep, $flag)
#
# This is a general routine which will take various network representations 
# as input and returns the desired transformed representation.
#
# The general supported conversions are as follow:
# 
# 1) CLASSFUL NET to NET/PREFIX
#    i.e. 128.86.0.0 -> 128.86.0.0/16
#
# 2) CLASSFUL RANGE to NET/PREFIX multiples
#    i.e. 128.86.0.0 - 128.91.0.0 
#                          |
#                          +-> 128.86.0.0/15
#                          +-> 128.88.0.0/14
#
# 3) NET/PREFIX verification
#    i.e. This will tell you if this a valid combination
#         If not, it will tell you what you want to know
#
# 4) Integer represented NET / PREFIX to NET/PREFIX
#    i.e. 2153119744/15 -> 128.86.0.0/15
#
# 5) NET/PREFIX to Integer represented NET
#     i.e. 128.86.0.0/15 -> 2153119744/15
#
# 6) NET and MASK to NET/PREFIX
#     i.e. 128.86.0.0 255.254.0.0 -> 128.86.0.0/15
#
#
# Due to possible ambiguity net2net can  take a optional
# defined flag ($flag) for the conversion. If $flag is null or not
# recognised net2net will attempt to work out the desired converstion for
# you. net2net is essentially a wrapper for other and each routine can be
# used on its own if preferred.
#
# It returns a status, $STAT
#            a optional message, $text
#            the converted rep as an array, @return_rep
#
#            return($STAT, $text, @return_rep)
# 
# The individual routines are
#
# clasfn_to_netpre ($net_rep)
# clasfr_to_netpre ($net_rep)
# netpre_verify ($net_rep)
# intnetpre_to_netpre ($net_rep)
# netpre_to_intnetpre ($net_rep)
# netmask_to_netpre ($net_rep)
#
# 


$CFN2NP     = 1;		# CLASSFUL NET to NET/PREFIX
$CFR2NP	    = 2;		# CLASSFUL RANGE to NET/PREFIX multiples
$NPVRFY	    = 3;		# NET/PREFIX Verification
$INP2NP     = 4;		# Integer represented NET/PREFIX to NET/PREFIX
$NP2INP     = 5;		# NET/PREFIX to Integer represented NET/PREFIX
$NM2NP	    = 6;		# NET and MASK to NET/PREFIX
#
$MODDED     = 2;		# Used to signify something has been modified
#
#
# Not used right now
%mapping = ( $CFN2NP, "CLASSFUL NET to NET/PREFIX",
	    $CFR2NP, "CLASSFUL RANGE to NET/PREFIX multiple",
	    $NPVRFY, "NET/PREFIX verfication",
	    $INP2NP, "Integer represented NET/PREFIX to NET/PREFIX",
	    $NP2INP, "NET/PREFIX to Integer represented NET/PREFIX",
	    $NM2NP, "NET and MASK to NET/PREFIX" );
# 
sub net2net {
    local($net_rep, $flag) = @_;

    local($len);
    local($STAT) = 1;
    local($text) = "";
    local(@returnstring) = ();
    local($one_net);

    if($opt_h) {
	foreach (sort keys %mapping) {
	    print "- $_ - $mapping{$_}\n";
	}
	exit;
    }
    if($opt_v) {
	printf "\$net_rep is \"$net_rep\" - \$flag is \"$flag\"\n";
    }
    if($opt_v && $flag) {
	printf "** Converting %s\n", $mapping{$flag} ;
    }
    
    if($flag eq "") {
	return $NOK, "cant work it out without a flag", @returnstring;
    }
    if($flag == $CFN2NP) { return(&clasfn_to_netpre($net_rep)); }
    
    if($flag == $CFR2NP) { return(&clasfr_to_netpre($net_rep)); }

    if($flag == $NPVRFY)  { return(&netpre_verify($net_rep)); }

    if($flag == $INP2NP) { return(&intnetpre_to_netpre($net_rep)); }

    if($flag == $NP2INP) { return(&netpre_to_intnetpre($net_rep)); }

    if($flag == $NM2NP)  { return(&netmask_to_netpre($net_rep)); } 
    
    return $NOK, "I'm lost", @returnstring;
}
#
sub clasfn_to_netpre {
    local($net_rep) = @_;
    local($len);
    local(@return_rep) = ();
    if ($net_rep =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/) {
	if ($1 > 254 || $1 < 1 ||
	    $2 > 255 || $2 < 0 ||
	    $3 > 255 || $3 < 0 ||
	    $4 > 255 || $4 < 0 ) {
	    return $NOK,
	    "$net_rep is not a classful net representation I understand",
	    @return_rep;
	}
	if ($1 >= 224) {	# and why not !!!
	    $len = 32;
	} elsif ($1 >= 192 && $4 == 0) {
            $len = 24;
	} elsif ($1 >= 128 && ($3 == 0 && $4 == 0)) {
            $len = 16;
	} elsif ($2 == 0 && $3 == 0 && $4 == 0) {
	    $len = 8;
	} else {
	    return $NOK,
	    "$net_rep is not a classful net representation I understand",
	    @return_rep;     
	}
	local($val) = &trimnet($net_rep);
	@return_rep = (@return_rep, $val."/".$len);
	return $OK, "", @return_rep;
    } else {
	return $NOK, 
	"$net_rep is not a classful net representation I understand", 
	@return_rep;
    } 
}    

sub clasfr_to_netpre {
    local($net_rep) = @_;
    local($len);
    local(@returnstring) = ();
    local($one_net);
    
    if ($net_rep =~ /^(\d+\.\d+\.\d+\.\d+)\s+\-\s+(\d+\.\d+\.\d+\.\d+)$/) {
	local($oldnet) = $1;	
	local($begin) = &quad2int($oldnet,1);
	local($end) = &quad2int($2,1);
	if($end < $begin) {
	    return $NOK, "range is invalid", @returnstring;
	}

	if ($oldnet =~ /(\d+)\.\d+\.\d+\.\d+/) {
	    if ($1 >= 192) {
		$len = 24;
		$one_net = 0x00000100;
	    } elsif ($1 >= 128) {
		$len = 16;
		$one_net = 0x00010000;
	    } else {
		$len = 8;
		$one_net = 0x01000000;
	    }
	}
	local($newbegin) = $begin;
	while ($newbegin <= $end) {
	    for ($pwr=1; $pwr<24; $pwr++) {
		$pwr2 = 2 ** $pwr;
		$thisend = $newbegin + ($pwr2-1)*$one_net;
		return @returnstring if !$newbegin;
		if (($thisend > $end) ||
		    $newbegin != ($newbegin & $masks[$len-$pwr])) {
		    $pwr--;
		    $giveback = sprintf("%s/%d", &int2quad($newbegin),
					$len-$pwr);
		    @returnstring = (@returnstring, $giveback);
		    $newbegin = $newbegin + $one_net * 2**$pwr;
		    last;
		}
	    }
	}
	return $OK, "", @returnstring; 
    } else {
	return $NOK,
	"$net_rep is not a classful range representation I understand", 
	@returnstring;
    } 
}    

sub netpre_verify {
    local($net_rep) = @_;
    local(@returnstring) = ();
    local($i);

    if($net_rep =~ /^(\d+\.\d+\.\d+\.\d+)\/(\d+)$/) {
	local($net) = $1;
	local($len) = $2;
	if(!&isnet($net) || !&islen($len)){
	    return $NOK,
	    "$net_rep has invalid syntax",
	    @returnstring;
	}
	@bytes = split(/\./, $net);
	for ($i = 0 ; $i < 4 ; $i++) {
	    $hval = ($hval << 8) + $bytes[$i];
	}
	$mask = $masks[$len];
       
	$newhval = $hval & $mask;
	if($newhval != $hval) {
	    $m =&int2quad($mask); # Just get the mask for those who care
	    for ($c = 0 , $bits=$hval ; $bits ; $bits <<= 1 ) {
		$c++;
	    }
	    $usepre = &int2quad($hval)."/".$c;
	    $uselen = &int2quad($newhval)."/".$len;
	    	    return $NOK,
	    "$net_rep is an invalid net and prefix combination:-\nlength $len".
		" \(mask $m\) is outside the range of $net\n".
		    "Possible values with these combinates are:\n".
			"based on length $uselen\n".
			    "based on prefix $usepre",
            @returnstring;
	}
	local($val) = &trimnet($net);
	@returnstring = "$val"."/".$len;
	return $OK, "", @returnstring;
    } else {
	return $NOK,
	"$net_rep is not an representation I understand",
	@returnstring;
    }
}	
    

sub intnetpre_to_netpre {
    local($net_rep) = @_;
    local(@returnstring) = ();
    if($net_rep =~ /^(\d+)\/(\d+)$/) {
	local($int) = $1;
	local($len) = $2;
	@returnstring = &int2quad($int)."/"."$len";
	return $OK, "", @returnstring;
    } else {
	return $NOK,
	"$net_rep is not an representation I understand",
	@returnstring;
    }
}

sub netpre_to_intnetpre {
    local($net_rep) = @_;
    local(@returnstring) = ();
    if($net_rep =~ /^(\d+\.\d+\.\d+\.\d+)\/(\d+)$/) {
	local($int) = $1;
	local($len) = $2;
	@returnstring = &quad2int($int,1)."/"."$len";
	return $OK, "", @returnstring;
    } else {
	return $NOK,
	"$net_rep is not an net/prefix representation I understand",
	@returnstring;
    }
}

sub netmask_to_netpre {
    local($net_rep) = @_;
    local(@returnstring) = ();
    local($NETMASK) = "[nN]*[eE]*[tT]*[mM][aA][sS][kK]";
    local($HEX) = "[0-9a-fA-f]";
    local($HEXMASK) = "0x$HEX$HEX$HEX$HEX$HEX$HEX$HEX$HEX";
    local($IPADDR) = "\\d+\\.\\d+\\.\\d+\\.\\d+";
    local($i);


    if($net_rep =~ /^$IPADDR\s+($NETMASK)*\s*$IPADDR$/
       || $net_rep =~ /^$IPADDR\s+($NETMASK)*\s*$HEXMASK$/) {
	local($n) = $1;
	local($m) = $3;
	
	if (&isnet($n)) {
	    if($m =~ /\./) {
		$hexm = &quad2int($m,1);
	    }  else {
		$hexm = hex($m);
	    }
	    
	    foreach $i (0..$#masks) {
		if($hexm == $masks[$i]) {
		    local($val) = "$n"."/"."$i";
		    local($stat, $text, @array) = 
			&netpre_verify("$val");
		    if($stat == $OK) {
			@returnstring = "$val";
			return $OK, "", @returnstring;
		    } else {
			return $NOK, 
			"$net_rep translates to $val\n$text", @returnstring;
		    }
		}     
	    }
	    local($tmp) = $hexm;
	    $tmp =~ s/^0x//;
	    local($msk) = &int2quad($tmp+0);
	    local($hex) = sprintf("0x%8x\n", $tmp);
	    chop($hex);
	    $hex =~ s/ /0/g;
	    return $NOK, "mask $msk \($hex\) is invalid", @returnstring;

	} else {
	    return $NOK, "$net_rep is invalid", @returnstring;
	}
    }
}

#
# old_to_new($oldnet)
#
# converts old style RIPE database network numbers (single classful net
# and classful ranges) to prefix/length format. Prefix/length is the
# internal representation used. Routine to convert a range into
# prefix/length is happily stolen from "aggis" by Dale Johnson, MERIT
# Thanks Dale ;-)

sub old_to_new {
    local($oldnet)=@_;
    
    local($len,$len2,$one_net);
    
    local(@returnstring) = ();

    &timer("old_to_new", 1) if $opt_V;

    # Conventional classful nets

    if ($oldnet =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)/) {
        if ($1 >= 192) {
            $len = 24;
	    $len = 32 if $4;
#            $one_net =  0x00000100;
        } elsif ($1 >= 128) {
            $len = 16;
	    $len = 24 if $3;
	    $len = 32 if $4;
#            $one_net = 0x00010000;
        } else {
            $len = 8;
	    $len = 16 if $2;
	    $len = 24 if $3;
	    $len = 32 if $4;
#            $one_net = 0x01000000;
        }
	$one_net = 2 ** (32 - $len);
    }
    # Special case, it can happen that we got a hostaddress and mask
    # let's make sure we remove the mask when we return this.
    # this is for ifaddr in inet-rtr

    if ($oldnet =~ /(\d+\.\d+\.\d+\.\d+)\s+\d+\.\d+\.\d+\.\d+/) {
	return "$1/$len";
    }

    if ($oldnet !~ /\-/) {
	&timer("old_to_new");
	return "$oldnet/$len";
    }

    # Now, we have a classful range, let's convert this into pref/len

    if ($oldnet =~ /^(\d+\.\d+\.\d+\.\d+)\s+\-\s+(\d+\.\d+\.\d+\.\d+)/) {
        
	local($begin) = &quad2int($1,1);
	local($newbegin)=$begin;
	
	local($end) = &quad2int($2,1);
	local($tmp) = $2;
	
	local($pwr,$thisend);
	
	$tmp =~ m/(\d+)\.(\d+)\.(\d+)\.(\d+)/;
	
	$len2 = 8 if $1;
	$len2 = 16 if $2;
	$len2 = 24 if $3;
	$len2 = 32 if $4;
	
	if ($len2 > $len) {
	    $one_net = 2 ** (32 - $len2);
	    $len = $len2;
	}
	
	while ($newbegin <= $end) {
	
	    for ($pwr=1; $pwr<32; $pwr++) {
		
		$thisend = $newbegin + (2 ** $pwr - 1)*$one_net;
		
		return @returnstring if !$newbegin;
		
		if (($thisend > $end) ||
		    ($newbegin != ($newbegin & $masks[$len-$pwr]))) {
		    
		    $pwr--;
		    
		    push(@returnstring, &int2quad($newbegin)."\/".($len-$pwr));
		    
		    $newbegin+= $one_net * 2**$pwr;
		    
		    last;
		
		}
	    }
	}
    }
    
    &timer("old_to_new") if $opt_V;
    
    return @returnstring;
    
}

#
# old_to_new($oldnet)
#
# converts old style RIPE database network numbers (single classful net
# and classful ranges) to prefix/length format. Prefix/length is the
# internal representation used. Routine to convert a range into
# prefix/length is happily stolen from "aggis" by Dale Johnson, MERIT
# Thanks Dale ;-)

sub OLDVERold_to_new {
    local($oldnet) = @_;
    local($len);
    local(@returnstring) = ();
    local($one_net);
    local($len2);

    &timer("old_to_new", 1) if $opt_V;

    # Conventional classful nets

    if ($oldnet =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)/) {
        if ($1 >= 192) {
            $len = 24;
	    $len = 32 if $4;
#            $one_net =  0x00000100;
        } elsif ($1 >= 128) {
            $len = 16;
	    $len = 24 if $3;
	    $len = 32 if $4;
#            $one_net = 0x00010000;
        } else {
            $len = 8;
	    $len = 16 if $2;
	    $len = 24 if $3;
	    $len = 32 if $4;
#            $one_net = 0x01000000;
        }
	$one_net = 2 ** (32 - $len);
    }
    # Special case, it can happen that we got a hostaddress and mask
    # let's make sure we remove the mask when we return this.
    # this is for ifaddr in inet-rtr

    if ($oldnet =~ /(\d+\.\d+\.\d+\.\d+)\s+\d+\.\d+\.\d+\.\d+/) {
	return "$1/$len";
    }

    if ($oldnet !~ /\-/) {
	&timer("old_to_new");
	return "$oldnet/$len";
    }

    # Now, we have a classful range, let's convert this into pref/len

    if ($oldnet =~ /^(\d+\.\d+\.\d+\.\d+)\s+\-\s+(\d+\.\d+\.\d+\.\d+)/) {
	local($begin) = &quad2int($1,1);
	local($end) = &quad2int($2,1);
	local($tmp) = $2;
	$tmp =~ m/(\d+)\.(\d+)\.(\d+)\.(\d+)/;
	$len2 = 8 if $1;
	$len2 = 16 if $2;
	$len2 = 24 if $3;
	$len2 = 32 if $4;
	if ($len2 > $len) {
	    $one_net = 2 ** (32 - $len2);
	    $len = $len2;
	}
	local($newbegin) = $begin;
	while ($newbegin <= $end) {
	    for ($pwr=1; $pwr<32; $pwr++) {
		$pwr2 = 2 ** $pwr;
		$thisend = $newbegin + ($pwr2-1)*$one_net;
		return @returnstring if !$newbegin;
		if (($thisend > $end) ||
		    $newbegin != ($newbegin & $masks[$len-$pwr])) {
		    $pwr--;
		    $giveback = sprintf("%s/%d", &int2quad($newbegin),
					$len-$pwr);
		    @returnstring = (@returnstring, $giveback);
		    $newbegin = $newbegin + $one_net * 2**$pwr;
		    last;
		}
	    }
	}
    }
    &timer("old_to_new") if $opt_V;
    return @returnstring;
}


1;

