続・VyOS と PPPoE と MSS clamp の設定と

2024/4/7追記 : VyOS 1.4 環境での方法書きました → 続々・VyOS 1.4 と PPPoE と MSS clamp の設定と - ..たれろぐ..



続・VyOS と PPPoE と MSS clamp の設定と - ..たれろぐろぐ.. からの移動トピで、 2019/03/21 当時の内容です。

要点

  • 以前書いてたエントリのアップデート(別解とも)

d.hatena.ne.jp

  • 以前は MSS 制限の必要なインタフェースと直接関係のないインタフェースにも MSS 制限ポリシーの設定が必要で、いまいちスマートでなかった
  • vyatta-postconfig-bootup.script を使い、インタフェースから出て行くパケットに対して MSS 制限ルールを追加して、無関係なインタフェースへのポリシー設定を排除した
  • VyOS のカバー外で設定しているので、別機能や将来の仕様変更時に衝突する不安あり

やりかた

MSS 制限ポリシーが次のように PPPoE インタフェースと LAN インタフェースの両方に設定されていることを前提環境とします。

# set interfaces ethernet eth0 pppoe 0 policy route MSSCLAMP
# set interfaces ethernet eth1 policy route MSSCLAMP

MSSCLAMP は以下のようなものを想定(Flet's用)。

policy {
    route MSSCLAMP {
        rule 10 {
            protocol tcp
            set {
                tcp-mss 1414
            }
            tcp {
                flags SYN,!RST
            }
        }
    }
}


まず、vi などで /config/scripts に次のスクリプトを作成します。
PPPoE インタフェースから出て行くパケットに対して MSS 制限するルールを iptables に追加するスクリプトです(以下では add-ifout-policy.sh としておきます)。

#!/bin/sh
# add mangle POSTROUTING rules
iptables -t mangle -N PPPOE-OUT
iptables -t mangle -A VYATTA_FW_OUT_HOOK -o pppoe0 -j MSSCLAMP-OUT
iptables -t mangle -A MSSCLAMP-OUT -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 1414
iptables -t mangle -A MSSCLAMP-OUT -m comment --comment "MSSCLAMP-OUT-10000 default-action accept" -j RETURN

実行属性付与することを忘れずに。

$ chmod +x /config/scripts/add-ifout-policy.sh


次に、 /config/scripts にある vyatta-postconfig-bootup.script というファイルの末尾に、先ほどのスクリプトを追記します。

$ vi  /config/scripts/vyatta-postconfig-bootup.script
#!/bin/sh
# This script is called from /etc/rc.local on boot after the Vyatta
# configuration is fully applied. Any modifications done to work around
# unfixed bugs and implement enhancements which are not complete in the Vyatta
# system can be placed here.

# add mangle POSTROUTING rules
/config/scripts/add-ifout-policy.sh

vyatta-postconfig-bootup.script は VyOS が設定を読み込み終えた後に呼ばれるスクリプトなので、種々の設定が行われた最後に add-ifout-policy.sh で設定される MSS 制限ルールが追加されることになります。

最後に、 LAN インタフェースに指定されている MSS 制限ポリシーを削除し、 commit します。

# delete interfaces ethernet eth1 policy route MSSCLAMP
# commit
# save

これで VyOS を再起動すると LAN インタフェース側で制限しなくても、PPPoE インタフェースに出入りするパケットに対して MSS 制限が効くようになります。

なんで?のお話

そもそもの問題は、PPPoE などの MTU が1500バイトに満たない経路を通る場合、MSS 制限をしてやらないと Webサイトによって見られたり見られなかったり妙に応答が遅くなったり、というものでした。
ご家庭用ルータでは、ここらへん自動的にやってくれてるのか大したトラブルにならないのですが、 VyOS はそこまで優しくありません。

VyOS の作法に則ると、次のような MSS 制限ポリシーを PPPoEインタフェースと LAN インタフェースの両方に指定することで、双方向で MSS 制限を働かせてやることになります。

なぜに PPPoE インタフェースと LAN インタフェースの双方に指定しないとダメなのか、というと VyOS でのポリシーベースルーティングは iptables の mangle テーブルの PREROUTING を使って実現しているので、インタフェースから出て行く(egress)パケットには影響を与えてません。

ポリシー設定後の iptables の具合を見てやるとよくわかるのですが、VyOS のポリシーベースルーティングの設定は、 VYATTA_FW_IN_HOOK チェインにて各インタフェースに対応するポリシー相当のチェインに飛ぶように設定されます。

vyos@vyos:~$ sudo iptables -t mangle -L -n -v
Chain PREROUTING (policy ACCEPT 6251K packets, 6256M bytes)
 pkts bytes target     prot opt in     out     source               destination 
6477K 6284M VYATTA_FW_IN_HOOK  all  --  *      *       0.0.0.0/0            0.0.0.0/0

Chain INPUT (policy ACCEPT 846K packets, 53M bytes)
 pkts bytes target     prot opt in     out     source               destination 

Chain FORWARD (policy ACCEPT 5631K packets, 6232M bytes)
 pkts bytes target     prot opt in     out     source               destination 

Chain OUTPUT (policy ACCEPT 780K packets, 51M bytes)
 pkts bytes target     prot opt in     out     source               destination 

Chain POSTROUTING (policy ACCEPT 6410K packets, 6283M bytes)
 pkts bytes target     prot opt in     out     source               destination 
6410K 6283M VYATTA_FW_OUT_HOOK  all  --  *      *       0.0.0.0/0            0.0.0.0/0

Chain VYATTA_FW_IN_HOOK (1 references)
 pkts bytes target     prot opt in     out     source               destination 
 2876  219K MSSCLAMP   all  --  eth1 *         0.0.0.0/0            0.0.0.0/0   
 187K   28M MSSCLAMP   all  --  pppoe0 *       0.0.0.0/0            0.0.0.0/0   

Chain MSSCLAMP (2 references)
 pkts bytes target     prot opt in     out     source               destination 
57092 2831K TCPMSS     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            /* MSSCLAMP-10 */ tcp flags:0x06/0x02 TCPMSS set 1414
 187K   28M RETURN     all  --  *      *       0.0.0.0/0            0.0.0.0/0            /* MSSCLAMP-10000 default-action accept */

Chain VYATTA_FW_OUT_HOOK (1 references)
 pkts bytes target     prot opt in     out     source               destination 

VyOS に入ってきたパケットは mangle PREROUTING -> VYATTA_FW_IN_HOOK -> 各インタフェースのポリシールール と渡されていくことになります。
この動作は PREROUTING の名の通り、パケットが入ってきた場合のみに働くので、VyOS から出て行くパケットにはなにも対処できません。
ここらのポリシーベースルーティングの仕組みについては、さくらインターネットの松本氏がまとめられてるのでそちらを参照してください。
research.sakura.ad.jp
iptables でのパケットの流れがわからんという人は https://ja.wikipedia.org/wiki/Iptables#/media/File:Netfilter-packet-flow.svg の絵を参照で)


外部との接続が PPPoE 1本だけ、という場合はこの設定で不自由しないわけですが、昨今速くなると話題の MAP-E やら DSLite やらの v4 over v6 な接続や、LAN直結専用線、のようなものと併存してやろうとすると LAN 側インタフェースに一律に MSS 制限ポリシーを仕込むと MSS 制限しなくていい通信まで MSS 制限してしまい、微妙に効率が悪くなったり、その他のポリシーベースルーティングの設定がし辛くなるなどの問題が出てきます。

そこで注目するのは、同 mangle テーブルに登録されているものの、現時点(VyOS1.1.8)で使われていない VYATTA_FW_OUT_HOOK に、時前で VYATTA_FW_IN_HOOK と同じようなルールを仕込んでしまえという、このエントリで紹介する手になります。
(前エントリの最後に書いていた『裏技臭がひどくて…』というのをやってしまったと)

先に書いたスクリプトを実行すると、 iptables の状態は次のような形になります。
これで出て行くパケットは VYATTA_FW_OUT_HOOK で対応するインタフェースに応じて MSSCLAMP-OUT に飛び、結果として MSS が制限されることになります。

vyos@vyos:~$ sudo iptables -t mangle -L -n -v
Chain PREROUTING (policy ACCEPT 6251K packets, 6256M bytes)
 pkts bytes target     prot opt in     out     source               destination 
6477K 6284M VYATTA_FW_IN_HOOK  all  --  *      *       0.0.0.0/0            0.0.0.0/0

Chain INPUT (policy ACCEPT 846K packets, 53M bytes)
 pkts bytes target     prot opt in     out     source               destination 

Chain FORWARD (policy ACCEPT 5631K packets, 6232M bytes)
 pkts bytes target     prot opt in     out     source               destination 

Chain OUTPUT (policy ACCEPT 780K packets, 51M bytes)
 pkts bytes target     prot opt in     out     source               destination 

Chain POSTROUTING (policy ACCEPT 6410K packets, 6283M bytes)
 pkts bytes target     prot opt in     out     source               destination 
6410K 6283M VYATTA_FW_OUT_HOOK  all  --  *      *       0.0.0.0/0            0.0.0.0/0

Chain VYATTA_FW_IN_HOOK (1 references)
 pkts bytes target     prot opt in     out     source               destination 
 187K   28M MSSCLAMP   all  --  pppoe0 *       0.0.0.0/0            0.0.0.0/0   

Chain MSSCLAMP (1 references)
 pkts bytes target     prot opt in     out     source               destination 
57092 2831K TCPMSS     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            /* MSSCLAMP-10 */ tcp flags:0x06/0x02 TCPMSS set 1414
 187K   28M RETURN     all  --  *      *       0.0.0.0/0            0.0.0.0/0            /* MSSCLAMP-10000 default-action accept */

Chain MSSCLAMP-OUT (1 references)
 pkts bytes target     prot opt in     out     source               destination 
 2152  116K TCPMSS     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp flags:0x06/0x02 TCPMSS set 1414
 101K   49M RETURN     all  --  *      *       0.0.0.0/0            0.0.0.0/0            /* MSSCLAMP-OUT-10000 default-action accept */

Chain VYATTA_FW_OUT_HOOK (1 references)
 pkts bytes target     prot opt in     out     source               destination 
 101K   49M MSSCLAMP-OUT  all  --  *      pppoe0  0.0.0.0/0            0.0.0.0/0   

これにてあるインタフェースに出入りするパケット、双方向で MSS 制限できるようになるので、その他インタフェースからは MSS 制限ポリシーを削除しても大丈夫、となります。

LAN 側から curl -X GET http://b.hatena.ne.jp/ したときのパケットキャプチャが次の通り。

monitor interfaces ethernet eth1 traffic filter "net 52.85.6.0 mask 255.255.255.0"
Capturing traffic on eth1.5 ...
[A]  0.007107 192.168.0.17 -> 52.85.6.14   TCP 54629 > 80 [SYN] Seq=0 Win=8192 Len=0 MSS=1460 WS=2
[B]  0.013234   52.85.6.14 -> 192.168.0.17 TCP 80 > 54629 [SYN, ACK] Seq=0 Ack=1 Win=29200 Len=0 MSS=1414 WS=8
[C]  0.014533 192.168.0.17 -> 52.85.6.14   TCP 54629 > 80 [ACK] Seq=1 Ack=1 Win=66456 Len=0
  • 同 PPPoE 側
vyos@vyos:~$ monitor interfaces ethernet pppoe0 traffic filter "net 52.85.6.0 mask 255.255.255.0"
Capturing traffic on pppoe0 ...
[A]  0.000000 220.147.153.14 -> 52.85.6.234  TCP 54637 > 80 [SYN] Seq=0 Win=8192 Len=0 MSS=1460 WS=2
[B]  0.005243  52.85.6.234 -> 220.147.153.14 TCP 80 > 54637 [SYN, ACK] Seq=0 Ack=1 Win=29200 Len=0 MSS=1460 WS=8
[C]  0.006409 220.147.153.14 -> 52.85.6.234  TCP 54637 > 80 [ACK] Seq=1 Ack=1 Win=66456 Len=0

LAN 側から PPPoE 側に出ていくパケット [A] の MSS option は変更されていないのに対して、PPPoE 側から LAN 側に戻ってきたパケット [B] は、 PPPoE インタフェースに指定した policy route 設定により MSS option が 1460 -> 1414 に変更されています。

monitor interfaces ethernet eth1 traffic filter "net 52.85.6.0 mask 255.255.255.0"
vyos@vyos:~$ monitor interfaces ethernet eth1 traffic filter "net 52.85.6.0 mask 255.255.255.0"
Capturing traffic on eth1.5 ...
[A]  0.000000 192.168.0.17 -> 52.85.6.58   TCP 54666 > 80 [SYN] Seq=0 Win=8192 Len=0 MSS=1460 WS=2
[B]  0.005228   52.85.6.58 -> 192.168.0.17 TCP 80 > 54666 [SYN, ACK] Seq=0 Ack=1 Win=29200 Len=0 MSS=1414 WS=8
[C]  0.005950 192.168.0.17 -> 52.85.6.58   TCP 54666 > 80 [ACK] Seq=1 Ack=1 Win=66456 Len=0
  • 同 PPPoE 側
vyos@vyos:~$ monitor interfaces ethernet pppoe0 traffic filter "net 52.85.6.0 mask 255.255.255.0"
Capturing traffic on pppoe0 ...
[A]  0.000000 220.147.153.14 -> 52.85.6.58   TCP 54667 > 80 [SYN] Seq=0 Win=8192 Len=0 MSS=1414 WS=2
[B]  0.005181   52.85.6.58 -> 220.147.153.14 TCP 80 > 54667 [SYN, ACK] Seq=0 Ack=1 Win=29200 Len=0 MSS=1460 WS=8
[C]  0.005900 220.147.153.14 -> 52.85.6.58   TCP 54667 > 80 [ACK] Seq=1 Ack=1 Win=66456 Len=0

対してスクリプトを実行して VYATTA_FW_OUT_HOOK にルールを追加した後では、LAN 側から PPPoE 側に出ていくパケット [A] も、PPPoE 側から LAN 側に戻ってきたパケット [B] も、両方とも MSS option が 1460 -> 1414 に変更されています。
前者(PPPoE 側に出ていくパケットへの変更)が、スクリプトで追加したルールの効果になります(後者はPPPoE インタフェースに指定した policy route 設定の効果)。


難点というか要注意なのは、 VyOS のあずかり知らぬ部分で設定を入れているため、なにかしらの別の設定と衝突したり、将来に機能が追加された際に衝突する可能性がある。というところです。

ここらはインタフェースの制限事項なので interface の項目内で設定できるようになるのが一番なのでしょうが 1.2.0 でも対応してないようで……


それではよい VyOS ライフを。