LoginSignup
44
37

More than 5 years have passed since last update.

シェルスクリプトで IPアドレス計算

Last updated at Posted at 2014-08-24

会社で、ローカルIPアドレスを何千件とインクリメントして、テストに利用しているのを見て、IP の計算ってどうやるんだ?ってことで、試しにやってみた。

具体的に「IPの計算」ってのは、以下を想定

  • ネットワークアドレスを求める
  • ブロードキャストアドレスを求める
  • 連続したIP の一覧を求める
  • あるIP が特定のネットワークの範囲にあるか判定する

どうやって計算するか

人間が使う IPアドレスは見やすいように、オクテット単位に区切られて、10進で表記されている。こんな感じ。

192.168.0.1

でもこれでは計算しづらいので、実際の 32bit値に変更して計算するのが良さそう。

# 10進数
3232235521

# 2進数
11000000 10101000 00000000 00000001

前準備

32bitの値で計算する必要があるので、以下のような関数があると便利。

  1. IPアドレス表記 -> 32bit値 に変換 (ip2decimal)
  2. 32bit値 -> IPアドレス表記 に変換 (decimal2ip)
  3. CIDR 表記のネットワークアドレスを 32bit値に変換 (cidr2decimal)

3 については、ぱっと見、意味がわからないが、
192.168.0.0/24 の24を 32bit値4294967040(255.255.255.0)に変換するという意味。

で、作成した関数が、以下のようなもの。

bash
# IPアドレス表記 -> 32bit値 に変換
function ip2decimal(){
    local IFS=.
    local c=($1)
    printf "%s\n" $(( (${c[0]} << 24) | (${c[1]} << 16) | (${c[2]} << 8) | ${c[3]} ))
}

# 32bit値 -> IPアドレス表記 に変換
function decimal2ip(){
    local n=$1
    printf "%d.%d.%d.%d\n" $(($n >> 24)) $(( ($n >> 16) & 0xFF)) $(( ($n >> 8) & 0xFF)) $(($n & 0xFF))
}

# CIDR 表記のネットワークアドレスを 32bit値に変換
function cidr2decimal(){
    printf "%s\n" $(( 0xFFFFFFFF ^ ((2 ** (32-$1))-1) ))
}
$ ip2decimal 192.168.0.1
3232235521
$ decimal2ip 3232235521
192.168.0.1

$ ip2decimal 255.255.255.0
4294967040
$ cidr2decimal 24
4294967040

やってることは、なんとなくわかると思いますが、一生懸命、頭にビット列の思い描きながら作りました。

計算

で、実際の計算。

1. ネットワークアドレスを求める

$ IP=192.168.0.1
$ CIDR=24
$
$ decimal2ip $(( $(ip2decimal $IP) & $(cidr2decimal $CIDR) ))
192.168.0.0

対象のIPアドレスを、ネットワークアドレスで AND(論理積&)すればよい。そのまま。

2. ブロードキャストアドレスを求める

$ IP=192.168.0.1
$ MASK=255.255.255.0
$
$ decimal2ip $(( $(ip2decimal $IP) | (0xFFFFFFFF ^ $(ip2decimal $MASK)) ))
192.168.0.255

対象のIPアドレスを、ネットワークアドレスの論理否定(ビット反転)と OR(論理和|)すればよい。(ただ、論理否定 ~ がうまく使えなかったので、XOR(排他的論理和)で代替)

3. 連続したIP の一覧を求める

function iplist(){
    local num=$(ip2decimal $1)
    local max=$(($num + $2 - 1))

    while :
    do
        decimal2ip $num
        [[ $num == $max ]] && break || num=$(($num+1))
    done
}
$ iplist 192.168.0.253 5
192.168.0.253
192.168.0.254
192.168.0.255
192.168.1.0
192.168.1.1

32bit値にしてインクリメントしているので、オクテットの境界と考える必要がない。

4. あるIP が特定のネットワークの範囲にあるか

function ipwith(){
   local addr=$1
   local mask=$2

   local num=$(ip2decimal $3)
   local net=$(( $(ip2decimal $addr) & $(ip2decimal $mask) ))
   local brd=$(( $(ip2decimal $addr) | (0xFFFFFFFF ^ $(ip2decimal $mask)) ))

   [ $net -le $num -a $num -le $brd ] && return 0 || return 1
}

192.168.0.200 192.168.1.200 が特定範囲(192.168.0.1/255.255.255.0)にあるか

$ ipwith 192.168.0.1 255.255.255.0 192.168.0.200
$ echo $?
0
$ ipwith 192.168.0.1 255.255.255.0 192.168.1.200
$ echo $?
1

指定されたIPが、ネットワークアドレスとブロードキャストアドレスの間にあるかどうかで判定。

まとめ

まぁ、使うことなさそう。 たまに使ってます (2016/03 追記)。

44
37
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
44
37