--- nono/host/hostnet.h 2026/04/29 17:05:11 1.1 +++ nono/host/hostnet.h 2026/04/29 17:06:02 1.1.1.9 @@ -12,26 +12,98 @@ #include "hostdevice.h" #include "macaddr.h" -#include "monitor.h" #include "netdriver.h" #include "spscqueue.h" -// パケット -class NetPacket : public std::array +// パケット。 +class NetPacket { - // ジャンボフレームでないイーサネットフレームは最大 1518バイトだが - // このパケットバッファの長さは 1519 バイト以上必要。 - // vm/lance が 1518 バイトを越えたことを検出するのに使っている。 - public: + NetPacket() { + Clear(); + } + // バッファをクリアする - void Clear() { length = 0; } + void Clear() { + length = 0; + readpos = 0; + } + + // このパケットの内容を置き換える。 + void Assign(const void *src, uint srclen); + + // バッファサイズ (パケット長) を変更する。 + void Resize(uint newlength); // パケットに1バイト追加する - void Append(uint8 data) { (*this)[length++] = data; } + void Append(uint8 data); + + // 読み出し開始位置を設定 + void Seek(size_t readpos_) { readpos = readpos_; } + + // バッファの先頭を返す。 + uint8 *Data() { return buf.data(); } + const uint8 *Data() const { return buf.data(); } + + // パケットサイズを取得。 + // NetDriver で受信してから HostNet で破棄されるまでの間だけ + // Length() が buf.size() を上回ることを許容する。 + // データは buf に収まる分だけ書いておくが、実際には Length バイトの + // パケットだったことを示す。どのみち捨てられるのでデータが尻切れなことは + // 気にしない。あわよくばモニタ上で長いパケットだと示唆できるかも知れない + // という程度。 + uint Length() const { return length; } + + // 読み出しの残りバイト数を返す + uint GetRemain() const { return Length() - readpos; } + + // 1バイト読み出す。EOP なら -1 を返す + uint32 Read() { + if (readpos < Length()) { + return buf[readpos++]; + } else { + return (uint32)-1; + } + } + + // ポインタを進めず指定の位置のデータを読み出す。 + // pos が不正なら -1 を返す。 + uint32 Peek(uint pos) const { + if (pos < Length()) { + return buf[pos]; + } else { + return (uint32)-1; + } + } - // バッファ内の有効長 - int length {}; + private: + // 有効長 + uint length {}; + // 現在の読み出し位置 + uint readpos {}; + + std::array buf {}; +}; + +// MAC アドレスフィルタ。 +// VM 側デバイスはこれを多重継承して実装すること。 +class IHWAddrFilter +{ + // インタフェース風にするため、メンバ変数を持たせないこと。 + public: + enum { + HPF_PASS = 0, // このフレームを受信する。 + HPF_DROP_UNICAST, // このユニキャストフレームを破棄する。 + HPF_DROP_MULTICAST, // このマルチキャストフレームを破棄する。 + }; + + public: + virtual ~IHWAddrFilter(); + + // デバイスがこの dstaddr 宛のフレームを受け付けるかどうかを返す。 + // デバイスは、自分宛て、ブロードキャスト、マルチキャスト + // (おそらくハッシュ)、プロミスキャスなどの状態を考慮すること。 + virtual int HWAddrFilter(const MacAddr& dstaddr) const = 0; }; class HostNetDevice : public HostDevice @@ -50,32 +122,36 @@ class HostNetDevice : public HostDevice uint64 read_bytes; uint64 write_pkts; uint64 write_bytes; - int txqfull_pkts; // キューが一杯だった回数 + uint64 txzero_pkts; // 送信長0で破棄したパケット数 + uint64 txunsupp_pkts; // 送信先が未対応で破棄したパケット数 + uint64 txunsupp_bytes; // 送信先が未対応で破棄したバイト数 + uint64 txqfull_pkts; // キューが一杯だった回数 uint64 txqfull_bytes; // キューに入れられなかったバイト数 uint64 rxdisable_pkts; // 上位層が受信無効 uint64 rxdisable_bytes; // 上位層が受信無効 uint64 rxjumbo_pkts; // 受信したジャンボパケット数 uint64 rxjumbo_bytes; // 受信したジャンボパケットのバイト数 - int rxqfull_pkts; // キューが一杯だった回数 + uint64 rxqfull_pkts; // キューが一杯だった回数 uint64 rxqfull_bytes; // キューに入れられなかったバイト数 - int rxfilter_pkts; // 受信フィルタで破棄したパケット数 - uint64 rxfilter_bytes; // フィルタされたバイト数 + uint64 rxnotme_pkts; // ユニキャストで破棄したパケット数 + uint64 rxnotme_bytes; // フィルタされたバイト数 + uint64 rxmcast_pkts; // マルチキャストで破棄したパケット数 + uint64 rxmcast_bytes; // フィルタされたバイト数 - int txq_peak; // キューのおおよその最大使用量 - int rxq_peak; // キューのおおよその最大使用量 + uint txq_peak; // キューのおおよその最大使用量 + uint rxq_peak; // キューのおおよその最大使用量 }; public: - HostNetDevice(); + HostNetDevice(Device *parent_, uint n, const std::string& portname_); ~HostNetDevice() override; void SetLogLevel(int loglevel_) override; - bool Init() override; + bool Create2() override; // パケットを送信する。 // data はプリアンブルを含まず、イーサネットフレームヘッダ(14バイト)、 // データ(46-1500バイト) からなる。FCS(CRC) (4バイト) は含まない。 - // VM から引き取ったパケットについて tx_pkts, tx_bytes を更新すること // 送信キューに追加できれば true、出来なければ false を返す。 bool Tx(const NetPacket& packet); @@ -87,41 +163,63 @@ class HostNetDevice : public HostDevice // 受信キューへの投入を許可/禁止する。 void EnableRx(bool enable); - // 自身の MAC アドレスを設定する。 - void SetMyAddr(const macaddr_t& myaddr_); + // マルチキャストフィルタを設定する。 + void SetMCastFilter(uint64 filter); - // プロミスキャスモードを設定する。 - void SetPromisc(bool promisc_); - - // ドライバ名を返す (ステータスパネルから呼ばれる) - const std::string GetDriverName() const; + // ドライバ名を返す。 + const std::string& GetDriverName(); // 統計情報を返す (ステータスパネルから呼ばれる) uint64 GetTXPkts() const { return stat.tx_pkts; } uint64 GetRXPkts() const { return stat.rx_pkts; } + // 統計情報に1パケット分加算する (これだけ SLIRP の裏スレッドから呼ばれる) + void CountTXUnsupp(size_t bytes); + // コンパイル済みのドライバ名一覧を返す static std::vector GetDrivers(); + // libslirp のバージョン文字列を返す + static const char *GetSlirpVersion(); + + // デバッグ用。 + static std::vector DumpHex(const void *, size_t); + static std::vector DumpFrame(const void *, size_t); + private: - bool SelectDriver(); - bool CreateNone(); - bool CreateTap(); - bool CreateBPF(); - bool CreateAFPacket(); + bool SelectDriver(bool startup) override; + void CreateNone(); + void CreateSlirp(bool startup); + void CreateTap(); + void CreateBPF(); + void CreateAFPacket(); int Read() override; // 外部への書き出し(パケットを送信) - void Write(uint32 data) override; + void Write() override; + + DECLARE_MONITOR_SCREEN(MonitorScreen); - DECLARE_MONITOR_CALLBACK(MonitorUpdate); + // デバッグ用 (下請け) + static std::vector DumpARP(const void *, size_t); + static std::vector DumpIPv4(const void *, size_t); + static std::vector DumpICMPv4(const char *, const char *, + const void *, size_t); + static std::vector DumpTCPv4(const char *, const char *, + const void *, size_t); + static std::vector DumpUDPv4(const char *, const char *, + const void *, size_t); + static std::vector DumpDHCP(const void *, size_t); + static std::vector DumpIPv6(const void *, size_t); + static std::vector DumpICMPv6(const char *, const char *, + const void *, size_t); + static std::string DumpDHCPAddrList(const uint8 *, size_t); + static std::string GetServByPort(uint16 port_be, const char *protoname); std::unique_ptr driver {}; bool rx_enable {}; - macaddr_t myaddr {}; // 自己 MAC アドレス - bool promisc {}; // プロミスキャスモードなら true // キュー NetTxQueue txq {}; @@ -130,7 +228,7 @@ class HostNetDevice : public HostDevice // 統計情報 struct stat_t stat {}; - std::string errmsg {}; + Monitor *monitor {}; - Monitor monitor { this }; + IHWAddrFilter *ihwaddrfilter {}; };