その手の平は尻もつかめるさ

ギジュツ的な事をメーンで書く予定です

NetworkManager 1.36においてL2TP/IPSecのトンネルが上手く張れない問題

NetworkManagerのversion 1.36においてL2TP/IPSecのトンネルを張ろうとすると上手く張れないという問題があります。
自分が使用しているUbuntu 22.04ではL2TP接続は標準では提供されていないのでnetwork-manager-l2tpパッケージをapt等でインストールする必要がありますが、この時に一緒に使用するNetworkManagerのバージョンによって当該問題が発生します。ちなみにNetworkManagerのバージョンは nmcli --version 等で参照可能です。
Ubuntu 22.04のpackage managerで利用可能なNetworkManagerのバージョンは1.36系であるため *1 だいたいこの問題を踏むことになると思われます。


症状としては network-manager-l2tp をインストールした上でL2TP/IPSec接続を試行すると以下のようなエラーメッセージが表示された上でどことも通信できなくなり、最終的にトンネル接続の確立がキャンセルされるというものが見られます。

NetworkManager[1134025]: xl2tpd[1134025]: handle_control: bad control packet!
NetworkManager[1134025]: xl2tpd[1134025]: network_thread: bad packet

L2TPやIPSecの設定に問題があるのかと思って色々と試してみたのですが改善せず、色々調べたところNetworkManagerの開発repositoryのissueに行き着きました:

これは既知の問題であるようで、

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/946#note_1406609

このコメントで示されているように、トンネルのために作成されるpppインターフェイスが「よくわからないIP」を持っていることに依るようです。
例示されているppp0の状態を借りて説明すると、

43: ppp0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1400 qdisc fq_codel state UNKNOWN group default qlen 3
    link/ppp 
    inet 172.16.100.10 peer 63.2.5.44/32 scope global ppp0
       valid_lft forever preferred_lft forever
    inet 172.16.100.10/32 scope global noprefixroute ppp0
       valid_lft forever preferred_lft forever

トンネルの宛先 63.2.5.44/32 に対して 172.16.100.10 がpeerされていることが問題のようです。
というわけでトンネル確立試行中に ip a del 172.16.100.10 peer 63.2.5.44 dev ppp0 などとしてその不正なpeerを削除してあげると、正常にトンネルが張られて通信できるようになります。


めでたしめでたし......と行きたいところですが、このままではトンネルを張るたびに手動でpeerの削除コマンドを打たなければならないのでそれは面倒です。そもそもこの削除コマンドはトンネル確立処理中に打たなければならず、適切なタイミングでの実行が求められて大層面倒です。自動化しましょう。

NetworkManagerには発生するイベントに応じてスクリプトが呼ばれるという仕組みがあるのでそれを利用します。
/etc/network/if-up.d に実行可能なスクリプトを置いておくとインターフェイスがupした時に呼ばれるので、

#!/bin/bash

set -euo pipefail

if [[ "$IFACE" =~ ^ppp[0-9]+ ]]; then
  invalid_addr_json=$(ip -j addr show dev "$IFACE" | jq -r '.[0].addr_info[] | select(.scope == "global" and .address != null)')
  sudo ip a del "$(echo "$invalid_addr_json" | jq -r .local)" \
    peer "$(echo "$invalid_addr_json" | jq -r .address)/$(echo "$invalid_addr_json" | jq -r .prefixlen)" \
    dev "$IFACE"
fi

というようなスクリプトを 999-remove-invalid-peer-on-ppp-if みたいな名前で /etc/network/if-up.d 配下に置いておくと、L2TPトンネル確立の際に自動的に不正なpeerが除去されるので正常にトンネルが張れるようになります。jqはインストールしておきましょう。
ただし、他の用途でpppインターフェイスを利用する際にこのスクリプトが有効になっていると、意図せず動作をおかしくしてしまう可能性があるのでその点についてはご留意ください。
なおこの時、このスクリプトに実行可能なpermissionを与えていないと上手く動かないので注意が必要です。chmod 755 などとして与えましょう。

めでたしめでたし。それはそうとして ip コマンドの結果をJSONで出力する -j オプションは便利でよいですね。


ちなみに

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/946#note_1725575

このコメントにあるように、NetworkManager 1.40では直っている問題のようです。アップグレードできるならアップグレードしたほうが良いでしょう。Ubuntu 22.04のpackage managerにも入ってほしいですが、ちょっと難しそうなのでUbuntu 24.04を待つしかなさそうな予感がしています。Ubuntu 23.04ではNwtworkManager 1.42を利用しているので *2 24.04では修正版が使えるようになることを期待しています。