tomyamaのブログ

日記・雑記。

IPアドレスのサブネットを計算するスクリプト

昔に書いたPerlスクリプトです。

PATH が通っている /usr/local/bin に入れて使用しています。

 

IPアドレスサブネットマスクを入力すると、ネットワークアドレスとブロードキャストアドレスを表示します。この2つのアドレスの間が、そのネットワークで端末に割り振れるIPアドレスです。

 

実行例

$ ipaddr --help
ipaddr: `--help': unknown argument
usage: ipaddr           # interactive mode
       ipaddr [ipaddress/mask]
       ipaddr [ipaddress] [mask]
$
$ ipaddr 192.168.174.128/25
[ 192.168.174.128/25 ]
         LABEL  DECIMAL          BINARY                               HEX
--------------  ---------------  -----------------------------------  ----------
    IP_ADDRESS  192.168.174.128  11000000-10101000-10101110-10000000  0xC0A8AE80
   SUBNET_MASK  255.255.255.128  11111111-11111111-11111111-10000000  0xFFFFFF80
  NETWORK_ADDR  192.168.174.128  11000000-10101000-10101110-10000000  0xC0A8AE80
BROADCAST_ADDR  192.168.174.255  11000000-10101000-10101110-11111111  0xC0A8AEFF
$
$ ipaddr
IP address ?: 192.168.174.128
Subnet mask ?: 26
[ 192.168.174.128/26 ]
         LABEL  DECIMAL          BINARY                               HEX
--------------  ---------------  -----------------------------------  ----------
    IP_ADDRESS  192.168.174.128  11000000-10101000-10101110-10000000  0xC0A8AE80
   SUBNET_MASK  255.255.255.192  11111111-11111111-11111111-11000000  0xFFFFFFC0
  NETWORK_ADDR  192.168.174.128  11000000-10101000-10101110-10000000  0xC0A8AE80
BROADCAST_ADDR  192.168.174.191  11000000-10101000-10101110-10111111  0xC0A8AEBF
$
$ ipaddr 192.168.174.128/255.255.255.0
[ 192.168.174.0/24 ]
         LABEL  DECIMAL          BINARY                               HEX
--------------  ---------------  -----------------------------------  ----------
    IP_ADDRESS  192.168.174.128  11000000-10101000-10101110-10000000  0xC0A8AE80
   SUBNET_MASK  255.255.255.0    11111111-11111111-11111111-00000000  0xFFFFFF00
  NETWORK_ADDR  192.168.174.0    11000000-10101000-10101110-00000000  0xC0A8AE00
BROADCAST_ADDR  192.168.174.255  11000000-10101000-10101110-11111111  0xC0A8AEFF
$

 

スクリプト

[ipaddr]

#!/usr/bin/perl -w
################################################################################
## IPADDR -- 引数として与えられたIPアドレスから、使用可能なアドレス空間を計算する
## - Only for personal use !
## - 2006/02/12(Sun)  Create  tomyama
################################################################################

use strict ;
use File::Basename ;

exit (&pl_main (@ARGV)) ;


##########
## スクリプトのエントリポイント
sub pl_main (@) {
        ## 初期化処理
        &initialize (@_) ;

        ## IPアドレスが指定されていなければ、入力を求める
        if (! defined ($main::ipaddr)) {
                print ("IP address ?: ") ;
                my $myparam = readline (*STDIN) ;       chomp ($myparam) ;
                &chk_ipaddr ($myparam) ;
        }

        ## MASK値が指定されていなければ、入力を求める
        if (! defined ($main::subnetmask)) {
                print ("Subnet mask ?: ") ;
                my $myparam = readline (*STDIN) ;       chomp ($myparam) ;
                &chk_subnetmask ($myparam) ;
        }

        ## 出力する
        &print_cidr ($main::ipaddr, $main::subnetmask) ;
        printf("%14s  %-15s  %-35s  %-10s\n",   'LABEL',
                                                'DECIMAL',
                                                'BINARY',
                                                'HEX') ;
        printf("%14s  %-15s  %-35s  %-10s\n",   '-' x 14,
                                                '-' x 15,
                                                '-' x 35,
                                                '-' x 10) ;
        &print_addr ('IP_ADDRESS',      $main::ipaddr) ;
        &print_addr ('SUBNET_MASK',     $main::subnetmask) ;
        &print_addr ('NETWORK_ADDR',    $main::ipaddr &  $main::subnetmask) ;
        &print_addr ('BROADCAST_ADDR',  $main::ipaddr | ~$main::subnetmask) ;

        return 0 ;
}

##########
## 初期化処理
sub initialize (@) {
        my @val = @_ ;

        ## グローバルな変数
        $main::appname = basename ($0) ;

        ## 引数解析
        while (my $myparam = shift (@val)) {
                if ($myparam =~ m!^(\d+(?:\.\d+){3})(/.*)?$!o) {
                        if (defined ($2)) {
                                unshift (@val, substr ($2, 1)) ;
                        }

                        if (! defined ($main::ipaddr)) {
                                &chk_ipaddr ($1) ;
                        } else {
                                &chk_subnetmask ($1) ;
                        }
                } elsif (defined ($main::ipaddr) &&
                                ! defined ($main::subnetmask) &&
                                $myparam =~ m/^(?:0x)?([\da-fA-F]{8})$/o) {
                        &chk_subnetmask ($myparam) ;
                } elsif (defined ($main::ipaddr) &&
                                ! defined ($main::subnetmask) &&
                                $myparam =~ m/^(\d+)$/o) {
                        &chk_subnetmask ($myparam) ;
                } else {
                        warn ("$main::appname: `$myparam': unknown argument\n") ;
                        &p_usage () ;   exit (1) ;
                }
        }

        return 0 ;
}

##########
## 引数チェック
sub chk_ipaddr ($) {
        my $myarg = shift (@_) ;

        $myarg =~ s/^\s+//o ;
        $myarg =~ s/\s+$//o ;

        if ($myarg =~ m/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/o) {
                if ($1<0 || 255<$1) {
                        warn ("$main::appname: `$1': out of range\n") ;
                        &p_usage () ;   exit (1) ;      }
                if ($2<0 || 255<$2) {
                        warn ("$main::appname: `$2': out of range\n") ;
                        &p_usage () ;   exit (1) ;      }
                if ($3<0 || 255<$3) {
                        warn ("$main::appname: `$3': out of range\n") ;
                        &p_usage () ;   exit (1) ;      }
                if ($4<0 || 255<$4) {
                        warn ("$main::appname: `$4': out of range\n") ;
                        &p_usage () ;   exit (1) ;      }
                $main::ipaddr  = pack ("CCCC", $1, $2, $3, $4) ;
        } else {
                warn ("$main::appname: `$myarg': mistake ip-address format\n") ;
                &p_usage () ;   exit (1) ;
        }

        return 0 ;
}

##########
## 引数チェック
sub chk_subnetmask ($) {
        my $myarg = shift (@_) ;

        $myarg =~ s/^\s+//o ;
        $myarg =~ s/\s+$//o ;

        if ($myarg =~ m/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/o) {
                if (!   ($1==0 || $1==128 || $1==192 || $1==224 || $1==240 ||
                                $1==248 || $1==252 || $1==254 || $1==255)) {
                        warn ("$main::appname: `$1': out of range\n") ;
                        &p_usage () ;   exit (1) ;      }
                if (! ($1==255 &&
                        (       $2==128 || $2==192 || $2==224 || $2==240 ||
                                $2==248 || $2==252 || $2==254 || $2==255
                        ) || (
                                $2==0
                        ))) {
                        warn ("$main::appname: `$2': out of range\n") ;
                        &p_usage () ;   exit (1) ;      }
                if (! ($2==255 &&
                        (       $3==128 || $3==192 || $3==224 || $3==240 ||
                                $3==248 || $3==252 || $3==254 || $3==255
                        ) || (
                                $3==0
                        ))) {
                        warn ("$main::appname: `$3': out of range\n") ;
                        &p_usage () ;   exit (1) ;      }
                if (! ($3==255 &&
                        (       $4==128 || $4==192 || $4==224 || $4==240 ||
                                $4==248 || $4==252 || $4==254 || $4==255
                        ) || (
                                $4==0
                        ))) {
                        warn ("$main::appname: `$4': out of range\n") ;
                        &p_usage () ;   exit (1) ;      }
                $main::subnetmask  = pack ("CCCC", $1, $2, $3, $4) ;
        } elsif ($myarg =~ m/^(?:0x)?([\da-fA-F]{2})([\da-fA-F]{2})([\da-fA-F]{2})([\da-fA-F]{2})$/o) {
                my @myhex ;
                $myhex[0] = uc ($1) ;
                $myhex[1] = uc ($2) ;
                $myhex[2] = uc ($3) ;
                $myhex[3] = uc ($4) ;
                if (!   ("$myhex[0]"eq'00'||
                                "$myhex[0]"eq'80'||"$myhex[0]"eq'C0'||
                                "$myhex[0]"eq'E0'||"$myhex[0]"eq'F0'||
                                "$myhex[0]"eq'F8'||"$myhex[0]"eq'FC'||
                                "$myhex[0]"eq'FE'||"$myhex[0]"eq'FF'
                        )) {
                        warn ("$main::appname: `$1': out of range\n") ;
                        &p_usage () ;   exit (1) ;      }
                if (! ("$myhex[0]"eq'FF' &&
                        (       "$myhex[1]"eq'80'||"$myhex[1]"eq'C0'||
                                "$myhex[1]"eq'E0'||"$myhex[1]"eq'F0'||
                                "$myhex[1]"eq'F8'||"$myhex[1]"eq'FC'||
                                "$myhex[1]"eq'FE'||"$myhex[1]"eq'FF'
                        ) || (
                                "$myhex[1]"eq'00'
                        ))) {
                        warn ("$main::appname: `$2': out of range\n") ;
                        &p_usage () ;   exit (1) ;      }
                if (! ("$myhex[1]"eq'FF' &&
                        (       "$myhex[2]"eq'80'||"$myhex[2]"eq'C0'||
                                "$myhex[2]"eq'E0'||"$myhex[2]"eq'F0'||
                                "$myhex[2]"eq'F8'||"$myhex[2]"eq'FC'||
                                "$myhex[2]"eq'FE'||"$myhex[2]"eq'FF'
                        ) || (
                                "$myhex[2]"eq'00'
                        ))) {
                        warn ("$main::appname: `$3': out of range\n") ;
                        &p_usage () ;   exit (1) ;      }
                if (! ("$myhex[2]"eq'FF' &&
                        (       "$myhex[3]"eq'80'||"$myhex[3]"eq'C0'||
                                "$myhex[3]"eq'E0'||"$myhex[3]"eq'F0'||
                                "$myhex[3]"eq'F8'||"$myhex[3]"eq'FC'||
                                "$myhex[3]"eq'FE'||"$myhex[3]"eq'FF'
                        ) || (
                                "$myhex[3]"eq'00'
                        ))) {
                        warn ("$main::appname: `$4': out of range\n") ;
                        &p_usage () ;   exit (1) ;      }
                $main::subnetmask  = pack ("CCCC",
                        hex ($myhex[0]), hex ($myhex[1]),
                        hex ($myhex[2]), hex ($myhex[3])) ;
        } elsif ($myarg =~ m/^(\d+)$/o) {
                if ($1<0 || 32<$1) {
                        warn ("$main::appname: `$1': out of range\n") ;
                        &p_usage () ;   exit (1) ;      }

                my $mystrbin = sprintf ("%32s", '1' x $1 . '0' x (32-$1)) ;
                my @myarray ;
                for (my $i=0; $i<4; $i++) {
                        $myarray[$i] = &bin2dec (substr ($mystrbin, ($i*8), 8)) ;
                }
                $main::subnetmask  = pack ("CCCC", @myarray) ;
        } else {
                warn ("$main::appname: `$myarg': mistake subnet-mask format\n") ;
                &p_usage () ;   exit (1) ;
        }

        return 0 ;
}

##########
## 書式表示
sub p_usage {
        warn (  "usage: ${main::appname}                # interactive mode\n",
                "       ${main::appname} [ipaddress/mask]\n",
                "       ${main::appname} [ipaddress] [mask]\n") ;

        return 0 ;
}

##########
##
sub print_cidr ($$) {
        my $myipaddr = shift (@_) ;
        my $mysubnet = shift (@_) ;

        my @bin ;
        foreach my $i (unpack ("CCCC", $mysubnet)) {
                push (@bin, &dec2bin ($i)) ;
        }
        my $mybinary = join ('', @bin) ;
        $mybinary =~ s/[^1]//go ;
        my $mymask = length ($mybinary) ;

        my @myarray = unpack ("CCCC", $myipaddr & $mysubnet) ;
        my $mynet = join ('.', @myarray) ;

        print ("[ $mynet/$mymask ]\n") ;
}

##########
##
sub print_addr ($$) {
        my $mytitle = shift (@_) ;
        my $myaddr  = shift (@_) ;

        my @octb ;
        my @octd ;
        my @octh ;
        foreach my $i (unpack ("CCCC", $myaddr)) {
                push (@octb, sprintf ("%08d", &dec2bin ($i))) ;
                push (@octd, $i) ;
                push (@octh, sprintf ("%02X", $i)) ;
        }

        printf("%14s  %-15s  %-35s  %-10s\n", "$mytitle",
                &bin2dec32 ($myaddr),
                join ('-', @octb),
                '0x' . join ('', @octh)
        ) ;
}


##### 低レベル関数群 #####

##########
##
sub dec2bin {
        my $mystr = unpack ("B32", pack ("N", shift (@_))) ;
        $mystr =~ s/^0+(?=\d)//o ;      ## これをやらないと整数の頭の0が残る
        return $mystr ;
}

##########
##
sub bin2dec {
        return unpack ("N", pack ("B32", substr ("0" x 32 . shift (@_), -32))) ;
}

##########
##
sub bin2dec32 ($) {
        my $myaddr = shift (@_) ;

        my @myarray = unpack ("CCCC", $myaddr) ;
        return join ('.', @myarray) ;
}