こんにちは、幅広い視野を持つエンジニアを目指しています田中と申します。
今日はネットワークアドレスの扱い方を見ていきます。
目的
特定のIPアドレスが、特定のネットワークの範囲内にあるかどうか調べたい。また、サブネットマスクやブロードキャストアドレスなどの算出や検証も併せて行いたい、何か良い方法はないか調べる
やってみた事
IPアドレスを整数(32bit unsigned int)0 ~ 4,294,967,295)に変換して値の範囲を調べる
きっかけ
疎通確認などでよく使うpingコマンドですが、
ping [IPアドレス]
のように使いますが、私はある日誤って以下のように入力してしまいました。
ping [正の整数]
結果は以下のようになります。
123456789101112131415161718 C:\Users\tanaka>ping 8.8.8.88.8.8.8 に ping を送信しています 32 バイトのデータ:8.8.8.8 からの応答: バイト数 =32 時間 =3ms TTL=598.8.8.8 からの応答: バイト数 =32 時間 =3ms TTL=598.8.8.8 からの応答: バイト数 =32 時間 =3ms TTL=598.8.8.8 からの応答: バイト数 =32 時間 =3ms TTL=598.8.8.8 の ping 統計:パケット数: 送信 = 4、受信 = 4、損失 = 0 (0% の損失)、ラウンド トリップの概算時間 (ミリ秒):最小 = 3ms、最大 = 3ms、平均 = 3msC:\Users\tanaka>ping 1234567897.91.205.21 に ping を送信しています 32 バイトのデータ:Ctrl+C^C
10進数ドット区切りのIPアドレス以外をパラメータとして与えてもエラーにはなりませんでした。上記はWindowsのpingコマンドの結果ですが、Linuxなどのpingコマンドでも同様の結果となります。
以上のことから、pingコマンドは32bitのIPアドレスを10進数として解釈して処理しているのではないかと考え、自分のプログラムやスクリプトにも応用できるのではないかと考えました。
IPアドレスを10進数32bit unsigned intに変換してpingに与えてみた
それでは、Google Public DNSの"8.8.8.8"を10進数の32ビットの符号なし整数に変えてpingにパラメータとして与えてみます。
10進数のint型0を32bitで表すと以下のようになります。
1 |
0000 0000 0000 0000 0000 0000 0000 0000 |
10進数のint型8を2進数で表すと1000なので"8.8.8.8"は以下であると考えました。
1 |
0000 1000 0000 1000 0000 1000 0000 1000 |
これを10進数に直します。
2^27 + 2^19 + 2^11 + 2^3
= 134217728 + 524288 + 2048 + 8
= 134744072
これをpingのパラメータに与えてみます。
123456789101112 C:\Users\tanaka>ping 1347440728.8.8.8 に ping を送信しています 32 バイトのデータ:8.8.8.8 からの応答: バイト数 =32 時間 =4ms TTL=598.8.8.8 からの応答: バイト数 =32 時間 =3ms TTL=598.8.8.8 からの応答: バイト数 =32 時間 =3ms TTL=598.8.8.8 からの応答: バイト数 =32 時間 =3ms TTL=598.8.8.8 の ping 統計:パケット数: 送信 = 4、受信 = 4、損失 = 0 (0% の損失)、ラウンド トリップの概算時間 (ミリ秒):最小 = 3ms、最大 = 4ms、平均 = 3ms
予想通りの結果となりました。
IPアドレスを10進数32bit unsigned intに変換して処理してみた
pingコマンドを真似てIPアドレスを32bit unsigned intとして扱い、TCP/IPネットワークの情報を算出してみます。
IPアドレスを32bit値に変換
IPアドレスの各オクテット(8bit)をint型の数字と見なし、4つ並べます。
1234 0000 0000 0000 0000 0000 0000 0000 1000 // 80000 0000 0000 0000 0000 0000 0000 1000 // 80000 0000 0000 0000 0000 0000 0000 1000 // 80000 0000 0000 0000 0000 0000 0000 1000 // 8これを第一オクテットから順に、24ビット左シフト、第二オクテットを16ビット左シフト、第三オクテットを8ビット左シフトしてORをとります。
123456 0000 1000 0000 0000 0000 0000 0000 00000000 0000 0000 1000 0000 0000 0000 00000000 0000 0000 0000 0000 1000 0000 00000000 0000 0000 0000 0000 0000 0000 1000================================== [OR]0000 1000 0000 1000 0000 1000 0000 1000 => 134744072
IPアドレスが正しいかどうか
10進数に変換したIPアドレスの範囲が0~4294967295の範囲に含まれるかどうか
CIDR(プレフィックス長)を32bitIPアドレスにする
2^((32 - プレフィックス長) - 1)を2進数にしたものと、0xFFFFFFFFでXORをとる
例: 24
1234 0000 0000 0000 0000 0000 0000 1111 11111111 1111 1111 1111 1111 1111 1111 1111=================================== [XOR]1111 1111 1111 1111 1111 1111 0000 0000 => 255.255.255.0
ネットワークアドレスを求める
2進数に変換したIPアドレスとプレフィックス長付きアドレス(CIDR)を2進数にしたもののANDをとる(ホスト部のビットをすべて0にする)
例: 192.168.0.1 , サブネットマスク255.255.255.0(192.168.0.1/24)
1234 1100 0000 1010 1000 0000 0000 0000 00011111 1111 1111 1111 1111 1111 0000 0000=================================== [AND]1100 0000 1010 1000 0000 0000 0000 0000 => 192.168.0.0
ブロードキャストアドレスを求める
プレフィックス長付きアドレス(CIDR)をネットワークアドレスを論理否定(NOT)したものとXORをとる(ホスト部のビットをすべて1にする)
例: 192.168.0.1, サブネットマスク255.255.255.0(192.168.0.1/24)
123456 1100 0000 1010 1000 0000 0000 0000 0000=================================== [NOT]0011 1111 0101 0111 1111 1111 1111 11111111 1111 1111 1111 1111 1111 0000 0000=================================== [XOR]1100 0000 1010 1000 0000 0000 1111 1111 => 192.168.0.255bashなどNOT演算ができない場合は以下で代用する(プレフィックス長付きアドレス(CIDR)と0xFFFFFFFFでXORをとったものとIPアドレスでORをとる)
1234567 <span class="go">1111 1111 1111 1111 1111 1111 0000 00001111 1111 1111 1111 1111 1111 1111 1111=================================== [XOR]0000 0000 0000 0000 0000 0000 1111 11111100 0000 1010 1000 0000 0000 0000 0001=================================== [OR]1100 0000 1010 1000 0000 0000 1111 1111 => 192.168.0.255</span>
連続したIPアドレスを求める
IPアドレスを32bit unsigned intにしてインクリメントするだけ、オクテット境界は考える必要なし
あるIP アドレスが特定のネットワークの範囲にあるか調べる
IPアドレスがネットワークアドレスとブロードキャストアドレスの間にあるか調べればよい
例: 192.168.0.200がデフォルトサブネットマスク255.255.255.0で192.168.0.1の範囲に所属するか
192.168.0.255 => 3232235775
192.168.0.1 => 3232235521
192.168.0.200 => 32322357203232235521 < 3232235720 < 3232235775 よって所属する
実際のコード例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
#!/bin/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) )) } # 指定された個数だけ連続したIPアドレスの一覧を表示 <span class="k">function</span> iplist<span class="o">(){</span> <span class="nb">local </span><span class="nv">num</span><span class="o">=</span><span class="k">$(</span>ip2decimal <span class="nv">$1</span><span class="k">)</span> <span class="nb">local </span><span class="nv">max</span><span class="o">=</span><span class="k">$((</span><span class="nv">$num</span> <span class="o">+</span> <span class="nv">$2</span> <span class="o">-</span> <span class="m">1</span><span class="k">))</span> <span class="k">while</span> : <span class="k">do</span> decimal2ip <span class="nv">$num</span> <span class="o">[[</span> <span class="nv">$num</span> <span class="o">==</span> <span class="nv">$max</span> <span class="o">]]</span> <span class="o">&&</span> <span class="nb">break</span> <span class="o">||</span> <span class="nv">num</span><span class="o">=</span><span class="k">$((</span><span class="nv">$num</span><span class="o">+</span><span class="m">1</span><span class="k">))</span> <span class="k">done</span> <span class="o">} # 特定のIPアドレスが特定のネットワークに含まれるか調べる </span><span class="k">function</span> ipwith<span class="o">(){</span> <span class="nb">local </span><span class="nv">addr</span><span class="o">=</span><span class="nv">$1</span> <span class="nb">local </span><span class="nv">mask</span><span class="o">=</span><span class="nv">$2</span> <span class="nb">local </span><span class="nv">num</span><span class="o">=</span><span class="k">$(</span>ip2decimal <span class="nv">$3</span><span class="k">)</span> <span class="nb">local </span><span class="nv">net</span><span class="o">=</span><span class="k">$((</span> <span class="k">$(</span>ip2decimal <span class="nv">$addr</span><span class="k">)</span> <span class="o">&</span> <span class="k">$(</span>ip2decimal <span class="nv">$mask</span><span class="k">)</span> <span class="k">))</span> <span class="nb">local </span><span class="nv">brd</span><span class="o">=</span><span class="k">$((</span> <span class="k">$(</span>ip2decimal <span class="nv">$addr</span><span class="k">)</span> <span class="o">|</span> <span class="o">(</span><span class="m">0</span>xFFFFFFFF <span class="o">^</span> <span class="k">$(</span>ip2decimal <span class="nv">$mask</span><span class="k">)</span><span class="o">)</span> <span class="k">))</span> <span class="o">[</span> <span class="nv">$net</span> -le <span class="nv">$num</span> -a <span class="nv">$num</span> -le <span class="nv">$brd</span> <span class="o">]</span> <span class="o">&&</span> <span class="k">return</span> <span class="m">0</span> <span class="o">||</span> <span class="k">return</span> 1 <span class="o">}</span> |
実行例
1234567891011121314151617181920212223242526272829303132333435363738394041 <span class="go"># IPアドレスを32bit値に変換$ ip2decimal 192.168.0.13232235521# 32bit値をIPアドレスに変換$ decimal2ip 3232235521192.168.0.1# CIDR表記のネットワークアドレスを32bit値に変換$ cidr2decimal 244294967040# 特定のIPアドレスのネットワークアドレスを求める$ IP=192.168.0.1$ CIDR=24$ decimal2ip $(( $(ip2decimal $IP) & $(cidr2decimal $CIDR) ))192.168.0.0# 特定のIPアドレスのブロードキャストアドレスを求める$ MASK=255.255.255.0$ decimal2ip $(( $(ip2decimal $IP) | (0xFFFFFFFF ^ $(ip2decimal $MASK)) ))192.168.0.255# 指定された数だけ特定のIPアドレスと連続したIPアドレスを求める$ iplist 192.168.0.253 5</span><span class="go">192.168.0.253</span><span class="go">192.168.0.254</span><span class="go">192.168.0.255</span><span class="go">192.168.1.0</span><span class="go">192.168.1.1# 特定のIPアドレスが特定のネットワークに含まれるか調べる$ ipwith 192.168.0.1 255.255.255.0 192.168.0.200</span><span class="go">$ echo $?</span><span class="go">0# 特定のIPアドレスが特定のネットワークに含まれるか調べる$ ipwith 192.168.0.1 $(decimal2ip $(cidr2decimal 24)) 192.168.1.200</span><span class="go">$ echo $?</span><span class="go">1</span>
IPアドレスを10進数32bit unsigned intとしてIPアドレスの範囲を調べることができました!
pingのソースコードを読んでみた
Linuxのpingのソースコードが見つからなかったのでFreeBSDのソースコードを読んでみます。
https://svnweb.freebsd.org/base/head/sbin/ping/ping.c
123456789101112 553 target = argv[optind]; // コマンドラインパラメータに渡された値を読んでいる.../* to->sin_addrにtargetの値をnetwork byte order形式で入れる。パラメータの値がIPアドレスとして解釈できるかどうか */606 if (inet_aton(target, &to->sin_addr) != 0) {607 hostname = target; // 解釈できる(32bit10進数値の場合もここを通る)608 }...841 (void)printf("PING %s (%s)", hostname, inet_ntoa(to->sin_addr)); // PING 134744072 (8.8.8.8)...と表示している
inet_ntoa()とinet_aton()が32bit数値とASCII文字列(10進数ドット区切りIPアドレス)の変換をしていそうです。
オンラインマニュアルのinet(3)にありました。
1234567 <b>inet_aton</b>() converts the Internet host address <i>cp</i> from the IPv4numbers-and-dots notation into binary form (in network byte order)and stores it in the structure that <i>inp</i> points to. <b>inet_aton</b>()returns nonzero if the address is valid, zero if not....
マニュアルによると16進数でも解釈できるとのことなのでやってみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
C:\Users\tanaka>ping 0x08080808 8.8.8.8 に ping を送信しています 32 バイトのデータ: 8.8.8.8 からの応答: バイト数 =32 時間 =3ms TTL=59 8.8.8.8 からの応答: バイト数 =32 時間 =3ms TTL=59 8.8.8.8 からの応答: バイト数 =32 時間 =3ms TTL=59 8.8.8.8 からの応答: バイト数 =32 時間 =4ms TTL=59 8.8.8.8 の ping 統計: パケット数: 送信 = 4、受信 = 4、損失 = 0 (0% の損失)、 ラウンド トリップの概算時間 (ミリ秒): 最小 = 3ms、最大 = 4ms、平均 = 3ms C:\Users\tanaka>ping 0x08.0x08.0x08.0x08 8.8.8.8 に ping を送信しています 32 バイトのデータ: 8.8.8.8 からの応答: バイト数 =32 時間 =8ms TTL=59 8.8.8.8 からの応答: バイト数 =32 時間 =5ms TTL=59 8.8.8.8 からの応答: バイト数 =32 時間 =8ms TTL=59 8.8.8.8 からの応答: バイト数 =32 時間 =8ms TTL=59 8.8.8.8 の ping 統計: パケット数: 送信 = 4、受信 = 4、損失 = 0 (0% の損失)、 ラウンド トリップの概算時間 (ミリ秒): 最小 = 5ms、最大 = 8ms、平均 = 7ms |
16進数でもできました!
参考にさせていただいたサイト様(TCP/IP情報取得シェルスクリプトをそのまま使わせて頂いています)
http://qiita.com/harasou/items/5c14c335388f70e178f5
記事は以上になります。
このたびはご覧戴き、ありがとうございました。
投稿者プロフィール
最新の投稿
- AWS2021年12月2日AWS Graviton3 プロセッサを搭載した EC2 C7g インスタンスが発表されました。
- セキュリティ2021年7月14日ゼロデイ攻撃とは
- セキュリティ2021年7月14日マルウェアとは
- WAF2021年7月13日クロスサイトスクリプティングとは?