激詰してもらっている
tuntap ドライバ コードレビュー(Solaris 11.4 観点)
レビュー初版日: 2026-02-27
最終更新日: 2026-03-12
対象ブランチ: solaris
対象OS: Solaris 11.4
更新メモ(2026-03-12)
このドキュメントは初版(2026-02-27)の指摘を保持しつつ、現行ソースとの照合結果を反映した。
対応記録は本書末尾の「付録: レビュー対応記録(2026-03-12)」に統合。
なお、同日夜の追加修正として以下を反映済み:
ee2c813: tunerr() 越境書き込み修正
ceac8c9: M_PROTO/M_PCPROTO 最小長チェック追加
9cac718: DL_UNITDATA_REQ の qwriter(PERIM_OUTER) 依存解除
a73fd07: tun_frame() fan-out の dupmsg 削減
153172b: tun_bind_req() の再入安全化
0. Solaris 11.4 における TUN/TAP の位置づけ
Solaris 11.4 組み込みのトンネル機能
Solaris 11 には iptun ドライバが標準搭載されており、dladm create-iptun で作成できる。
dladm create-iptun -T ipv6 -a local=192.168.1.1 -a remote=192.168.2.1 ip6tun0
対応プロトコル: IPv4-in-IPv4(4in4)、IPv6-in-IPv4(6in4)、GRE
これはカーネル内での IP カプセル化用であり、ユーザー空間プロセスがパケットを直接読み書きする仕組みではない 。
なぜこのリポジトリが必要か
OpenVPN・WireGuard・vpnc などのユーザー空間 VPN は、
カーネルドライバが提供する /dev/tun や /dev/tap ファイルディスクリプタに対して
read()/write() を行うことでパケットを送受信する。
この「ユーザー空間向け TUN/TAP デバイス」は Solaris 11.4 に標準搭載されていないため、
本リポジトリのドライバが必要となる。
特徴
Solaris iptun
このリポジトリの tun/tap
用途
IP カプセル化(カーネル内)
ユーザー空間プロセスがパケットを読み書き
代表的な利用
6in4 トンネル、GRE
OpenVPN、WireGuard、vpnc
ユーザー空間への fd
なし
あり(read()/write() でパケット I/O)
/dev デバイス
/dev/iptun(管理用)
/dev/tun、/dev/tap
GLDv3 対応
あり
なし(DLPI/STREAMS スタイル)
Solaris 11.4 での確認コマンド
modinfo | grep tun # ロード済みドライバを確認
ls /dev/tun /dev/tap # デバイスノードの存在確認
pkg info driver/network/tun # IPS パッケージとして提供されているか確認
凡例
重要度
意味
🔴 CRITICAL
カーネルパニック・セキュリティ脆弱性・データ破壊につながるバグ
🟠 HIGH
動作不正・メモリ破壊・競合状態などの深刻な問題
🟡 MEDIUM
機能制限・移植性問題・非推奨 API の使用
🔵 LOW
コード品質・スタイル・軽微なバグ
1. tun.c — カーネルドライバ本体
🔴 [CRITICAL-1] TUNNEWPPA/TUNSETPPA でのオフバイワン(境界外配列アクセス)
ファイル : tun.c
箇所 : tun_ioctl() 内 TUNNEWPPA/TUNSETPPA ケース
// 現状(バグあり)
if ( p < -1 || p > TUNMAXPPA ){ // TUNMAXPPA = 20 のとき p==20 が通過してしまう
tuniocack (wq , mp , M_IOCNAK , 0 , EINVAL );
return ;
}
// → tun_ppa[20] は tun_ppa[TUNMAXPPA] で配列外(有効は0〜19)
tun_ppa は static struct tunppa *tun_ppa[TUNMAXPPA] で宣言され、
有効インデックスは 0 〜 TUNMAXPPA-1(= 0〜19)。
p == TUNMAXPPA (20) のとき p > TUNMAXPPA は偽となり、
tun_ppa[20] への境界外アクセスが発生する。
TUNSETPPA / tun_attach_req() にも同様の問題がある。
修正案 :
// TUNNEWPPA
if ( p < -1 || p >= TUNMAXPPA ){ // > → >=
// TUNSETPPA / tun_attach_req
if ( p < 0 || p >= TUNMAXPPA ){ // > → >=
🔴 [CRITICAL-2] TUNNEWPPA ioctl で mp->b_cont の NULL チェックなし
ファイル : tun.c
箇所 : tun_ioctl() 内 TUNNEWPPA ケース
case TUNNEWPPA :
p = * (int * )mp -> b_cont -> b_rptr ; // b_cont が NULL の場合カーネルパニック
M_IOCTL メッセージに b_cont がない(データブロックなし)場合、
mp->b_cont は NULL となる。
ユーザープログラムが不正な ioctl を発行した場合(または I_STR でなく直接 ioctl() を使った場合)に
カーネルパニックが発生する。
修正案 :
case TUNNEWPPA :
if (mp -> b_cont == NULL || MBLKL (mp -> b_cont ) < sizeof (int )) {
tuniocack (wq , mp , M_IOCNAK , 0 , EINVAL );
return ;
}
p = * (int * )mp -> b_cont -> b_rptr ;
🟠 [HIGH-1] tun_frame() とリスト変更操作の間に競合状態の可能性(再評価)
ファイル : tun.c
箇所 : tun_frame(), tun_attach_req(), tun_detach_req()
現在の DL_UNITDATA_REQ は qwriter を通さない direct path で処理される(9cac718)。
tun_frame() は ppa->p_str を走査しつつ配送するため、attach/detach/close と競合し得る点は設計課題として残る。
直列化で抑えるより、共有状態を明示ロック(per-PPA ロック)で守る方針が妥当。
Solaris 11.4 のマルチスレッド STREAMS では、ppa->p_str リストを安全に走査・変更するために、アクセスパターンを統一する必要がある。
現行方針 :
DL_UNITDATA_REQ の direct path は維持し、per-PPA ロック設計で整合性を担保する:
/* direct path (current) */
case DL_UNITDATA_REQ :
tun_unitdata_req (wq , mp );
break ;
詳細は tun-driver-per-ppa-lock-design.md を参照。
🟠 [HIGH-2] TAP モードで全 PPA が同一 MAC アドレスを持つ
ファイル : tun.c
箇所 : tun_generate_mac_addr(), tun_alloc_ppa()
// tun_generate_mac_addr(): ドライバロード時に一度だけ1つの MAC アドレスを生成
static struct ether_addr localmacaddr ; // グローバル1個
// tun_alloc_ppa(): 各 PPA に同じ MAC をコピー
bcopy (& localmacaddr , & ppa -> etheraddr , ETHERADDRL );
複数の tap インターフェース(tap0, tap1, ...)が同時に使われると、
すべてが同一の MAC アドレスを持つことになり、Ethernet フレームの配送が正しく機能しない。
修正案 : 各 PPA のユニーク MAC 生成(例: ベースアドレス + PPA ID):
static void tun_alloc_ppa_mac (struct tunppa * ppa )
{
bcopy (& localmacaddr , & ppa -> etheraddr , ETHERADDRL );
/* PPA ID を最終バイトに加算してユニーク化 */
ppa -> etheraddr .ether_addr_octet [5 ] =
(uchar_t )((localmacaddr .ether_addr_octet [5 ] + ppa -> id ) & 0xff );
}
🟡 [MEDIUM-1] u_long / u_short の使用(64ビット非対応型)
ファイル : if_tun.h
struct tunstr {
u_long flags ; // 64ビット環境では8バイト(本来32ビットで十分)
u_long state ; // DLPI state は t_uscalar_t を使うべき
u_long sap ; // SAP は 16ビット値
u_long minor ; // minor_t を使うべき
};
struct tundladdr {
u_short sap ; // uint16_t が望ましい
};
Solaris 11.4 の 64ビット amd64/sparcv9 カーネルでは u_long は8バイト。
DLPI プロトコルの仕様では state は t_uscalar_t(= uint32_t)であり、
サイズ不一致による構造体パディング/整合性の問題が発生する可能性がある。
修正案 :
struct tunstr {
struct tunstr * s_next ;
struct tunstr * p_next ;
queue_t * rq ;
struct tunppa * ppa ;
uint32_t flags ;
t_uscalar_t state ; /* DLPI state */
t_uscalar_t sap ;
minor_t minor ;
};
🟡 [MEDIUM-2] tunclose() でリストに str が見つからない場合 NULL 参照
ファイル : tun.c
箇所 : tunclose() (行 ~345)
for (prev = & tun_str ; (tmp = * prev ); prev = & tmp -> s_next )
if ( tmp == str ) break ;
* prev = tmp -> s_next ; // str が見つからなかった場合、tmp == NULL → パニック
str がリストに存在しない場合(状態異常時)、ループ終了時に tmp == NULL となり、
tmp->s_next で NULL ポインタ参照が発生する。
修正案 :
for (prev = & tun_str ; (tmp = * prev ); prev = & tmp -> s_next )
if ( tmp == str ) break ;
if (tmp != NULL ) /* 安全チェック追加 */
* prev = tmp -> s_next ;
else
cmn_err (CE_WARN , "tun: tunclose: str %p not found in list\n" , (void * )str );
🟡 [MEDIUM-3] tun_physaddr_req() で関数型が void なのに static でない
ファイル : tun.c
void tun_physaddr_req (queue_t * wq , mblk_t * mp ) // static が抜けている
他の static 関数と一貫性がなく、不要なカーネルシンボル露出になる。
修正案 : static void tun_physaddr_req(...) に変更。
🟡 [MEDIUM-4] 権限チェックが未実装(Solaris 11.4 特有)
ファイル : tun.c
箇所 : tunopen()
static int tunopen (queue_t * rq , dev_t * dev , int flag , int sflag , cred_t * credp )
{
// credp を使用した権限チェックが一切ない
Solaris 11.4 では最小権限モデル(Least Privilege)が重視される。
/dev/tun・/dev/tap はネットワークに直接アクセスできるため、
secpolicy_net_rawaccess(credp) または PRIV_NET_RAWACCESS のチェックを行うべき。
修正案 :
#include <sys/policy.h>
static int tunopen (queue_t * rq , dev_t * dev , int flag , int sflag , cred_t * credp )
{
int rc ;
if ((rc = secpolicy_net_rawaccess (credp )) != 0 )
return rc ;
// ...
🟡 [MEDIUM-5] Solaris 11.4 では IPS パッケージが標準(SVR4 パッケージ/手動インストール非推奨)
ファイル : Makefile.in, configure.in
現在は sudo make install で直接 /usr/kernel/drv/ に配置する方式。
Solaris 11.4 は IPS (Image Packaging System) が標準で、
手動ファイル配置は IPS の追跡外となり、パッケージ更新やゾーン管理と干渉する。
推奨対応 : IPS パッケージマニフェスト (.p5m) の作成。
最低限の例:
set name=pkg.fmri value=pkg:/driver/network/tuntap@1.3.2
set name=pkg.description value="TUN/TAP pseudo device driver"
driver name=tun
driver name=tap
file path=usr/kernel/drv/amd64/tun mode=0755
file path=usr/kernel/drv/amd64/tap mode=0755
file path=usr/kernel/drv/tun.conf mode=0644
file path=usr/kernel/drv/tap.conf mode=0644
file path=usr/include/net/if_tun.h mode=0644
🔵 [LOW-1] DBG マクロが GCC 拡張構文(Sun Studio 非互換)
ファイル : if_tun.h
#define DBG ( a ... ) // GCC 拡張(GNU C)
a... は GCC/Clang 独自の可変引数マクロ省略記法。
Solaris Studio (Oracle Developer Studio) でビルドする場合はコンパイルエラーになる。
修正案 :
#ifdef TUN_DEBUG
#define DBG cmn_err
#else
#define DBG (...) ((void)0) // C99 標準の可変引数マクロ
#endif
🔵 [LOW-2] configure.in でGCCバージョンチェックに誤った変数名
ファイル : configure.in
GCC_MAJOR_VERSION=` echo $GCC_VERSION | cut -f 1 -d .`
GCC_MINOR_VERSION=` echo $GCC_VERSION | cut -f 2 -d .`
...
if test " $GCC_MAJOR_VERSION " -ge 4 \
-a " $MINOR_VERSION " -ge 5 ; then # ← $GCC_MINOR_VERSION であるべき
KCFLAGS=" $KCFLAGS -m64 -mcmodel=large -mno-red-zone"
$GCC_MINOR_VERSION ではなく $MINOR_VERSION(OS のマイナーバージョン = 11)が使われている。
Solaris 11 では $MINOR_VERSION は常に 11 なので 11 >= 5 は常に真となり、
GCC >= 4 の場合は常に -mcmodel=large が選ばれる。
現実には GCC 4.x+ が前提なので偶発的に正しく動いているが、潜在的なバグ。
修正案 :
if test " $GCC_MAJOR_VERSION " -ge 4 \
-a " $GCC_MINOR_VERSION " -ge 5 ; then
🔵 [LOW-3] TUNTAP_TUN 時の DL_ENABMULTI_REQ / DL_DISABMULTI_REQ の扱い
ファイル : tun.c
箇所 : tun_dlpi() 内
#elif defined(TUNTAP_TUN)
case DL_ENABMULTI_REQ :
case DL_DISABMULTI_REQ :
#endif
default :
tundlerrack (wq , mp , prim , DL_UNSUPPORTED , 0 );
TUN モードでは DL_ENABMULTI_REQ / DL_DISABMULTI_REQ を fall-through で DL_UNSUPPORTED にエラーとしている。
TAP モードでは DL_OK_ACK を返す。
Solaris 11.4 の IP スタックは場合によって multicast ioctl を発行することがあり、
エラー時の動作が想定外になりうるため、TUN でも DL_OK_ACK を返す方が堅牢。
🔵 [LOW-4] Makefile.in clean ターゲットに重複・誤記
ファイル : Makefile.in
clean :
rm -f tun tap *.o *~
rm -f tun tun *.o *~ # "tun tun" という重複、かつ tap が抜けている
修正案 :
clean :
rm -f tun tap *.o *~
2. tun-test/tunctl.c — テストユーティリティ
🟠 [HIGH-3] get_devname() でのメモリリーク
ファイル : tun-test/tunctl.c
char * get_devname (char * interface )
{
char * devname = NULL ;
int i ;
for ( i = 0 ; i < TUNMAXPPA ; i ++ ){ // ループが TUNMAXPPA 回まわる
if ((instance = strpbrk (interface , "0123456789" )) == NULL )
continue ;
devname = malloc (30 ); // 毎回 malloc するが前の領域を free しない
bzero (devname , 30 );
strncpy (devname , interface , instance - interface );
}
return (devname ); // 最後の malloc した1つだけ返し、残りは全てリーク
}
malloc(30) が最大 TUNMAXPPA (20) 回呼ばれるが、
最後の1つ以外は free() されないメモリリークが発生する。
また、strpbrk() は毎回同じポインタを返すため、
ループ自体に意味がなく、同じ処理を20回繰り返すだけになっている。
修正案 :
char * get_devname (char * interface )
{
char * instance ;
char * devname ;
if ((instance = strpbrk (interface , "0123456789" )) == NULL )
return NULL ;
devname = malloc (instance - interface + 1 );
if (devname == NULL )
return NULL ;
strncpy (devname , interface , instance - interface );
devname [instance - interface ] = '\0' ;
return devname ;
}
🟠 [HIGH-4] main() でのメモリリーク(node・get_devname() 返り値)
ファイル : tun-test/tunctl.c
if (node == NULL ){
node = malloc (NODE_LEN );
bzero (node , NODE_LEN );
snprintf (node , NODE_LEN , "/dev/%s" , get_devname (tun )); // get_devname() 返り値をfreeしない
}
// ...
close (ip_fd );
close (tun_fd );
exit (0 ); // node も get_devname() の返り値も free されない
プロセス終了時にOSが回収するため実害は少ないが、静的解析ツールでの誤検出を避けるためにも修正が望ましい。
修正案 :
if (node == NULL ){
char * devname = get_devname (tun );
if (devname == NULL ) {
fprintf (stderr , "Invalid device name: %s\n" , tun );
exit (1 );
}
node = malloc (NODE_LEN );
if (node == NULL ) { perror ("malloc" ); exit (1 ); }
snprintf (node , NODE_LEN , "/dev/%s" , devname );
free (devname );
}
🟡 [MEDIUM-6] delete_if() で ARP unlink が TAP 専用だが理由のコメントが不明確
ファイル : tun-test/tunctl.c
if ( strncmp (tun , "tap" , 3 ) == 0 ){
/* just in case, unlink arp's stream */
arp_muxid = ifr .lifr_arp_muxid ;
if (ioctl (ip_fd , I_PUNLINK , arp_muxid ) < 0 ){
/* ignore err */
}
}
Solaris 11.4 では ARP の muxid は IPv4インターフェースのプランブ時に設定される。
arp_muxid が有効かどうか(ゼロでないか)のチェックなしに I_PUNLINK を呼ぶとエラーになりうる。
エラーを無視しているため動作上は問題ないが、意図を明示すべき。
修正案 :
if ( strncmp (tun , "tap" , 3 ) == 0 && ifr .lifr_arp_muxid != 0 ){
(void ) ioctl (ip_fd , I_PUNLINK , ifr .lifr_arp_muxid ); /* best effort */
}
🔵 [LOW-5] add_if() で IPv6 プランブが未対応
ファイル : tun-test/tunctl.c
現在の add_if() は IPv4 (/dev/ip) への I_PLINK のみ実装。
Solaris 11.4 で IPv6 対応インターフェースとして使う場合、
/dev/ip6 へのリンクも必要。
OpenVPN + IPv6 トンネルなど用途によっては問題になる。
3. tun-test/test.sh — テストスクリプト
🟠 [HIGH-5] nobuild 変数のタイポ(テストが常に実行される可能性)
ファイル : tun-test/test.sh
main () {
if [ " nobuild" = " $1 " ]; then
nobuild=1
cd ../
else
nobuld=0 # ← タイポ: nobuild ではなく nobuld
fi
...
if [ " $nobuild " -eq 0 ]; then # $nobuild は未設定 → 空文字列
nobuild 以外の引数(または引数なし)で呼ばれた場合、
else 節で nobuld=0(タイポ)と設定し、
$nobuild は未設定のままになる。
[ "" -eq 0 ] は sh 実装により動作が異なり、
Solaris /usr/xpg4/bin/sh では算術比較エラーとなる可能性がある。
修正案 :
else
nobuild=0 # nobuld → nobuild
fi
🟠 [HIGH-6] TESTLOGFILE が初期化前に使用される
ファイル : tun-test/test.sh
main () {
echo " ##" | tee -a $TESTLOGFILE # ← init() 前に呼ばれる
echo " ## Initializing test env" | tee -a $TESTLOGFILE
echo " ##" | tee -a $TESTLOGFILE
init # ← ここで TESTLOGFILE が設定される
init() が呼ばれる前に $TESTLOGFILE を使用しているため、
最初の3行のログはファイルに書かれない(tee -a "" はエラー扱い)。
修正案 : init() を最初に呼ぶ:
main () {
if [ " nobuild" = " $1 " ]; then
nobuild=1
cd ../
else
nobuild=0
fi
init # ← 最初に呼ぶ
echo " ##" | tee -a $TESTLOGFILE
echo " ## Initializing test env" | tee -a $TESTLOGFILE
🔵 [LOW-6] uninstall() で make uinstall のタイポ
ファイル : tun-test/test.sh
uninstall (){
sudo make uinstall | tee -a $TESTLOGFILE # ← uninstall の typo
make uinstall は存在しない Makefile ターゲット → エラーになるが、test.sh の uninstall() は呼ばれていないため現状は問題なし。
修正案 :
sudo make uninstall | tee -a $TESTLOGFILE
🔵 [LOW-7] loadcheck() で modinfo の出力が空の場合の [ -z $loaded ] は引用符が必要
ファイル : tun-test/test.sh
loadcheck (){
loaded=` modinfo| grep " $1 (TUN/TAP" `
if [ -z $loaded ]; then # $loaded を引用符で囲む必要がある
$loaded が空の場合、[ -z $loaded ] は [ -z ] となり、
Solaris sh では構文エラーになりうる。
修正案 :
if [ -z " $loaded " ]; then
4. configure.in / Makefile.in
🔴 [CRITICAL-3] GCC (x86-64) でカーネルモジュールに SSE/MMX 命令が生成される
ファイル : configure.in
箇所 : amd64 ケースの KCFLAGS 設定
参照 : Writing Device Drivers in Oracle Solaris 11.4 — Compiling and Linking the Driver
Solaris x86 カーネルは MMX / SSE 命令をサポートしない。
カーネルコンテキストでこれらの命令を実行すると カーネルパニック になる。
この制約は GCC でも Studio でも同様に適用される。
Oracle 公式ドキュメントの記述(E61061 — eqbvb)
Oracle Solaris 11.4 "Writing Device Drivers" ガイド(E61061)は以下を明記している:
x86 アーキテクチャー向けにコンパイルする場合は、コンパイルしても MMX 命令または
SSE 命令が生成されないことを確認する必要があります。x86 カーネルでは MMX 命令および
SSE 命令はサポートされません。MMX 命令または SSE 命令を使用するとカーネルパニックが
発生するため、使用しないようにしてください。
Oracle Developer Studio の場合: -xregs=no%float
GCC の場合: -mno-mmx -mno-sse -mno-sse2
また同ドキュメントが示す amd64 向け GCC の公式推奨ビルドコマンド :
gcc -D_KERNEL -m64 -mcmodel=kernel -mno-red-zone \
-ffreestanding -nodefaultlibs \
-mno-mmx -mno-sse -mno-sse2 \
-c mydriver.c
ld -r -o mydriver mydriver.o
初版レビュー時点(2026-02-27)の configure.in との差分
フラグ
公式推奨
現状 configure.in
影響
-mno-red-zone
✅ 必須
✅ あり
—
-mno-mmx -mno-sse -mno-sse2
✅ 必須
❌ なし
カーネルパニック
-ffreestanding
✅ 必須
❌ なし
標準ライブラリ依存コードが混入の恐れ
-nodefaultlibs
✅ 推奨
❌ なし
標準ライブラリのシンボルリンクの恐れ
-mcmodel=kernel/large
✅ 必須
✅ あり (large)
—
-m64
✅ 必須
✅ あり
—
GCC x86-64 で SSE が生成される理由
x86-64 ABI では SSE2 がベースライン命令セット に含まれており、GCC は SSE2 を自由に使用してよいとみなす
GCC 4.x 以前は -O0 相当ではほぼ SSE を使わないが、-O2 以上では整数演算・メモリコピーにも XMM レジスタを積極利用する
GCC 15 では自動ベクトル化 (auto-vectorization) がデフォルト有効で、ループやメモリ操作に AVX/AVX2 を含む SIMD 命令が生成されうる
Sun Studio 12.4 と GCC の対応関係
コンパイラ
問題フラグ
禁止フラグ
Studio 12.4+
デフォルト最適化で SSE
-xregs=no%float
GCC (x86-64)
デフォルト ABI + -O2 以上
-mno-sse -mno-sse2 -mno-mmx(または -mgeneral-regs-only)
修正案
-mgeneral-regs-only は GCC 5 以降で利用可能(GCC 15 で確実にサポート済み)。
汎用レジスタのみ使用を許可し、FPU/MMX/SSE/AVX レジスタを一切使わないことをコンパイラに強制する
(-mno-sse -mno-sse2 -mno-mmx の上位互換)。
# configure.in の amd64 / GCC ケース(修正後)
if test " $GCC " = yes; then
if test " $GCC_MAJOR_VERSION " -ge 4 \
-a " $GCC_MINOR_VERSION " -ge 5 ; then
KCFLAGS=" $KCFLAGS -m64 -mcmodel=large -mno-red-zone \
-ffreestanding -mgeneral-regs-only"
else
KCFLAGS=" $KCFLAGS -m64 -mcmodel=kernel -mno-red-zone \
-ffreestanding -mgeneral-regs-only"
fi
GCC 5 未満(-mgeneral-regs-only が使えない場合)の代替:
KCFLAGS=" $KCFLAGS -ffreestanding -mno-mmx -mno-sse -mno-sse2 -mno-sse3 -mno-avx"
-ffreestanding の役割 : GCC に「ホスト環境(標準 C ランタイム)が存在しない」と伝える。
これにより memcpy()・memset() などを GCC 組み込みに置換する挙動を抑制し、
カーネル内の実装(bcopy・bzero)との整合性を保つ。
PERF-1 との関係
-O2 の追加(PERF-1)と本修正はセットで適用すること。
-O2 のみ追加して SSE 禁止フラグを入れないと、最適化によって SSE 命令が生成され
カーネルパニックが発生するリスクが高まる。
🟡 [MEDIUM-7] configure.in で AC_PREFIX_DEFAULT に引数なし
ファイル : configure.in
AC_PREFIX_DEFAULT(PREFIX) はデフォルトプレフィックスを設定するマクロで、
引数必須。引数なしは Autoconf の警告/エラーの原因となる。
修正案 : 削除または明示的に指定:
AC_PREFIX_DEFAULT ( [ /usr/local] )
🔵 [LOW-8] configure.in が古い AC_INIT 形式 / autoconf 2.13 前提
ファイル : configure.in
AC_INIT ( [ tuntap] , [ 1.3.2] , [ admin2@whiteboard.ne.jp] )
拡張子が .in で configure.in を使っているのは旧来の慣習。
Autoconf 2.50 以降は configure.ac が標準。
Solaris 11.4 上の最新 Autoconf ではこのままでも動作するが、
将来の互換性のために configure.ac へのリネームを推奨。
5. Solaris 11.4 アーキテクチャ上の制限事項
🟡 [MEDIUM-8] GLDv3 非対応 — dladm / ipadm との統合なし
Solaris 11.4 のネットワーク管理は GLDv3 (Generic LAN Driver version 3)ベースの
mac(9E) ドライバフレームワークが標準。
本ドライバは旧来の DLPI/STREAMS スタイルで実装されており、以下の機能が未対応:
dladm show-link でのインターフェース表示
dladm create-vnic などの仮想 NIC 作成との統合
IPMP (IP Network Multipathing) サポート
Crossbow ネットワーク仮想化との統合
Vanity naming (インターフェース名の自由設定)
現状の I_PLINK を使った手動プランブは機能するが、
ipadm create-if などのモダンな管理ツールでは使えない。
Solaris 11.4 で完全な統合を得るには mac(9E) ベースの再実装が望ましいが、
これは大きな作業になる。既存の用途(OpenVPN 互換など)では
現状の DLPI スタイルでも動作する。
🟡 [MEDIUM-9] tun.conf / tap.conf で instance=0 のみ宣言
ファイル : tun.conf, tap.conf
name="tun" parent="pseudo" instance=0;
instance=0 のみの宣言では、tunattach() が呼ばれるのは1回のみ。
これは複数の物理デバイスのような構造ではなく、CLONE デバイスとして機能するため
現状で正しい設計になっている。ただし、Solaris 11.4 の IPS パッケージ経由でインストールする場合、
ドライバのバインディングは .conf ファイルではなく add_drv(1M) の引数で制御する方式が主流。
6. パフォーマンス改善観点
🔴 [PERF-1] KCFLAGS に最適化フラグ (-O2) が設定されていない
ファイル : configure.in
KCFLAGS=" $CFLAGS $KCFLAGS -D_KERNEL -I."
# -O や -O2 が明示されていない → GCC のデフォルト -O0(最適化なし)でビルドされる
GCC のデフォルトは最適化なし(-O0)。カーネルモジュールは通常 -O2 でビルドする。
最適化なしでは、インライン展開・デッドコード除去・ループ最適化が行われず、
実測でスループットが数十% 低下することがある。
⚠️ 重要 : -O2 を追加する場合は CRITICAL-3 の SSE 禁止フラグ (-mgeneral-regs-only) と
必ずセットで適用すること 。-O2 のみ追加すると GCC が SSE/AVX 命令を積極生成し、
x86 Solaris カーネルパニックの原因となる。
修正案 (configure.in、amd64/GCC ケース):
# KCFLAGS への -O2 追加(架線の KCFLAGS 設定のさらに前に)
KCFLAGS=" $CFLAGS $KCFLAGS -O2 -D_KERNEL -I."
# amd64 ケースに -mgeneral-regs-only を追加(SSE/MMX 禁止)
KCFLAGS=" $KCFLAGS -m64 -mcmodel=large -mno-red-zone -mgeneral-regs-only"
🟠 [PERF-2] tun_eth_hdr() が毎パケット allocb() を呼ぶ
ファイル : tun.c
箇所 : tun_eth_hdr(), tun_unitdata_ind()
// tun_eth_hdr(): 14バイトの Ethernet ヘッダのために毎回 allocb()
size = sizeof (struct ether_header ); // 14 バイト
if ( !(nmp = allocb (size , BPRI_LO )) ){
freemsg (mp );
return NULL ;
}
nmp -> b_cont = mp ; // 元のメッセージを後ろに連結
// tun_unitdata_ind(): DLPI ヘッダのために毎回 allocb()
size = sizeof (dl_unitdata_ind_t ) + TUN_ADDR_LEN + TUN_ADDR_LEN ;
if ( !(nmp = allocb (size , BPRI_LO )) ){
freemsg (mp );
return NULL ;
}
allocb() は毎回 kmem_alloc() を呼ぶわけではないが(STREAMS の mblk プールから取得)、
高スループット時には mblk の確保・解放が頻繁に発生する。
改善案 : 送信側(tun_eth_hdr)では、元の mblk の前方に余白があれば
b_rptr を後退させてヘッダを直接書き込む(ゼロコピー的な手法):
/* 元のブロックに前方余白があればそこに Ethernet ヘッダを書き込む */
if (mp -> b_rptr - mp -> b_datap -> db_base >= sizeof (struct ether_header )
&& DB_REF (mp ) == 1 ) {
mp -> b_rptr -= sizeof (struct ether_header );
/* ヘッダをここに書き込む */
return mp ;
}
/* 余白がなければ従来通り allocb */
🟠 [PERF-3] tun_msg_len() で msgdsize() を使わない独自実装
ファイル : tun.c
箇所 : tun_msg_len()
static int tun_msg_len (mblk_t * mp )
{
int len = 0 ;
do {
len += MBLKL (mp );
} while (( mp = mp -> b_cont ) != NULL );
return (len );
}
Solaris STREAMS には msgdsize(mp) という標準関数があり、
同等の処理をカーネル内で最適化済みの実装で行う。
独自実装は不要であり、標準関数に置き換えたほうが保守性・パフォーマンスともに優れる。
修正案 :
// tun_msg_len() の呼び出し箇所を msgdsize() に置換
// type = tun_msg_len(nmp);
type = (int )msgdsize (nmp );
tun_msg_len() 関数自体を削除可能。
🟠 [PERF-4] mi_maxpsz = 2048 — Jumbo Frame 非対応によるスループット制限
ファイル : tun.c
static struct module_info tunminfo = {
125 ,
"tap" / "tun" ,
21 , /* mi_minpsz */
2048 , /* mi_maxpsz ← Ethernet 標準 MTU は 1500、Jumbo Frame は最大 9000 */
(32 * 1024 ), /* mi_hiwat */
21 /* mi_lowat */
};
mi_maxpsz = 2048 は Jumbo Frame(最大 9000 バイト程度)を想定していない。
VPN トンネルで Jumbo Frame を使う環境(データセンター内部など)では、
フラグメント処理が強制され、スループットが大幅に低下する。
改善案 :
#define TUN_MAX_PACKET (9 * 1024) /* Jumbo Frame 対応 */
static struct module_info tunminfo = {
...
TUN_MAX_PACKET , /* mi_maxpsz */
(64 * 1024 ), /* mi_hiwat (スループット優先なら拡大) */
21
};
🟠 [PERF-5] モジュールキュー mi_hiwat = 32KB と TUN_SO_HIWAT = 1MB の不整合
ファイル : tun.c
/* モジュール情報 */
(32 * 1024 ), /* mi_hiwat = 32KB(モジュールキューの上限) */
/* TUNNEWPPA ioctl でストリームヘッドに設定 */
#define TUN_SO_HIWAT 1024*1024 /* ストリームヘッド = 1MB */
sop -> so_hiwat = TUN_SO_HIWAT ;
ストリームヘッド(ユーザー空間側)のバッファは 1MB に設定されているが、
ドライバモジュール内キューの上限は 32KB のまま。
ユーザー空間が大量データを書き込む際に、モジュールキューが早期に詰まり
フロー制御(canputnext() = false)が頻繁に発動する。
改善案 : mi_hiwat を TUN_SO_HIWAT に合わせるか、
または ndd(1M) 等でチューニング可能なパラメータとして公開する:
(256 * 1024 ), /* mi_hiwat (32KB → 256KB に拡大) */
🟡 [PERF-6] ppa->p_str リストの線形スキャン
ファイル : tun.c
箇所 : tun_frame()
for ( tmp = ppa -> p_str ; tmp ; tmp = tmp -> p_next ){
/* ... SAP フィルタリング、MAC アドレス比較 ... */
}
各フレームのルーティング時に ppa->p_str リスト全体を線形スキャンする。
通常の OpenVPN 使用では1〜2ストリームのみで問題ないが、
多数のクライアントが同時接続するサーバーサイド用途では
SAP をキーとしたハッシュテーブルにするとスケールする。
改善案 (多ストリーム用途の場合):
/* SAP → tunstr のハッシュテーブル(2^n サイズ) */
#define TUN_SAP_HASH_SIZE 16
struct tunstr * sap_hash [TUN_SAP_HASH_SIZE ];
#define TUN_SAP_HASH (sap ) ((sap) & (TUN_SAP_HASH_SIZE - 1))
🟡 [PERF-7] qwriter(PERIM_OUTER) のエスカレーションコスト
ファイル : tun.c
/* tunwput() 内 */
case M_IOCTL :
qwriter (wq , mp , tun_ioctl , PERIM_OUTER ); // 外側ペリメーター取得
break ;
qwriter(PERIM_OUTER) はキュー全体の外側ペリメーターを排他ロックで取得するため、
他スレッドが内側ペリメーターを保持している間は待機が発生する。
ioctl は低頻度なので通常は問題ないが、
tun_attach_req / tun_detach_req と tun_frame が頻繁に交錯する場合は
ロック競合のボトルネックになりうる。
データパス(M_DATA)は qwriter を経由しない直接処理になっているのは適切。
ioctl・attach・detach のコントロールパスは現状維持で問題ない。
🔵 [PERF-8] TUN_SO_HIWAT がコンパイル時定数で実行時チューニング不可
ファイル : if_tun.h, tun.c
#define TUN_SO_HIWAT 1024*1024 /* 1MB 固定 */
高スループット環境(10GbE 以上)では 1MB では不足することがある。
Solaris の ndd(1M) コマンドや ddi_prop_* API を使って
チューニングパラメータとして公開すると運用時に調整可能になる。
改善案 :
/* ドライバ変数として公開 */
static uint_t tun_so_hiwat = 1024 * 1024 ; /* デフォルト 1MB、ndd でチューニング可 */
パフォーマンス改善 影響度サマリー
No.
内容
期待効果
実装コスト
PERF-1
KCFLAGS に -O2 追加
高 (数十% のスループット向上)
低
PERF-2
tun_eth_hdr() の allocb 削減
中(高負荷時の CPU 削減)
中
PERF-3
tun_msg_len() → msgdsize()
低(コード整理効果が主)
低
PERF-4
mi_maxpsz 拡大(Jumbo Frame)
高 (Jumbo Frame 環境限定)
低
PERF-5
mi_hiwat 拡大(32KB → 256KB)
中(フロー制御発動頻度低減)
低
PERF-6
ppa->p_str ハッシュ化
低(多ストリーム限定)
高
PERF-7
qwriter エスカレーション最適化
低(コントロールパスは低頻度)
中
PERF-8
TUN_SO_HIWAT を ndd チューニング可に
低(運用柔軟性向上)
中
最優先 : PERF-1(-O2 追加)は1行の変更で最大の効果が見込める。
次点 : PERF-4(mi_maxpsz 拡大)と PERF-5(mi_hiwat 拡大)も変更量が小さく効果が大きい。
まとめ・優先度別アクション一覧(2026-03-12 時点)
現行ソース(tun.c / if_tun.h / configure.in / tun-test/*)と
本書末尾の対応記録を照合した結果を反映。
No.
優先度
状態
内容
ファイル
CRITICAL-1
CRITICAL
完了
オフバイワン境界外アクセス修正
tun.c
CRITICAL-2
CRITICAL
完了
mp->b_cont の NULL/長さ検証を追加
tun.c
CRITICAL-3
CRITICAL
完了
SSE/MMX 禁止フラグを導入(-mno-*, -xregs=no%float)
configure.in
HIGH-1
HIGH
要再評価
DL_UNITDATA_REQ は direct path。共有状態同期は per-PPA ロック設計で継続課題
tun.c
HIGH-2
HIGH
完了
TAP の PPA ごとに MAC を一意化
tun.c
HIGH-3
HIGH
完了
get_devname() のリーク修正
tun-test/tunctl.c
HIGH-4
HIGH
完了
main() の devname/node 解放対応
tun-test/tunctl.c
HIGH-5
HIGH
完了
nobuild タイポ修正
tun-test/test.sh
HIGH-6
HIGH
完了
TESTLOGFILE 初期化順序修正
tun-test/test.sh
HIGH-7
HIGH
完了
tunerr() の越境書き込み修正
tun.c
HIGH-8
HIGH
完了
M_PROTO/M_PCPROTO の最小長チェックを追加
tun.c
MEDIUM-1
MEDIUM
完了
u_long/u_short を uint32_t/t_uscalar_t/minor_t/uint16_t へ更新
if_tun.h
MEDIUM-2
MEDIUM
完了
tunclose() の NULL 保護追加
tun.c
MEDIUM-3
MEDIUM
完了
tun_physaddr_req() を static 化
tun.c
MEDIUM-4
MEDIUM
保留
tunopen() の権限チェック(運用影響ありのため保留)
tun.c
MEDIUM-5
MEDIUM
完了
IPS パッケージマニフェストを追加
pkg/tuntap.p5m
MEDIUM-6
MEDIUM
完了
ARP muxid のゼロチェック追加
tun-test/tunctl.c
MEDIUM-7
MEDIUM
完了
AC_PREFIX_DEFAULT([/]) を設定
configure.in
MEDIUM-8
MEDIUM
保留
GLDv3 / dladm 非対応(設計上の制限)
—
MEDIUM-9
MEDIUM
完了
IPS 向け driver アクションを manifest に定義
pkg/tuntap.p5m
LOW-1
LOW
完了
DBG を C99 可変引数マクロへ更新
if_tun.h
LOW-2
LOW
完了
GCC 分岐判定に GCC_MINOR_VERSION を使用
configure.in
LOW-3
LOW
完了
TUN でも DL_ENABMULTI_REQ/DL_DISABMULTI_REQ を DL_OK_ACK 応答(実体制御は未実装)
tun.c
LOW-4
LOW
完了
clean ターゲット重複行を削除
Makefile.in
LOW-5
LOW
完了
tunctl に /dev/ip6 への best-effort plumb/unplumb を追加
tun-test/tunctl.c
LOW-6
LOW
完了
make uninstall へ修正
tun-test/test.sh
LOW-7
LOW
完了
loadcheck() 実装見直しで引用符問題を解消
tun-test/test.sh
LOW-8
LOW
保留
configure.in → configure.ac 移行
configure.in
PERF-9
MEDIUM
完了
tun_frame() fan-out を 1-pass 化し dupmsg を削減
tun.c
MEDIUM-10
MEDIUM
完了
tun_bind_req() の static 作業領域をスタック化
tun.c
7. 今後の対応
2026-03-12 時点で、CRITICAL 項目(1-3)と追加 HIGH 項目(7-8)は完了済み。
次の優先順位で未対応項目を処理する。
multicast 制御実体化(DL_ENABMULTI_REQ / DL_DISABMULTI_REQ)
per-PPA ロック導入(HIGH-1 再評価項目)
kstat 可観測性の追加と性能回帰実測(PPS/多stream)
MEDIUM-4: tunopen() 権限チェック(運用影響確認後)
設計課題: MEDIUM-8(GLDv3 非対応), LOW-8(configure.ac 移行)
更新時は「問題提起」と「対応済みステータス」を分離し、
「まとめ・優先度別アクション一覧」を先に更新してから個別セクションを追記する。
コードについて
このツリーに収められているのは Solaris 用の STREAMS ベースの TUN/TAP 擬似デバイスドライバです。
if_tun.h と tun.c が本体で、tun-test 以下にはユーザランドのテストプログラムが入っています。
全体構造
tun.c はモジュール/ドライバとしてロードされる STREAMS ドライバで、TUN か TAP のどちらか一方でコンパイルされる (TUNTAP_TAP もしくは TUNTAP_TUN が必須)。
tun_str リストと tun_ppa[] 配列で開かれたストリームと仮想インタフェース (PPA: per‑path adapter) を管理する。
tunppa は各仮想インタフェースの制御ストリームと、TAP では MAC アドレスを保持。
tunstr はストリーム単位の状態を保持。フラグや DLPI 状態、SAP、割り当てられた PPA へのポインタなどを持つ。
if_tun.h には上記構造体と I/OCTL 定義、フラグのマクロが書かれています。
PROMISC/ALL などのスニッファ条件を判定する SNIFFER() マクロもここにあります。
ドライバのエントリポイント
_init, _fini, _info で mod_install/mod_remove/mod_info を呼び、ロード/アンロードを担当。
tunprobe/tunattach/tundetach/tuninfo が dev_ops に登録され、デバイスノードの作成や情報取得を行う。
tunattach では ddi_create_minor_node で /dev/tun あるいは /dev/tap のクローンデバイスを作成し、TAP ならローカル MAC を生成。
STREAMS キューとコールバック
tunrinit/tunwinit によって、リードパスは tunopen/tunclose、ライトパスは tunwput/tunwsrv が呼ばれる。
tunopen は CLONEOPEN に対応し、新しい minor 番号の割り当てと tunstr 構造体の初期化、リストへの挿入を行う。
tunclose はストリームを PPA から切り離し、制御ストリームであれば PPA 自体を解放。最後に tunstr をリストから削除してメモリを返す。
IOCTL/DLPI ハンドラ
tun_ioctl がライトキューに入る I/OCTL へ対応。PPA の作成 (TUNNEWPPA)、ストリームの PPA への紐付け (TUNSETPPA)、取得などを実装する。
IOCTL 以外にも DLPI プリミティブを処理する多数の関数があり、リクエストの検査と ACK/NAK の返信をする。
tun_info_req、tun_attach_req、tun_detach_req、tun_bind_req など BOOTP シリーズに対応。
プロミスコン(on/off) ではフラグを変更し、物理アドレス要求や単一データ要求なども実装。
TAP モードでは tun_set_physaddr_req や tun_enabmulti_req など付随処理もある。
これらの DLPI 関連関数は共通のヘルパとして
tunchmsg(メッセージ確保/初期化)、tundlokack/tundlerrack(ACK/エラー返信)、
tunerr(M_ERROR 送出)を利用しています。
データフレームの経路
tun_unitdata_req はプロトコルストリームから制御ストリームへ送るフレームを形成。TAP では引数からイーサネットヘッダを付加し、TUN ではヘッダをゼロクリアする。
tun_unitdata_ind は制御ストリームからプロトコルストリームへ送るインジケーションを組み立てる。TAP/TUN で宛先・送信元アドレスの扱いが異なる。
tun_eth_hdr がイーサネットヘッダの生成を行う(TAP では実アドレス、TUN ではタイプだけ)。
フレームの振り分けは tun_frame に集約される。ストリームの種類とフラグに応じて:
PPA に未接続なら捨てる。
PPA に接続済みストリーム群を走査し、スニッファ条件 (TUN_ALL_PHY/SAP/MULTI) に合致すれば複製して配信。生データ (TUN_RAW) 指定なら DLPI ヘッダを付けずに送る。
制御ストリームから来たデータは、各プロトコルストリームへフィルタリングして転送。TAP モードでは MAC レベルで eligible 判定 (tun_frame_is_eligible)、TUN では SAP 値チェック。
プロトコル側から来たデータは制御ストリームへ送り、キューがいっぱいならキューィング/ドロップ。
TAP固有ロジック
tun_generate_mac_addr でランダムにローカル MAC を生成し、各 PPA にコピー。
tun_frame_is_eligible で受信ストリームが受け取るべきフレームかどうかを確認(ブロードキャスト、マルチキャスト、宛先アドレス比較、SAP 比較など)。
tun_msg_len が 802.3 長さフィールドを計算。
そのほかユーティリティ
tun_alloc_ppa は PPA を新規確保または指定番号で割り当て、配列に登録。
tunpromiscon_req/tunpromiscoff_req がプロミスコン状態ビットを管理。
tun_physaddr_req は PPA の MAC を返す。
動作の流れ
/dev/tun あるいは /dev/tap に open すると tunopen が呼ばれ、tunstr が作られてストリームが動作開始。
TUNNEWPPA IOCTL で仮想デバイスを作成し、返されたインスタンス番号を TUNSETPPA でプロトコルストリームにセット。
DLPI の ATTACH→BIND→UNITDATA_REQ が順次送受信され、ソケット風にパケットをやり取りできる。
制御ストリーム側 (TUN_CONTROL フラグ) には TAP/TUN が生成したパケットが流れ、プロトコルストリームへフェッチされる。
テストコード
tun-test/ 以下には簡単な CLI 付きテスト (test.sh と tunctl.c) があり、ユーザーランドから PPA の生成やパケット送受信を試せるようになっています。
このドライバは UNIX STREAMS と DLPI プロトコルに密接に結びついており、TUN と TAP の振る舞いをビルド時に切り替えることで、パケットをユーザー空間へ中継する仕組みをシンプルに実装しています。各関数は状態遷移とエラーチェックを細かく行い、メッセージの再利用や複製によって効率的にフレームを振り分けます。必要であれば各ブロックをさらに掘り下げて説明できますので、気になる箇所があればお知らせください。
付録: レビュー対応記録(2026-03-12)
概要
リポジトリレビューで抽出した不具合・リスクに対して、tun.c / tun-test/tunctl.c / tun-test/test.sh を修正し、回帰確認用の軽量テストを追加した。
対応内容
TUNMAXPPA の境界チェック修正(off-by-one)
p > TUNMAXPPA を p >= TUNMAXPPA に変更し、配列外アクセスを防止。
対象: tun_ioctl(TUNNEWPPA/TUNSETPPA), tun_attach_req
M_IOCTL 入力検証の追加
mp->b_cont の NULL チェック、sizeof(int) 未満の長さチェックを追加。
不正な ioctl ペイロードでの不正参照を防止。
二重アタッチ防止
既に str->ppa != NULL の場合、TUNNEWPPA / TUNSETPPA を拒否。
DL_ATTACH_REQ は「既存 PPA と同一の場合は許可・異なる場合は拒否」に調整し、IF_UNITSEL 経路との互換性を維持。
tun_detach_req / tunclose では unlink 時の NULL 保護を追加。
メッセージ長・オフセット検証強化
DL_UNITDATA_REQ の最小長チェックを追加。
dl_dest_addr_offset / dl_dest_addr_length の妥当性を検証し、DL_BADADDR を返すよう修正。
フレーム先頭参照前に msgdsize と pullupmsg で長さを保証。
TUN: 1byte 以上
TAP: sizeof(struct ether_header) 以上
DL_SET_PHYS_ADDR_REQ の検証追加
DL_SET_PHYS_ADDR_REQ_SIZE の最小長チェック追加。
ppa == NULL の状態チェック追加。
dl_addr_offset / dl_addr_length の妥当性検証追加。
コピー元を固定オフセットではなく dl_addr_offset 由来に修正。
tunctl のメモリリーク・安全性修正
get_devname() の不要ループを廃止し、1回だけ安全に確保する実装へ変更。
main() で devname / node のエラー処理と解放処理を追加。
lifr_name への代入を strncpy から snprintf に変更。
tun-test/test.sh の修正
nobuld typo を nobuild に修正。
unplumball() の引数を tun0/tap0 相当に正しく扱えるよう修正(tun / tap を渡す)。
-z 判定の未クォートを修正。
Issue Broadcom BCM57762/BCM57766 をサポートした Solaris bge driver #27 (satokaz/hitorigoto)の内容確認と反映
追加対応(再レビュー結果の反映)
tun.c:
TUNNEWPPA で allocb() 失敗時に、確保済み PPA をロールバックする処理を追加。
一時的な履歴コメントを削除して可読性を改善。
fast path の重複 msgdsize() 事前走査を削減し、pullupmsg() ベースの最小検証へ整理。
tun-test/test.sh:
失敗時に終了コードへ反映するよう整理。
sudo -n を利用した非対話実行対応。
trap による常時クリーンアップとロックディレクトリで同時実行競合を抑制。
固定 tun0/tap0 は維持しつつ、TEST_PPA_INDEX 環境変数で変更可能化。
ログファイル名を PID 付きにして衝突確率を低減。
tun-test/regression-test.sh:
bash -n を sh -n に変更。
二重アタッチガードの件数確認と、PPAロールバック確認を追加。
tun-test/tunctl.c:
/dev/<name> のバッファを動的長で確保し、snprintf 戻り値を検証。
Makefile.in / tun-test/Makefile.in:
.PHONY 追加、test ターゲットを $(MAKE) -C ... へ変更。
KCFLAGS に -Wno-incompatible-pointer-types を追加(GCC 15 環境での型互換エラー回避)。
configure.in:
no-float/no-SIMD フラグを変数化し、重複定義を削減。
追加対応(HIGH-1/HIGH-2 の反映と再調整)
tun.c:
TAP の PPA 割当時に tun_alloc_ppa_mac() を通して PPA ID ベースで MAC 末尾バイトを変化させ、複数 tapN の MAC 重複を回避。
その後の性能改善で DL_UNITDATA_REQ は qwriter 経由を廃止し、direct path(tun_unitdata_req(wq, mp))へ再調整(9cac718)。
tun-test/regression-test.sh:
DL_UNITDATA_REQ の qwriter 非依存(require_no_match)を検証。
direct path 呼び出しの存在(require_match)を検証。
tun_alloc_ppa_mac() と PPA ID ベース MAC 一意化ロジックの存在チェックを追加。
追加対応(MEDIUM/LOW 残件の反映)
if_tun.h:
DBG( a... ) を C99 互換の DBG(...) へ変更。
tunstr の型を uint32_t / t_uscalar_t / minor_t へ更新。
tundladdr.sap を uint16_t へ更新。
tun.c:
tun_physaddr_req() を static 化。
TUN/TAP 共通で DL_ENABMULTI_REQ / DL_DISABMULTI_REQ に DL_OK_ACK を返すよう統一。
configure.in:
AC_PREFIX_DEFAULT([/]) を設定。
amd64/GCC 分岐で MINOR_VERSION ではなく GCC_MINOR_VERSION を参照するよう修正。
Makefile.in:
clean ターゲットの重複削除行(rm -f tun tun ...)を削除。
tun-test/test.sh:
make uinstall を make uninstall へ修正。
tun-test/tunctl.c:
ARP unlink 時に lifr_arp_muxid != 0 のガードを追加。
/dev/ip6 を使った IPv6 への best-effort plumb/unplumb を追加。
pkg/tuntap.p5m(新規):
IPS 用マニフェストを追加し、driver name=tun/tap と関連ファイルの配備ルールを定義。
tun-test/regression-test.sh:
上記修正(型、multicast ack、configure 判定、clean、uninstall、IPv6/ARP、IPS manifest)の静的回帰チェックを追加。
実機検証で見つかった不整合の修正(Solaris 11.4)
tun.c:
tun_attach_req() の str->ppa ガードを見直し、同一PPAへの DL_ATTACH_REQ を許可。
これにより IF_UNITSEL の ENXIO を解消し、tunctl -t tun0/tap0 が成功することを確認。
tun-test/test.sh:
cleanup 中の再 unplumb 失敗で failed=1 が残り、成功時でも exit 1 になる問題を修正。
main() の成功パスで return 0 を明示。
tun-test/regression-test.sh:
DL_ATTACH_REQ 互換ガード(str->ppa != NULL && str->ppa != ppa)の存在チェックを追加。
E2E テストスクリプトの追加
tun-test/e2e-openvpn.sh:
2ホスト構成で OpenVPN static-key を使った E2E 疎通テストを実行するスクリプトを追加。
--peer, --local-transport-ip, --remote-transport-ip を必須引数として、ローカル/リモートの tunN を作成・起動・疎通・クリーンアップまで自動化。
tun-test/Makefile.in / Makefile.in:
test-e2e-openvpn ターゲットを追加し、E2E_ARGS 経由で引数を渡せるようにした。
tun-test/regression-test.sh:
e2e-openvpn.sh の存在・構文の静的チェックを追加。
README.md:
依存なしミニ relay E2E 経路の追加
tun-test/tunrelay.c(新規):
TUNSETPPA と DLIOCRAW で既存 tunN/tapN へ接続し、poll() で TUN/TAP と UDP を双方向転送する最小 relay を追加。
OpenVPN 等に依存せず、単体バイナリで E2E 経路検証が可能。
tun-test/e2e-relay.sh(新規):
2ホストの tunN 作成、ifconfig 設定、relay 起動、ping 疎通、後始末までを自動化。
--peer, --local-transport-ip, --remote-transport-ip を必須引数として実行する。
tun-test/Makefile.in / Makefile.in:
tunrelay ビルドターゲットを追加。
test-e2e-relay ターゲットを追加し、E2E_RELAY_ARGS(未指定時は E2E_ARGS)を relay スクリプトへ引き渡す。
test-e2e のデフォルトを dependency-free relay 経路へ変更。
tun-test/regression-test.sh:
tunrelay.c の TUNSETPPA / DLIOCRAW / poll() の存在チェックを追加。
test-e2e の relay 既定化と引数フォールバック導線の静的チェックを追加。
実機 panic(2026-03-12 13:35:49 JST)の原因解析と対処
事象:
Host A でドライバ動作中に panic。panicstr は BAD TRAP: type=7 (#nm Device not available)。
fmdump -Vp -u 6f01e6d3-ebee-46fe-8bf0-ac6cf2d74e8b の stack は tun:tunchmsg.constprop.0 -> tun:tunwsrv。
原因:
configure.in には no-FPU/no-SSE フラグ修正を入れていたが、生成済み configure が古いままで未同期。
その結果、実機で ./configure 後の Makefile KCFLAGS に -mno-mmx -mno-sse -mno-sse2 -msoft-float が入らず、カーネルモジュールに不適切な命令列が混入した可能性が高い。
対処:
configure 本体へ no-FPU/no-SSE フラグ反映(NOFP_KCFLAGS_GCC / NOFP_KCFLAGS_STUDIO)。
amd64/GCC 分岐の条件を MINOR_VERSION ではなく GCC_MINOR_VERSION へ修正。
tun-test/regression-test.sh に configure 本体の静的検証(no-FPU flags と条件式)を追加し、再発防止。
修正後確認:
実機で再 ./configure 後、KCFLAGS に -mno-mmx -mno-sse -mno-sse2 -mno-80387 -msoft-float が含まれることを確認。
実機で gmake clean && gmake && sudo gmake install 後、tunctl -t tun0 -b / tunctl -d tun0 -b の smoke 実行時に新規 panic は発生せず。
実機 panic(2026-03-12 14:04:40 JST, 168.138.214.190)の追加解析
事象:
fmdump -Vp の UUID 6ce6b4dd-98e6-4cbd-8656-db6a95f7bc2e で BAD TRAP: type=7 (#nm Device not available) を確認。
stack は前回と同じく tun:tunchmsg.constprop.0 -> tun:tunwsrv。
直接原因:
panic時に使われた tun バイナリを dis すると、tunchmsg.constprop.0 ほかに xmm 命令(movdqa, movups, punpcklqdq)が存在。
カーネル内で SSE 命令実行により #nm が発生したと判断。
なぜ混入したか:
実機ビルドログが Nothing to be done for 'all' で、古い生成物(tun/tap)を再コンパイルせずに install していた。
その結果、no-FPU/no-SSE フラグ未適用の stale バイナリが導入された。
併発要因:
e2e-relay.sh / e2e-openvpn.sh の既定 PPA_INDEX が 92/90 で、TUNMAXPPA=20 の範囲外。
tunctl -t tun92 失敗経路で tunchmsg が通り、panic を引き起こしやすい条件になっていた。
対処:
e2e-relay.sh / e2e-openvpn.sh の既定 PPA_INDEX を 9 に変更。
MAX_PPA_INDEX=19 の上限ガードを追加し、範囲外値は即エラー終了。
tun-test/regression-test.sh に既定値・上限チェックの静的検証を追加。
追加テスト
tun-test/regression-test.sh を追加し、今回の修正ポイントに対する静的回帰チェックを実装。
チェック内容:
境界チェック修正の残存確認
ioctl ペイロード検証コードの存在確認
二重アタッチ防止コードの存在確認
DL_BADADDR / pullupmsg ガードの存在確認
test.sh typo 修正確認
tunctl.c のリーク原因ループ除去確認
sh -n tun-test/test.sh 実行
configure.in のカーネル向けレジスタ制約フラグ確認(GCC/Studio)
test.sh の失敗伝播・非対話 sudo・構文検証
TUNNEWPPA のロールバック有無(静的確認)
./configure 実行(Makefile 再生成)
make 実行(再試行後に成功)
make -C /export/home/kazus/Gits/tuntap tun tap 実行(成功)
make -C /export/home/kazus/Gits/tuntap/tun-test tunrelay 実行(成功)
実機(Solaris 11.4)で gmake -C tun-test tunrelay 実行(成功)
実機(Solaris 11.4)で gmake -C tun-test test-regression 実行(成功)
実機(Solaris 11.4)で fmdump -Vp -u <panic UUID> により panic stack を確認
実機で修正済み configure による KCFLAGS 再検証(no-FPU/no-SSE フラグ入り)を実施
変更ファイル
/export/home/kazus/Gits/tuntap/tun.c
/export/home/kazus/Gits/tuntap/tun-test/tunctl.c
/export/home/kazus/Gits/tuntap/tun-test/test.sh
/export/home/kazus/Gits/tuntap/tun-test/regression-test.sh(新規)
/export/home/kazus/Gits/tuntap/tun-test/e2e-openvpn.sh(新規)
/export/home/kazus/Gits/tuntap/tun-test/e2e-relay.sh(新規)
/export/home/kazus/Gits/tuntap/tun-test/tunrelay.c(新規)
/export/home/kazus/Gits/tuntap/tun-test/Makefile.in
/export/home/kazus/Gits/tuntap/Makefile.in
/export/home/kazus/Gits/tuntap/configure.in
/export/home/kazus/Gits/tuntap/if_tun.h
/export/home/kazus/Gits/tuntap/pkg/tuntap.p5m(新規)
/export/home/kazus/Gits/tuntap/README.md
実行結果
tun-test/regression-test.sh : regression checks passed
sh -n tun-test/test.sh : test.sh syntax OK
sh -n tun-test/regression-test.sh : regression-test syntax OK
./configure : Makefile, tun-test/Makefile を再生成
make(1回目): incompatible-pointer-types で失敗
Makefile.in 調整後 ./configure 再実行
make(2回目): tun / tap のビルド成功
実機(Host A, Solaris 11.4)で gmake -C tun-test test-regression 成功
実機で sudo gmake install → sudo sh tun-test/test.sh nobuild → sudo gmake uninstall 成功
実機で /dev/ip6 への I_PLINK は EINVAL のため warning を出すが、IPv4 経路テストは成功
ローカルで tun-test/e2e-openvpn.sh の sh -n と test-regression を通過(2ホスト E2E は別途実行)
ローカルで make -C tun-test tunrelay 成功
ローカルで tun-test/regression-test.sh 成功(relay 静的チェック含む)
実機(Host A, Solaris 11.4)で ./configure、gmake -C tun-test tunrelay、gmake -C tun-test test-regression 成功
実機で panic 発生(2026-03-12 13:35:49 JST, UUID: 6f01e6d3-ebee-46fe-8bf0-ac6cf2d74e8b)を確認し、configure 未同期起因の no-FPU フラグ欠落を修正
修正後の実機で ./configure→gmake→sudo gmake install→tunctl -t tun0 -b / tunctl -d tun0 -b を実施し、新規 panic なし
実機 168.138.214.190 で panic 発生(2026-03-12 14:04:40 JST, UUID: 6ce6b4dd-98e6-4cbd-8656-db6a95f7bc2e)を確認
panic時バイナリ tun に SSE 命令(xmm)が含まれることを dis で確認
stale 生成物の再利用(Nothing to be done for 'all')と E2E 既定 PPA 範囲外(90/92)を修正
--ppa-index 9 で 2ホスト relay E2E を再実行(168.138.214.190 ↔ 132.145.125.154)し、両ホストで tun9 作成・relay 起動までは成功
ただし underlay 到達性不足により ping 10.252.0.2 は不達(relay E2E ping failed)
追加確認として A↔B の UDP 単発テスト(public/private 各IP)を実施し、いずれも受信タイムアウト
未実施
multicast 制御実体化の実装と E2E 検証
kstat 導入後の性能回帰測定(PPS/多stream)
激詰してもらっている
tuntap ドライバ コードレビュー(Solaris 11.4 観点)
レビュー初版日: 2026-02-27
最終更新日: 2026-03-12
対象ブランチ: solaris
対象OS: Solaris 11.4
更新メモ(2026-03-12)
このドキュメントは初版(2026-02-27)の指摘を保持しつつ、現行ソースとの照合結果を反映した。
対応記録は本書末尾の「付録: レビュー対応記録(2026-03-12)」に統合。
なお、同日夜の追加修正として以下を反映済み:
ee2c813:tunerr()越境書き込み修正ceac8c9:M_PROTO/M_PCPROTO最小長チェック追加9cac718:DL_UNITDATA_REQのqwriter(PERIM_OUTER)依存解除a73fd07:tun_frame()fan-out のdupmsg削減153172b:tun_bind_req()の再入安全化0. Solaris 11.4 における TUN/TAP の位置づけ
Solaris 11.4 組み込みのトンネル機能
Solaris 11 には
iptunドライバが標準搭載されており、dladm create-iptunで作成できる。対応プロトコル: IPv4-in-IPv4(4in4)、IPv6-in-IPv4(6in4)、GRE
これはカーネル内での IP カプセル化用であり、ユーザー空間プロセスがパケットを直接読み書きする仕組みではない。
なぜこのリポジトリが必要か
OpenVPN・WireGuard・vpnc などのユーザー空間 VPN は、
カーネルドライバが提供する
/dev/tunや/dev/tapファイルディスクリプタに対してread()/write()を行うことでパケットを送受信する。この「ユーザー空間向け TUN/TAP デバイス」は Solaris 11.4 に標準搭載されていないため、
本リポジトリのドライバが必要となる。
iptuntun/tapread()/write()でパケット I/O)/devデバイス/dev/iptun(管理用)/dev/tun、/dev/tapSolaris 11.4 での確認コマンド
凡例
1. tun.c — カーネルドライバ本体
🔴 [CRITICAL-1]
TUNNEWPPA/TUNSETPPAでのオフバイワン(境界外配列アクセス)ファイル:
tun.c箇所:
tun_ioctl()内 TUNNEWPPA/TUNSETPPA ケースtun_ppaはstatic struct tunppa *tun_ppa[TUNMAXPPA]で宣言され、有効インデックスは
0〜TUNMAXPPA-1(= 0〜19)。p == TUNMAXPPA (20)のときp > TUNMAXPPAは偽となり、tun_ppa[20]への境界外アクセスが発生する。TUNSETPPA/tun_attach_req()にも同様の問題がある。修正案:
🔴 [CRITICAL-2]
TUNNEWPPAioctl でmp->b_contの NULL チェックなしファイル:
tun.c箇所:
tun_ioctl()内TUNNEWPPAケースM_IOCTLメッセージにb_contがない(データブロックなし)場合、mp->b_contは NULL となる。ユーザープログラムが不正な ioctl を発行した場合(または
I_STRでなく直接ioctl()を使った場合)にカーネルパニックが発生する。
修正案:
🟠 [HIGH-1]
tun_frame()とリスト変更操作の間に競合状態の可能性(再評価)ファイル:
tun.c箇所:
tun_frame(),tun_attach_req(),tun_detach_req()DL_UNITDATA_REQはqwriterを通さない direct path で処理される(9cac718)。tun_frame()はppa->p_strを走査しつつ配送するため、attach/detach/close と競合し得る点は設計課題として残る。Solaris 11.4 のマルチスレッド STREAMS では、
ppa->p_strリストを安全に走査・変更するために、アクセスパターンを統一する必要がある。現行方針:
DL_UNITDATA_REQの direct path は維持し、per-PPA ロック設計で整合性を担保する:詳細は
tun-driver-per-ppa-lock-design.mdを参照。🟠 [HIGH-2] TAP モードで全 PPA が同一 MAC アドレスを持つ
ファイル:
tun.c箇所:
tun_generate_mac_addr(),tun_alloc_ppa()複数の tap インターフェース(
tap0,tap1, ...)が同時に使われると、すべてが同一の MAC アドレスを持つことになり、Ethernet フレームの配送が正しく機能しない。
修正案: 各 PPA のユニーク MAC 生成(例: ベースアドレス + PPA ID):
🟡 [MEDIUM-1]
u_long/u_shortの使用(64ビット非対応型)ファイル:
if_tun.hSolaris 11.4 の 64ビット amd64/sparcv9 カーネルでは
u_longは8バイト。DLPI プロトコルの仕様では
stateはt_uscalar_t(=uint32_t)であり、サイズ不一致による構造体パディング/整合性の問題が発生する可能性がある。
修正案:
🟡 [MEDIUM-2]
tunclose()でリストにstrが見つからない場合 NULL 参照ファイル:
tun.c箇所:
tunclose()(行 ~345)strがリストに存在しない場合(状態異常時)、ループ終了時にtmp == NULLとなり、tmp->s_nextで NULL ポインタ参照が発生する。修正案:
🟡 [MEDIUM-3]
tun_physaddr_req()で関数型がvoidなのにstaticでないファイル:
tun.c他の
static関数と一貫性がなく、不要なカーネルシンボル露出になる。修正案:
static void tun_physaddr_req(...)に変更。🟡 [MEDIUM-4] 権限チェックが未実装(Solaris 11.4 特有)
ファイル:
tun.c箇所:
tunopen()Solaris 11.4 では最小権限モデル(Least Privilege)が重視される。
/dev/tun・/dev/tapはネットワークに直接アクセスできるため、secpolicy_net_rawaccess(credp)またはPRIV_NET_RAWACCESSのチェックを行うべき。修正案:
🟡 [MEDIUM-5] Solaris 11.4 では IPS パッケージが標準(SVR4 パッケージ/手動インストール非推奨)
ファイル:
Makefile.in,configure.in現在は
sudo make installで直接/usr/kernel/drv/に配置する方式。Solaris 11.4 は IPS (Image Packaging System) が標準で、
手動ファイル配置は IPS の追跡外となり、パッケージ更新やゾーン管理と干渉する。
推奨対応: IPS パッケージマニフェスト (
.p5m) の作成。最低限の例:
🔵 [LOW-1]
DBGマクロが GCC 拡張構文(Sun Studio 非互換)ファイル:
if_tun.ha...は GCC/Clang 独自の可変引数マクロ省略記法。Solaris Studio (Oracle Developer Studio) でビルドする場合はコンパイルエラーになる。
修正案:
🔵 [LOW-2]
configure.inでGCCバージョンチェックに誤った変数名ファイル:
configure.in$GCC_MINOR_VERSIONではなく$MINOR_VERSION(OS のマイナーバージョン = 11)が使われている。Solaris 11 では
$MINOR_VERSIONは常に11なので11 >= 5は常に真となり、GCC >= 4 の場合は常に
-mcmodel=largeが選ばれる。現実には GCC 4.x+ が前提なので偶発的に正しく動いているが、潜在的なバグ。
修正案:
🔵 [LOW-3]
TUNTAP_TUN時のDL_ENABMULTI_REQ/DL_DISABMULTI_REQの扱いファイル:
tun.c箇所:
tun_dlpi()内TUN モードでは
DL_ENABMULTI_REQ/DL_DISABMULTI_REQを fall-through でDL_UNSUPPORTEDにエラーとしている。TAP モードでは
DL_OK_ACKを返す。Solaris 11.4 の IP スタックは場合によって multicast ioctl を発行することがあり、
エラー時の動作が想定外になりうるため、TUN でも
DL_OK_ACKを返す方が堅牢。🔵 [LOW-4]
Makefile.inclean ターゲットに重複・誤記ファイル:
Makefile.in修正案:
clean: rm -f tun tap *.o *~2. tun-test/tunctl.c — テストユーティリティ
🟠 [HIGH-3]
get_devname()でのメモリリークファイル:
tun-test/tunctl.cmalloc(30)が最大 TUNMAXPPA (20) 回呼ばれるが、最後の1つ以外は
free()されないメモリリークが発生する。また、
strpbrk()は毎回同じポインタを返すため、ループ自体に意味がなく、同じ処理を20回繰り返すだけになっている。
修正案:
🟠 [HIGH-4]
main()でのメモリリーク(node・get_devname()返り値)ファイル:
tun-test/tunctl.cプロセス終了時にOSが回収するため実害は少ないが、静的解析ツールでの誤検出を避けるためにも修正が望ましい。
修正案:
🟡 [MEDIUM-6]
delete_if()で ARP unlink が TAP 専用だが理由のコメントが不明確ファイル:
tun-test/tunctl.cSolaris 11.4 では ARP の muxid は IPv4インターフェースのプランブ時に設定される。
arp_muxidが有効かどうか(ゼロでないか)のチェックなしにI_PUNLINKを呼ぶとエラーになりうる。エラーを無視しているため動作上は問題ないが、意図を明示すべき。
修正案:
🔵 [LOW-5]
add_if()で IPv6 プランブが未対応ファイル:
tun-test/tunctl.c現在の
add_if()は IPv4 (/dev/ip) へのI_PLINKのみ実装。Solaris 11.4 で IPv6 対応インターフェースとして使う場合、
/dev/ip6へのリンクも必要。OpenVPN + IPv6 トンネルなど用途によっては問題になる。
3. tun-test/test.sh — テストスクリプト
🟠 [HIGH-5]
nobuild変数のタイポ(テストが常に実行される可能性)ファイル:
tun-test/test.shnobuild以外の引数(または引数なし)で呼ばれた場合、else節でnobuld=0(タイポ)と設定し、$nobuildは未設定のままになる。[ "" -eq 0 ]は sh 実装により動作が異なり、Solaris
/usr/xpg4/bin/shでは算術比較エラーとなる可能性がある。修正案:
🟠 [HIGH-6]
TESTLOGFILEが初期化前に使用されるファイル:
tun-test/test.shinit()が呼ばれる前に$TESTLOGFILEを使用しているため、最初の3行のログはファイルに書かれない(
tee -a ""はエラー扱い)。修正案:
init()を最初に呼ぶ:🔵 [LOW-6]
uninstall()でmake uinstallのタイポファイル:
tun-test/test.shmake uinstallは存在しない Makefile ターゲット → エラーになるが、test.shのuninstall()は呼ばれていないため現状は問題なし。修正案:
🔵 [LOW-7]
loadcheck()でmodinfoの出力が空の場合の[ -z $loaded ]は引用符が必要ファイル:
tun-test/test.sh$loadedが空の場合、[ -z $loaded ]は[ -z ]となり、Solaris sh では構文エラーになりうる。
修正案:
4. configure.in / Makefile.in
🔴 [CRITICAL-3] GCC (x86-64) でカーネルモジュールに SSE/MMX 命令が生成される
ファイル:
configure.in箇所:
amd64ケースのKCFLAGS設定参照: Writing Device Drivers in Oracle Solaris 11.4 — Compiling and Linking the Driver
Solaris x86 カーネルは MMX / SSE 命令をサポートしない。
カーネルコンテキストでこれらの命令を実行すると カーネルパニック になる。
この制約は GCC でも Studio でも同様に適用される。
Oracle 公式ドキュメントの記述(E61061 — eqbvb)
Oracle Solaris 11.4 "Writing Device Drivers" ガイド(E61061)は以下を明記している:
また同ドキュメントが示す amd64 向け GCC の公式推奨ビルドコマンド:
gcc -D_KERNEL -m64 -mcmodel=kernel -mno-red-zone \ -ffreestanding -nodefaultlibs \ -mno-mmx -mno-sse -mno-sse2 \ -c mydriver.c ld -r -o mydriver mydriver.o初版レビュー時点(2026-02-27)の configure.in との差分
-mno-red-zone-mno-mmx -mno-sse -mno-sse2-ffreestanding-nodefaultlibs-mcmodel=kernel/largelarge)-m64GCC x86-64 で SSE が生成される理由
-O0相当ではほぼ SSE を使わないが、-O2以上では整数演算・メモリコピーにも XMM レジスタを積極利用するSun Studio 12.4 と GCC の対応関係
-xregs=no%float-O2以上-mno-sse -mno-sse2 -mno-mmx(または-mgeneral-regs-only)修正案
-mgeneral-regs-onlyは GCC 5 以降で利用可能(GCC 15 で確実にサポート済み)。汎用レジスタのみ使用を許可し、FPU/MMX/SSE/AVX レジスタを一切使わないことをコンパイラに強制する
(
-mno-sse -mno-sse2 -mno-mmxの上位互換)。GCC 5 未満(
-mgeneral-regs-onlyが使えない場合)の代替:KCFLAGS="$KCFLAGS -ffreestanding -mno-mmx -mno-sse -mno-sse2 -mno-sse3 -mno-avx"PERF-1 との関係
-O2の追加(PERF-1)と本修正はセットで適用すること。-O2のみ追加して SSE 禁止フラグを入れないと、最適化によって SSE 命令が生成されカーネルパニックが発生するリスクが高まる。
🟡 [MEDIUM-7]
configure.inでAC_PREFIX_DEFAULTに引数なしファイル:
configure.inAC_PREFIX_DEFAULT(PREFIX)はデフォルトプレフィックスを設定するマクロで、引数必須。引数なしは Autoconf の警告/エラーの原因となる。
修正案: 削除または明示的に指定:
🔵 [LOW-8]
configure.inが古いAC_INIT形式 /autoconf 2.13前提ファイル:
configure.in拡張子が
.inでconfigure.inを使っているのは旧来の慣習。Autoconf 2.50 以降は
configure.acが標準。Solaris 11.4 上の最新 Autoconf ではこのままでも動作するが、
将来の互換性のために
configure.acへのリネームを推奨。5. Solaris 11.4 アーキテクチャ上の制限事項
🟡 [MEDIUM-8] GLDv3 非対応 —
dladm/ipadmとの統合なしSolaris 11.4 のネットワーク管理は GLDv3(Generic LAN Driver version 3)ベースの
mac(9E)ドライバフレームワークが標準。本ドライバは旧来の DLPI/STREAMS スタイルで実装されており、以下の機能が未対応:
dladm show-linkでのインターフェース表示dladm create-vnicなどの仮想 NIC 作成との統合現状の I_PLINK を使った手動プランブは機能するが、
ipadm create-ifなどのモダンな管理ツールでは使えない。Solaris 11.4 で完全な統合を得るには
mac(9E)ベースの再実装が望ましいが、これは大きな作業になる。既存の用途(OpenVPN 互換など)では
現状の DLPI スタイルでも動作する。
🟡 [MEDIUM-9]
tun.conf/tap.confでinstance=0のみ宣言ファイル:
tun.conf,tap.confinstance=0のみの宣言では、tunattach()が呼ばれるのは1回のみ。これは複数の物理デバイスのような構造ではなく、CLONE デバイスとして機能するため
現状で正しい設計になっている。ただし、Solaris 11.4 の IPS パッケージ経由でインストールする場合、
ドライバのバインディングは
.confファイルではなくadd_drv(1M)の引数で制御する方式が主流。6. パフォーマンス改善観点
🔴 [PERF-1] KCFLAGS に最適化フラグ (
-O2) が設定されていないファイル:
configure.inGCC のデフォルトは最適化なし(
-O0)。カーネルモジュールは通常-O2でビルドする。最適化なしでは、インライン展開・デッドコード除去・ループ最適化が行われず、
実測でスループットが数十% 低下することがある。
修正案(
configure.in、amd64/GCC ケース):🟠 [PERF-2]
tun_eth_hdr()が毎パケットallocb()を呼ぶファイル:
tun.c箇所:
tun_eth_hdr(),tun_unitdata_ind()allocb()は毎回kmem_alloc()を呼ぶわけではないが(STREAMS の mblk プールから取得)、高スループット時には mblk の確保・解放が頻繁に発生する。
改善案: 送信側(
tun_eth_hdr)では、元のmblkの前方に余白があればb_rptrを後退させてヘッダを直接書き込む(ゼロコピー的な手法):🟠 [PERF-3]
tun_msg_len()でmsgdsize()を使わない独自実装ファイル:
tun.c箇所:
tun_msg_len()Solaris STREAMS には
msgdsize(mp)という標準関数があり、同等の処理をカーネル内で最適化済みの実装で行う。
独自実装は不要であり、標準関数に置き換えたほうが保守性・パフォーマンスともに優れる。
修正案:
tun_msg_len()関数自体を削除可能。🟠 [PERF-4]
mi_maxpsz = 2048— Jumbo Frame 非対応によるスループット制限ファイル:
tun.cmi_maxpsz = 2048は Jumbo Frame(最大 9000 バイト程度)を想定していない。VPN トンネルで Jumbo Frame を使う環境(データセンター内部など)では、
フラグメント処理が強制され、スループットが大幅に低下する。
改善案:
🟠 [PERF-5] モジュールキュー
mi_hiwat = 32KBとTUN_SO_HIWAT = 1MBの不整合ファイル:
tun.cストリームヘッド(ユーザー空間側)のバッファは 1MB に設定されているが、
ドライバモジュール内キューの上限は 32KB のまま。
ユーザー空間が大量データを書き込む際に、モジュールキューが早期に詰まり
フロー制御(
canputnext()= false)が頻繁に発動する。改善案:
mi_hiwatをTUN_SO_HIWATに合わせるか、または
ndd(1M)等でチューニング可能なパラメータとして公開する:🟡 [PERF-6]
ppa->p_strリストの線形スキャンファイル:
tun.c箇所:
tun_frame()各フレームのルーティング時に
ppa->p_strリスト全体を線形スキャンする。通常の OpenVPN 使用では1〜2ストリームのみで問題ないが、
多数のクライアントが同時接続するサーバーサイド用途では
SAP をキーとしたハッシュテーブルにするとスケールする。
改善案(多ストリーム用途の場合):
🟡 [PERF-7]
qwriter(PERIM_OUTER)のエスカレーションコストファイル:
tun.cqwriter(PERIM_OUTER)はキュー全体の外側ペリメーターを排他ロックで取得するため、他スレッドが内側ペリメーターを保持している間は待機が発生する。
ioctl は低頻度なので通常は問題ないが、
tun_attach_req/tun_detach_reqとtun_frameが頻繁に交錯する場合はロック競合のボトルネックになりうる。
データパス(
M_DATA)はqwriterを経由しない直接処理になっているのは適切。ioctl・attach・detach のコントロールパスは現状維持で問題ない。
🔵 [PERF-8]
TUN_SO_HIWATがコンパイル時定数で実行時チューニング不可ファイル:
if_tun.h,tun.c高スループット環境(10GbE 以上)では 1MB では不足することがある。
Solaris の
ndd(1M)コマンドやddi_prop_*API を使ってチューニングパラメータとして公開すると運用時に調整可能になる。
改善案:
パフォーマンス改善 影響度サマリー
-O2追加tun_eth_hdr()の allocb 削減tun_msg_len()→msgdsize()mi_maxpsz拡大(Jumbo Frame)mi_hiwat拡大(32KB → 256KB)ppa->p_strハッシュ化qwriterエスカレーション最適化TUN_SO_HIWATを ndd チューニング可に最優先: PERF-1(
-O2追加)は1行の変更で最大の効果が見込める。次点: PERF-4(
mi_maxpsz拡大)と PERF-5(mi_hiwat拡大)も変更量が小さく効果が大きい。まとめ・優先度別アクション一覧(2026-03-12 時点)
現行ソース(
tun.c/if_tun.h/configure.in/tun-test/*)と本書末尾の対応記録を照合した結果を反映。
tun.cmp->b_contの NULL/長さ検証を追加tun.c-mno-*,-xregs=no%float)configure.inDL_UNITDATA_REQは direct path。共有状態同期は per-PPA ロック設計で継続課題tun.ctun.cget_devname()のリーク修正tun-test/tunctl.cmain()のdevname/node解放対応tun-test/tunctl.cnobuildタイポ修正tun-test/test.shTESTLOGFILE初期化順序修正tun-test/test.shtunerr()の越境書き込み修正tun.cM_PROTO/M_PCPROTOの最小長チェックを追加tun.cu_long/u_shortをuint32_t/t_uscalar_t/minor_t/uint16_tへ更新if_tun.htunclose()の NULL 保護追加tun.ctun_physaddr_req()をstatic化tun.ctunopen()の権限チェック(運用影響ありのため保留)tun.cpkg/tuntap.p5mtun-test/tunctl.cAC_PREFIX_DEFAULT([/])を設定configure.indladm非対応(設計上の制限)driverアクションを manifest に定義pkg/tuntap.p5mDBGを C99 可変引数マクロへ更新if_tun.hGCC_MINOR_VERSIONを使用configure.inDL_ENABMULTI_REQ/DL_DISABMULTI_REQをDL_OK_ACK応答(実体制御は未実装)tun.ccleanターゲット重複行を削除Makefile.intunctlに/dev/ip6への best-effort plumb/unplumb を追加tun-test/tunctl.cmake uninstallへ修正tun-test/test.shloadcheck()実装見直しで引用符問題を解消tun-test/test.shconfigure.in→configure.ac移行configure.intun_frame()fan-out を 1-pass 化しdupmsgを削減tun.ctun_bind_req()のstatic作業領域をスタック化tun.c7. 今後の対応
2026-03-12 時点で、CRITICAL 項目(1-3)と追加 HIGH 項目(7-8)は完了済み。
次の優先順位で未対応項目を処理する。
DL_ENABMULTI_REQ/DL_DISABMULTI_REQ)HIGH-1再評価項目)kstat可観測性の追加と性能回帰実測(PPS/多stream)tunopen()権限チェック(運用影響確認後)MEDIUM-8(GLDv3 非対応),LOW-8(configure.ac移行)更新時は「問題提起」と「対応済みステータス」を分離し、
「まとめ・優先度別アクション一覧」を先に更新してから個別セクションを追記する。
コードについて
このツリーに収められているのは Solaris 用の STREAMS ベースの TUN/TAP 擬似デバイスドライバです。
if_tun.hとtun.cが本体で、tun-test以下にはユーザランドのテストプログラムが入っています。全体構造
tun.cはモジュール/ドライバとしてロードされる STREAMS ドライバで、TUN か TAP のどちらか一方でコンパイルされる (TUNTAP_TAPもしくはTUNTAP_TUNが必須)。tun_strリストとtun_ppa[]配列で開かれたストリームと仮想インタフェース (PPA: per‑path adapter) を管理する。tunppaは各仮想インタフェースの制御ストリームと、TAP では MAC アドレスを保持。tunstrはストリーム単位の状態を保持。フラグや DLPI 状態、SAP、割り当てられた PPA へのポインタなどを持つ。if_tun.hには上記構造体と I/OCTL 定義、フラグのマクロが書かれています。PROMISC/ALL などのスニッファ条件を判定する
SNIFFER()マクロもここにあります。ドライバのエントリポイント
_init,_fini,_infoでmod_install/mod_remove/mod_infoを呼び、ロード/アンロードを担当。tunprobe/tunattach/tundetach/tuninfoがdev_opsに登録され、デバイスノードの作成や情報取得を行う。tunattachではddi_create_minor_nodeで/dev/tunあるいは/dev/tapのクローンデバイスを作成し、TAP ならローカル MAC を生成。STREAMS キューとコールバック
tunrinit/tunwinitによって、リードパスはtunopen/tunclose、ライトパスはtunwput/tunwsrvが呼ばれる。tunopenは CLONEOPEN に対応し、新しい minor 番号の割り当てとtunstr構造体の初期化、リストへの挿入を行う。tuncloseはストリームを PPA から切り離し、制御ストリームであれば PPA 自体を解放。最後にtunstrをリストから削除してメモリを返す。IOCTL/DLPI ハンドラ
tun_ioctlがライトキューに入る I/OCTL へ対応。PPA の作成 (TUNNEWPPA)、ストリームの PPA への紐付け (TUNSETPPA)、取得などを実装する。tun_info_req、tun_attach_req、tun_detach_req、tun_bind_reqなど BOOTP シリーズに対応。tun_set_physaddr_reqやtun_enabmulti_reqなど付随処理もある。これらの DLPI 関連関数は共通のヘルパとして
tunchmsg(メッセージ確保/初期化)、tundlokack/tundlerrack(ACK/エラー返信)、tunerr(M_ERROR 送出)を利用しています。データフレームの経路
tun_unitdata_reqはプロトコルストリームから制御ストリームへ送るフレームを形成。TAP では引数からイーサネットヘッダを付加し、TUN ではヘッダをゼロクリアする。tun_unitdata_indは制御ストリームからプロトコルストリームへ送るインジケーションを組み立てる。TAP/TUN で宛先・送信元アドレスの扱いが異なる。tun_eth_hdrがイーサネットヘッダの生成を行う(TAP では実アドレス、TUN ではタイプだけ)。フレームの振り分けは
tun_frameに集約される。ストリームの種類とフラグに応じて:TUN_ALL_PHY/SAP/MULTI) に合致すれば複製して配信。生データ (TUN_RAW) 指定なら DLPI ヘッダを付けずに送る。tun_frame_is_eligible)、TUN では SAP 値チェック。TAP固有ロジック
tun_generate_mac_addrでランダムにローカル MAC を生成し、各 PPA にコピー。tun_frame_is_eligibleで受信ストリームが受け取るべきフレームかどうかを確認(ブロードキャスト、マルチキャスト、宛先アドレス比較、SAP 比較など)。tun_msg_lenが 802.3 長さフィールドを計算。そのほかユーティリティ
tun_alloc_ppaは PPA を新規確保または指定番号で割り当て、配列に登録。tunpromiscon_req/tunpromiscoff_reqがプロミスコン状態ビットを管理。tun_physaddr_reqは PPA の MAC を返す。動作の流れ
/dev/tunあるいは/dev/tapに open するとtunopenが呼ばれ、tunstrが作られてストリームが動作開始。TUNNEWPPAIOCTL で仮想デバイスを作成し、返されたインスタンス番号をTUNSETPPAでプロトコルストリームにセット。ATTACH→BIND→UNITDATA_REQが順次送受信され、ソケット風にパケットをやり取りできる。TUN_CONTROLフラグ) には TAP/TUN が生成したパケットが流れ、プロトコルストリームへフェッチされる。テストコード
tun-test/以下には簡単な CLI 付きテスト (test.shとtunctl.c) があり、ユーザーランドから PPA の生成やパケット送受信を試せるようになっています。このドライバは UNIX STREAMS と DLPI プロトコルに密接に結びついており、TUN と TAP の振る舞いをビルド時に切り替えることで、パケットをユーザー空間へ中継する仕組みをシンプルに実装しています。各関数は状態遷移とエラーチェックを細かく行い、メッセージの再利用や複製によって効率的にフレームを振り分けます。必要であれば各ブロックをさらに掘り下げて説明できますので、気になる箇所があればお知らせください。
付録: レビュー対応記録(2026-03-12)
概要
リポジトリレビューで抽出した不具合・リスクに対して、
tun.c/tun-test/tunctl.c/tun-test/test.shを修正し、回帰確認用の軽量テストを追加した。対応内容
TUNMAXPPAの境界チェック修正(off-by-one)p > TUNMAXPPAをp >= TUNMAXPPAに変更し、配列外アクセスを防止。tun_ioctl(TUNNEWPPA/TUNSETPPA),tun_attach_reqM_IOCTL入力検証の追加mp->b_contのNULLチェック、sizeof(int)未満の長さチェックを追加。ioctlペイロードでの不正参照を防止。str->ppa != NULLの場合、TUNNEWPPA/TUNSETPPAを拒否。DL_ATTACH_REQは「既存 PPA と同一の場合は許可・異なる場合は拒否」に調整し、IF_UNITSEL経路との互換性を維持。tun_detach_req/tuncloseでは unlink 時のNULL保護を追加。DL_UNITDATA_REQの最小長チェックを追加。dl_dest_addr_offset/dl_dest_addr_lengthの妥当性を検証し、DL_BADADDRを返すよう修正。msgdsizeとpullupmsgで長さを保証。sizeof(struct ether_header)以上DL_SET_PHYS_ADDR_REQの検証追加DL_SET_PHYS_ADDR_REQ_SIZEの最小長チェック追加。ppa == NULLの状態チェック追加。dl_addr_offset/dl_addr_lengthの妥当性検証追加。dl_addr_offset由来に修正。tunctlのメモリリーク・安全性修正get_devname()の不要ループを廃止し、1回だけ安全に確保する実装へ変更。main()でdevname/nodeのエラー処理と解放処理を追加。lifr_nameへの代入をstrncpyからsnprintfに変更。tun-test/test.shの修正nobuldtypo をnobuildに修正。unplumball()の引数をtun0/tap0相当に正しく扱えるよう修正(tun/tapを渡す)。-z判定の未クォートを修正。satokaz/hitorigoto)の内容確認と反映configure.inのamd64向けKCFLAGSを強化。-mno-mmx -mno-sse -mno-sse2 -mno-80387 -msoft-floatを追加-xregs=no%floatを追加tun.c:TUNNEWPPAでallocb()失敗時に、確保済み PPA をロールバックする処理を追加。msgdsize()事前走査を削減し、pullupmsg()ベースの最小検証へ整理。tun-test/test.sh:sudo -nを利用した非対話実行対応。trapによる常時クリーンアップとロックディレクトリで同時実行競合を抑制。tun0/tap0は維持しつつ、TEST_PPA_INDEX環境変数で変更可能化。PID付きにして衝突確率を低減。tun-test/regression-test.sh:bash -nをsh -nに変更。tun-test/tunctl.c:/dev/<name>のバッファを動的長で確保し、snprintf戻り値を検証。Makefile.in/tun-test/Makefile.in:.PHONY追加、testターゲットを$(MAKE) -C ...へ変更。KCFLAGSに-Wno-incompatible-pointer-typesを追加(GCC 15 環境での型互換エラー回避)。configure.in:tun.c:tun_alloc_ppa_mac()を通して PPA ID ベースで MAC 末尾バイトを変化させ、複数tapNの MAC 重複を回避。DL_UNITDATA_REQはqwriter経由を廃止し、direct path(tun_unitdata_req(wq, mp))へ再調整(9cac718)。tun-test/regression-test.sh:DL_UNITDATA_REQのqwriter非依存(require_no_match)を検証。require_match)を検証。tun_alloc_ppa_mac()と PPA ID ベース MAC 一意化ロジックの存在チェックを追加。if_tun.h:DBG( a... )を C99 互換のDBG(...)へ変更。tunstrの型をuint32_t/t_uscalar_t/minor_tへ更新。tundladdr.sapをuint16_tへ更新。tun.c:tun_physaddr_req()をstatic化。DL_ENABMULTI_REQ/DL_DISABMULTI_REQにDL_OK_ACKを返すよう統一。configure.in:AC_PREFIX_DEFAULT([/])を設定。MINOR_VERSIONではなくGCC_MINOR_VERSIONを参照するよう修正。Makefile.in:cleanターゲットの重複削除行(rm -f tun tun ...)を削除。tun-test/test.sh:make uinstallをmake uninstallへ修正。tun-test/tunctl.c:lifr_arp_muxid != 0のガードを追加。/dev/ip6を使った IPv6 への best-effort plumb/unplumb を追加。pkg/tuntap.p5m(新規):driver name=tun/tapと関連ファイルの配備ルールを定義。tun-test/regression-test.sh:tun.c:tun_attach_req()のstr->ppaガードを見直し、同一PPAへのDL_ATTACH_REQを許可。IF_UNITSELのENXIOを解消し、tunctl -t tun0/tap0が成功することを確認。tun-test/test.sh:cleanup中の再unplumb失敗でfailed=1が残り、成功時でもexit 1になる問題を修正。main()の成功パスでreturn 0を明示。tun-test/regression-test.sh:DL_ATTACH_REQ互換ガード(str->ppa != NULL && str->ppa != ppa)の存在チェックを追加。tun-test/e2e-openvpn.sh:--peer,--local-transport-ip,--remote-transport-ipを必須引数として、ローカル/リモートのtunNを作成・起動・疎通・クリーンアップまで自動化。tun-test/Makefile.in/Makefile.in:test-e2e-openvpnターゲットを追加し、E2E_ARGS経由で引数を渡せるようにした。tun-test/regression-test.sh:e2e-openvpn.shの存在・構文の静的チェックを追加。README.md:tun-test/tunrelay.c(新規):TUNSETPPAとDLIOCRAWで既存tunN/tapNへ接続し、poll()で TUN/TAP と UDP を双方向転送する最小 relay を追加。tun-test/e2e-relay.sh(新規):tunN作成、ifconfig設定、relay 起動、ping疎通、後始末までを自動化。--peer,--local-transport-ip,--remote-transport-ipを必須引数として実行する。tun-test/Makefile.in/Makefile.in:tunrelayビルドターゲットを追加。test-e2e-relayターゲットを追加し、E2E_RELAY_ARGS(未指定時はE2E_ARGS)を relay スクリプトへ引き渡す。test-e2eのデフォルトを dependency-free relay 経路へ変更。tun-test/regression-test.sh:tunrelay.cのTUNSETPPA/DLIOCRAW/poll()の存在チェックを追加。test-e2eの relay 既定化と引数フォールバック導線の静的チェックを追加。panicstrはBAD TRAP: type=7 (#nm Device not available)。fmdump -Vp -u 6f01e6d3-ebee-46fe-8bf0-ac6cf2d74e8bの stack はtun:tunchmsg.constprop.0 -> tun:tunwsrv。configure.inには no-FPU/no-SSE フラグ修正を入れていたが、生成済みconfigureが古いままで未同期。./configure後のMakefileKCFLAGSに-mno-mmx -mno-sse -mno-sse2 -msoft-floatが入らず、カーネルモジュールに不適切な命令列が混入した可能性が高い。configure本体へ no-FPU/no-SSE フラグ反映(NOFP_KCFLAGS_GCC/NOFP_KCFLAGS_STUDIO)。MINOR_VERSIONではなくGCC_MINOR_VERSIONへ修正。tun-test/regression-test.shにconfigure本体の静的検証(no-FPU flags と条件式)を追加し、再発防止。./configure後、KCFLAGSに-mno-mmx -mno-sse -mno-sse2 -mno-80387 -msoft-floatが含まれることを確認。gmake clean && gmake && sudo gmake install後、tunctl -t tun0 -b/tunctl -d tun0 -bの smoke 実行時に新規 panic は発生せず。fmdump -Vpの UUID6ce6b4dd-98e6-4cbd-8656-db6a95f7bc2eでBAD TRAP: type=7 (#nm Device not available)を確認。tun:tunchmsg.constprop.0 -> tun:tunwsrv。tunバイナリをdisすると、tunchmsg.constprop.0ほかにxmm命令(movdqa,movups,punpcklqdq)が存在。#nmが発生したと判断。Nothing to be done for 'all'で、古い生成物(tun/tap)を再コンパイルせずにinstallしていた。e2e-relay.sh/e2e-openvpn.shの既定PPA_INDEXが92/90で、TUNMAXPPA=20の範囲外。tunctl -t tun92失敗経路でtunchmsgが通り、panic を引き起こしやすい条件になっていた。e2e-relay.sh/e2e-openvpn.shの既定PPA_INDEXを9に変更。MAX_PPA_INDEX=19の上限ガードを追加し、範囲外値は即エラー終了。tun-test/regression-test.shに既定値・上限チェックの静的検証を追加。追加テスト
tun-test/regression-test.shを追加し、今回の修正ポイントに対する静的回帰チェックを実装。チェック内容:
ioctlペイロード検証コードの存在確認DL_BADADDR/pullupmsgガードの存在確認test.shtypo 修正確認tunctl.cのリーク原因ループ除去確認sh -n tun-test/test.sh実行configure.inのカーネル向けレジスタ制約フラグ確認(GCC/Studio)test.shの失敗伝播・非対話 sudo・構文検証TUNNEWPPAのロールバック有無(静的確認)./configure実行(Makefile再生成)make実行(再試行後に成功)make -C /export/home/kazus/Gits/tuntap tun tap実行(成功)make -C /export/home/kazus/Gits/tuntap/tun-test tunrelay実行(成功)gmake -C tun-test tunrelay実行(成功)gmake -C tun-test test-regression実行(成功)fmdump -Vp -u <panic UUID>により panic stack を確認configureによるKCFLAGS再検証(no-FPU/no-SSE フラグ入り)を実施変更ファイル
/export/home/kazus/Gits/tuntap/tun.c/export/home/kazus/Gits/tuntap/tun-test/tunctl.c/export/home/kazus/Gits/tuntap/tun-test/test.sh/export/home/kazus/Gits/tuntap/tun-test/regression-test.sh(新規)/export/home/kazus/Gits/tuntap/tun-test/e2e-openvpn.sh(新規)/export/home/kazus/Gits/tuntap/tun-test/e2e-relay.sh(新規)/export/home/kazus/Gits/tuntap/tun-test/tunrelay.c(新規)/export/home/kazus/Gits/tuntap/tun-test/Makefile.in/export/home/kazus/Gits/tuntap/Makefile.in/export/home/kazus/Gits/tuntap/configure.in/export/home/kazus/Gits/tuntap/if_tun.h/export/home/kazus/Gits/tuntap/pkg/tuntap.p5m(新規)/export/home/kazus/Gits/tuntap/README.md実行結果
tun-test/regression-test.sh:regression checks passedsh -n tun-test/test.sh:test.sh syntax OKsh -n tun-test/regression-test.sh:regression-test syntax OK./configure:Makefile,tun-test/Makefileを再生成make(1回目):incompatible-pointer-typesで失敗Makefile.in調整後./configure再実行make(2回目):tun/tapのビルド成功Host A, Solaris 11.4)でgmake -C tun-test test-regression成功sudo gmake install→sudo sh tun-test/test.sh nobuild→sudo gmake uninstall成功/dev/ip6へのI_PLINKはEINVALのため warning を出すが、IPv4 経路テストは成功tun-test/e2e-openvpn.shのsh -nとtest-regressionを通過(2ホスト E2E は別途実行)make -C tun-test tunrelay成功tun-test/regression-test.sh成功(relay 静的チェック含む)Host A, Solaris 11.4)で./configure、gmake -C tun-test tunrelay、gmake -C tun-test test-regression成功6f01e6d3-ebee-46fe-8bf0-ac6cf2d74e8b)を確認し、configure未同期起因の no-FPU フラグ欠落を修正./configure→gmake→sudo gmake install→tunctl -t tun0 -b/tunctl -d tun0 -bを実施し、新規 panic なし168.138.214.190で panic 発生(2026-03-12 14:04:40 JST, UUID:6ce6b4dd-98e6-4cbd-8656-db6a95f7bc2e)を確認tunに SSE 命令(xmm)が含まれることをdisで確認Nothing to be done for 'all')と E2E 既定 PPA 範囲外(90/92)を修正--ppa-index 9で 2ホスト relay E2E を再実行(168.138.214.190↔132.145.125.154)し、両ホストでtun9作成・relay 起動までは成功ping 10.252.0.2は不達(relay E2E ping failed)未実施
kstat導入後の性能回帰測定(PPS/多stream)