Mewz (WebAssembly x Unikernel) を libkrun で動かしてみた - 外部記憶装置 ã®è©³ç´°ãè¨ãã·ãªã¼ãº
- ãã®1 libkrun ã試ã
- ãã®2 libkrun ã®æ§é
- ãã®3 Mewz 追å å®è£ (Linux zeropage, kernel cmd params)
- ãã®4 Mewz 追å å®è£ (Virtio MMIO)
- ãã®5 Mewz on libkrun ãã¦ã¿ã
- ãã®6 Mewz 追å å®è£ (virtio-console)
- ãã®7 virtio-vsock ã«ã¤ãã¦
- ãã®8 Mewz 追å å®è£ (virtio-vsock)
- ãã®9 TSI ã®ä»çµã¿ â ãã®è¨äº
ç®æ¬¡
TSI ã¨ã¯
Transparent Socket Impersonation (TSI) ã¯ãã²ã¹ãOSå´ã® TCP/IP ã¹ã¿ãã¯ã«ä»£ãã virtio-vsock çµç±ã§ TCP/IP ã®ã½ã±ãããå®ç¾ãã likbrun ã®æ©è½ã§ããã TSI ãç¨ããããèæ¯ã¨ãã¦ã¯ãä»ã®ã³ã³ããã¨å ±åããããã«ãã¹ãå´ã®ã¢ãã¬ã¹ãå©ç¨ãããã¨ããéè¦ãããã®ã§ã¯ãªããã¨æãã ã²ã¹ãã virtio-net ãç¨ããå ´åã¯ãpasst ã«ãããã£ããæ¥ç¶ãçµç«¯ãã¦ãã¹ãå´ã®ã¢ãã¬ã¹ã使ãæ§é ã¨ãªã£ã¦ãããæ§è½é¢ã«ããã¦é£ããããã TSI ãå®è£ ãããã¨èããããã 詳細ã¯ä»¥ä¸ã®è¨äºã§è§£èª¬ããã¦ããã
åè¨äºãããTSI ã®å ¨ä½å³ãå¼ç¨ããã
ã²ã¹ãOSã§ã¯ãAF_INET ã§ä½æãããã½ã±ããã«ã¤ãã¦ãå
é¨ã® TSI å°ç¨ã¢ã¸ã¥ã¼ã«ã§å¦çãè¡ãã
connect(2)
ã bind(2)
ã«ç¸å½ããå¦çãå¼ã°ããã¨ããTSI ã¢ã¸ã¥ã¼ã«ã¯ libkrun ã«å¯¾ãã¦æ¥ç¶å
ããã¤ã³ããããã¼ãã®æ
å ±ãå¶å¾¡ç¨ã® vsock çµç±ã§éç¥ãã¤ã¤ãéä¿¡ç¨ã® vsock ãä½æããã
æ¥ç¶å¾ã«ã¤ãã¦ã¯ã send(2)
, recv(2)
ã«å¯¾å¿ããå¦çãå¼ã°ããã¨éä¿¡ç¨ã® vsock ã«å¯¾ãã¦éåä¿¡ãè¡ãã
libkrun ã§ã¯ãå¶å¾¡ç¨ã® vsock çµç±ã®éç¥ã«å¿ãã¦ãã¹ãä¸ã§ AF_INET ãªã½ã±ããã®ç¢ºä¿ãæ¥ç¶å¦çãvsock ã½ã±ããã¸ã®éä¿¡ã®ä¸ç¶ãæ ãã
ãã®ããã«ãTSI ã®è¦ç´ ã¨ãã¦ã¯ virtio-vsock ä¸ã«æ§ç¯ãããéä¿¡ä¸ç¶ã¨ãæ¢åã® AF_INET ãªã½ã±ããã®æ çµã¿ã¸ã®çµ±åã¨ãã2ç¹ãä¸å¿ã§ããã virtio-vsock ã«ã¤ãã¦ã¯ååå®è£ ãããããä»å㯠TSI ã«ãããéä¿¡ã®å¶å¾¡æ¹æ³ãã¾ã¨ããã
TSI ã«ãããéä¿¡ã®å¶å¾¡
connect æã®æµã
connect æã¯ä»¥ä¸ã®ãããªæµã㧠TcpProxy ãä½æããProxy ã AF_INET ãªã½ã±ããã¨éä¿¡ç¨ vsock éã®ä¸ç¶ãè¡ãã
listen æã®æµã
listen æ㯠libkrun èµ·ç¹ã§éä¿¡ç¨ vsock ãæ°ããä½æãããããè¤éãªããã¼ã¨ãªã£ã¦ããã
VSOCK_TYPE_DGRAM ã«ããå¶å¾¡éä¿¡
libkrun ã§ã¯ TSI ã®å¶å¾¡éä¿¡ã«ç¬èªã® VSOCK_TYPE_DGRAM = 3
ãç¨ããã
ããã¯ã³ãã¯ã·ã§ã³ã¬ã¹ãªéä¿¡ãæä¾ãããã®ã§ãããUDP ã®ãã±ãã¹ãçããªããã®ãæ¦å¿µã¨ãã¦ã¯é¡ä¼¼ãã¦ããã
libkrun ã§ã¯ããã¼ãã«å¿ãã¦å¶å¾¡éä¿¡ã®ç¨éãåãæ¿ãã¦ããã
pub(crate) fn send_dgram_pkt(&mut self, pkt: &VsockPacket) -> super::Result<()> { ... pkt.dst_port() { defs::TSI_PROXY_CREATE => self.process_proxy_create(pkt), // 1024 çª defs::TSI_CONNECT => self.process_connect(pkt), // 1025 çª defs::TSI_GETNAME => self.process_getname(pkt), // 1026 çª defs::TSI_SENDTO_ADDR => self.process_sendto_addr(pkt), // 1027 çª defs::TSI_SENDTO_DATA => self.process_sendto_data(pkt), // 1028 çª defs::TSI_LISTEN => self.process_listen_request(pkt), // 1029 çª defs::TSI_ACCEPT => self.process_accept_request(pkt), // 1030 çª defs::TSI_PROXY_RELEASE => self.process_proxy_release(pkt), // 1031 çª _ => { if pkt.op() == uapi::VSOCK_OP_RW { self.process_dgram_rw(pkt); } else { error!("unexpected dgram pkt: {}", pkt.op()); } } } Ok(()) }
TSI_PROXY_CREATE
TSI ã§ã¯ãã¾ã AF_INET ãªã½ã±ããã«å¯¾å¿ãããããã·ãä½æããã
å¶å¾¡ç¨ã® vsock çµç±ã§ä¸è¨ã®ãã±ãã(TsiProxyCreate
)ããã¹ã(libkrun) ã«å¯¾ãã¦éä¿¡ããã
#[repr(C)] pub struct TsiProxyCreate { pub peer_port: u32, pub _type: u16, }
peer_port
ã¯ã²ã¹ãå´ãä½æããéä¿¡ç¨ vsock ã®ãã¼ã«ã«ãã¼ããæå®ããã
likbrun ã®å
é¨ã§ã¯ãpeer_port
ãã TcpProxy ã®ç®¡çç¨ id ãçæããã
_type
ã¯éä¿¡ã®ç¨®é¡(SOCK_STREAM, SOCK_DGRAM
) ãæå®ããã
match req._type { defs::SOCK_STREAM => { debug!("vsock: proxy create stream"); let id = (req.peer_port as u64) << 32 | defs::TSI_PROXY_PORT as u64; match TcpProxy::new( id, self.cid, defs::TSI_PROXY_PORT, req.peer_port, pkt.src_port(), mem.clone(), queue.clone(), self.rxq.clone(), )
ããã«ãããlibkrun å é¨ã§ TcpProxy ãªãã㯠UdpProxy ãä½æããéä¿¡ç¨ vsock ããã®ãã¼ã¿ã libkrun ãæ㤠AF_INET ãªã½ã±ããããéåä¿¡ããæºåãæ´ãã
TSI_PROXY_RELEASE
Proxy ã«ã¤ãã¦ãä½æããããã®ã¯ã²ã¹ãå´ã®ã½ã±ããã® close æã«åé¤ããå¿
è¦ãããã
åé¤ã«ã¤ãã¦ãå¶å¾¡éä¿¡çµç±ã§è¡ãã
åé¤ããããã«ã¯ä¸è¨ã®ãã±ãã(TsiReleaseReq
)ãå¶å¾¡éä¿¡çµç±ã§éä¿¡ããã
#[repr(C)] pub struct TsiReleaseReq { pub peer_port: u32, pub local_port: u32, }
ä½ææã¨ç°ãªããlocal_port
ãæå®ããå¿
è¦ãããã
ããã¯éä¿¡ç¨ vsock ã®ãã¹ãå´(libkrun) ã®ãã¼ã«ã«ãã¼ãã«å¯¾å¿ããã
ãã®ããã«ãªã£ã¦ããçç±ã¨ãã¦ã¯ãã²ã¹ãå´ã§ accept
ãè¡ãæ¥ç¶ãåãå
¥ããã¨ããã®é½åº¦æ°ãã Proxy ãä½æãããããã§ããã
æ°ããä½ãããæ¥ç¶ã«é¢ããéä¿¡ç¨ã® vsock ã® libkrun å´ã®ãã¼ã«ã«ãã¼ãã¯ã©ã³ãã ã«çæãããããã Proxy ã®ç®¡ç id ã¨ãã¦ç¨ããããã
if let Some((peer_port, accept_fd)) = update.new_proxy { let local_port: u32 = thread_rng.gen_range(1024..u32::MAX); let new_id: u64 = (peer_port as u64) << 32 | local_port as u64; let new_proxy = TcpProxy::new_reverse( new_id, self.cid, id, local_port, peer_port, accept_fd, self.mem.clone(), self.queue.clone(), self.rxq.clone(), );
TsiReleaseReq
ãåãåãã¨ãlibkrun 㯠Proxy ã®åé¤ãè¡ãã
TSI_CONNECT
ã²ã¹ããã TSI ãç¨ããæ¥ç¶ãè¡ãå ´åãå¶å¾¡éä¿¡ã§æ¥ç¶å
ã®ã¨ã³ããã¤ã³ãã«é¢ããæ
å ±ãéç¥ããå¿
è¦ãããã
ã¾ããã²ã¹ããã libkrun ã«å¯¾ãã¦å¶å¾¡éä¿¡ã§ä¸è¨ã®ãã±ãã(TsiConnectReq
)ãéä¿¡ããã
#[repr(C)] pub struct TsiConnectReq { pub peer_port: u32, pub addr: Ipv4Addr, pub port: u16, }
peer_port
㯠TSI_CREATE_PROXY ã¨åæ§ã«éä¿¡ç¨ vsock ã®ã²ã¹ãå´ãã¼ãã§ããã
addr, port
ã¯ããããæ¥ç¶å
ã¨ã³ããã¤ã³ãã®æ
å ±ã示ãã¦ããã
ã²ã¹ãå´ã TsiConnectReq
ãéåºããã®ã¡ãlibkrun å´ã§ã¯ AF_INET
ãªã½ã±ãããç¨ãã¦æ¥ç¶ã試ã¿ãã
if self.status == ProxyStatus::Connecting { update.polling = Some((self.id, self.fd, EventSet::IN | EventSet::OUT)); } else { if self.status == ProxyStatus::Connected { update.polling = Some((self.id, self.fd, EventSet::IN)); } self.push_connect_rsp(result); }
æ¥ç¶å¦çãå®äºããæ¥ç¶æåã失æãå¤æããæç¹ã§ãã²ã¹ãå´ã«å¯¾ãã¦ãã®çµæãå«ããã±ãããå¶å¾¡ç¨ vsock çµç±ã§è¿éããã
#[repr(C)] pub struct TsiConnectRsp { pub result: i32, }
ã²ã¹ãå´ã¯ãã®ãã±ããã®åä¿¡ããã£ã¦æ¥ç¶å¦çãå®äºããã¨ã¿ãªããçµæã«å¿ãã¦å¾ç¶ã®å¦çãè¡ãã
TSI_LISTEN
ã²ã¹ãå´ã§ listen
ãè¡ãå ´åãbind
ãããã¼ãããããåãå
¥ããéä¿¡ç¨ vsock ã®ãã¼ããéç¥ããå¿
è¦ãããã
ã²ã¹ãå´ã¯ä¸è¨ã®ãã±ãã(TsiListenReq
)ãlibkrun ã«å¯¾ãã¦éä¿¡ããã
#[repr(C)] #[derive(Debug)] pub struct TsiListenReq { pub peer_port: u32, pub addr: Ipv4Addr, pub port: u16, pub vm_port: u32, pub backlog: i32, }
peer_port
ã¯ããã¾ã§ã¨åãããã«éä¿¡ç¨ vsock ã®ã²ã¹ãå´ã®ãã¼ã«ã«ãã¼ããæå®ããã
addr
㨠port
ã«ã¤ãã¦ã¯ AF_INET ãªã½ã±ããã bind(2)
ããããã®ã¢ãã¬ã¹ãæå®ããã
vm_port
ã«ã¤ãã¦ã¯ãæ°ãã«åãå
¥ããæ¥ç¶ãä¸ç¶ããå
ã¨ãªãéä¿¡ç¨ vsock ã®ã²ã¹ãå´ãã¼ã«ã«ãã¼ããæå®ããã
TsiListenReq
ãåãåã£ã likbrun å´ã§ã¯ãbind(2)
㨠listen(2)
ãè¡ãã
ãã®æãã²ã¹ãå´ã®ãã¼ãããã¹ãå´ã§å
¬éãããã¼ããããã³ã°ã®è¨å®ã«å¾ããå®éã«ã¯ãããã«ããããã¹ãå´ã®ãã¼ãã« bind(2)
ãè¡ãã
fn try_listen(&mut self, req: &TsiListenReq, host_port_map: &Option<HashMap<u16, u16>>) -> i32 { if self.status == ProxyStatus::Listening || self.status == ProxyStatus::WaitingOnAccept { return 0; } let port = if let Some(port_map) = host_port_map { if let Some(port) = port_map.get(&req.port) { *port } else { return -libc::EPERM; } } else { req.port }; match bind( self.fd, &SockaddrIn::from(SocketAddrV4::new(req.addr, port)), )
ãã¼ããããã®è¨å®ã¯APIã¨ãã¦ç¨æããã¦ãã krun_set_port_map
çµç±ã§è¡ãã
https://github.com/naoki9911/libkrun/blob/ec84848039177fb37da6716255b545b8d2f5c8e3/examples/chroot_vm.c#L270-L277
const char *const port_map[] = { "18000:8000", 0 }; ... // Map port 18000 in the host to 8000 in the guest (if networking uses TSI) if (cmdline.net_mode == NET_MODE_TSI) { if (err = krun_set_port_map(ctx_id, &port_map[0])) { errno = -err; perror("Error configuring port map"); return -1; }
bind(2), listen(2)
å®äºå¾ãlibkrun ã¯ã²ã¹ãã«å¯¾ã㦠TsiListenRsp
ãã±ãããè¿éãå®äºéç¥ãè¡ãã
#[repr(C)] #[derive(Debug)] pub struct TsiListenRsp { pub result: i32, }
TSI_ACCEPT
accept
ã«ã¤ãã¦ã¯ããã¾ã§ã¨ç°ãªãããã¼ã¨ãªã£ã¦ããã
ã¾ããã²ã¹ãå´ãã libkrun ã«å¯¾ãã¦ä¸è¨ã®ãã±ãããéä¿¡ããæ°ããæ¥ç¶ã®æç¡ã確èªããã
#[repr(C)] #[derive(Debug)] pub struct TsiAcceptReq { pub peer_port: u32, pub flags: u32, }
libkrun å´ã§ã¯ãpending ã¨ãªã£ã¦ããæ¥ç¶ãåå¨ããããªããã¯ãã³ãããã¯ãªå ´åã¯ããã«å¿ã㦠TsiAcceptRes
ãè¿éããã
fn accept(&mut self, req: TsiAcceptReq) -> ProxyUpdate { debug!("accept: id={} flags={}", req.peer_port, req.flags); let mut update = ProxyUpdate::default(); if self.pending_accepts > 0 { self.pending_accepts -= 1; self.push_accept_rsp(0); update.signal_queue = true; } else if (req.flags & libc::O_NONBLOCK as u32) != 0 { self.push_accept_rsp(-libc::EWOULDBLOCK); update.signal_queue = true; } else { self.status = ProxyStatus::WaitingOnAccept; } update }
#[repr(C)] #[derive(Debug)] pub struct TsiAcceptRsp { pub result: i32, }
ããã§ãAF_INET å´ã® accept(2)
ã®å¦çã«ã¤ãã¦è¿½ããããã
Listen ãè¡ãã¨ãlibkrun å´ã§ã¯ Proxy ã«å¯¾å¿ããã¯ã¼ã«ã¼ã¹ã¬ãããç«ã¡ä¸ãããæ¥ç¶å¾
æ©ç¶æ
ã«ãªãã
Proxy ã® process_event
ã§ã¯ãepoll(7)
ã§éç¥ãããã¤ãã³ãã«å¿ã㦠accept(2)
ãè¡ãã
match accept(self.fd) { Ok(accept_fd) => { update.new_proxy = Some((self.peer_port, accept_fd)); } Err(e) => warn!("error accepting connection: id={}, err={}", self.id, e), };
æ°ããæ¥ç¶ãåãå
¥ããã¨ãããã«å¯¾å¿ãã Proxy ãä½æããã²ã¹ãå´ã®éä¿¡ç¨ vsock ã«å¯¾ã㦠VSOCK_OP_REQUEST
ãéä¿¡ãæ¥ç¶å¦çãéå§ããã
let local_port: u32 = thread_rng.gen_range(1024..u32::MAX); let new_id: u64 = (peer_port as u64) << 32 | local_port as u64; let new_proxy = TcpProxy::new_reverse( new_id, self.cid, id, local_port, peer_port, accept_fd, self.mem.clone(), self.queue.clone(), self.rxq.clone(), ); self.proxy_map .write() .unwrap() .insert(new_id, Mutex::new(Box::new(new_proxy))); if let Some(proxy) = self.proxy_map.read().unwrap().get(&new_id) { proxy.lock().unwrap().push_op_request(); };
ã²ã¹ãå´ã§ã¯å¯¾å¿ããéä¿¡ç¨ vsock ãæ°ããæ¥ç¶ãåãå
¥ããVSOCK_OP_RESPONSE
ãè¿ãã
libkrun ã§ã¯ãVSOCK_OP_RESPONSE
ãåãåã㨠enqueue_accept
ã§ç¶æ
ã«å¿ã㦠TsiAcceptRes
ãè¿ãããå¦çå¾
ã¡ã¨ã㦠pending_accepts ãã¤ã³ã¯ãªã¡ã³ãããã
fn enqueue_accept(&mut self) { debug!("enqueue_accept: control_port: {}", self.control_port); if self.status == ProxyStatus::WaitingOnAccept { self.status = ProxyStatus::Listening; self.push_accept_rsp(0); } else { self.pending_accepts += 1; } }
#[repr(C)] #[derive(Debug)] pub struct TsiListenRsp { pub result: i32, }
æ©è½ã¨ãã¦ã¯ AF_INET ã§åãå ¥ããæ¥ç¶ã«ã¤ãã¦éä¿¡ç¨ vsock ã§å ããã¦æ¥ç¶ãè¡ããaccept ã«ã¤ãã¦ã¯ API ã¨ãã¦æ°è¦æ¥ç¶ã®æç¡ã®ç¢ºèªã®ã¿è¡ã£ã¦ãããã¨ã«ãªãã
TSI_GETNAME
getpeername(2)
ã«ç¸å½ããæ©è½ãæä¾ããã
ã²ã¹ãå´ã¯libkrun ã«å¯¾ã㦠TsiGetnameReq
ãã±ãããéä¿¡ããã
#[repr(C)] pub struct TsiGetnameReq { pub peer_port: u32, pub local_port: u32, pub peer: u32, }
peer_port, local_port
ã«ã¤ãã¦ã¯ TSI_PROXY_RELEASE ã¨åãã§ããã
peer
ã«ã¤ãã¦ã¯æªä½¿ç¨ã§ããããã使éã¯ä¸æã§ããã
libkrun ã¯å¯¾å¿ãã AF_INET ãªã½ã±ããã«å¯¾ã㦠getpeername(2)
ãå®è¡ãããã®çµæã TsiGetnameRsp
ã«å
¥ãã¦è¿ãã
#[repr(C)] #[derive(Debug)] pub struct TsiGetnameRsp { pub addr: Ipv4Addr, pub port: u16, pub result: i32, }
TSI_SENDTO_ADDR, TSI_SENDTO_DATA
ããã㯠UDP ãªã½ã±ããã«ããã sendto(2)
ã«ç¸å½ããæ©è½ãæä¾ããã
TcpProxy ã§ã¯ç¡è¦ãããã
TSI_SENDTO_ADDR ã§éä¿¡å ãæå®ããTSI_SENDTO_DATA ã§ãã¼ã¿æ¬ä½ãéä¿¡ããã
TSI_SENDTO_ADDR ã§ã¯ä¸è¨ã®ãã±ãã(TsiSendtoAddr
)ã«éä¿¡å
ã¢ãã¬ã¹ãå
¥ãã¦libkrunã«å¯¾ãã¦éä¿¡ããã
#[repr(C)] #[derive(Debug)] pub struct TsiSendtoAddr { pub peer_port: u32, pub addr: Ipv4Addr, pub port: u16, }
libkrun å
é¨ã§ã¯ãæå®ããã¢ãã¬ã¹ã sendto éä¿¡å
ã¢ãã¬ã¹ã¨ãã¦ç»é²ãããã
ã¾ããAF_INET ãªã½ã±ãããbind(2)
ããã¦ããªãå ´å㯠bind(2)
ãå®è¡ããã
TSI_SENDTO_DATA ã«ã¤ãã¦ã¯ããã±ããã«å«ã¾ãããã¼ã¿å ¨ä½ããã¤ãã¼ãã¨ã㦠UdpProxy ããéä¿¡ããã
ã¾ã¨ã
TSI 㯠vsock çµç±ã§ AF_INET ãªã½ã±ãããå®ç¾ãã libkrun ã®æ©è½ã§ããã ã²ã¹ãOS 㯠virtio-vsock çµç±ã§ libkrun ã® TSI ã¢ã¸ã¥ã¼ã«ã¨å¶å¾¡éä¿¡ãè¡ããã¨ã§ãã®æ©è½ãå©ç¨ã§ããã ä»åã¯ãã®å¶å¾¡å¨ããã¾ã¨ããã 次å㯠Mewz ã¸ã®å®è£ ãè¡ãã