Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

4.2. ARPモジュール

ARPモジュールを移植します。ARPはIPアドレスからMACアドレスを解決するプロトコルで、これを追加するとユニキャスト宛のIPパケットを送信できるようになります。ARPはタイマー機能を利用するため、あわせてプラットフォーム依存のタイマー関連コードも用意します。

ARPモジュールのコードのコピー

ARPモジュールのコードをmicropsからコピーします。修正は不要で、そのまま利用できます。

$ cp $MICROPS/arp.{h,c} kernel/net/

タイマー機能の追加

ARPモジュールはキャッシュの有効期限管理のために、初期化時にタイマー(timer_register())を登録します。この呼び出しでビルドと実行が通るように、プラットフォーム依存のタイマー関連コードを追加します。ただし中身は空のスタブで、timer_register()は渡された登録内容を保持せず成功(0)を返すだけです。タイマーを駆動する仕組みも用意しないため、この段階では周期的な処理は一切行われません(ARPキャッシュの有効期限管理は働きませんが、以降の動作確認には支障ありません)。実際にタイマーを機能させるのは応用課題で扱います。

📝 kernel/net/platform/xv6-riscv/timer.h

#ifndef TIMER_H
#define TIMER_H

#include <sys/time.h>

extern int
timer_register(struct timeval interval, void (*handler)(void));

extern int
timer_init(void);
extern int
timer_run(void);
extern int
timer_shutdown(void);

#endif

📝 kernel/net/platform/xv6-riscv/timer.c

#include <stdio.h>
#include <sys/time.h>

#include "platform.h"

#include "util.h"

int
timer_register(struct timeval interval, void (*handler)(void))
{
    return 0;
}

int
timer_init(void)
{
    return 0;
}

int
timer_run(void)
{
    return 0;
}

int
timer_shutdown(void)
{
    return 0;
}

arp.cはキャッシュのタイムスタンプ比較にtimersub()timercmp()のマクロを使うので、簡易libcのsys/time.hに追加します。

📝 kernel/net/platform/xv6-riscv/libc/sys/time.h

...

 #include <time.h>
 
+#define timerclear(tv) ((tv)->tv_sec = (tv)->tv_usec = 0)
+
+#define timersub(a, b, res)                          \
+    do {                                             \
+        (res)->tv_sec = (a)->tv_sec - (b)->tv_sec;   \
+        (res)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
+        if ((res)->tv_usec < 0) {                    \
+            (res)->tv_sec--;                         \
+            (res)->tv_usec += 1000000;               \
+        }                                            \
+    } while (0)
+
+#define timercmp(a, b, CMP) \
+    (((a)->tv_sec == (b)->tv_sec) ? ((a)->tv_usec CMP (b)->tv_usec) : ((a)->tv_sec CMP (b)->tv_sec))
+
 #endif

platform.hplatform.cにタイマーの初期化・起動を組み込みます。

📝 kernel/net/platform/xv6-riscv/platform.h

...

 #include "intr.h"
+#include "timer.h"
 
 #endif

📝 kernel/net/platform/xv6-riscv/platform.c

...

     intr_register(INTR_IRQ_SOFT, net_softirq_handler, 0, NULL);
+    if (timer_init() == -1) {
+        return -1;
+    }
     return 0;
 }

...

     if (intr_run() == -1) {
         return -1;
     }
+    if (timer_run() == -1) {
+        return -1;
+    }
     return 0;
 }

...

IPモジュールからのアドレス解決の有効化

4.1で暫定的にコメントアウトしていたip.carp_resolve()呼び出しを有効に戻します。

📝 kernel/net/ip.c

...
 #include "util.h"
 #include "net.h"
-//#include "arp.h"
+#include "arp.h"
 #include "ip.h"

...

 static int
 ip_output_device(struct ip_iface *iface, const uint8_t *data, size_t len, ip_addr_t target)
 {
     char addr[IP_ADDR_STR_LEN];
     uint8_t hwaddr[NET_DEVICE_ADDR_LEN] = {};
-//    int ret;
+    int ret;

...
         } else {
-//            ret = arp_resolve(NET_IFACE(iface), target, hwaddr);
-//            if (ret != ARP_RESOLVE_FOUND) {
-//                return ret;
-//            }
-            return -1;
+            ret = arp_resolve(NET_IFACE(iface), target, hwaddr);
+            if (ret != ARP_RESOLVE_FOUND) {
+                return ret;
+            }
         }
...
 }
...

初期化関数の呼び出し

net.cでARPモジュールのヘッダをインクルードし、net_init()からarp_init()を呼び出すようにします。

📝 kernel/net/net.c

...
-//#include "arp.h"
+#include "arp.h"
...

 int
 net_init(void)
 {
...
-//    if (arp_init() == -1) {
-//        errorf("arp_init() failure");
-//        return -1;
-//    }
+    if (arp_init() == -1) {
+        errorf("arp_init() failure");
+        return -1;
+    }
...
 }
...

Makefileの修正

arp.otimer.oをビルド対象に追加します。

📝 Makefile

...
   $N/ether.o \
   $N/ip.o \
+  $N/arp.o \
   $P/platform.o \
   $P/intr.o \
+  $P/timer.o \
   $P/driver/virtio_net.o \
...

動作確認

再ビルドした後、make qemuを実行してxv6を起動します。今度はユニキャストアドレス192.0.2.2に対してpingを実行します。

$ ping -c 3 192.0.2.2

xv6側のログに、ホストからのARP要求を受信して応答を返し(アドレス解決)、続いてICMP Echo要求を受信している様子が出力されます。

10:05:00.101 [D] net_input: dev=net0, type=0x0806, len=28 (kernel/net/net.c:232)
10:05:00.102 [D] arp_input: dev=net0, len=28 (kernel/net/arp.c:250)
10:05:00.103 [D] arp_cache_insert: INSERT: pa=192.0.2.1, ha=xx:xx:xx:xx:xx:xx (kernel/net/arp.c:185)
10:05:00.104 [D] arp_reply: dev=net0, len=28 (kernel/net/arp.c:224)
10:05:00.105 [D] net_input: dev=net0, type=0x0800, len=84 (kernel/net/net.c:232)
10:05:00.106 [D] ip_input: permit, dev=net0, iface=192.0.2.2 (kernel/net/ip.c:377)

Note

pingへの応答(ICMP Echo Reply)はまだ返りません。ICMPモジュールを移植していないため、Echo要求はIPモジュールまで届いた後、対応するプロトコルが見つからず破棄されます。次の4.3で応答できるようになります。