~nabeken/diary/

Gentoo Linux(6年くらい)とFreeBSD(1年くらい)とOpenBSD(新参者)を使う日々。


IHANet BGP peering overview

OpenBSD 5.1でGRE over IPSec with NAT-T + rdomain

Posted on Sun Dec 02 15:07:40 +0900 2012 by nabeken

安価な3G回線経由のGRE over IPSec (+NATトラバーサル)でOpenBSD 5.1同士を接続してみました。 3G回線はrdomain 1に隔離し、greのみをrdomain 0へ追加する構成にしました。

構成

3G回線(バックアップ回線)は月額980円の イオンSIM をb-mobile WiFi刺し、用意しました。 OpenBSD 5.1では870円で購入した GW-USNano2 を装着し、無線LANへ接続しました。

前回の記事 では無線LAN I/Fをデフォルトのrdomain 0に所属させたまま、VPNエンドポイントへのスタティックルーティング を登録する手法を紹介しました。しかし、この手法では問題がありました。 gwはNAPTで各サービスへトラフィックを転送しています。VPNエンドポイントもそのサービスを利用しています。 この手法ではVPNエンドポイントからの通信で戻りが非対称(IPアドレスも変わってしまう)になってしまうため、 実運用には使えませんでした。

この問題を回避するため、rdomain(Routing Domain)によるルーティングテーブルの分離を行ないました。 OpenBSDの場合は特定のプロセスをrdomainにattachするだけでなく、ネットワークインターフェースもrdomainにattachすることもできます。 (FreeBSDだとVIMAGEのほうが近い。Linuxではnamespaceによってネットワークスタックを分離できるようです。)

バックアップ回線用のネットワークインターフェースを別rdomainに収容することで既存ルーティング に影響を与えずにネットワークを拡張できます。さらに素晴しいことに、OpenBSDのgif(4)やgre(4)は outer(encapしたトラフィックのI/F)のrdomainとinner(encapする前のトラフィックのI/F)のrdomainを別々に指定できます。 つまり、トンネルのための回線は既存ルーティングに影響を与えないようにしつつ、トンネルで運ぶ対象のトラフィック は既存ルーティングに載せることができます。

最終的な構成は以下を目指します。この上にBirdでOSPFを流します。3G回線を通るgreはコストを上げておき、 平常時はPPPoE経由のOpenVPNにトラフィックが流れるようにします。:

+----------+    GRE(outer) over IPSec w/ nat-t
| ipsec-gw |----------(via 3G)----------+
+--+----+--+                            |
   |    |                               | urtwn0         rdomain 1
   |    |        OpenVPN         +------+-----+   -------------------------
   |    +------(via PPPoE)-------+ openbsd-gw |          rdomain 0
   |                       tun0  +------------+
   |                                    | gre0
   +------------------------------------+
                GRE(inner)

ipsec-gwは固定IPv4アドレスが振られています。 openbsd-gwは非固定IPv4アドレスによるNAT配下にいる想定です。

Routing Domain in OpenBSD

OpenBSDのrdomainは Virtualizing the OpenBSD Routing Table が詳しいです。

OpenBSD 5.1ではrdomainはIPv4のみです(参考: rtable vs rdomain)。 参考によれば、作業中とのことなので、遠くないリリースでは対応すると思われます。 今回の構成ではtunneldomainだけがrdomain対象なので、GREで通すIPv6パケットは問題なく通過します。

バックアップ回線のセットアップ

無線LANのNICはurtwn(4)を使用しました。

# dmesg | grep urtwn
urtwn0 at uhub0 port 1 "Planex Communications Inc. GW-USNano2" rev 2.00/2.00 addr 2
urtwn0: MAC/BB RTL8188CUS, RF 6052 1T1R, address 00:XX:XX:XX:XX:XX

# cat /etc/hostname.urtwn0
nwid hogehoge wpakey secret
rdomain 1
dhcp

こうすることで、DHCPによって取得したデフォルトゲートウェイはrdomain 1へ入ります。 このままでは /etc/resolv.conf は上書きされてしまいます。5.1のdhclientには ignoreオプションが入っていないため、 /etc/dhclient.conf で予めDNSサーバを指定しておきます。

# cat /etc/dhclient.conf
supersede domain-name-servers 8.8.8.8;
request subnet-mask, broadcast-address, time-offset, routers, domain-name;

GREとIPSecを以下のように構成します。

+----------+ (gre0: 192.168.0.1/32)                        +------------+
| ipsec-gw +-----------------------------------------------+ openbsd-gw |
+----------+                        (gre0: 192.168.0.2/32) +------------+
(lo1: 10.0.0.1/32)                                       (lo1: 10.0.0.2/32)
      |                                                          |
      +-----------------------[ IPSec ]--------------------------+

それぞれのlo1にGREの終端のためにIPアドレスを振ります。ここではまだ起動させません。

ipsec-gw# cat /etc/hostname.lo1
inet 10.0.0.2/32

ipsec-gw# cat /etc/hostname.gre0
tunnel 10.0.0.2 10.0.0.1
192.168.0.2 192.168.0.1 netmask 255.255.255.255

ipsec-gw# cat /etc/hostname.enc0
up

openbsd-gw# cat /etc/hostname.lo1
rdomain 1
inet 10.0.0.1/32

openbsd-gw# cat /etc/hostname.gre0
tunneldomain 1
tunnel 10.0.0.1 10.0.0.2
192.168.0.1 192.168.0.2 netmask 255.255.255.255

enc(4) をrdomain 1に作成します。

openbsd-gw# cat /etc/hostname.enc1
rdomain 1
up

pf

ipsec-gw, openbsd-gw共通として:

  • gre0はskip

    set skip on { lo0, gre0 }
    

ipsec-gwでは:

  • enc0はskip

    set skip on { enc0 }
    
  • 不特定多数からのesp/udpを許可

    NAT-T(NATトラバーサル)ではudpにカプセル化されるので併せて許可します。

    pass in on $ext_if proto udp from any to ($ext_if) port { isakmp, ipsec-nat-t }
    pass in on $ext_if proto esp from any to ($ext_if)
    

openbsd-gwでは:

  • enc1はskip

    set skip on { enc1 }
    
  • ipsec-gwから/へのesp/udpを許可

    ipsec_gw = "X.X.X.X"
    pass out on $ext_if proto udp from ($ext_if) to $ipsec_gw port { isakmp, ipsec-nat-t }
    pass out on $ext_if proto esp from ($ext_if) to $ipsec_gw
    

設定したら pfctl -ef /etc/pf.conf しておきます。

GRE

GREを通しておきます。

ipsec-gw# sh /etc/netstart enc0
ipsec-gw# sh /etc/netstart lo1
ipsec-gw# sh /etc/netstart gre0
ipsec-gw# ifconfig lo1
lo1: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 33152
    priority: 0
    groups: lo
    inet6 fe80::1%lo1 prefixlen 64 scopeid 0xe
    inet 10.0.0.2 netmask 0xffffffff

ipsec-gw# ifconfig gre0
gre0: flags=9011<UP,POINTOPOINT,LINK0,MULTICAST> mtu 1476
    priority: 0
    groups: gre
    physical address inet 10.0.0.2 --> 10.0.0.1
    inet6 fe80::xxxx:xxxx:xxxx:xxxx%gre0 ->  prefixlen 64 scopeid 0xf
    inet 192.168.0.2 --> 192.168.0.1 netmask 0xffffffff

openbsd-gw# sh /etc/netstart enc1
openbsd-gw# sh /etc/netstart lo1
openbsd-gw# sh /etc/netstart gre0
openbsd-gw# ifconfig enc1
enc1: flags=20041<UP,RUNNING,NOINET6> rdomain 1
    priority: 0
    groups: enc
    status: active

openbsd-gw# ifconfig lo1
lo1: flags=28049<UP,LOOPBACK,RUNNING,MULTICAST,NOINET6> rdomain 1 mtu 33152
    priority: 0
    groups: lo
    inet 10.0.0.1 netmask 0xffffffff

openbsd-gw# ifconfig gre0
gre0: flags=9011<UP,POINTOPOINT,LINK0,MULTICAST> mtu 1476
    priority: 0
    groups: gre
    physical address inet 10.0.0.1 --> 10.0.0.2 rdomain 1
    inet6 fe80::yyyy:yyyy:yyyy:yyyy%gre0 ->  prefixlen 64 scopeid 0x25
    inet 192.168.0.1 --> 192.168.0.2 netmask 0xffffffff

IPSecのセットアップ

ipsec.conf(5) を用意します。

ipsec-gw# cat /etc/ipsec.conf
ike passive esp proto gre from 10.0.0.2/32 to 10.0.0.1/32 peer any srcid ipsec-gw.example.com psk secret

openbsd-gw# cat /etc/ipsec.conf
ipsec_gw = "X.X.X.X"
me = "10.0.0.1"
remote = "10.0.0.2"
ike dynamic esp proto gre from $me/32 to $remote/32 peer $ipsec_gw psk secret

デバッグのため、まずは手でisakmpd(8)を動かします。

ipsec-gw# isakmpd -K -d -v
ipsec-gw# ipsecctl -f /etc/ipsec.conf

openbsd-gw# route -T 1 exec isakmpd -K -d -v
openbsd-gw# route -T 1 exec ipsecctl -f /etc/ipsec.conf

疎通を確認します。

ipsec-gw# ping -c 5 192.168.0.1
openbsd-gw# ping -c 5 192.168.0.2
// このpingはrdomain 0から通ることに注意

うまくいけばphase 2が完了し、FLOWが確立しているはずです。

ipsec-gw# ipsecctl -sa
FLOWS:
flow esp in from 10.0.0.1 to 10.0.0.2 peer Z.Z.Z.Z srcid ipsec-gw.example.com dstid openbsd-gw.localdomain type use
flow esp out from 10.0.0.2 to 10.0.0.1 peer Z.Z.Z.Z srcid ipsec-gw.example.com dstid openbsd-gw.localdomain type require

SAD:
esp tunnel from Z.Z.Z.Z to X.X.X.X spi 0x03212efb auth hmac-sha2-256 enc aes
esp tunnel from X.X.X.X to Z.Z.Z.Z spi 0xae7c067e auth hmac-sha2-256 enc aes

openbsd-gw# route -T 1 exec ipsecctl -sa
FLOWS:
flow esp in from 10.0.0.2 to 10.0.0.1 peer X.X.X.X srcid openbsd-gw.localdomain dstid ipsec-gw.example.com type use
flow esp out from 10.0.0.1 to 10.0.0.2 peer X.X.X.X srcid openbsd-gw.localdomain dstid ipsec-gw.example.com type require

SAD:
esp tunnel from Y.Y.Y.Y to X.X.X.X spi 0x03212efb auth hmac-sha2-256 enc aes
esp tunnel from X.X.X.X to X.X.X.X spi 0xae7c067e auth hmac-sha2-256 enc aes

忘れずに起動スクリプトに仕込んでおきます。

ipsec-gw# echo ipsec=\"YES\" >> /etc/rc.conf.local
ipsec-gw# echo isakmpd_flags=\"-K\" >> /etc/rc.conf.local
openbsd-gw# echo 'route -T 1 exec isakmpd -K' >> /etc/rc.local
openbsd-gw# echo 'route -T 1 exec ipsecctl -f /etc/ipsec.conf' >> /etc/rc.local

bird

上で用意したgreをcost 50としてOSPFに突っ込みます(birdのデフォルトコスト値は10) 。 適当なエリアに以下のinterfaceを追加します。

# ipsec-gw
interface "gre0" {
        hello 10;
        dead 40;
        priority 100;
        cost 50;
};

# openbsd-gw
interface "gre0" {
        hello 10;
        dead 40;
        priority 0;
        cost 50;
};

再起動し、neighborが確立していることを確認します(tun0はPPPoE経由のOpenVPN)。

# ipsec-gw
bird> show ospf neighbors
TKNETWORKS:
Router ID       Pri          State      DTime   Interface  Router IP
10.0.5.10         1         full/bdr    00:38   tun0       fe80::yyyy:yyyy:yyyy:yyyy
10.0.5.10         0         full/ptp    00:38   gre0       fe80::xxxx:xxxx:xxxx:xxxx

# openbsd-gw
bird> show ospf neighbors
TKNETWORKS:
Router ID       Pri          State      DTime   Interface  Router IP
10.7.15.10      100         full/dr     00:37   tun0       fe80::xxxx:xxxx:xxxx:xxxx
10.7.15.10      100         full/ptp    00:37   gre0       fe80::yyyy:yyyy:yyyy:yyyy

お待ちかねの経路切り替えを観察します。OpenVPNとbirdでリンクアップ検知の設定をまだ詰めていないため復旧時間がやや長いです。

icmp_seq=4まではPPPoE経由のOpenVPNを通っています。ここで、 openbsd-gw 側のOpenVPNを落します。 その結果が Host is down になっています。無事にicmp_seq=39からは3G回線経由に切り替わっています。 すぐにOpenVPNをstartさせ、icmp_seq=98で元の状態に収束しているのが確認できました。

openbsd-gw# ping6 2001:db8::1
PING6(56=40+8+8 bytes) 2001:380:6dc:7aa::2 --> 2001:db8::1
16 bytes from 2001:db8::1, icmp_seq=0 hlim=64 time=4.612 ms
16 bytes from 2001:db8::1, icmp_seq=1 hlim=64 time=4.758 ms
16 bytes from 2001:db8::1, icmp_seq=2 hlim=64 time=4.414 ms
16 bytes from 2001:db8::1, icmp_seq=3 hlim=64 time=4.224 ms
16 bytes from 2001:db8::1, icmp_seq=4 hlim=64 time=4.703 ms
ping6: sendmsg: Host is down
ping6: wrote 2001:db8::1 16 chars, ret=-1
ping6: sendmsg: Host is down
ping6: wrote 2001:db8::1 16 chars, ret=-1
ping6: sendmsg: Host is down
ping6: wrote 2001:db8::1 16 chars, ret=-1
ping6: sendmsg: Host is down
ping6: wrote 2001:db8::1 16 chars, ret=-1
ping6: sendmsg: Host is down
ping6: wrote 2001:db8::1 16 chars, ret=-1
ping6: sendmsg: Host is down
ping6: wrote 2001:db8::1 16 chars, ret=-1
ping6: sendmsg: Host is down
ping6: wrote 2001:db8::1 16 chars, ret=-1
ping6: sendmsg: Host is down
ping6: wrote 2001:db8::1 16 chars, ret=-1
16 bytes from 2001:db8::1, icmp_seq=39 hlim=64 time=3988.44 ms
16 bytes from 2001:db8::1, icmp_seq=40 hlim=64 time=3968.82 ms
16 bytes from 2001:db8::1, icmp_seq=41 hlim=64 time=4470.2 ms
16 bytes from 2001:db8::1, icmp_seq=42 hlim=64 time=4390.02 ms
16 bytes from 2001:db8::1, icmp_seq=43 hlim=64 time=3469.87 ms
16 bytes from 2001:db8::1, icmp_seq=44 hlim=64 time=2740.37 ms
16 bytes from 2001:db8::1, icmp_seq=45 hlim=64 time=1750.51 ms
16 bytes from 2001:db8::1, icmp_seq=46 hlim=64 time=831.138 ms
16 bytes from 2001:db8::1, icmp_seq=47 hlim=64 time=411.364 ms
16 bytes from 2001:db8::1, icmp_seq=48 hlim=64 time=441.882 ms
16 bytes from 2001:db8::1, icmp_seq=49 hlim=64 time=642.324 ms
16 bytes from 2001:db8::1, icmp_seq=50 hlim=64 time=972.566 ms
16 bytes from 2001:db8::1, icmp_seq=51 hlim=64 time=1262.94 ms
16 bytes from 2001:db8::1, icmp_seq=52 hlim=64 time=1283.34 ms
16 bytes from 2001:db8::1, icmp_seq=53 hlim=64 time=1293.71 ms
16 bytes from 2001:db8::1, icmp_seq=54 hlim=64 time=1294.96 ms
16 bytes from 2001:db8::1, icmp_seq=55 hlim=64 time=1304.32 ms
16 bytes from 2001:db8::1, icmp_seq=56 hlim=64 time=1477.74 ms
16 bytes from 2001:db8::1, icmp_seq=57 hlim=64 time=1255.12 ms
16 bytes from 2001:db8::1, icmp_seq=58 hlim=64 time=705.29 ms
16 bytes from 2001:db8::1, icmp_seq=59 hlim=64 time=145.904 ms
16 bytes from 2001:db8::1, icmp_seq=60 hlim=64 time=156.342 ms
16 bytes from 2001:db8::1, icmp_seq=61 hlim=64 time=146.49 ms
16 bytes from 2001:db8::1, icmp_seq=62 hlim=64 time=236.893 ms
16 bytes from 2001:db8::1, icmp_seq=63 hlim=64 time=285.795 ms
16 bytes from 2001:db8::1, icmp_seq=64 hlim=64 time=147.682 ms
16 bytes from 2001:db8::1, icmp_seq=65 hlim=64 time=148.085 ms
16 bytes from 2001:db8::1, icmp_seq=66 hlim=64 time=138.578 ms
16 bytes from 2001:db8::1, icmp_seq=67 hlim=64 time=148.899 ms
16 bytes from 2001:db8::1, icmp_seq=68 hlim=64 time=289.307 ms
16 bytes from 2001:db8::1, icmp_seq=69 hlim=64 time=149.47 ms
16 bytes from 2001:db8::1, icmp_seq=70 hlim=64 time=149.384 ms
16 bytes from 2001:db8::1, icmp_seq=71 hlim=64 time=150.285 ms
16 bytes from 2001:db8::1, icmp_seq=72 hlim=64 time=150.676 ms
16 bytes from 2001:db8::1, icmp_seq=73 hlim=64 time=151.024 ms
16 bytes from 2001:db8::1, icmp_seq=74 hlim=64 time=148.4 ms
16 bytes from 2001:db8::1, icmp_seq=75 hlim=64 time=152.07 ms
16 bytes from 2001:db8::1, icmp_seq=76 hlim=64 time=152.222 ms
16 bytes from 2001:db8::1, icmp_seq=77 hlim=64 time=152.889 ms
16 bytes from 2001:db8::1, icmp_seq=78 hlim=64 time=153.04 ms
16 bytes from 2001:db8::1, icmp_seq=79 hlim=64 time=163.178 ms
16 bytes from 2001:db8::1, icmp_seq=80 hlim=64 time=273.66 ms
16 bytes from 2001:db8::1, icmp_seq=81 hlim=64 time=173.819 ms
16 bytes from 2001:db8::1, icmp_seq=82 hlim=64 time=144.435 ms
16 bytes from 2001:db8::1, icmp_seq=83 hlim=64 time=144.619 ms
16 bytes from 2001:db8::1, icmp_seq=84 hlim=64 time=145.237 ms
16 bytes from 2001:db8::1, icmp_seq=85 hlim=64 time=235.374 ms
16 bytes from 2001:db8::1, icmp_seq=86 hlim=64 time=145.794 ms
16 bytes from 2001:db8::1, icmp_seq=87 hlim=64 time=144.179 ms
16 bytes from 2001:db8::1, icmp_seq=88 hlim=64 time=146.573 ms
16 bytes from 2001:db8::1, icmp_seq=89 hlim=64 time=157.132 ms
16 bytes from 2001:db8::1, icmp_seq=90 hlim=64 time=147.372 ms
16 bytes from 2001:db8::1, icmp_seq=91 hlim=64 time=307.801 ms
16 bytes from 2001:db8::1, icmp_seq=92 hlim=64 time=148.159 ms
16 bytes from 2001:db8::1, icmp_seq=93 hlim=64 time=148.573 ms
16 bytes from 2001:db8::1, icmp_seq=94 hlim=64 time=148.974 ms
16 bytes from 2001:db8::1, icmp_seq=95 hlim=64 time=446.143 ms
16 bytes from 2001:db8::1, icmp_seq=96 hlim=64 time=149.767 ms
16 bytes from 2001:db8::1, icmp_seq=97 hlim=64 time=49.931 ms
16 bytes from 2001:db8::1, icmp_seq=98 hlim=64 time=4.134 ms
16 bytes from 2001:db8::1, icmp_seq=99 hlim=64 time=4.699 ms
16 bytes from 2001:db8::1, icmp_seq=100 hlim=64 time=4.104 ms
16 bytes from 2001:db8::1, icmp_seq=101 hlim=64 time=4.151 ms
16 bytes from 2001:db8::1, icmp_seq=102 hlim=64 time=4.384 ms
16 bytes from 2001:db8::1, icmp_seq=103 hlim=64 time=4.352 ms

さくらのVPS上のOpenBSDをハブにすることで、PPPoEと3G回線を組み合せた構成が作れました。

更新履歴

  • rc.confでipsec.confを読み込むように修正 (Sun, 2 Dec 2012 15:07:40 +0900)
  • 初稿 (Thu, 25 Oct 2012 00:37:58 +0900)