LwIPでIPv6


フレッツ光でIPv6が利用できるので蟹さんで使っているLwIPのRAWインターフェースでIPv6を使えるようにしてみました。

lwipopts.hにipv6のサポートを入れます。

lwipopts.h
#define LWIP_IPV4       1
#define LWIP_IPV6       1

これでsrc/core/ipv6の下のソースファイルをコンパイルしてライブラリに追加します。

rtlbm-mrubyをビルドしてみたところ、ビルドが通らなくなりました。LWIP_IPV6を1にすると型が変わってしまい修正する必要がありました。型はIPv4用の型を直接指定せず共用の方を使うようにしました。

コンパイルが通るようになって、試してみて前と同じように動く事を確認しました。

これでIPv6のサポートを入れます。まず追加したのは以下のコードです。

net.c
        netif_create_ip6_linklocal_address(&netif, 1);
        netif.ip6_autoconfig_enabled = 1;

rtl_ether.c
        netif->output_ip6 = ethip6_output;

です。これでアドレスがインターフェースに追加されます。おそらく最初に実装したときdoc/NO_SYS_SampleCode.cを参考にして自分が削ったのだと思いますが、まったく記憶にありませんでした。

アドレスは拾えたのですが、ping6に応答しません。調べて見たところ、以下の設定で答えるようになりました。バグなのかもしれません。

lwipopts.h
#define LWIP_IPV6_MLD   0

アドレスが付いたら次はDNSでIPv6のアドレスを拾えるようにします。これにはdns_gethostbynameではなくてdns_gethostbyname_addrtypeを使います。typeをIPADDR_TYPE_V6(1)を指定するとV4同様にアドレスが拾えます。アドレスは128ビットになっているので、Cコードの中では配列で扱います。

いろいろ考えて見てmruby側のmrbgem(mruby-yabm)にlookup6というメソッドを追加してIPアドレスを文字列で返す事にしました。通常IPv6のアドレスを表記する場合は16ビット毎に:で区切られていて、RFCで0を省略のルールもあるのですが、処理が面倒なので省略しない方式で実装しました。

2001:0df0:0232:eea0:0000:0000:0000:fff3

これで名前解決はできたので、とりあえずSNTPの処理をIPv6対応にします。蟹さんはHTTPSの証明書の有効期限を確認のためにある程度正確な時間が必要で、RTCがないのでSNTPで時間を合わせるようにしています。SNTPで使えるntp.nict.jpはIPv6のアドレスのサーバもあります。このサーバをlookup6してIPv6のアドレスを拾います。それを既存のsntpメソッドに渡すわけですが、sntpメソッド内でアドレスの文字列が39文字の場合はIPv6でそれ以外はIPv4として処理するようにしました。

IPアドレスを文字列で扱うようにしたのはmruby 2の最初の頃に最上位ビットが立った数値が扱えなかったため、ワークアラウンドでそうしました。

FreeBSDでsnoopを使いながら確認して、SNTPのUDPのパケットを送信しているとこもIPv6対応にしました。

とりあえずパケットが送られて戻ってくるのですがパケットが処理されません。

いろいろ試していたところRAの処理などが起動後にあるので、数秒まってからSNTPのパケットを送信したところちゃんと動くようになりました。

start = yabm.count()
while yabm.count() < start + 3 * 1000 do
end
ntpaddr6 = yabm.lookup6("ntp.nict.jp")
yabm.print ntpaddr6 + "\r\n"
yabm.sntp(ntpaddr6)

IoT(Internet of Test)でご愛用のThingspeakは今のところIPv4のみなので、HTTPSなどはおいおい対応してみたいと思います。

何度見てもLwIPのコードは見通しが悪く奇奇怪怪です。誰かもっとまともなスタック作ってくれませんか?

BCMBMにもサポートを入れてみて気がついたのですが、BCMのIFではマルチキャストを受け取らない設定でした。router advertisementは33:33:00:00:00:01で飛んでくるので、これが受けられるようにする必要がありました。

#if 1
        WRITECSR(sc, R_RCV_CONFIG, M_RCFG_AM);   /* All multicast */
#else
        WRITECSR(sc, R_RCV_CONFIG, 0);
#endif

元々#if 0になっていました。おそらくIPv4の01:00:5eはデフォルトで受けられるけど、それ以外は設定が必要な気がします。

また蟹さんはネットワークバイトオーダーのBig Endianですが、BCMはLittle EndianでV6のアドレスを積むときにhtonlする必要がありました。