読者です 読者をやめる 読者になる 読者になる

IPアドレス、MACアドレスをインクリメント

IPアドレスMACアドレスをインクリメントしたい、というへんな依頼を受けたのでスクリプト書いてみた。
どっちも基本方針はこう。

  1. いったん数値に変換
  2. インクリメント
  3. 文字列に戻す

そしてモジュールなにも使わない。

IPアドレス

「ちゃんとしたIPアドレスかどうかを見る必要はない。最後のバイトが0か255にならなければいいよ。」
ということでずいぶん楽になった。入力チェックもなし。

#!/usr/bin/perl
use strict;
use warnings;

my $ip = shift;
my $numIncr = shift;

my $nIP = ipToNum($ip);
for(0..$numIncr-1){
  $nIP++ if $nIP % 256 == 255;
  $nIP++ if $nIP % 256 == 0;

  print numToIP($nIP++) ."\n";
}


sub ipToNum {
  unpack 'N', pack 'C*', split /\./, shift;
}

sub numToIP {
  join '.', unpack 'C*', pack 'N', shift;
}

実行

$ perl incr_ip.pl 100.255.255.250 10
100.255.255.250
100.255.255.251
100.255.255.252
100.255.255.253
100.255.255.254
101.0.0.1
101.0.0.2
101.0.0.3
101.0.0.4
101.0.0.5

でけた。

MACアドレス

こっちもちゃんとしたMACアドレスかどうかは気にせず、入力チェックもなし。

#!/usr/bin/perl
use strict;
use warnings;
#no warnings;
use bigint;

my $mac = shift;
my $numIncr = shift;

my $nMac = macToNum($mac);
print numToMac($nMac++) ."\n" for 0..$numIncr-1;


sub macToNum {
  my $mac = shift;
  $mac =~ tr/://d;

  hex $mac;
}

sub numToMac {
  my $num = shift;

  my $mac = sprintf "%04lx%08x", $num>>32, $num & 0xffffffff;
  join ":", $mac =~ /../g;
}

実行

$ perl incr_mac.pl 00:00:00:ff:ff:f9 10 
00:00:00:ff:ff:f9
00:00:00:ff:ff:fa
00:00:00:ff:ff:fb
00:00:00:ff:ff:fc
00:00:00:ff:ff:fd
00:00:00:ff:ff:fe
00:00:00:ff:ff:ff
00:00:01:00:00:00
00:00:01:00:00:01
00:00:01:00:00:02

でけた。
・・・といいたいところだけど、00:00:ff:ff:ff:ffより大きなアドレスを扱おうとすると警告がでます。

$ perl incr_mac.pl 0f:ff:ff:ff:ff:f9 10 
Integer overflow in hexadecimal number at incr_mac.pl line 18.
Hexadecimal number > 0xffffffff non-portable at incr_mac.pl line 18.
0f:ff:ff:ff:ff:f9
0f:ff:ff:ff:ff:fa
0f:ff:ff:ff:ff:fb
0f:ff:ff:ff:ff:fc
0f:ff:ff:ff:ff:fd
0f:ff:ff:ff:ff:fe
0f:ff:ff:ff:ff:ff
10:00:00:00:00:00
10:00:00:00:00:01
10:00:00:00:00:02

このスクリプトならたぶん無視してOK。Perlの数値は内部的にはdoubleで、MACアドレスの48ビットはdoubleの52ビット仮数部に収まるし、演算的には問題はないはず。
(メッセージ自体はuse warningを消すと1つ消えてno warningsで全部消えますが。)
しかし「sprintf "%04lx%08x", $num>>32, $num & 0xffffffff;」は「sprintf "%012lx", $num;」じゃダメなんだなぁ。