QEMU Network

introduction(architecture)

virtual network device 跟 network emulation 的程式碼會各實作出一個 NetClientState, 然後兩者互相 peering. 當兩者要傳出資料給對方時, 都會呼叫 qemu_send_packet*() 相關的 API 來完成.

qemu_send_packet*() 則會呼叫 peer 的 NetClientState 之中的 NetClientInfo->receive*() 相關 API 來接收對方給的資料.

所以每套 virtual network device 跟 network emulation 都需要提供 NetClientInfo 的介面,

  • virtual network device 的 NetClientInfo->receive*() 實作 QEMU 如何把資料傳回 guest OS driver.

  • network emulation 的 NetClientInfo->receive*() 實作 QEMU 如何把資料透過 host OS 的 APIs 送出到 remote OS.

舉例來說, 以下是一個 guest OS sending packet 的範例, 使用 e1000-slirp 的網路設定. sending packets 的 control flow (QEMU part):

VM Exit => address_space_rw()
=> e1000_mmio_write() => start_xmit() => ... => e1000_send_packet()
=> qemu_send_packet()
=> net_slirp_receive() => slirp_input() => ip_input() => tcp_input() ... => send() syscall

第一行是 QEMU 接受 guest 的 MMIO exit 並轉交給 e1000 網卡 emulation 的邏輯, 這邊不詳述. 第二行是 e1000 程式碼的實作, 可以發現 e1000 最後透過 qemu_send_packet*() 送出封包給 network emulation 的部份做模擬. 第四行則轉接到 slirp 所提供的 NetClientInfo->receive() API, 可以看到 API 開始透過 slirp 的 tcp/ip stack 來 decapsulate 封包, 最後把資料透過 system call [1] 送出.

以下是架構參考圖:

../../_images/e1000_slirp.png

p.s.

qemu_send_packet() 跟 NetClientInfo

basic struct

overview:

- struct NetClientState

  - NetClientInfo* info
  - NetClientState* peer
  - NetQueue *incoming_queue

  - QTAILQ_ENTRY(NetClientState) next

NetQueue methods:

- methods

  - qemu_net_queue_append():  parameter 把要 sending 的資料暫存到 queue->packets
  - qemu_net_queue_flush(): 把所有 queue->packets 的資料都進行 qemu_net_queue_deliver()
  - qemu_net_queue_deliver(): 呼叫該 NetQueue  deliver method: queue->deliver()
  - qemu_net_queue_send(): 進行 qemu_net_queue_deliver + flush, 如果 deliver 失敗則改成 append

- member

  - QTAILQ_HEAD(packets, NetPacket) packets
  - NetQueueDeliverFunc *deliver

qemu_send_packet() calls peer NetClientInfo’s receive functions

qemu_send_packet(NetClientState* nc)

// => qemu_net_queue_send(nc->peer->imcoming_queue)
=> nc->peer->incoming_queue->deliver(nc, ..., nc->peer->incoming_queue->opaque) = qemu_deliver_packet_iov()

   // peer = nc->peer->incoming_queue->opaque ??
   [1.] peer->info->receive_iov(peer, iov, iovcnt);
   [2.] nc_sendv_compat(peer, iov, iovcnt, flags);

        [a.] peer->info->receive()
        [b.] peer->info->receive_raw()