試験運用中なLinux備忘録・旧記事

はてなダイアリーで公開していた2007年5月-2015年3月の記事を保存しています。

GNU/LinuxホストにおけるSun xVM VirtualBoxのブリッジ接続の設定を見直し

以前、GNU/LinuxホストにおけるSun xVM VirtualBoxでのブリッジ接続を試して「VERR_HOSTIF_INIT_FAILED」のエラーが出たときに、「TUN/TAPのカーネルモジュールのソースを一部修正する」という、あまりよくない方法で問題を回避していたが、その後Gentoo LinuxからMandriva Linuxに乗り換えた後もこのエラーが出てしまい、カーネルをビルドするのも(Gentooではないため)色々面倒のもとになるため、付属マニュアルや他の備忘録などを参考にして試したところ、この修正をせずにブリッジ接続することは可能ということが分かった。
(2008/12/19)Sun xVM VirtualBoxバージョン2.1.0からブリッジ接続が簡単になっている。Sun xVM VirtualBoxの覚え書き(2008/12/19現在)」を参照。以下の内容はそれ未満のバージョンを対象としている。

  1. 一般ユーザでTUN/TAPを使用するための追加設定
  2. 準備と後始末を支援するスクリプトを書き直し
  3. Mandriva Linux 2009.0でのメモ
    1. パッケージ
    2. TUN/TAP
    3. sudo

一般ユーザでTUN/TAPを使用するための追加設定

ブリッジの作成・設定後にVBoxAddIFというコマンドを用いて

sudo VBoxAddIF [TUN/TAPインターフェース名] $(whoami) [ブリッジ名]

のようにして一般ユーザがTUN/TAPデバイスを通じてブリッジを使用して通信できるように許可をする。ただ、仮想マシン設定のネットワークデバイス設定の「設定アプリケーション」などにこの記述を含んだシェルスクリプトを指定しても動かなかった。*1そのときのログは以下のようになった。

00:00:56.292 VirtualBox 2.0.4 r38406 linux.amd64 (Oct 23 2008 21:56:58) release log
00:00:56.292 Log opened 2008-11-12T11:29:42.893307000Z
00:00:56.292 OS Product: Linux
00:00:56.292 OS Release: 2.6.27.4-desktop-2mnb
00:00:56.292 OS Version: #1 SMP Thu Nov 6 13:36:39 EST 2008
00:00:56.292 Package type: LINUX_64BITS_GENERIC
00:00:56.462 SUP: Loaded VMMR0.r0 (/opt/VirtualBox-2.0.4/VMMR0.r0) at 0xffffffffa0f03b20 - ModuleInit at ffffffffa0f0fa00 and ModuleTerm at ffffffffa0f0fa60
00:00:56.462 SUP: VMMR0EntryEx located at ffffffffa0f103c0, VMMR0EntryFast at ffffffffa0f0fb00 and VMMR0EntryInt at ffffffffa0f0faf0
00:00:56.534 Failed to open the host network interface vbox0
00:00:56.534 ERROR [COM]: aRC=NS_ERROR_FAILURE (0x80004005) aIID={e3c6d4a1-a935-47ca-b16d-f9e9c496e53e} aComponent={Console} aText={Failed to open the host network interface vbox0} aWarning=false, preserve=false
00:00:56.537 ERROR [COM]: aRC=NS_ERROR_FAILURE (0x80004005) aIID={e3c6d4a1-a935-47ca-b16d-f9e9c496e53e} aComponent={Console} aText={Failed to initialize Host Interface Networking.
00:00:56.537 VBox status code: -3100 (VERR_HOSTIF_INIT_FAILED)} aWarning=false, preserve=false
00:00:56.553 Power up failed (vrc=VERR_HOSTIF_INIT_FAILED, hrc=NS_ERROR_FAILURE (0X80004005))

そのため、事前に個別のTUN/TAPインターフェースに対して許可するようにこのコマンドを実行しておく必要があった。実際には後述のスクリプトの中で実行している。

結局、2つの設定アプリケーションの欄は空にすることに。

準備と後始末を支援するスクリプトを書き直し

下のスクリプトは、以前書いたものと同様、ディストリのネットワーク設定を変更せずに一時的にSun xVM VirtualBoxでブリッジ接続ができるような状態に切り替えるためのもので、元に戻すこともできる。以前から変更した点は

  • スクリプト中の変数に設定を書かないで処理する方針
  • 一般ユーザでTUN/TAPを使用するための処理を含めた(デバイスは「vbox[数字]」・複数*2可)
  • 「eth0」などの元のインターフェース名をファイルに保存し、戻すときに使用する形にすることで、自動で名前を処理
  • 一度開始(ブリッジに切り替え)したら次に実行したときには停止(ブリッジから元のインターフェースに戻す)し、「start」「stop」の指定は不要にした
  • エラーになったときにどこで失敗したのかが分かるようにする
  • DHCP使用時にクライアント(dhclientdhcpcd)を検出して設定

など。getoptsを使用しているため、bash向け。
[任意]ファイル名: ~/bin/virtualbox-bridge-support.sh

#! /bin/bash

## virtualbox-bridge-support.sh
## Sun xVM VirtualBoxでブリッジ接続を使用する準備と後始末を行うスクリプト
## GNU/Linuxホスト・OS起動時にブリッジを使用しない使い方向け
## (ディストリのネットワーク設定は変更しない)
## (C) 2008 kakurasan
## ライセンス: GPL-3

PATH=/bin:/sbin:/usr/bin:/usr/sbin
IFACEFILE=~/.VirtualBox/original_interface
MODULES='tun bridge vboxdrv'

usage()
{
  ## 使用法表示
  printf "Usage: %s ( [OPTION...] )
  -b [bridge]          Use [bridge] as bridge interface name (default br0)
  -h                   Print this help
  -m [mod1,mod2, ...]  Load additional kernel module(s)
  -t [num]             Set number of TUN/TAP interface (default 1)\n" $(basename ${0})
  exit 0
}

die()
{
  ## エラー表示
  echo "ERROR: "${1} >&2
  exit 1
}

start()
{
  local i mod
  local orig_iface=$(netstat -rn | awk '/^0\.0\.0\.0/ { print $NF }')
  local ip_address=$(ifconfig ${orig_iface} | head -n 2 | tail -n 1 | cut -f 2 -d : | cut -f 1 -d \ )
  local broadcast=$(ifconfig ${orig_iface} | head -n 2 | tail -n 1 | cut -f 3 -d : | cut -f 1 -d \ )
  local net_mask=$(ifconfig ${orig_iface} | head -n 2 | tail -n 1 | cut -f 4 -d : | cut -f 1 -d \ )
  local ifacefiledir=$(dirname ${IFACEFILE})
  ## 必要なモジュールを読み込む
  for mod in ${MODULES}; do
    sudo modprobe -q ${mod} || die "Cannot load module \"${mod}\""
  done
  ## 元のインターフェース名を保存するディレクトリ
  mkdir -p ${ifacefiledir} || die "Cannot create directory \"${ifacefiledir}\""
  ## インターフェース名をファイルに書き出して保存
  echo ${orig_iface} > ${IFACEFILE} || die "Cannot save network interface name to \"${IFACEFILE}\""
  ## ブリッジの作成
  sudo brctl addbr ${BRIDGE_IFACE} || die "Cannot create bridge \"${BRIDGE_IFACE}\""
  ## ブリッジの設定
  sudo brctl stp ${BRIDGE_IFACE} off || die "Cannot turn off Spanning-Tree Protocol"
  ## 元のインターフェースのIPアドレス割り当てを解除
  sudo ifconfig ${orig_iface} 0.0.0.0 || die "Cannot unset IP address (${orig_iface})"
  ## ブリッジに元のインターフェースを追加
  sudo brctl addif ${BRIDGE_IFACE} ${orig_iface} || die "Cannot add \"${orig_iface}\" to \"${BRIDGE_IFACE}\""
  ## ブリッジにIPアドレスなどを設定して有効にする
  if [[ ${USE_DHCP} -eq 0 ]]; then
    sudo ifconfig ${BRIDGE_IFACE} ${ip_address} broadcast ${broadcast} netmask ${net_mask} up || die "Cannot up \"${BRIDGE_IFACE}\""
    sudo route add default gw ${DEFAULT_GW} || die "Cannot set default gw (${DEFAULT_GW})"
  else
    ## IPアドレスの解放
    sudo ${DHCP_CLIENT} ${RELEASE_OPT} ${orig_iface} || die "Cannot release IP address"
    ## 新しいインターフェースで取得
    sudo ${DHCP_CLIENT} ${BRIDGE_IFACE} || die "\"${DHCP_CLIENT} ${BRIDGE_IFACE}\" failed"
  fi
  ## ユーザへの許可
  for ((i=0; i<${NUM_TAP}; i++)); do
    sudo VBoxAddIF vbox${i} $(whoami) ${BRIDGE_IFACE}
  done
}

stop()
{
  local i
  local orig_iface=$(cat ${IFACEFILE})
  local bridge_iface=$(netstat -rn | awk '/^0\.0\.0\.0/ { print $NF }')
  local ip_address=$(ifconfig ${bridge_iface} | head -n 2 | tail -n 1 | cut -f 2 -d : | cut -f 1 -d \ )
  local broadcast=$(ifconfig ${bridge_iface} | head -n 2 | tail -n 1 | cut -f 3 -d : | cut -f 1 -d \ )
  local net_mask=$(ifconfig ${bridge_iface} | head -n 2 | tail -n 1 | cut -f 4 -d : | cut -f 1 -d \ )
  ## ユーザへの許可を削除
  local num_tap=$(ifconfig | egrep "vbox[0-9]+" | wc -l)
  for ((i=0; i<${num_tap}; i++)); do
    sudo VBoxDeleteIF vbox${i}
  done
  ## ブリッジを無効にする
  sudo ifconfig ${bridge_iface} down || die "Cannot down \"${bridge_iface}\""
  ## ブリッジから元のインターフェースを削除
  sudo brctl delif ${bridge_iface} ${orig_iface} || die "Cannot delete \"${orig_iface}\" from \"${bridge_iface}\""
  if [ ${USE_DHCP} -eq 1 ]; then
    ## IPアドレスの解放
    sudo ${DHCP_CLIENT} ${RELEASE_OPT} ${bridge_iface} || die "Cannot release IP address"
  fi
  ## ブリッジを削除
  sudo brctl delbr ${bridge_iface} || die "Cannot delete \"${bridge_iface}\""
  ## インターフェースを再設定
  if [ ${USE_DHCP} -eq 0 ]; then
    sudo ifconfig ${orig_iface} ${ip_address} broadcast ${broadcast} netmask ${net_mask} || die "Cannot setup \"${bridge_iface}\""
    sudo route add default gw ${DEFAULT_GW} || die "Cannot set default gw (${DEFAULT_GW})"
  else
    ## 新しいインターフェースで取得
    sudo ${DHCP_CLIENT} ${orig_iface} || die "\"${DHCP_CLIENT} ${orig_iface}\" failed"
  fi
  ## モジュールを削除
  sudo modprobe -qr ${MODULES_LIST}
  ## 元のインターフェース名を保存したファイルを削除
  rm ${IFACEFILE} -f
}

## 以下メイン処理

## 引数の処理
NUM_TAP=1
BRIDGE_IFACE=br0
while getopts 'b:dht:m:' OPT
do
  case ${OPT} in
   b)
    BRIDGE_IFACE=${OPTARG}
    ;;
   t)
    if echo ${OPTARG} | egrep "^[0-9]+$" > /dev/null; then
      NUM_TAP=${OPTARG}
    else
      die "Invalid number of TUN/TAP devices"
    fi
    ;;
   m)
    ## 引数で指定されたコンマ区切りの追加モジュール名をスペース区切りにして追加
    MODULES="${MODULES} $(echo ${OPTARG} | tr , ' ')"
    ;;
   ?)
    usage
    ;;
  esac
done
shift $((${OPTIND} - 1))

## DHCPクライアントのチェック
USE_DHCP=0
if pgrep "^dhcpcd$" >/dev/null; then
  USE_DHCP=1
  DHCP_CLIENT=dhcpcd
  RELEASE_OPT="-k"
elif pgrep "^dhclient$" >/dev/null; then
  USE_DHCP=1
  DHCP_CLIENT=dhclient
  RELEASE_OPT="-r"
fi

## デフォルトゲートウェイの一時保存
DEFAULT_GW=$(route | grep default | awk -F\  '{printf $2}')

## インターフェース名の書き出されたファイルがなければ開始、あれば停止とする
if [[ ! -f ${IFACEFILE} ]]; then
  start
else
  stop
fi

## EOF

(2008/11/13)DHCP関係の処理などを修正

Mandriva Linux 2009.0でのメモ

パッケージ
以前と同様で、以下のパッケージが必要。

TUN/TAP
Mandriva Linux 2009.0ではグループ「tun」のメンバは/dev/net/tunへ書き込む権限を持つので、「archivemountのMandriva Linux向けRPMパッケージを作成」の後半に書いた要領で、このグループに通常のユーザを追加しておく。更に「vboxusers」グループにも追加しておく必要がある。

sudo
基本的な設定をした上で、下のように設定すると、グループ「vboxusers」のメンバが上のスクリプト中で管理者権限で使用する必要のあるコマンド群をパスワードなしで実行できるようになる。

(略)
# Cmnd alias specification
Cmnd_Alias TUNTAP_BRIDGE=/sbin/modprobe, \
                         /sbin/ifconfig, \
                         /sbin/route, \
                         /sbin/dhclient, \
                         /sbin/dhcpcd, \
                         /usr/bin/VBoxTunctl, \
                         /usr/bin/VBoxAddIF, \
                         /usr/bin/VBoxDeleteIF, \
                         /usr/sbin/brctl
(中略)
%vboxusers  ALL = NOPASSWD: TUNTAP_BRIDGE
(以下略)

使用したバージョン:

*1:処理の順番の関係か、ここに書いても先にエラーが出てしまう

*2:同時に複数の仮想ネットワークカードを使用する場合で、3つだと「3」を指定し、「vbox0」「vbox1」「vbox2」が使用できるようになる