はじめに
最近家族がアパートを借りて別に住むようになったのですが、実家においてあるNASやMacのTimemachine Backupサーバにアクセスできないということで、VPNを使用して実家とアパートの2拠点間で、同一セグメントのネットワークを構築しようと思いました。
最初はWireGuradを使ってみようと思ったんですが、問題になるのはインターネットからのアクセスをどうするかです。ルータのポート空けたり、固定IPとかDynamic DNSとかも面倒。セキュリティも気になります。
tailscale VPN
検索して出てきたのがtailscale VPN。アプリケーションをインストールしてGoogleアカウントでログインするだけでP2PベースのVPN網を構築できます。Win/Mac/スマホ/Linux(RaspberryPi)に対応で、今のところ無料で100台まで利用可能。これはすばらしいということで、tailscaleを使うことにしました。
参考:Internet Watch: 100台まで無料のVPNサービス「tailscale」、リンクだけでマシンのシェアも可能!?
GRE(GRETAP) : Ethernet over IP
拠点間同一セグメントネットワーク構築には、IP(L3)のトンネリングではなくEthernet(L2)のトンネリングが必要となります。調べてみると、GREプロトコルがL2/L3の両方のトンネリングに対応しており、Linuxでも使いやすそうなのでこれにしました。
ここにあるRedHatの資料「IPv4 でイーサネットフレームを転送するための GRETAP トンネルの設定」がまさにそれです:
RaspberryPiを仮想スイッチングハブ(仮想ブリッジ)とし、両拠点に用意したその仮想スイッチングハブを、同じく仮想のLANケーブル(実態はVPN + GREのL2トンネリング)でお互いに接続するイメージとなります。
下準備
RaspberryPiを2台用意
お互いの拠点に設置し、tailscaleをインストールしておきます。
セットアップはこちらを参照: Raspberry Pi 3/4 (RaspberryPi OS 64bit) 一括セットアップ手順
tailscaleの認証を無期限にしておくために、tailscaleのWeb管理画面でraspberry piの設定を選択し(右端の":"アイコン)、"disable key expiry"を選択してください。
なお、拠点間で動画などサイズの大きいデータの受け渡しをしたい場合、RaspberryPi 4と64bit OSの使用をお勧めします。当初片側を有線100MbpsのRaspberryPi3にしていたのですが、拠点間で動画データを流すのは厳しかったです。
拠点間のブロードバンドルータ設定
ブロードバンドルータをたとえば以下のように設定します。同じネットワークアドレスですが、お互い重ならないIPアドレスをDHCPで付与するようにします。
拠点A: ルータアドレス192.168.1.1/24, DHCPアドレス配布範囲: 192.168.1.2 - 192.168.1.99
拠点B: ルータアドレス192.168.1.100/24, DHCPアドレス配布範囲: 192.168.1.101 - 192.168.1.199
Raspberry Piの設定
必要なカーネルモジュールの追加
- GREプロトコルのサポート
sudo modprobe ip_gre
lsmod | grep gre
上記で、ip_greが表示されることを確認。なお、GRE over IPv6をする場合はip6_greが必要になりますが、tailscaleのMTUが1280とIPv6で許容される最小のMTUサイズになっている関係で、IPv6版は使いませんでした。
- 仮想ブリッジのサポート
sudo modprobe bridge
lsmod | grep bridge
上記で、bridgeが表示されることを確認。
ブリッジ経由の通信内容をebtablesでフィルタリングするため、br_netfilterも追加。
$ sudo modprobe br_netfilter
$ lsmod | grep netfilter
念のため、ブリッジのフィルタリングが有効になっていることを確認。
$ sysctl net.bridge.bridge-nf-call-iptables
net.bridge.bridge-nf-call-iptables = 1
$ sysctl net.bridge.bridge-nf-call-ip6tables
net.bridge.bridge-nf-call-ip6tables = 1
再起動後も有効になるように設定
ip_gre
bridge
br_netfilter
ebtables, iproute2のインストール
iproute2はインストール済みかと思いますが、念のためインストール。なお、ブリッジ関連の操作は旧来のbridge-utils(brctlコマンド)がiproute2(ipコマンド)で置き換えられているのでipコマンドだけで十分なのですが、ネットではbrctlを使った例も多く、参考にするために入れました。
ebtablesはコマンド自体は最初から存在するのですが、明示的にインストールしないと/etc/ethertypesがない旨のエラーが出ました。
sudo apt-get -y install ebtables iproute2 bridge-utils
eth0のdhcpを無効化
ちょっと戸惑ったところですが、仮想ブリッジ(br_lan)を作成しそこにeth0を接続した場合、LANに接続されるのはeth0ではなくbr_lanという扱いになります。そのため、eth0にはIPアドレスを付与せず、br_lanがDHCPでアドレスを取得するように設定します。
denyinterfaces eth0
/etc/network/interfaces の設定
現行のRaspberryPi OSでは/etc/network/interfacesは空となっていますが、ここに記述した設定も普通に有効になるようです。
関係するインタフェースは以下の4つ:
- eth0: 物理インタフェース
- gretap1: GRETAP L2トンネルインタフェース。gretap0は予約済みなのでgretap1の名前にしています。
- br_lan: 仮想ブリッジインタフェース。ここにeth0とgretap1を接続します。デフォルトではSTP(ネットのループ検出)が有効になってますが、不要なので無効化。
- tailscale0: tailscale VPNインタフェース(これはtailscaledが自動で作成するので、ここでは記述不要)
MTUについて
トンネリングインタフェースのMTUは、"実際のMTU - トンネリングに必要なヘッダ"とするのが一般的な模様です。今回の場合、gretap1のMTUはtailscale0のMTU値1280から38(GREヘッダ: 4, Ether: 14, IPヘッダ: 20)を引いた値、1242が適切です。
と最初は思ったのですが、今回はLayer2のトンネリングのため、経路途中のMTUに影響されることなく、入ってきたEthernetフレームをそのまま相手側に渡す必要があります。そのため、ブリッジ接続するbr_lan, eth0, gretap1のMTUはすべて1500に固定。その上で、gretap1から tailscale0(MTU:1280)を経由するパケットはフラグメント化が必須となります。
gretap1インタフェース作成コマンド pre-up ip link add gretap1 type gretap remote ... ignore-df nopmtudisc
の中のignore-df nopmtudisc
オプションがそれです。
ちなみにこの辺、10年以上の長い間バグがあったようなのですが、タイミングがいいことに5.10.10と割と最近のカーネルで修正されています -
https://phabricator.vyos.net/T3555
MTUについてはまだ怪しい点があるため、後述します。
追記:gretap1のMTUは1500以上でないとパケットがドロップしました。実験的には1514でよさそうですが、1542を設定します。
DHCPパケットのフィルタリング設定
お互いの拠点にブロードバンドルータが存在するので、拠点間をDHCPリクエストなどが通らないようにブロックします。ブリッジレベルの通信のフィルタリングとなるので、いつものiptablesではなくebtablesでの設定が必要です(これを知らずにiptablesでパケットを遮断しようとして結構はまりました)。interfacesファイルでの該当行は以下。IPv4/IPv6のDHCP、およびIPv6のRouter Solicitation/Advertisementがgretap1から送信されないようにブロックします。
# Block DHCP
post-up ebtables -I FORWARD -o gretap1 -p IPv4 --ip-proto udp --ip-dport 67:68 -j DROP
post-up ebtables -I FORWARD -o gretap1 -p IPv6 --ip6-proto udp --ip6-dport 67:68 -j DROP
# Block Router Solicitation/Advertisement
post-up ebtables -I FORWARD -o gretap1 -p IPv6 --ip6-proto ipv6-icmp --ip6-icmp-type 133:134 -j DROP
なお余談ですが、tailscaleは実際に通信が確立した段階(systemdでの一連のサービスの起動の後)でiptablesの設定を追加します。その設定はルール先頭に挿入されるため、iptablesのINPUT, FORWARDにDROP系の独自ルールを入れている場合(tailscaleのACCEPTルールが先に適用されて)無効化されるので注意が必要です。
main側のRaspberry piの/etc/network/interfaces
設定ファイル中remote
以降のIPアドレスは適宜置き換えてください。
また、仮想ブリッジインタフェースは通常だと起動のたびにランダムなMACアドレスが設定されます。DHCPで固定IPアドレスを割り当てたかったので、ブリッジインタフェースの初期化時にhwaddress ether 92:6a:68:ed:97:03
を追加してMACアドレスを固定にしています。
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet static
address 0.0.0.0
pre-up ip link set dev eth0 promisc on
post-down ip link set eth0 promisc off
auto gretap1
iface gretap1 inet static
address 0.0.0.0
pre-up ip link add gretap1 type gretap remote 100.86.118.83 ignore-df nopmtudisc # main
#pre-up ip link add gretap1 type gretap remote 100.92.41.28 ignore-df nopmtudisc # sub
pre-up ip link set dev gretap1 promisc on # arp off # multicast off
up ip link set dev gretap1 up mtu 1542 # 1500 + 42(GRE:4 + Ether:18 + IP:20)
# Block DHCP
post-up ebtables -I FORWARD -o gretap1 -p IPv4 --ip-proto udp --ip-dport 67 -j DROP
post-up ebtables -I FORWARD -o gretap1 -p IPv4 --ip-proto udp --ip-dport 68 -j DROP
post-up ebtables -I FORWARD -o gretap1 -p IPv6 --ip6-proto udp --ip6-dport 67 -j DROP
post-up ebtables -I FORWARD -o gretap1 -p IPv6 --ip6-proto udp --ip6-dport 68 -j DROP
# Block Router Solicitation/Advertisement
post-up ebtables -I FORWARD -o gretap1 -p IPv6 --ip6-proto ipv6-icmp --ip6-icmp-type 133:134 -j DROP
# Clamp IPv4 TCP MSS to avoid fragmentation.
# MSS = 1280(tailscale MTU) - 42(GRE:4 + Ether:18 + IP:20) - 40(IP/TCP) = 1198
post-up iptables -t mangle -I POSTROUTING -m physdev --physdev-out gretap1 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 1198
auto br_lan
iface br_lan inet dhcp
hwaddress ether 92:6a:68:ed:97:03
pre-up ip link add name br_lan type bridge
bridge_stp off
#bridge_waitport 0
#bridge_fd 0
pre-up ip link set dev br_lan promisc on
pre-up ip link set dev eth0 master br_lan
pre-up ip link set dev gretap1 master br_lan
pre-up ip link set dev br_lan up mtu 1500
post-down ip link set dev eth0 nomaster
post-down ip link set dev gretap1 nomaster
post-down ip link del gretap1
post-down ip link del br_lan
sub側のRaspberry Piの/etc/network/interfaces
mainとほぼ同一。gretapインタフェースのリモートアドレスが違うのと、仮想ブリッジインタフェースのMACアドレス指定がないだけです。
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet static
address 0.0.0.0
pre-up ip link set dev eth0 promisc on
post-down ip link set eth0 promisc off
auto gretap1
iface gretap1 inet static
address 0.0.0.0
#pre-up ip link add gretap1 type gretap remote 100.86.118.83 ignore-df nopmtudisc # main
pre-up ip link add gretap1 type gretap remote 100.92.41.28 ignore-df nopmtudisc # sub
pre-up ip link set dev gretap1 promisc on # arp off # multicast off
up ip link set dev gretap1 up mtu 1542 # 1500 + 42(GRE:4 + Ether:18 + IP:20)
# Block DHCP
post-up ebtables -I FORWARD -o gretap1 -p IPv4 --ip-proto udp --ip-dport 67:68 -j DROP
post-up ebtables -I FORWARD -o gretap1 -p IPv6 --ip6-proto udp --ip6-dport 67:68 -j DROP
# Block Router Solicitation/Advertisement
post-up ebtables -I FORWARD -o gretap1 -p IPv6 --ip6-proto ipv6-icmp --ip6-icmp-type 133:134 -j DROP
# Clamp IPv4 TCP MSS to avoid fragmentation.
# MSS = 1280(tailscale MTU) - 42(GRE:4 + Ether:18 + IP:20) - 40(IP/TCP) = 1198
post-up iptables -t mangle -I POSTROUTING -m physdev --physdev-out gretap1 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 1198
auto br_lan
iface br_lan inet dhcp
pre-up ip link add name br_lan type bridge
bridge_stp off
#bridge_waitport 0
#bridge_fd 0
pre-up ip link set dev br_lan promisc on
pre-up ip link set dev eth0 master br_lan
pre-up ip link set dev gretap1 master br_lan
pre-up ip link set dev br_lan up mtu 1500
post-down ip link set dev eth0 nomaster
post-down ip link set dev gretap1 nomaster
post-down ip link del gretap1
post-down ip link del br_lan
ブリッジの構成を確認
$ brctl show
bridge name bridge id STP enabled interfaces
br_lan 8000.46242a251582 no eth0
gretap1
ここで問題発生
上記設定を終えRaspberry Piを再起動、sudo arping <相手側ルータのIPアドレス> -i gretap1 -0
で取り合えず疎通確認してみますが、ifconfig gretap1でみるとパケット数が増えておらず相手が受け取ってません。syslogをよくみると以下のエラーが出てました。
Jun 11 09:07:29 raspberrypi4 tailscaled[576]: Drop: Unknown{100.92.41.28:0 > 100.86.118.83:0} 84 unknown
どうやらtailscaleがGREプロトコルを許可していない模様です... ここにくるまで紆余曲折があり、かなり時間を費やしていたのにがっかり。
tailscaleにGREプロトコル対応を追加
幸いtailscaleはgo言語で記述されたオープンソースで、githubにて公開されています (https://github.com/tailscale/tailscale)。
あきらめる前にやるだけやってみようと、syslogに出ていた"Drop"や"Unknown"でソースの該当箇所を探してみました。
この辺(filter.go) を見るに、ICMP, UDP, TCP以外は通しておらず、GRE IPプロトコルがUnknownとしてDropされているので、GREプロトコルを通すように修正します。また、この辺(packet.go) にもGRE IPプロトコル対応が必要です。
開発はRaspberry Pi上で行い、./build_dist.sh tailscale.com/cmd/tailscaled
で出来たtailscaledバイナリを/usr/sbinにコピー。
テストケースも追加して、go test ../...
でテストも確認。
Pull Requestをあげておきました: GRE L2/L3 tunneling protocol support。バージョン1.12でマージ予定とのことです。
めでたく当初の目標が達成できました!
追記:残念ながら、「GREプロトコルを標準で通すのはちょっと...(しかし現状プロトコル選択手段無し)」とのことで見送りになりました。
追記2:2021/12以降のtailscaleでは、任意のIPプロトコルを通すように対応されています。ただ、自分で試したところ設定を間違っているのかうまくいきませんでした。
もしかしてtailscaleの改造いらなかった?
man ip-link
でgretapの設定オプション見ると、encapオプションというのがありました。GRE IPパケットをUDPでさらにカプセル化してくれるようです(Foo-Over-UDP)。気づくのが遅かった...
encap { fou | gue | none } - specifies type of
secondary UDP encapsulation. "fou" indicates Foo-
Over-UDP, "gue" indicates Generic UDP
Encapsulation.
追記:後日試したところ、Raspberry Piではこの機能が標準ではOFFになっていました(CONFIG_NET_FOU_IP_TUNNELS オプション)。対応するにはカーネルモジュールのリビルドが必要となります。
いろいろ確認してみる
ebtablesの動作を確認する
以下のコマンドで、適用回数(pcnt)を確認(元の表示は改行がおかしいので修正済み)。
ブリッジレベルなのにLayer3のフィルタリングが出来るってなんかすごいですね。
$ sudo ebtables -L --Lc
Bridge table: filter
Bridge chain: INPUT, entries: 0, policy: ACCEPT
Bridge chain: FORWARD, entries: 5, policy: ACCEPT
-p IPv6 -o gretap1 --ip6-proto ipv6-icmp --ip6-icmp-type 133:134/0:255 -j DROP , pcnt = 68 -- bcnt = 5400
-p IPv6 -o gretap1 --ip6-proto udp --ip6-dport 68 -j DROP , pcnt = 0 -- bcnt = 0
-p IPv6 -o gretap1 --ip6-proto udp --ip6-dport 67 -j DROP , pcnt = 0 -- bcnt = 0
-p IPv4 -o gretap1 --ip-proto udp --ip-dport 68 -j DROP , pcnt = 10 -- bcnt = 3280
-p IPv4 -o gretap1 --ip-proto udp --ip-dport 67 -j DROP , pcnt = 107 -- bcnt = 35249
Bridge chain: OUTPUT, entries: 0, policy: ACCEPT
ちゃんとIPv6のRAなどが遮断されてます。ちなみに最初はこれを遮断していなかったため、IPv6対応サイト(Qiitaもです)の表示に不具合が出てました。
MTUを確認するが、どうも怪しい
- RapsberryPi同士でMTUを確認
RaspberryPi同士のpingにて拠点間のMTUが1500であることを確認してみます。
本来なら1500 - 20(IP header) - 8(ICMP header) = 1472バイト送信できるはずが、1458バイトしか送信できません。差分の14バイトがどこから来ているのか不明(Ethernetヘッダー?)。
pi@raspberrypi4:~ $ ping -M do -s 1458 raspberrypi3.local
PING raspberrypi3.local (192.168.2.121) 1458(1486) bytes of data.
1466 bytes from 192.168.2.121 (192.168.2.121): icmp_seq=1 ttl=64 time=12.3 ms
1466 bytes from 192.168.2.121 (192.168.2.121): icmp_seq=2 ttl=64 time=11.8 ms
^C
--- raspberrypi3.local ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 2ms
rtt min/avg/max/mdev = 11.842/12.057/12.272/0.215 ms
pi@raspberrypi4:~ $ ping -M do -s 1459 raspberrypi3.local
PING raspberrypi3.local (192.168.2.121) 1459(1487) bytes of data.
From 192.168.2.68 (192.168.2.68) icmp_seq=1 Frag needed and DF set (mtu = 1500)
From 192.168.2.68 (192.168.2.68) icmp_seq=2 Frag needed and DF set (mtu = 1500)
^C
--- raspberrypi3.local ping statistics ---
2 packets transmitted, 0 received, +2 errors, 100% packet loss, time 3ms
実際、拠点間のRaspberryPi同士のscpコマンドがストールする。よろしくない状況です。
ifconfig gretap1でもTX errorが出てます。おそらくこのMTU問題だと思われます。
$ ifconfig gretap1
gretap1: flags=4419<UP,BROADCAST,RUNNING,PROMISC,MULTICAST> mtu 1500
inet6 fe80::14be:fdff:feeb:7771 prefixlen 64 scopeid 0x20<link>
ether 16:be:fd:eb:77:71 txqueuelen 1000 (Ethernet)
RX packets 51998 bytes 6357910 (6.0 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 47575 bytes 7269295 (6.9 MiB)
TX errors 166 dropped 0 overruns 0 carrier 148 collisions 0
- RaspberryPi以外からMTUを確認
Windowsから相手側拠点ルーターとRaspberryPiへのpingを行い、MTUを確認してみました。
>ping -f -l 1472 -n 1 192.168.2.108
192.168.2.108 に ping を送信しています 1472 バイトのデータ:
要求がタイムアウトしました。
192.168.2.108 の ping 統計:
パケット数: 送信 = 1、受信 = 0、損失 = 1 (100% の損失)、
>ping 192.168.2.100
192.168.2.100 に ping を送信しています 32 バイトのデータ:
192.168.2.100 からの応答: バイト数 =32 時間 =33ms TTL=64
192.168.2.100 からの応答: バイト数 =32 時間 =20ms TTL=64
やはり失敗します。しかも普通はMTU超過時に表示される「パケットの断片化が必要ですが、DF が設定されています。」のメッセージもなくタイムアウト。
- gretap1のMTUを1500+14に変更
ブリッジにつなげるインタフェースのMTUは同一でないと、パケットの転送に問題が出ます。そのため、br_lan, eth0, gretap1のMTUを最初はすべて1500にしていました。しかし、限界サイズのパケットがgretap1を通っていない状況なので、gretap1のMTUが結果的に足りてないように見えます。eth0から来たMTU1500のパケットをgretap1から送信する場合、GRETAPのヘッダがついてさらにパケットサイズが大きくなるのが原因ではないかと思います。
そこでgretap1のMTUを、上記pingテストでわかった謎の差分14バイトを加えた1514バイトとしてみました:
>ping -f -l 1472 -n 1 192.168.2.100
192.168.2.100 に ping を送信しています 1472 バイトのデータ:
192.168.2.100 からの応答: バイト数 =1472 時間 =10ms TTL=64
192.168.2.100 の ping 統計:
パケット数: 送信 = 1、受信 = 1、損失 = 0 (0% の損失)、
ラウンド トリップの概算時間 (ミリ秒):
最小 = 10ms、最大 = 10ms、平均 = 10ms
>ping -f -l 1473 -n 1 192.168.2.100
192.168.2.100 に ping を送信しています 1473 バイトのデータ:
パケットの断片化が必要ですが、DF が設定されています。
192.168.2.100 の ping 統計:
パケット数: 送信 = 1、受信 = 0、損失 = 1 (100% の損失)、
と、MTU1500でのpingが成功するようになりました。1500を超える場合のメッセージも期待通りです。ただし、この対応を行っても、なぜかRaspberryPi間のpingの状況は変わりませんでした。納得いきませんが、とりあえずいったんここまでとします。
パケットのフラグメント化を確認してみる
上記pingの内容をtcp dumpで確認してみます。
tcpdump -v -i tailscale0 '((ip[6:2] > 0) and (not ip[6] = 64))'
でtailscale0を流れるフラグメント化されたパケットだけを見ることが出来ます。gretap1インタフェースを見てもフラグメント処理後の内容しか見れないので、tailscale0インタフェースの通信をチェックします。
$ sudo tcpdump -v -i tailscale0 '((ip[6:2] > 0) and (not ip[6] = 64))'
09:29:22.363936 IP (tos 0x0, ttl 64, id 19551, offset 0, flags [+], proto GRE (47), length 1276)
100.86.118.83 > 100.92.41.28: GREv0, Flags [none], length 1256
IP truncated-ip - 210 bytes missing! (tos 0x0, ttl 64, id 24567, offset 0, flags [DF], proto TCP (6), length 1448)
192.168.2.100.http > 192.168.2.34.58448: Flags [.], seq 1408:2816, ack 1, win 980, length 1408: HTTP
09:29:22.364020 IP (tos 0x0, ttl 64, id 19551, offset 1256, flags [none], proto GRE (47), length 230)
100.86.118.83 > 100.92.41.28: gre
最初のパケットに210 bytes missing!と出ていますが、その後のパケットが230バイト。IPヘッダ20バイトを除くとちょうど210バイトなので、意図したとおりフラグメント化されていることは確認できました。パケットの内容にflags:DFとあるように、フラグメント不可のパケットもGREでカプセル化の上フラグメント化されていることがわかります。
MSS Clampを試す
上記MTU問題の低減+TCP通信でのフラグメント化を避けるため、MSSの値を小さくしてみることにします。IPv4のTCP限定ですが、フラグメント化されずに通信できることになります。
MSSの値は1280(taiscaleのMTU) - 42(GREヘッダ: 4, Ether: 18, IPヘッダ: 20) - 20(IPヘッダ) - 20(TCPヘッダ) = 1198バイト
設定ファイル中の該当行は以下です。iptablesの-m physdev --physdev-out gretap1
オプションで物理インタフェースを直接指定し、MSSの書き換えが出来ます。
iptables -t mangle -I POSTROUTING -m physdev --physdev-out gretap1 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 1198
Windowsから相手側拠点ルーターのWebインタフェースにアクセスし、tcpdumpでMSSが書き換えられていること、フラグメント化がおきていないことを確認します。
速度測定編 にて計測してますが、MSS Clampを行うと2割ほど速度が改善されるようです。
続き
速度を求めてtailscaleからWireguardへ移行しました。
参考情報
結構古い情報も多く、パケットフィルタリングも今はiptablesからnetfilterに移行しているので注意が必要です。
- https://wiki.linuxfoundation.org/networking/bridge
- https://backreference.org/2013/07/23/gre-bridging-ipsec-and-nfqueue/
- https://bugzilla.kernel.org/show_bug.cgi?id=14837
- https://qiita.com/homihomu/items/804847373a1a7e27bbf7
- https://david-waiting.medium.com/a-beginners-guide-to-generic-routing-encapsulation-fb2b4fb63abb