Linux上でrp-pppoeを使用してISP検証用PPPoEサーバーを構築する

Network tutorial - IT technology blog
Network tutorial - IT technology blog

クイックスタート(5分で検証環境を構築)

MikroTik、Cisco、OpenWrtなどのルーターを手軽にテストするために、UbuntuやDebian上で軽量なPPPoEサーバーが必要ですか?以下は、コマンド入力だけで即座に稼働させる手順です。

必要なパッケージである pppoe(rp-pppoe ツール群を含む)、ppp デーモン、および iptables をインストールします:

sudo apt update
sudo apt install -y ppp pppoe iptables

/etc/ppp/pppoe-server-options にセッション設定ファイルのテンプレートを作成します:

sudo bash -c 'cat <<EOF > /etc/ppp/pppoe-server-options
require-chap
login
lcp-echo-interval 10
lcp-echo-failure 2
ms-dns 8.8.8.8
ms-dns 1.1.1.1
netmask 255.255.255.255
default-mru 1492
default-asyncmap
EOF'

/etc/ppp/chap-secrets で接続用アカウントを定義します。固定IPを割り当てることも、サーバーに自動割り当てさせることも可能です:

# クライアント   サーバー  シークレット    IPアドレス
"client01"      *       "Password@123"  10.0.0.50
"client02"      *       "Password@456"  *

IPフォワーディングを有効化し、NATを設定します。ここでは eth0 がインターネット側、eth1 が検証用スイッチに接続されていると仮定します:

sudo sysctl -w net.ipv4.ip_forward=1
sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
sudo iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

インターフェース eth1 上でPPPoEサーバーデーモンを起動します:

sudo pppoe-server -I eth1 -L 10.0.0.1 -R 10.0.0.10 -N 20

これで完了です。クライアントルーターから eth1 ポートにケーブルを接続し、ユーザー client01 でダイヤルアップ接続すると、正しくIP 10.0.0.50 が割り当てられ、正常に外部ネットワークへ通信できることが確認できます。

技術的な仕組み

なぜISPラボではDHCPではなくPPPoEが必要なのか?

家庭内ネットワークや一般的なオフィス環境であれば、DHCPだけで十分です。しかし、ISP(BNG/BRAS)の動作を模擬するラボ環境を構築する場合、DHCPの限界が顕著になります。

DHCPはユーザー認証を必須とせず、加入者契約ごとのセッション分離が困難であり、ログインセッションに応じた動的なQoS制御を適用することもほぼ不可能です。そのため、通信キャリア各社はFTTHアクセス回線において長年PPPoEを採用し続けています。

PPPoEハンドシェイクのシーケンス

PPPoEは、従来のPPPフレームを直接Ethernetフレームにカプセル化(encapsulate)します。ハンドシェイクプロセスは大きく2つのフェーズに分かれます:

  • Discoveryフェーズ(レイヤー2): クライアントとサーバーは、以下の4つのメッセージを順次交換して相互検出を行います:PADI(クライアントからのブロードキャストによるサーバー探索) → PADO(サーバーからのユニキャストによるサービス提案) → PADR(クライアントからの接続要求) → PADS(サーバーによるセッション確認とSession IDの割り当て)。
  • PPPセッションフェーズ: Session IDが確立されると、両者間でLCP(Link Control Protocol)ネゴシエーションを開始します。続いてCHAPによるパスワード認証が行われ、最後にIPCPプロトコルによってIPアドレス、デフォルトゲートウェイ、DNSサーバーの設定が完了します。

pppoe-serverコマンドのパラメータ解説

短いコマンドラインですが、運用の要となる重要なパラメータが含まれています:

  • -I eth1:Discoveryパケット(PADI)をリッスンするインターフェースを指定します。PPPoE DiscoveryはEtherType 0x8863 のレイヤー2で直接動作するため、このNICに事前にIPアドレスを割り当てておく必要はありません。
  • -L 10.0.0.1:トンネルのサーバー側IPアドレスです。クライアント側ではこのアドレスがデフォルトゲートウェイとして設定されます。
  • -R 10.0.0.10:動的割り当て用IPプール(IP Pool)の開始アドレスです。chap-secrets で * を指定した場合、このアドレスから連番で割り当てられます。
  • -N 20:同時に接続可能な最大セッション数を20に制限します。クライアントから大量のブロードキャスト要求が送信された際に、仮想マシンのメモリ枯渇を防ぐ役割を果たします。

最適化と運用

1. MTUの罠とTCP MSSクランピングの根本解決

標準的なEthernetフレームのMTUは1500バイトです。一方、PPPoEヘッダーに6バイト、PPPのProtocol IDに2バイトを消費するため、合計8バイトのオーバーヘッドが発生します。その結果、仮想インターフェース pppX の最大MTUは 1492バイト に制限されます。

ここにトラブルの原因が潜んでいます。

クライアントは通常、デフォルトのMSSである1460バイトでTCPパケットを送信します。これにIPヘッダー(20バイト)とTCPヘッダー(20バイト)が加わると、合計サイズは1500バイトになります。このパケットがPPPoEトンネルに入ると全体で1508バイトに達し、即座にフラグメンテーション(パケット分割)が発生します。

さらに厄介なのは、パケットにDF(Don’t Fragment)フラグが設定されている場合です。中継ルーターがパケットを破棄する一方で、ICMP Type 3 Code 4(Destination Unreachable / Fragmentation Needed)のエラー通知を誤って遮断してしまうことがあります。その結果、いわゆる「ブラックホール現象」が生じます。IPへのpingは1msで通るにもかかわらず、HTTPSのWebサイトが開けなかったり、ファイル転送が完全にフリーズしたりします。

これを解決する最も確実な方法は、iptablesのmangleテーブルでMSSを強制的に調整することです:

sudo iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

このコマンドは、インターフェースのMTUを自動的に判別し、SYNパケットのMSSを安全なサイズ(通常は1452バイト)に縮小します。これにより、Webトラフィックの詰まりが解消され、スムーズに通信できるようになります。

2. Systemdによるサービス化

検証用仮想マシンの再起動後もサーバーが自動起動するように、/etc/systemd/system/pppoe-server.service にサービスユニットを作成します:

[Unit]
Description=RP-PPPoE Server Service
After=network.target

[Service]
Type=forking
ExecStartPre=/sbin/iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
ExecStartPre=/sbin/iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
ExecStart=/usr/sbin/pppoe-server -I eth1 -L 10.0.0.1 -R 10.0.0.10 -N 50
ExecStop=/usr/bin/killall pppoe-server
Restart=on-failure

[Install]
WantedBy=multi-user.target

デーモンをリロードしてサービスを有効化・起動します:

sudo systemctl daemon-reload
sudo systemctl enable --now pppoe-server

トラブルシューティングと現場でのノウハウ

以前、60人規模のオフィス向けルーターを検証していた際、非常に不可解なパケットロス事象に遭遇したことがあります。午前中のWebブラウジングやGoogle MeetによるWeb会議は全く問題なく利用できていましたが、午後になると「15MBのPDF資料がアップロードできない」「Nextcloudから画像がダウンロードできない」という報告が相次ぎました。

ppp0 ポートで tcpdump を実行してパケットキャプチャを行ったところ、原因が判明しました。中継コアスイッチがICMP Type 3 Code 4を誤ってすべてドロップしていたのです。大容量の画像ファイルなどはDFフラグ=1の状態で上限1500バイトまで膨らんでおり、クライアントにMTU引き下げのシグナルが戻らないままサイレントドロップされていました。

この経験以降、PPPoEやGREトンネルを構築する際は、他のサービスをテストする前にまずmangleテーブルへ TCPMSS --clamp-mss-to-pmtu のルールを追加することを徹底しています。

セッションのリアルタイムデバッグと監視

クライアントが接続に成功するたび、Linuxカーネルは ppp0、ppp1 といった仮想インターフェースを自動生成します。接続中のインターフェース一覧は次のコマンドで素早く確認できます:

ip link show | grep ppp

パスワード不一致やLCPネゴシエーション失敗などのエラーを特定するには、ターミナルで journalctl を使用してリアルタイムログを追跡します:

sudo journalctl -t pppd -f

原因が特定できない場合は、/etc/ppp/pppoe-server-options に debug の1行を追加してください。LCP ConfReq、ConfAckフレームやCHAP Challengeの詳細なやり取りが出力され、トラブルシューティングを迅速に行うことができます。

Share: