|
|
nono 1.7.0
//
// nono
// Copyright (C) 2020 nono project
// Licensed under nono-license.txt
//
//
// ネットワークのホストデバイス
//
// 送信時のフロー
//
// VM thread : Host thread
//
// 各デバイス::送信() :
// |
// HostNetDevice::Tx() : HostDevice::ThreadRun
// |
// +-------------->| queue |----+ … 送信キューに追加し
// +-------------->| pipe |----+ … パイプでホストスレッドに通知
// | |
// <-+ : v
// --- kevent
// : |
// HostNetDevice::Write()
// : |
// NetDriver*::Write()
// : |
// +-----> Send to the real world
// 受信のフロー
//
// VM thread : Host thread
//
// : HostDevice::ThreadRun
//
// : +-----< Recv from the real world
// |
// : v
// --- kevent
// : |
// HostNetDevice::Read()
// |
// | queue |<---+ … 受信キューに追加
// ‖
// ‖ HostNetDevice::*(rx_func)()
// |
// +-------------| Message |<---+ … メッセージで VM スレッドに通知
// v
// 各デバイス::RxMessage() ‖ … メッセージコールバック
// | ‖
// 各デバイス::Rx() ‖ … イベントコールバック
// | ‖
// HostCOMDevice::Rx() <==++ … ここで queue から読み出す
// ログ名
// Ethernet HostDevice NetDriver
// | | |
// v v v
// Lance -> HostNetDevice -> NetDriver***
// 表示名 (Lance) (HostNet0) (HostNet0.***)
// 識別名 "lance" "hostnet0" "hostnet0"
//
// HostNet は、特に NetDriver 派生クラス選択時はフォールバックも含めて
// どれが選択されるか複雑なこともあるので、ログ表示名に自分のドライバ名
// も表示したい。
#include "hostnet.h"
#include "config.h"
#include "monitor.h"
#include "netdriver_none.h"
#if defined(HAVE_HOSTNET_AFPACKET)
#include "netdriver_afpacket.h"
#endif
#if defined(HAVE_HOSTNET_BPF)
#include "netdriver_bpf.h"
#endif
#if defined(HAVE_HOSTNET_SLIRP)
#include "netdriver_slirp.h"
#include <slirp/libslirp-version.h>
#endif
#if defined(HAVE_HOSTNET_TAP)
#include "netdriver_tap.h"
#endif
#include "uimessage.h"
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>
// コンパイル済みのドライバ名一覧を返す (MainApp から呼ばれる)
/*static*/ std::vector<std::string>
HostNetDevice::GetDrivers()
{
std::vector<std::string> list;
#if defined(HAVE_HOSTNET_AFPACKET)
list.emplace_back("afpacket");
#endif
#if defined(HAVE_HOSTNET_BPF)
list.emplace_back("bpf");
#endif
#if defined(HAVE_HOSTNET_SLIRP)
list.emplace_back("usermode");
#endif
#if defined(HAVE_HOSTNET_TAP)
list.emplace_back("tap");
#endif
return list;
}
// libslirp のバージョン文字列を返す。
// libslirp が有効でなければ NULL を返す。
/*static*/ const char *
HostNetDevice::GetSlirpVersion()
{
#if defined(SLIRP_VERSION_STRING)
return SLIRP_VERSION_STRING;
#else
return NULL;
#endif
}
// コンストラクタ
HostNetDevice::HostNetDevice(Device *parent_, uint n,
const std::string& portname_)
: inherited(parent_, OBJ_HOSTNET(n), portname_)
{
ihwaddrfilter = dynamic_cast<IHWAddrFilter *>(parent);
assert(ihwaddrfilter);
monitor = gMonitorManager->Regist(ID_MONITOR_HOSTNET(n), this);
monitor->SetCallback(&HostNetDevice::MonitorScreen);
monitor->SetSize(48, 24);
#if defined(HAVE_HOSTNET_SLIRP)
// SLIRP モニタは usermode を使う時のみ必要だが、現状モニタは
// 起動時に1回登録しなければならない (以後増減できない) という制約が
// あるのでここで登録だけしておく。うーんこの…。
// usermode は現状高々1つなので、登録されてない時だけ登録する。
Monitor *mon = gMonitorManager->Find(ID_MONITOR_SLIRP);
if (mon == NULL) {
gMonitorManager->Regist(ID_MONITOR_SLIRP, NULL);
}
#endif
}
// デストラクタ
HostNetDevice::~HostNetDevice()
{
}
// ログレベル設定
void
HostNetDevice::SetLogLevel(int loglevel_)
{
inherited::SetLogLevel(loglevel_);
RWLockGuard_Read guard(driverlock);
if ((bool)driver) {
driver->SetLogLevel(loglevel_);
}
}
// 動的コンストラクションその2
bool
HostNetDevice::Create2()
{
if (inherited::Create2() == false) {
return false;
}
if (SelectDriver(true) == false) {
return false;
}
return true;
}
// ドライバ(再)選択。
// エラーが起きた場合、
// 起動時なら、warn() 等で表示して false を返す(終了する)。
// 実行中なら、warn() 等で表示してもいいが、必ず None にフォールバックして
// true を返すこと。
bool
HostNetDevice::SelectDriver(bool startup)
{
RWLockGuard_Write guard(driverlock);
driver.reset();
errmsg.clear();
std::string key = GetConfigKey();
const ConfigItem& item = gConfig->Find(key + "-driver");
std::string type = item.AsString();
// auto は usermode か none と同義にする。
if (type == "auto") {
#if defined(HAVE_HOSTNET_SLIRP)
type = "usermode";
#else
type = "none";
#endif
}
enum {
ERR_CONFIG, // 設定でのエラー
ERR_INVALID, // 知らないドライバ
ERR_NOTSUPP, // コンパイルされてない
} reason = ERR_CONFIG;
if (type == "none") {
CreateNone();
} else if (type == "usermode") {
#if defined(HAVE_HOSTNET_SLIRP)
CreateSlirp(startup);
#else
reason = ERR_NOTSUPP;
#endif
} else if (type == "tap") {
#if defined(HAVE_HOSTNET_TAP)
CreateTap();
#else
reason = ERR_NOTSUPP;
#endif
} else if (type == "bpf") {
#if defined(HAVE_HOSTNET_BPF)
CreateBPF();
#else
reason = ERR_NOTSUPP;
#endif
} else if (type == "afpacket") {
#if defined(HAVE_HOSTNET_AFPACKET)
CreateAFPacket();
#else
reason = ERR_NOTSUPP;
#endif
} else {
// 知らないドライバ種別
reason = ERR_INVALID;
}
if ((bool)driver == false) {
// 指定のドライバが使えなかった場合、
// o 起動時ならエラー終了する。
// o 実行中なら none にフォールバックする。
if (startup) {
switch (reason) {
case ERR_INVALID:
item.Err("Invalid driver name");
break;
case ERR_CONFIG:
item.Err("Could not configure the driver");
warnx("(See details with option -C -L%s=1)", key.c_str());
break;
case ERR_NOTSUPP:
item.Err("Hostnet driver %s not compiled", type.c_str());
break;
default:
assert(false);
}
return false;
} else {
// UI に通知してフォールバック。
int n = GetId() - OBJ_HOSTNET0;
gMainApp.GetUIMessage()->Post(UIMessage::HOSTNET_FAILED, n);
CreateNone();
}
}
assert((bool)driver);
// ドライバ名は Capitalize だがログはほぼ小文字なので雰囲気を揃える…
putmsg(1, "selected host driver: %s",
string_tolower(driver->GetDriverName()).c_str());
return true;
}
// None ドライバを生成する。
// 戻り値はなく、成否は (bool)driver で判断する。
void
HostNetDevice::CreateNone()
{
try {
driver.reset(new NetDriverNone(this));
} catch (...) { }
if ((bool)driver) {
if (driver->InitDriver()) {
// 成功
return;
}
errmsg = driver->errmsg;
driver.reset();
}
}
// Slirp ドライバを生成する。
// 戻り値はなく、成否は (bool)driver で判断する。
void
HostNetDevice::CreateSlirp(bool startup)
{
#if defined(HAVE_HOSTNET_SLIRP)
try {
driver.reset(new NetDriverSlirp(this));
} catch (...) { }
if ((bool)driver) {
if (driver->InitDriver(startup)) {
// 成功
return;
}
errmsg = driver->errmsg;
driver.reset();
}
#endif
}
// Tap ドライバを生成する。
// 戻り値はなく、成否は (bool)driver で判断する。
void
HostNetDevice::CreateTap()
{
#if defined(HAVE_HOSTNET_TAP)
int n = GetId() - OBJ_HOSTNET0;
const std::string keyname = string_format("hostnet%d-tap-devpath", n);
const ConfigItem& item = gConfig->Find(keyname);
const std::string& devpath = item.AsString();
try {
driver.reset(new NetDriverTap(this, devpath));
} catch (...) { }
if ((bool)driver) {
if (driver->InitDriver()) {
// 成功
return;
}
errmsg = driver->errmsg;
driver.reset();
}
#endif
}
// bpf ドライバを生成する。
// 戻り値はなく、成否は (bool)driver で判断する。
void
HostNetDevice::CreateBPF()
{
#if defined(HAVE_HOSTNET_BPF)
int n = GetId() - OBJ_HOSTNET0;
const std::string keyname = string_format("hostnet%d-bpf-ifname", n);
const ConfigItem& item = gConfig->Find(keyname);
const std::string& ifname = item.AsString();
try {
driver.reset(new NetDriverBPF(this, ifname));
} catch (...) { }
if ((bool)driver) {
if (driver->InitDriver()) {
// 成功
return;
}
errmsg = driver->errmsg;
driver.reset();
}
#endif
}
// AFPacket ドライバを生成する。
// 戻り値はなく、成否は (bool)driver で判断する。
void
HostNetDevice::CreateAFPacket()
{
#if defined(HAVE_HOSTNET_AFPACKET)
int n = GetId() - OBJ_HOSTNET0;
const std::string keyname = string_format("hostnet%d-afpacket-ifname", n);
const ConfigItem& item = gConfig->Find(keyname);
const std::string& ifname = item.AsString();
try {
driver.reset(new NetDriverAFPacket(this, ifname));
} catch (...) { }
if ((bool)driver) {
if (driver->InitDriver()) {
// 成功
return;
}
errmsg = driver->errmsg;
driver.reset();
}
#endif
}
// VM からの送信 (VM スレッドで呼ばれる)
bool
HostNetDevice::Tx(const NetPacket& packet)
{
// 長さ 0 のパケットは破棄する。バグでもないと通常は起きない。
// runt frame はどうするか?
if (packet.Length() < 1) {
stat.txzero_pkts++;
putlog(1, "Tx: drop empty packet");
return true;
}
// 送信キューに入れて..
if (txq.Enqueue(packet) == false) {
stat.txqfull_pkts++;
stat.txqfull_bytes += packet.Length();
putlog(2, "txq exhausted");
return false;
}
stat.tx_pkts++;
stat.tx_bytes += packet.Length();
// ざっくりピーク値
stat.txq_peak = std::max((uint)txq.Length(), stat.txq_peak);
// パイプで通知。
return WritePipe(PIPE_TX);
}
// rxq への投入を行う場合は true。
// VM スレッドから呼ばれる。
void
HostNetDevice::EnableRx(bool enable)
{
rx_enable = enable;
// 受信を停止した時はキューに残っているのを破棄する。
// (ゲスト再起動によるデバイスリセット時とか)
if (rx_enable == false) {
// 統計情報に足すためだけに一旦取り出す。
NetPacket drop;
while (rxq.Dequeue(&drop)) {
stat.rxdisable_pkts++;
stat.rxdisable_bytes += drop.Length();
}
}
}
// パケットをキューから取り出す。
// VM 側から呼ばれる。
bool
HostNetDevice::Rx(NetPacket *dst)
{
if (rxq.Dequeue(dst) == false) {
// キューが空
return false;
}
uint length = dst->Length();
stat.rx_pkts++;
stat.rx_bytes += length;
if (__predict_false(loglevel >= 2)) {
putlogn("Recv %u bytes", length);
if (loglevel >= 4) {
std::string buf;
for (uint i = 0; i < length; i++) {
if (i % 16 == 0) {
buf += string_format("%04x:", i);
}
buf += string_format(" %02x", dst->Peek(i));
if (i % 16 == 7) {
buf += " ";
} else if (i % 16 == 15) {
putlogn("%s", buf.c_str());
buf = "";
}
}
if (buf.empty() == false) {
putlogn("%s", buf.c_str());
}
}
}
return true;
}
// 外部からの読み込み (パケットを受信)。
// 戻り値はキューに投入したパケット数。
int
HostNetDevice::Read()
{
int queued = 0;
int left = 0;
RWLockGuard_Read guard(driverlock);
assert((bool)driver);
do {
NetPacket *p;
p = rxq.BeginWrite();
if (p == NULL) {
NetPacket discard;
left = driver->Read(&discard);
if (left < 0) {
break;
}
stat.rxqfull_pkts++;
stat.rxqfull_bytes += discard.Length();
continue;
}
// driver->Read() は VM に投入すべきパケットがなければ負数を返す。
// パケットがあれば p に書き戻して、driver 側のバッファに残っている
// パケット数 (といっても 0 か 1以上) を返してくる。
left = driver->Read(p);
if (left < 0) {
rxq.CancelWrite();
break;
}
NetPacket& packet = *p;
stat.read_pkts++;
stat.read_bytes += packet.Length();
if (rx_enable == false) {
rxq.CancelWrite();
stat.rxdisable_pkts++;
stat.rxdisable_bytes += packet.Length();
continue;
}
// イーサネットフレームより長いパケットはゲスト OS が期待していない。
// ここでドロップしてみる。
// これによりゲストでの dropping chained buffer が出なくなる。
if (packet.Length() > 1518) {
rxq.CancelWrite();
stat.rxjumbo_pkts++;
stat.rxjumbo_bytes += packet.Length();
continue;
}
// ホストカーネルから直接着信したパケット(というかフレームか)は
// 60バイトパディングされないまま読み出せるので、ここでパディングする。
while (packet.Length() < 60) {
packet.Resize(60);
}
// デバイスがこのアドレス宛のフレームを受け取るか。
// 受け取らないと分かっているフレームは VM へのキューに入れる前に
// 落としておきたい。
MacAddr dstaddr(packet.Data());
auto res = ihwaddrfilter->HWAddrFilter(dstaddr);
if (res != 0) {
switch (res) {
case IHWAddrFilter::HPF_DROP_UNICAST:
stat.rxnotme_pkts++;
stat.rxnotme_bytes += packet.Length();
break;
case IHWAddrFilter::HPF_DROP_MULTICAST:
stat.rxmcast_pkts++;
stat.rxmcast_bytes += packet.Length();
break;
default:
assertmsg(false, "corrupted res=%u", res);
}
rxq.CancelWrite();
continue;
}
// CRC XXX 計算すること
packet.Resize(packet.Length() + 4);
rxq.EndWrite();
queued++;
} while (left > 0);
// ざっくりピーク値
stat.rxq_peak = std::max((uint)rxq.Length(), stat.rxq_peak);
return queued;
}
// 外部への書き出し(パケットを送信)
void
HostNetDevice::Write()
{
const NetPacket *p;
RWLockGuard_Read guard(driverlock);
assert((bool)driver);
// 送信キューから取り出す
while ((p = txq.BeginRead()) != NULL) {
const NetPacket& packet = *p;
uint length = packet.Length();
if (__predict_false(loglevel >= 2)) {
putlogn("Send %u bytes", length);
if (loglevel >= 4) {
std::string buf;
for (uint i = 0; i < length; i++) {
if (i % 16 == 0) {
buf += string_format("%04x:", i);
}
buf += string_format(" %02x", packet.Peek(i));
if (i % 16 == 7) {
buf += " ";
} else if (i % 16 == 15) {
putlogn("%s", buf.c_str());
buf = "";
}
}
if (buf.empty() == false) {
putlogn("%s", buf.c_str());
}
}
}
stat.write_pkts++;
stat.write_bytes += length;
driver->Write(packet.Data(), length);
txq.EndRead();
}
}
// ドライバ名を返す
const std::string&
HostNetDevice::GetDriverName()
{
RWLockGuard_Read guard(driverlock);
assert((bool)driver);
return driver->GetDriverName();
}
// 統計情報に1パケット分加算する。
// これだけ SLIRP 裏スレッドから呼ばれるため。
// 現状書き込むのが SLIRP スレッドだけなのでロックとかはしていない。
void
HostNetDevice::CountTXUnsupp(size_t bytes)
{
stat.txunsupp_pkts++;
stat.txunsupp_bytes += bytes;
}
// モニタ
void
HostNetDevice::MonitorScreen(Monitor *, TextScreen& screen)
{
screen.Clear();
screen.Print(0, 0, "Device(Port) : %s", GetPortName().c_str());
{
RWLockGuard_Read guard(driverlock);
screen.Print(0, 1, "HostNet Driver: %s",
driver->GetDriverName().c_str());
// 次の2行はドライバ依存情報
driver->MonitorScreenMD(screen, 2);
}
// 01234567890123456
// Write to host
// Drop:TxQ Full
// Drop:Rx Disabled
// Drop:Jumbo Frame
// Drop:Addr Filter
int y = 5;
screen.Print(0, y++, "%-17s%13s %17s", "<Tx>", "Packets", "Bytes");
screen.Print(0, y++, "%-17s%13s %17s", "VM sends",
format_number(stat.tx_pkts).c_str(),
format_number(stat.tx_bytes).c_str());
screen.Print(0, y++, "%-17s%13s %17s", "Write to host",
format_number(stat.write_pkts).c_str(),
format_number(stat.write_bytes).c_str());
screen.Print(0, y++, "%-17s%13s %17s", "Drop:Empty Frame",
format_number(stat.txzero_pkts).c_str(),
"0");
screen.Print(0, y++, "%-17s%13s %17s", "Drop:Unsupported",
format_number(stat.txunsupp_pkts).c_str(),
format_number(stat.txunsupp_bytes).c_str());
screen.Print(0, y++, "%-17s%13s %17s", "Drop:TxQ Full",
format_number(stat.txqfull_pkts).c_str(),
format_number(stat.txqfull_bytes).c_str());
y++;
screen.Print(0, y++, "%-17s%13s %17s", "<Rx>", "Packets", "Bytes");
screen.Print(0, y++, "%-17s%13s %17s", "Read from host",
format_number(stat.read_pkts).c_str(),
format_number(stat.read_bytes).c_str());
screen.Print(0, y++, "%-17s%13s %17s", "VM receives",
format_number(stat.rx_pkts).c_str(),
format_number(stat.rx_bytes).c_str());
screen.Print(0, y++, "%-17s%13s %17s", "Drop:Rx Disabled",
format_number(stat.rxdisable_pkts).c_str(),
format_number(stat.rxdisable_bytes).c_str());
screen.Print(0, y++, "%-17s%13s %17s", "Drop:Jumbo Frame",
format_number(stat.rxjumbo_pkts).c_str(),
format_number(stat.rxjumbo_bytes).c_str());
screen.Print(0, y++, "%-17s%13s %17s", "Drop:RxQ Full",
format_number(stat.rxqfull_pkts).c_str(),
format_number(stat.rxqfull_bytes).c_str());
screen.Print(0, y++, "%-17s%13s %17s", "Drop:Unicast",
format_number(stat.rxnotme_pkts).c_str(),
format_number(stat.rxnotme_bytes).c_str());
screen.Print(0, y++, "%-17s%13s %17s", "Drop:Multicast",
format_number(stat.rxmcast_pkts).c_str(),
format_number(stat.rxmcast_bytes).c_str());
y++;
screen.Print(5, y++, "Capacity Peak");
screen.Print(0, y++, "TxQ %4u/%4u %4u",
(uint)txq.Length(), (uint)txq.Capacity(), stat.txq_peak);
screen.Print(0, y++, "RxQ %4u/%4u %4u",
(uint)rxq.Length(), (uint)rxq.Capacity(), stat.rxq_peak);
}
// 16進ダンプに整形して、文字列の配列(改行を含まない)を返す。
/*static*/ std::vector<std::string>
HostNetDevice::DumpHex(const void *src, size_t srclen)
{
std::vector<std::string> lines;
std::string buf;
for (size_t i = 0; i < srclen; i++) {
buf += string_format(" %02x", ((const uint8 *)src)[i]);
if (i % 16 == 7) {
buf += ' ';
}
if (i % 16 == 15) {
lines.emplace_back(buf);
buf.clear();
}
}
if (buf.empty() == false) {
lines.emplace_back(buf);
}
return lines;
}
// イーサネットフレームを整形して、文字列の配列(改行は含まない)を返す。
/*static*/ std::vector<std::string>
HostNetDevice::DumpFrame(const void *src, size_t srclen)
{
std::vector<std::string> lines;
struct eth_header {
uint8 dst[6];
uint8 src[6];
uint16 type;
} __packed;
const eth_header& eh = *(const eth_header *)src;
uint16 type = ntohs(eh.type);
std::string ethtype;
switch (type) {
case 0x0800: ethtype = "IPv4"; break;
case 0x0806: ethtype = "ARP"; break;
case 0x86dd: ethtype = "IPv6"; break;
default:
ethtype = string_format("%04x", type);
break;
}
lines.emplace_back(string_format("Eth: %02x:%02x:%02x:%02x:%02x:%02x -> "
"%02x:%02x:%02x:%02x:%02x:%02x (%s)",
eh.src[0], eh.src[1], eh.src[2], eh.src[3], eh.src[4], eh.src[5],
eh.dst[0], eh.dst[1], eh.dst[2], eh.dst[3], eh.dst[4], eh.dst[5],
ethtype.c_str()));
// ペイロードは次の行につなげる。
const uint8 *payload = (const uint8 *)src + sizeof(eth_header);
size_t payloadlen = srclen - sizeof(eth_header);
std::vector<std::string> lines2;
switch (type) {
case 0x0800:
lines2 = DumpIPv4(payload, payloadlen);
break;
case 0x0806:
lines2 = DumpARP(payload, payloadlen);
break;
case 0x86dd:
lines2 = DumpIPv6(payload, payloadlen);
break;
default:
break;
}
if (lines2.empty() == false) {
for (auto& buf : lines2) {
lines.emplace_back(buf);
}
}
return lines;
}
// ARP パケットを整形して、文字列の配列(改行は含まない)を返す。
/*static*/ std::vector<std::string>
HostNetDevice::DumpARP(const void *src, size_t srclen)
{
std::vector<std::string> lines;
struct arp_header {
uint16 hwtype;
uint16 prototype;
uint8 hwsize;
uint8 protosize;
enum : uint16 {
ARP_REQUEST = 1,
ARP_REPLY = 2,
} opcode;
uint8 dstmac[6];
uint8 dstip[4];
uint8 srcmac[6];
uint8 srcip[4];
} __packed;
const arp_header& a = *(const arp_header *)src;
uint16 opcode = ntohs(a.opcode);
std::string opstr;
switch (opcode) {
case arp_header::ARP_REQUEST:
lines.emplace_back(string_format(
" ARP: Who has %u.%u.%u.%u? Tell %u.%u.%u.%u",
a.dstip[0], a.dstip[1], a.dstip[2], a.dstip[3],
a.srcip[0], a.srcip[1], a.srcip[2], a.srcip[3]));
break;
case arp_header::ARP_REPLY:
lines.emplace_back(string_format(
" ARP: Reply %u.%u.%u.%u is at %02x:%02x:%02x:%02x:%02x:%02x",
a.dstip[0], a.dstip[1], a.dstip[2], a.dstip[3],
a.dstmac[0], a.dstmac[1], a.dstmac[2],
a.dstmac[3], a.dstmac[4], a.dstmac[5]));
break;
default:
lines.emplace_back(string_format(" ARP: unknown op 0x%04x", opcode));
break;
}
return lines;
}
// IPv4 パケットを整形して、文字列の配列(改行は含まない)を返す。
/*static*/ std::vector<std::string>
HostNetDevice::DumpIPv4(const void *src, size_t srclen)
{
std::vector<std::string> lines;
struct ipv4_header {
uint8 ver_len;
uint8 tos;
uint16 total_len;
uint16 id;
uint16 fragment;
uint8 ttl;
uint8 proto;
uint16 cksum;
uint8 src[4];
uint8 dst[4];
} __packed;
const ipv4_header& ip = *(const ipv4_header *)src;
const uint8 *payload = (const uint8 *)src + sizeof(ipv4_header);
size_t payloadlen = srclen - sizeof(ipv4_header);
char srcname[INET_ADDRSTRLEN];
char dstname[INET_ADDRSTRLEN];
srcname[0] = '\0';
dstname[0] = '\0';
inet_ntop(AF_INET, &ip.src[0], srcname, (socklen_t)sizeof(srcname));
inet_ntop(AF_INET, &ip.dst[0], dstname, (socklen_t)sizeof(dstname));
switch (ip.proto) {
case 1:
lines = DumpICMPv4(srcname, dstname, payload, payloadlen);
break;
case 6:
lines = DumpTCPv4(srcname, dstname, payload, payloadlen);
break;
case 17:
lines = DumpUDPv4(srcname, dstname, payload, payloadlen);
break;
default:
{
std::string protoname;
const struct protoent *pent = getprotobynumber(ip.proto);
if (pent) {
protoname = string_format("(%s)", pent->p_name);
}
lines.emplace_back(string_format(" IPv4 %s -> %s 0x%02x%s",
srcname, dstname, ip.proto, protoname.c_str()));
break;
}
}
return lines;
}
// ICMP(v4) パケットを整形して、文字列の配列(改行は含まない)を返す。
/*static*/ std::vector<std::string>
HostNetDevice::DumpICMPv4(const char *srcname, const char *dstname,
const void *src, size_t srclen)
{
std::vector<std::string> lines;
struct icmp_header {
uint8 type;
uint8 code;
uint16 cksum;
} __packed;
const icmp_header& icmp = *(const icmp_header *)src;
size_t payloadlen = srclen - sizeof(icmp_header);
std::string msg = string_format(" ICMP %s -> %s ", srcname, dstname);
uint type = icmp.type;
uint code = icmp.code;
switch (type) {
case 0: // ICMP_ECHOREPLY
msg += string_format("Echo Reply %zu bytes", payloadlen);
break;
case 3: // ICMP_UNREACH
{
static const char * const reasons[] = {
"Destination Network Unreachable", // 0
"Destination Host Unreachable", // 1
"Destination Protocol Unreachable", // 2
"Destination Port Unreachable", // 3
"Fragment needed and DF set", // 4
"Source Route Failed", // 5
"Destination Network Unknown", // 6
"Destination Host Unknown", // 7
"Source Host Isolated", // 8
"Communication with Destination Network is prohibited", // 9
"Communication with Destination Host is prohibited", // 10
"Bad ToS for Destination Network", // 11
"Bad ToS for Destination Host", // 12
"Communication is Administratively prohibited", // 13
"Host Precedence Violation", // 14
"Precedence cutoff", // 15
};
if (code < countof(reasons)) {
msg += reasons[code];
} else {
msg += string_format("Network Unreachable: code=%02x", code);
}
break;
}
case 5: // ICMP_REDIRECT
msg += string_format("Redirect code=%x", code);
break;
case 8: // ICMP_ECHO
msg += string_format("Echo Request %zu bytes", payloadlen);
break;
default:
msg += string_format("Type=0x%02x Code=0x%02x", type, code);
break;
}
lines.emplace_back(msg);
return lines;
}
// TCPv4 パケットを整形して、文字列の配列(改行は含まない)を返す。
/*static*/ std::vector<std::string>
HostNetDevice::DumpTCPv4(const char *srcname, const char *dstname,
const void *src, size_t srclen)
{
std::vector<std::string> lines;
struct tcp_header {
uint16 sport;
uint16 dport;
uint32 seq;
uint32 ack;
uint8 off;
uint8 flags;
uint16 window;
uint16 cksum;
uint16 urp;
} __packed;
const tcp_header& tcp = *(const tcp_header *)src;
size_t payloadlen = srclen - sizeof(tcp_header);
std::string flagstr;
if (tcp.flags != 0) {
flagstr = ' ';
if ((tcp.flags & 0x01)) flagstr += ",FIN";
if ((tcp.flags & 0x02)) flagstr += ",SYN";
if ((tcp.flags & 0x04)) flagstr += ",RST";
if ((tcp.flags & 0x08)) flagstr += ",PUSH";
if ((tcp.flags & 0x10)) flagstr += ",ACK";
if ((tcp.flags & 0x20)) flagstr += ",URG";
if ((tcp.flags & 0x40)) flagstr += ",ECE";
if ((tcp.flags & 0x80)) flagstr += ",CWR";
flagstr[1] = '<';
flagstr += '>';
}
std::string sserv = GetServByPort(tcp.sport, "tcp");
std::string dserv = GetServByPort(tcp.dport, "tcp");
lines.emplace_back(string_format(" TCP %s:%u%s -> %s:%u%s%s %zu bytes",
srcname, ntohs(tcp.sport), sserv.c_str(),
dstname, ntohs(tcp.dport), dserv.c_str(),
flagstr.c_str(), payloadlen));
return lines;
}
// UDPv4 パケットを整形して、文字列の配列(改行は含まない)を返す。
/*static*/ std::vector<std::string>
HostNetDevice::DumpUDPv4(const char *srcname, const char *dstname,
const void *src, size_t srclen)
{
std::vector<std::string> lines;
struct udp_header {
uint16 sport;
uint16 dport;
uint16 len;
uint16 cksum;
} __packed;
const udp_header& udp = *(const udp_header *)src;
const uint8 *payload = (const uint8 *)src + sizeof(udp_header);
size_t payloadlen = srclen - sizeof(udp_header);
std::string sserv = GetServByPort(udp.sport, "udp");
std::string dserv = GetServByPort(udp.dport, "udp");
int sport = ntohs(udp.sport);
int dport = ntohs(udp.dport);
lines.emplace_back(string_format(" UDP %s:%u%s -> %s:%u%s %zu bytes",
srcname, sport, sserv.c_str(),
dstname, dport, dserv.c_str(),
payloadlen));
if (dport == 67 || dport == 68) {
auto lines2 = DumpDHCP(payload, payloadlen);
for (auto& buf : lines2) {
lines.emplace_back(buf);
}
}
return lines;
}
// BOOTP/DHCP パケットを整形して、文字列の配列(改行は含まない)を返す。
/*static*/ std::vector<std::string>
HostNetDevice::DumpDHCP(const void *src, size_t srclen)
{
std::vector<std::string> lines;
struct bootp_msg {
uint8 op; // Message Operation Code
uint8 htype; // Hardware Type
uint8 hlen; // Hardware Address Length
uint8 hops;
uint32 xid; // Transaction ID
uint16 secs; // Seconds Elapsed (払い出し期間)
uint16 flags;
uint32 ciaddr; // Client IP Address (クライアントが使用)
uint32 yiaddr; // Your IP Address (払い出し IP)
uint32 siaddr; // Server IP Address (BOOTP)
uint32 giaddr; // Gateway IP Address
uint8 chaddr[16]; // Client Hardware Address
uint8 sname[64]; // Server Name (BOOTP)
uint8 file[128]; // Boot Filename (BOOTP)
// ここから options
} __packed;
const bootp_msg& bootp = *(const bootp_msg *)src;
std::string msg = " DHCP "; // IP,UDP の次のインデント
if (bootp.op == 1) {
msg += "Request";
} else if (bootp.op == 2) {
msg += "Reply";
} else {
msg += string_format("OP=0x%02x?", bootp.op);
}
if (bootp.htype != 1) {
msg += string_format(" htype=0x%02x?", bootp.htype);
}
if (bootp.hlen != 6) {
msg += string_format(" hlen=0x%02x?", bootp.hlen);
}
if (bootp.ciaddr != 0) {
char ci[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &bootp.ciaddr, ci, (socklen_t)sizeof(ci));
msg += " Client=";
msg += ci;
}
if (bootp.yiaddr != 0) {
char yi[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &bootp.yiaddr, yi, (socklen_t)sizeof(yi));
msg += " YourIP=";
msg += yi;
}
lines.emplace_back(msg);
const uint8 *options = (const uint8 *)src + sizeof(bootp_msg);
size_t optionslen = srclen - sizeof(bootp_msg);
// オプションの先頭4バイトはマジック。
if (optionslen >= 4 && memcmp(options, "\x63\x82\x53\x63", 4) == 0) {
// 以降は Code-Length-Value 形式。
const uint8 *s = options + 4;
optionslen -= 4;
while (s - options < optionslen) {
uint8 code = *s++;
if (code == 255) { // End
break;
}
if (code == 0) { // Pad
continue;
}
static const char * const optnames[] = {
"", // 0
"Subnet Mask", // 1
"Time Offset", // 2
"Router", // 3
"Time Server", // 4
"IEN116 Name Server", // 5
"DNS Server", // 6
"Log Server", // 7
"Quotes Server", // 8
"LPR Server", // 9
"Impress Server", // 10
"RLP Server", // 11
"Hostname", // 12
"Boot File Size", // 13
"Merit Dump File", // 14
"Domain Name", // 15
"Swap Server", // 16
"Root Path", // 17
"Extension File", // 18
"Forward Enable", // 19
"Source Route Enable", // 20
"Policy Filter", // 21
"Max DG Assembly", // 22
"Default IP TTL", // 23
"MTU Timeout", // 24
"MTU Plateau", // 25
"MTU Interface", // 26
"MTU Subnet", // 27
"Broadcast Address", // 28
"Mask Discovery", // 29
"Mask Supplier", // 30
"Router Discovery", // 31
"Router Request", // 32
"Static Route", // 33
"Trailers", // 34
"ARP Timeout", // 35
"Ethernet", // 36
"Default TCP TTL", // 37
"Keepalive Time", // 38
"Keepalive Data", // 39
"NIS Domain", // 40
"NIS Servers", // 41
"NTP Servers", // 42
"Vendor Specific", // 43
"NETBIOS Name Server", // 44
"NETBIOS Dist Server", // 45
"NETBIOS Node Type", // 46
"NETBIOS Scope", // 47
"X Window Font", // 48
"X Window Manager", // 49
"Requested Address", // 50
"Lease Time", // 51
"Overload", // 52
"DHCP Message Type", // 53
"DHCP Server Id", // 54
"Parameter List", // 55
"DHCP Message", // 56
"DHCP Max Msg Size", // 57
"Renewal Time", // 58
"Rebinding Time", // 59
"Class Id", // 60
"Client Id", // 61
"Netware/IP Domain", // 62
"Netware/IP Option", // 63
"NIS Domain Name", // 64
"NIS Server Addr", // 65
"TFTP Server Name", // 66
"Bootfile Name", // 67
"Home Agent Addrs", // 68
"SMTP Server", // 69
"POP3 Server", // 70
"NNTP Server", // 71
"WWW Server", // 72
"Finger Server", // 73
"IRC Server", // 74
"StreetTalk Server", // 75
"STDA Server", // 76
"User Class", // 77
"Directory Agent", // 78
"Service Scope", // 79
"Naming Authority", // 80
"Client FQDN", // 81
"Agent Circuit ID", // 82
"Agent Remote ID", // 83
"Agent Subnet Mask", // 84
"NDS Servers", // 85
"NDS Tree Name", // 86
"NDS Context", // 87
"TimeZone", // 88
"FQDN", // 89
"Authentication", // 90
"Vines TCP/IP", // 91
"Server Selection", // 92
"Client System", // 93
"Client NDI", // 94
"LDAP", // 95
"IPv6 Transitions", // 96
"UUID/GUID", // 97
"User-Auth", // 98
"", // 99
"Printer Name", // 100
"MDHCP", // 101
"", // 102
"", // 103
"", // 104
"", // 105
"", // 106
"", // 107
"Swap Path", // 108
"", // 109
"IPX Compatability", // 110
"", // 111
"Netinfo Address", // 112
"Netinfo Tag", // 113
"URL", // 114
"DHCP Failover", // 115
"DHCP AutoConfig", // 116
"Name Service Search", // 117
"Subnet Selection", // 118
};
std::string opt = " ";
if (code < countof(optnames) && optnames[code][0] != '\0') {
opt += optnames[code];
} else {
opt += '?';
}
opt += string_format("(%d): ", code);
size_t len = *s++;
switch (code) {
case 1: // Subnet Mask
case 50: // Requested Address
case 54: // DHCP Server Id
// 単一アドレス
opt += DumpDHCPAddrList(s, 1);
break;
case 3: // Router
case 6: // DNS Server
// アドレスリスト
opt += DumpDHCPAddrList(s, len / 4);
break;
case 12: // Hostname
case 15: // Domain Name
{
// 文字列 (終端文字なし)
std::vector<char> buf(len + 1);
memcpy(buf.data(), s, len);
opt += buf.data();
break;
}
case 51: // Lease Time
{
uint32 sec =
(s[0] << 24)
| (s[1] << 16)
| (s[2] << 8)
| (s[3]);
opt += string_format("%d sec", sec);
break;
}
case 53: // DHCP Message Type
{
static const char * const msgtypes[] = {
"0",
"DHCP DISCOVER",
"DHCP OFFER",
"DHCP REQUEST",
"DHCP DECLINE",
"DHCP ACK",
"DHCP NAK",
"DHCP RELEASE",
"DHCP INFORM",
};
int type = s[0];
if (type < countof(msgtypes)) {
opt += msgtypes[type];
} else {
opt += string_format("0x%02x", type);
}
break;
}
default:
// 簡易ダンプしとくか。
for (size_t i = 0; i < len; i++) {
opt += string_format("%02x", s[i]);
if (i % 4 == 3) {
opt += ' ';
}
}
break;
}
lines.emplace_back(opt);
s += len;
}
}
return lines;
}
// s から num 個の IPv4 アドレスリストを文字列にして返す。
// DumpDHCP() の下請け。
/*static*/ std::string
HostNetDevice::DumpDHCPAddrList(const uint8 *s, size_t num)
{
std::string buf;
for (size_t i = 0; i < num; i++) {
char addr[INET_ADDRSTRLEN];
inet_ntop(AF_INET, s, addr, (socklen_t)sizeof(addr));
if (i != 0) {
buf += ' ';
}
buf += addr;
}
return buf;
}
// IPv6 パケットを整形して、文字列の配列(改行は含まない)を返す。
/*static*/ std::vector<std::string>
HostNetDevice::DumpIPv6(const void *src, size_t srclen)
{
std::vector<std::string> lines;
struct ipv6_header {
uint8 ver_cls;
uint8 cls_flow;
uint16 flowlabel;
uint16 payload_len;
uint8 nexthdr;
uint8 hoplimit;
uint8 src[16];
uint8 dst[16];
} __packed;
const ipv6_header& ip6 = *(const ipv6_header *)src;
const uint8 *payload = (const uint8 *)src + sizeof(ipv6_header);
size_t payloadlen = srclen - sizeof(ipv6_header);
char srcname[INET6_ADDRSTRLEN];
char dstname[INET6_ADDRSTRLEN];
srcname[0] = '\0';
dstname[0] = '\0';
inet_ntop(AF_INET6, &ip6.src[0], srcname, (socklen_t)sizeof(srcname));
inet_ntop(AF_INET6, &ip6.dst[0], dstname, (socklen_t)sizeof(dstname));
switch (ip6.nexthdr) {
case 58:
lines = DumpICMPv6(srcname, dstname, payload, payloadlen);
break;
default:
{
std::string protoname;
const struct protoent *pent = getprotobynumber(ip6.nexthdr);
if (pent) {
protoname = string_format("(%s)", pent->p_name);
}
lines.emplace_back(string_format(" IPv6 %s -> %s 0x%02x%s",
srcname, dstname, ip6.nexthdr, protoname.c_str()));
break;
}
}
return lines;
}
// ICMPv6 パケットを整形して、文字列の配列(改行は含まない)を返す。
/*static*/ std::vector<std::string>
HostNetDevice::DumpICMPv6(const char *srcname, const char *dstname,
const void *src, size_t srclen)
{
std::vector<std::string> lines;
// フォーマット自体は ICMPv4 と同じ。
struct icmp_header {
uint8 type;
uint8 code;
uint16 cksum;
} __packed;
const icmp_header& icmp6 = *(const icmp_header *)src;
std::string msg = string_format(" ICMPv6 %s -> %s ", srcname, dstname);
uint type = icmp6.type;
uint code = icmp6.code;
switch (type) {
case 1:
static const char * const reasons[] = {
"No route to destination", // 0
"Administratively prohibited", // 1
"Beyond scope of source address", // 2
"Address unreachable", // 3
"Port unreachable", // 4
"Source address failed ingress/egress policy", // 5
"Reject route to destination", // 6
"Error in source routing header", // 7
};
if (code < countof(reasons)) {
msg += reasons[code];
} else {
msg += string_format("Destination Unreachable: code=%02x", code);
}
break;
case 2:
msg += "Packet too big";
break;
case 3:
msg += "Time exceeded";
break;
case 4:
msg += "Parameter Problem";
break;
case 128:
msg += "Echo Request";
break;
case 129:
msg += "Echo Reply";
break;
case 130:
msg += "Multicast Listener Query";
break;
case 131:
msg += "Multicast Listener Report";
break;
case 132:
msg += "Multicast Listener Done";
break;
case 133:
msg += "NDP Router Solicitation";
break;
case 134:
msg += "NDP Router Advertisement";
break;
case 135:
msg += "NDP Neighbor Solicitation";
break;
case 136:
msg += "NDP Neighbor Advertisement";
break;
case 137:
msg += "NDP Redirect";
break;
case 138:
msg += "Router Renumber";
break;
case 139:
msg += "ICMP Node Information Query";
break;
case 140:
msg += "ICMP Node Information Reply";
break;
default:
msg += string_format("Type=0x%02x Code=0x%02x", type, code);
break;
}
lines.emplace_back(msg);
return lines;
}
// ポート番号に対する名前を括弧をつけて返す。表示用。
// 名前が引けなければ空文字列を返す。
/*static*/ std::string
HostNetDevice::GetServByPort(uint16 port_be, const char *protoname)
{
std::string name;
// getservbyport() の引数はネットワークバイトオーダ…。
// かつ、戻り値は内部のスタティックな領域を指している可能性がある。
const struct servent *serv = getservbyport(port_be, protoname);
if (serv) {
name = string_format("(%s)", serv->s_name);
}
return name;
}
//
// NetPacket
//
// このパケットを src から srclen バイトの内容に置き換える。
void
NetPacket::Assign(const void *src, uint srclen)
{
Resize(srclen);
uint copylen = std::min(srclen, (uint)buf.size());
memcpy(buf.data(), src, copylen);
}
// パケット長を変更する。
void
NetPacket::Resize(uint newlength)
{
uint cliplength = newlength;
if (__predict_false(cliplength > buf.size())) {
cliplength = buf.size();
}
// 新たに増えたところは 0 で埋める。
if (cliplength > length) {
memset(Data() + length, 0, cliplength - length);
}
// 長さは buf.size() を超えることを許容する。
length = newlength;
}
// パケットに1バイト追加する。
void
NetPacket::Append(uint8 data)
{
if (__predict_true(length < buf.size())) {
buf[length] = data;
}
length++;
}
//
// IHWAddrFilter
//
// IHWAddrFilter の デストラクタ
IHWAddrFilter::~IHWAddrFilter()
{
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.