BitVisorからUDP/IPパケットを送信

昨年の以下の記事

- http://qiita.com/deep_tkkn/items/ee6563aafb0d33de8450
- http://qiita.com/hdk_2/items/2431710fcf257904fc8a

にあるように、BitVisorにはUDP/IPでログを出力する機能があります。これは、TCP/IPスタックを使用せず、UDPパケットを独自に生成して送信しています。そこで、これを流用すると簡単にUDPパケットを生成できます!

# コードの場所

ログ出力なのでcore/tty.cというファイルにあります。

- tty_syslog_putchar()関数: ログを1行文溜めてsyslog形式で送信
- tty_udp_putchar()関数: ログを1バイトずつ独自形式で送信
- mkudp()関数: 上2つの関数から使用されるUDP/IPパケット生成ルーチン

このmkudp()関数でUDP/IPのヘッダー生成、チェックサム計算などが行われますので、これを流用すると簡単にUDPパケットを生成できます。

使い方はtty_syslog_putchar()関数を見ると良いです。

```c:core/tty.c
                memcpy (pkt + 12, "\x08\x00", 2);
                pktsiz = mkudp (pkt + 14,
                                (char *)config.vmm.tty_syslog.src_ipaddr, 514,
                                (char *)config.vmm.tty_syslog.dst_ipaddr, 514,
                                buf, len) + 14;
```

pktというのが8ビット型の配列で、Ethernetフレームを入れるものです。よって先頭12バイトはMACアドレスに、12バイト目にIPv4のフレームタイプ番号0800, 14バイト目以降にUDP/IPパケットを入れます。FCSはおそらくネットワークデバイス側で生成してくれるはずなので省略されています。

送信するところは以下の部分です。

```c:core/tty.c
        if (pktsiz)
                LIST1_FOREACH (tty_udp_list, p)
                        p->tty_send (p->handle, pkt, pktsiz);
```

MACアドレスを埋めているのは、ここで実際に呼び出されるnet/netapi.cのnet_tty_send()関数です。

```c:net/netapi.c
static void
net_tty_send (void *tty_handle, void *packet, unsigned int packet_size)
{
        struct netdata *handle = tty_handle;
        char *pkt;

        pkt = packet;
        memcpy (pkt + 0, config.vmm.tty_mac_address, 6);
        memcpy (pkt + 6, handle->mac_address, 6);
        handle->tty_phys_func->send (handle->tty_phys_handle, 1, &packet,
                                     &packet_size, false);
}
```

こんな感じで送信先MACアドレス、送信元MACアドレスを入れています。

# データ送信に利用する

上に書いた通りログ出力機能にべったりとした実装になっていますので、データ送信に使うのもログ出力にべったりとさせるのが簡単です。すなわち、ポート番号やIPアドレスくらいは変えられますが、MACアドレスは変えられないという形で実装すると簡単です。

## 実装例

ここでは、dbgshのsendintコマンドから、sendlogに対して何か送ると、UDP/IPでsyslogと同じホストの1234番ポートに65,536バイトのログバッファー全体を1,024バイト単位に分割して送りつけるという簡単な例を示します。(タブがスペースになっていますので試す時にはご注意ください。)

```diff
diff --git a/core/tty.c b/core/tty.c
--- a/core/tty.c
+++ b/core/tty.c
@@ -380,6 +380,27 @@ tty_init_iohook (void)
 #endif
 }
 
+static int
+sendlog_msghandler (int m, int c, struct msgbuf *buf, int bufcnt)
+{
+       if (m != MSG_INT)
+               return 0;
+       struct tty_udp_data *p;
+       static char pkt[1536];
+       unsigned int pktsiz;
+       int i;
+       memcpy (pkt + 12, "\x08\x00", 2);
+       for (i = 0; i < 65536; i += 1024) {
+               pktsiz = mkudp (pkt + 14,
+                               (char *)config.vmm.tty_syslog.src_ipaddr, 1234,
+                               (char *)config.vmm.tty_syslog.dst_ipaddr, 1234,
+                               (char *)logbuf.log + i, 1024) + 14;
+               LIST1_FOREACH (tty_udp_list, p)
+                       p->tty_send (p->handle, pkt, pktsiz);
+       }
+       return 0;
+}
+
 static void
 tty_init_msg (void)
 {
@@ -393,6 +414,7 @@ tty_init_msg (void)
        msgregister ("ttyin", ttyin_msghandler);
        msgregister ("ttyout", ttyout_msghandler);
        msgregister ("ttylog", ttylog_msghandler);
+       msgregister ("sendlog", sendlog_msghandler);
 }
 
 INITFUNC ("global0", tty_init_global);
```

## 実行例

```:送信側
$ ~/bitvisor/tools/dbgsh/dbgsh
> sendint 
sendint> sendlog 
send 0 to sendlog
sendint> 
```

```:受信側
$ nc -l -u 1234 > /tmp/1
^C
$ ls -l !$
ls -l /tmp/1
-rw-rw-r-- 1 hdk hdk 65536 Dec  5 23:01 /tmp/1
$ hd /tmp/1 | tail
00000910  6e 64 6c 65 72 3a 20 70  6f 72 74 3a 20 35 30 30  |ndler: port: 500|
00000920  30 2d 35 30 31 66 0a 76  69 72 74 69 6f 5f 6e 65  |0-501f.virtio_ne|
00000930  74 20 68 6f 6f 6b 20 30  78 36 30 30 30 0a 76 69  |t hook 0x6000.vi|
00000940  72 74 69 6f 5f 6e 65 74  3a 20 72 65 73 65 74 0a  |rtio_net: reset.|
00000950  62 6e 78 3a 20 44 69 73  61 62 6c 65 20 69 6e 74  |bnx: Disable int|
00000960  65 72 72 75 70 74 0a 62  6e 78 3a 20 45 6e 61 62  |errupt.bnx: Enab|
00000970  6c 65 20 69 6e 74 65 72  72 75 70 74 0a 00 00 00  |le interrupt....|
00000980  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00010000
$ 
```

このように65,536バイトのデータをネットワーク経由で転送することができました。