openWrt は nft / nftables になっているので、注意する。
最初にパケットを定期的に送る。(watch を使うのがコツ / ping コマンドで送ると継続パケットになり、パケットがマークされたままになり、経路が変わっても以前の経路を通る)
ssh s0 -- watch -n 2 'ping -c 1 1.0.0.1'
LAN(br-lan)からのForwardをAcceptするには
## src net を書いたり iptables -I FORWARD -o "${VTI_IF}" -s 172.16.9.0/24 -j ACCEPT ## または iptables -I FORWARD -o "${VTI_IF}" -i br-lan -j ACCEPT
iptablesでルールを書いて先頭に入れてAcceptしたら良い。ただし。OpenWrtはNFT/nfstable であるので、これを書き換える必要がある。
たとえば次のようになる。
nft insert rule inet fw4 forward iifname $IIF oifname $VTI_IF accept;
ただ、上記の場合だと不要になったあとに削除が面倒なので、ユニークなコメントを入れておく。
nft insert rule inet fw4 forward iifname $IIF oifname $VTI_IF accept comment "\"${UNIQUE_COMMENT}\"";
ただ、これだとルーティングで広めにとると全部転送されてしまうので、もう少し限定する。
nft insert rule inet fw4 forward iifname $IIF ip daddr 1.1.1.1 accept comment "\"${UNIQUE_COMMENT}\"";
これで、削除するときに、コメントをマーク代わり使って削除できる。
HANDLES=$(nft -a list table inet fw4 | grep "${UNIQUE_COMMENT}" | grep -oP '(?<=handle )+\d+') for h in $HANDLES; do nft delete rule inet fw4 forward handle $h done
マスカレードを入れる。そのままでは応答パケット先が不明になるため、OpenWrtでマスカレードして解決する
nft add table ${TABLE} nft add chain ${TABLE} prerouting '{ type nat hook prerouting priority 0; }' nft add chain ${TABLE} postrouting '{ type nat hook postrouting priority 0; }' nft add rule ${TABLE} postrouting oifname "${VTI_IF}" masquerade
これで、通信できた。
あとは、これらを設定にまとめてup-down スクリプトにまとめてvti をつかうようにする。(後述)
最初にパッケージのインストール
opkg install \ strongswan-default \ strongswan-swanctl \ strongswan-mod-eap-mschapv2 \ strongswan-mod-eap-identity \ strongswan-default \ strongswan-ipsec \
ipsec の設定を書く
/etc/ipsec.conf
include /etc/config/custom/strongswan/ipsec.conf
/etc/config/custom/strongswan/ipsec.conf
include ./*/ipsec.conf
/etc/config/custom/strongswan/ipsec.secrets
include ./*/ipsec.secrets
/etc/strongswan.conf
## (中略) ## 末尾に追加する。最後の記述で上書きされる。 include /etc/config/custom/strongswan/strongswan.d/vti.conf
/etc/config/custom/strongswan/strongswan.d/vti.conf
# vim: set ft=css et : ## 上書きする設定。 charon { plugins { resolve { file = /etc/resolve.ipsec.conf load = yes } }} charon { install_routes = no install_virtual_ip = no }
接続設定を書く
/etc/config/custom/strongswan/vpnx.example.com/ipsec.conf
conn myConn keyexchange=ikev2 right=vpnx.example.com rightid=vpnx.example.com rightauth=pubkey rightsubnet=0.0.0.0/0 type=tunnel leftauth=eap leftsourceip=%config4 leftid=takuya001 eap_identity=takuya001 auto=add leftupdown=/etc/config/custom/strongswan/vpnx.example.com/link-updown.sh mark=218
/etc/config/custom/strongswan/vpnx.example.com/ipsec.secrets
# 認証(パスワード)を書く # for ikev2 or ms-chap-v2 takuya001 vpnx.example.com : EAP "xxxxxxxxxxxx"
up-down スクリプトを作る
mkdir -p /etc/config/custom/strongswan/vpnx.example.com/ touch /etc/config/custom/strongswan/vpnx.example.com/link-updown.sh chmod +x /etc/config/custom/strongswan/vpnx.example.com/link-updown.sh vi /etc/config/custom/strongswan/vpnx.example.com/link-updown.sh
中身
#!/usr/bin/env bash set -o nounset #set -o errexit UNIQUE_NAME=$( basename $( dirname $0 ) ) VTI_IF="vti${PLUTO_UNIQUEID}" TABLE="inet ${UNIQUE_NAME}" UNIQUE_COMMENT="takuya:${UNIQUE_NAME}_accept" SSH_SERVER=xxx.xxx.xxx.xxx SAMPLE_IP=1.1.1.1 ## vpn 先にネットワークがある場合 VPN_GW=${PLUTO_MY_SOURCEIP%.*}.1 VPN_NET=${PLUTO_MY_SOURCEIP%.*}.0/24 add_nft(){ IIF=br-lan ## clear nft remove_nft ## MASQUERADE nft add table ${TABLE} nft add chain ${TABLE} prerouting '{ type nat hook prerouting priority 0; }' nft add chain ${TABLE} postrouting '{ type nat hook postrouting priority 0; }' nft add rule ${TABLE} postrouting oifname "${VTI_IF}" masquerade ## accept nft insert rule inet fw4 forward iifname $IIF ip daddr { ${SSH_SERVER} } counter accept comment "\"${UNIQUE_COMMENT}\"" nft insert rule inet fw4 forward iifname $IIF ip daddr { ${SAMPLE_IP} } counter accept comment "\"${UNIQUE_COMMENT}\"" } remove_nft(){ ## nft nft delete table ${TABLE} 2>/dev/null HANDLES=$(nft -a list table inet fw4 | grep "${UNIQUE_COMMENT}" | grep -oP '(?<=handle )+\d+') for h in $HANDLES; do nft delete rule inet fw4 forward handle $h done } vpn_routing(){ ## add vpn gw if [[ ! -z $VPN_NET ]]; then ip route add $VPN_NET dev "${VTI_IF}" fi ## static routing ip route add ${SAMPLE_IP} dev "${VTI_IF}" ip route add ${SSH_SERVER} dev "${VTI_IF}" } function show_env(){ env | grep PLUTO echo SHELL=$SHELL } function link_up_down(){ case "${PLUTO_VERB}" in up-client) ## vti tunnel device ip tunnel add "${VTI_IF}" mode vti \ local "${PLUTO_ME}" remote "${PLUTO_PEER}" \ okey "${PLUTO_MARK_OUT%%/*}" ikey "${PLUTO_MARK_IN%%/*}" ip link set "${VTI_IF}" up ip addr add ${PLUTO_MY_SOURCEIP} dev "${VTI_IF}" ## routing / ルーティングはVIF削除で一緒に消える vpn_routing # firewall add_nft ;; down-client) remove_nft ip tunnel del "${VTI_IF}" ;; esac } function main(){ show_env link_up_down $@ } if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then main "$@" fi
起動と終了
ipsec restart ipsec up myconn ipsec status ipsec down myconn ipsec status
接続の確認
ping 1.1.1.1 tcpdupm -i vtiX host 1.1.1.1 tcpdupm -i pppoe host vpn.example.com
vtiXのパケットは中身が見えていて、pppoe 経由だとUDP-encap: ESP
になっていれば暗号化するトンネルと経由している。