Mona に lwip-1.3.1 移植しよう

開始 12:50

  1. contrib/ports/unix/sys_arch.c をベースにする。
  2. 事前調査により mutex, condition, thread_self() などが必要な事が分かっているので用意してある
  3. Makefile 空の main.cpp を用意する。
  4. flymake 用に check-syntax ターゲットを用意する。.emacs で flymake-buildfile-dirs を設定。
  5. lwip-1.3.1/src/core, api の c ファイルを Makefile に追加

make してみる 13:00

こんな警告が出てる。

lwip-1.3.1/src/include/lwip/sockets.h:321: 警告: ‘struct timeval’ declared inside parameter list
lwip-1.3.1/src/include/lwip/sockets.h:321: 警告: its scope is only this definition or declaration, which is probably not what you want

コードを見てみる。

#ifndef LWIP_TIMEVAL_PRIVATE
#define LWIP_TIMEVAL_PRIVATE 1
#endif

#if LWIP_TIMEVAL_PRIVATE
typedef struct timeval {
  long    tv_sec;         /* seconds */
  long    tv_usec;        /* and microseconds */
} timeval;
#endif /* LWIP_TIMEVAL_PRIVATE */

...
...

int lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,
                struct timeval *timeout);

これのどこが悪いんだろう。ひょっとして LWIP_TIMEVAL_PRIVATE を外で定義すべきなのかな。lwipopts.h で定義したら消えた。


次はこれ。

lwip-1.3.1/src/include/ipv4/lwip/ip.h:130: 警告: ‘packed’ attribute ignored for field of type ‘struct ip_addr’

CFLAGS の不足っぽいので port unixMakefile を見てみよう。-fpack-struct を発見。 -DLWIP_DEBUG もついでに追加しよう。追加したけど消えない。
コード見てみる。

  PACK_STRUCT_FIELD(struct ip_addr src);

分かった。arch/cc.h でアーキテクチャ依存の構造体パックのコードを書けば良いのか。コンパイルオプションの -fpack-struct をつけているので空定義にして良さそうだ。

どんどん行くよ。

In file included from lwip-1.3.1/src/core/pbuf.c:72:
./arch/perf.h:59: 警告: ‘struct tms’ declared inside parameter list
./arch/perf.h:59: 警告: its scope is only this definition or declaration, which is probably not what you want

tms の構造体定義が無い。perf.c は arch 依存だから Mona 用に用意すべきってことか。と思って unix port 見てみたらどこからも呼ばれてなかった。コメントアウトしよう。

警告は続く13:30

lwip-1.3.1/src/api/sockets.c:323: 警告: the address of ‘naddr’ will never be NULL
ip_addr_debug_print(SOCKETS_DEBUG, &naddr);

naddr は ipaddr として以下のマクロに渡る。

#define ip_addr_debug_print(debug, ipaddr) \
  LWIP_DEBUGF(debug, ("%"U16_F".%"U16_F".%"U16_F".%"U16_F,              \
                      ipaddr != NULL ?                                  \
                      (u16_t)(ntohl((ipaddr)->addr) >> 24) & 0xff : 0,  \
                      ipaddr != NULL ?                                  \
                      (u16_t)(ntohl((ipaddr)->addr) >> 16) & 0xff : 0,  \
                      ipaddr != NULL ?                                  \
                      (u16_t)(ntohl((ipaddr)->addr) >> 8) & 0xff : 0,   \
                      ipaddr != NULL ?                                  \
                      (u16_t)ntohl((ipaddr)->addr) & 0xff : 0))

naddr は auto 変数だからアドレスは NULL にならないよってことか。無視して良い警告だと思う。


lwip-1.3.1/src/api/tcpip.c: In function ‘tcpip_init’:
lwip-1.3.1/src/api/tcpip.c:555: 警告: passing argument 2 of ‘sys_thread_new’ from incompatible pointer type

これは Mona の thread_create に __fastcall をつけているからだ。 lwip 側で合わせるしかない。

リンクエラー 13:37

lwip-1.3.1/src/core/netif.o: In function `netif_set_up':
/home/taro/mona/core/net_server/lwip-1.3.1/src/core/netif.c:406: undefined reference to `_etharp_request'

lwip-1.3.1/src/netif/etharp.c に定義がある。 eth は Mona が提供し arp は lwip に任せたいところだ。port unix を見てみる。

# NETIFFILES: Files implementing various generic network interface functions.'
NETIFFILES=$(LWIPDIR)/netif/etharp.c mintapif.c

etharp.c を Makefile に加えよう。


次もリンクエラー。

/home/taro/mona/core/net_server/lwip-1.3.1/src/core/netif.c:378: undefined reference to `_debug_flags'
lwip-1.3.1/src/core/netif.o: In function `netif_find':

unix port では main.c に置かれているようだ。


疲れたので水を飲もう。

/home/taro/mona/core/net_server/lwip-1.3.1/src/core/netif.c:350: undefined reference to `_ntohl'

何となく役割が分かる関数だ。ネットワークバイトオーダーはビッグエンディアンMona はリトルエンディアン。
これは Mona 側のライブラリにあるべきなので追加しよう。
追加した。

リンクエラーは続く 14:03

/home/taro/mona/core/net_server/lwip-1.3.1/src/core/raw.c:237: undefined reference to `_ip_route'

core/ipv4 以下を Makefile に追加。include path も同様。

ビルド通った 14:09

あれ。ビルド通ってしまった。Ether の I/F はどこで通すんだ?そのまえに git commit しておくか。
例のごとく unix port を見たら netif 構造体に Ether in/out の関数ポインタを設定するようだ。移植性高くて素晴らしいね。
unix port の main.c を移植すれば良さそう。その前にトイレ。

main.c の移植開始 14:22

getopt を利用している部分をばっさりと削除。IPアドレスなどは最初は固定で良い。timer, snmp はとりあえず OFF で。echo サーバーが動く事を目指す。

netif_add(&netif, &ipaddr, &netmask, &gw, NULL, mintapif_init, ip_input);

ここで Unix では TAP I/F の初期化関数を渡す。Mona 用の etherif を用意しよう。

init の I/F は

err_t etherif_init(struct netif *netif)

struct mintapif は具体的に何に使われているんだろうか。netif->state に格納して使いまわすのか。Mona では Ether Frame の送信は virtionet クラスの仕事なのでそれを使えば良かろう。

落ち着こう 14:51

main.c の移植が色々と作業を増やしてしまったのでやるべき事をノートに書き出した。あちこち行ったり来たりだと頭の中でキャッシュヒットしづらくなるのでまずは main.c に注力する。main.c から必要とされる関数があれば I/F をヘッダに書くだけにとどめて後回しにする。

結局 main から必要とされる etherif は以下 2つ。

err_t etherif_init(struct netif* netif);
int etherif_select(struct netif *netif);

よく見てみると timer は tcp の resend などのきっかけとして使われているみたいなので必要だ。
main のコンパイル通った。

timer 15:00

10 msec 毎に timer が更新されれば良さそう。

  /* timer reload is in 10msec steps */
  tmr.it_interval.tv_sec = 0;
  tmr.it_interval.tv_usec = 10000;
  /* set to half period (enables timer) */
  tmr.it_value.tv_sec = 0;
  tmr.it_value.tv_usec = 5000;

it_value と it_interval の違いが分からないので man setitimer 。

タイマーは it_value からゼロへ向けて減っていき、シグナルを生成し、 it_interval に初期化される。
タイマーがゼロに設定された場合 (it_value がゼロか、タイマーが満了した時に it_interval
がゼロの場合) は停止する。

最初だけ 5 msec 後に発火するというように読める。


Mona ではタイマースレッドを作って対応する事にした。

static void __fastcall timerThread(void* arg)
{
    int intervalMsec = (int)arg;
    sleep(intervalMsec);

//    snmp_inc_sysuptime();

    struct itmr* tp = &timers[TIMER_NUM - 1];
    for(unsigned char i = TIMER_NUM; i > 0; i--) {
        if (tp->interval != 0) {
            /* timer is running */
            if (tp->cnt == 0) {
                /* timer expired */
                tp->event |= 1;
            } else {
                /* timer ticking */
                tp->cnt--;
            }
        }
        tp--;
    }

}

etherif 15:23

電池が残り少ない。間に合うか。
init でやるべき事を列挙。

  • ハードウェア初期化
  • mintapif->ethaddr の設定
  • mintapif->lasttime = 0;
  • in/out の設定
  • MTU などパラメータの設定。

うわ。隣で不倫話が始まった。親を悲しませたくないとか。男が妻持ち。コードに集中しよう。男が抽象的な話でごまかそうとしている!。
input / output は ether frame をきっちり作ってくれるのだろうか。作ってくれるっぽい。

ここで電池切れた。

todo

  • main.cpp の SIG_BLK がきっかけかも。
  • timer.c の snmp_inc_sysuptime();
  • etherif で snmp いくつか
  • virtio net クラスの send はテストされていない
  • DLWIP_DEBUG