2019/02/13(水)perl にて IPv4 と IPv6 のデュアルスタックサーバを作る

Perl において、この用途には IO::Socket::INET というコアモジュールが広く使われていて、
ちまたにはこれを、IO::Socket::IP に変えるだけで IPv4/IPv6 デュアルスタックが実現するかのような話が広く知られているようだが、実際はどうも違う模様。。

今まで IPv4 で動作していたこのコード:
#!/usr/local/bin/perl
#
use utf8 ; binmode(STDOUT, ":utf8") ;
use IO::Socket::INET ;                     # ソケットインタフェース使用宣言

$comm_queue  = 5 ;                         # コネクション待ち受けキューの数
$port        = 9999 ;                      # port no.

### 通信ソケット生成
# ソケットオープン
$reqsock = IO::Socket::INET->new (LocalPort => $port,
                                  Listen    => $comm_queue,
                                  Proto     => 'tcp',
                                  Reuse     => 1,
                                 ) ;
if (not $reqsock) {
  err_trap("通信ソケットが作成できません。",$!) ;
  exit ;
}

### サーバメインルーチン
for (;;) {
  $sock = $reqsock->accept() ;
  if (not $sock) {
    err_trap("クライアントの要求受け付けに失敗しました。(accept error)",$!) ;
    exit ;
  }

  if ($child = fork()) {
  # 親プロセスの実行コード
    $sock->close() ;
    waitpid($child,0) ;
    next ;                                 # 次のコネクション要求を待つ

  } elsif (defined($child)) {
  # 子プロセスの実行コード(メインルーチン)
    $reqsock->close() ;
    select($sock) ; $| = 1;                # 常に flash するようにする
    select(STDOUT) ;
    binmode $sock ,':encoding(UTF-8)' ;

  以下、サーバの処理プログラム・・・・
  }
}
IO::Socket:INET の部分を、IO::Socket::IP に書き換えても、IPv6 しか受け付けない状態になります。
もしかしたら、IPv4射影アドレスを使えばいいのかもしれないですが、現状環境には全く合わない。

試行錯誤の結果、以下で動作する模様:
#!/usr/local/bin/perl
#
use utf8 ; binmode(STDOUT, ":utf8") ;
use IO::Socket::INET ;                     # IPv4 Socket インタフェースライブラリ使用宣言
use IO::Socket::INET6 ;                    # IPv6 Socket インタフェースライブラリ使用宣言
use IO::Select ;

$comm_queue  = 5 ;                         # コネクション待ち受けキューの数
$port        = 9999 ;                      # port no.

### 通信ソケット生成
$select = IO::Select->new ;

# ソケットオープン[IPv6]
$psock6 = IO::Socket::INET6->new (LocalPort => $port,
                                  Listen    => $comm_queue,
                                  Type      => SOCK_STREAM,
                                  Reuse     => 1,
                                  Proto     => "tcp"
                                 ) ;
if (not $psock6) {
  err_trap("通信ソケットが作成できません。[IPv6]",$!) ;
  exit ;
} else {
  $select->add($psock6) ;
}

# ソケットオープン[IPv4]
$psock4 = IO::Socket::INET->new (LocalPort => $port,
                                 Listen    => $comm_queue,
                                 Type      => SOCK_STREAM,
                                 Reuse     => 1,
                                 Proto     => "tcp"
                                ) ;
if (not $psock4) {
  err_trap("通信ソケットが作成できません。[IPv4]",$!) ;
  exit ;
} else {
  $select->add($psock4) ;
}

### サーバメインルーチン
for (;;) {
  while (my @ready = $select->can_read) {
    foreach my $reqsock (@ready) {
      $sock = $reqsock->accept() ;
      if (not $sock) {
        err_trap("クライアントの要求受け付けに失敗しました。(accept error)",$!) ;
        exit ;
      }

      if ($child = fork()) {
      # 親プロセスの実行コード
        $sock->close() ;
        waitpid($child,0) ;
        next ;                                 # 次のコネクション要求を待つ

      } elsif (defined($child)) {
      # 子プロセスの実行コード(メインルーチン)
        select($sock) ; $| = 1;                # 常に flash するようにする
        select(STDOUT) ;
        binmode $sock ,':encoding(UTF-8)' ;

      以下、サーバの処理プログラム・・・・
      }
    }
  }
}
参考になったのは、これ → How best to support IPv4/v6 in Perl server

2019/02/07(木)FreeBSD の portupgrade

2019/02/07 18:47 サーバ運営・管理
FreeBSDを10 から11にしたあと、portupgrade で ports 導入の各ソフトウェアを更新したりすると、
===>  Cleaning for ImageMagick7-nox11-7.0.8.22
--->  Cleaning out obsolete shared libraries
No such file or directory @ realpath_rec - /usr/local/lib/compat/pkg/db5
No such file or directory @ realpath_rec - /usr/local/lib/compat/pkg/db5
No such file or directory @ realpath_rec - /usr/local/lib/compat/pkg/db5
No such file or directory @ realpath_rec - /usr/local/lib/compat/pkg/db5
No such file or directory @ realpath_rec - /usr/local/lib/compat/pkg/db5
No such file or directory @ realpath_rec - /usr/local/lib/compat/pkg/db5
のようなメッセージが毎回出るようにになります。実害は無いのでほったらかし状態だったが、気になるものは気になるので、対策を。。
# cd /usr/local/lib/compat/pkg
# ls -al
lrwxr-xr-x  1 root  wheel        22  7月  4  2018 libdb_cxx-5.3.so.0@ -> db5/libdb_cxx-5.3.so.0
lrwxr-xr-x  1 root  wheel        18  7月  4  2018 libdb_cxx-5.so.0@ -> libdb_cxx-5.3.so.0
lrwxr-xr-x  1 root  wheel        22  7月  4  2018 libdb_stl-5.3.so.0@ -> db5/libdb_stl-5.3.so.0
lrwxr-xr-x  1 root  wheel        18  7月  4  2018 libdb_stl-5.so.0@ -> libdb_stl-5.3.so.0
lrwxr-xr-x  1 root  wheel        18  7月  4  2018 libdb-5.3.so.0@ -> db5/libdb-5.3.so.0
lrwxr-xr-x  1 root  wheel        14  7月  4  2018 libdb-5.so.0@ -> libdb-5.3.so.0
実に簡単。上記6つのシンボリックリンクを削除するだけ。これで解決しました。
OSをメジャーバージョンアップデートしたら、依存ライブラリは変更されていることが殆どなので、できるだけ早めに各ソフトウェアは再コンパイルするのが無難。

FreeBSD は、後方互換機能で何事もなく動作することが多いです。
しかしそれに頼り切っていると経験上、ある日突然、不可解な障害に悩むことになるのです。