昨年の以下の記事
- 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()関数を見ると良いです。
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はおそらくネットワークデバイス側で生成してくれるはずなので省略されています。
送信するところは以下の部分です。
if (pktsiz)
LIST1_FOREACH (tty_udp_list, p)
p->tty_send (p->handle, pkt, pktsiz);
MACアドレスを埋めているのは、ここで実際に呼び出されるnet/netapi.cのnet_tty_send()関数です。
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 --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バイトのデータをネットワーク経由で転送することができました。