1- package vmnet
2-
3- /*
4- #cgo darwin CFLAGS: -mmacosx-version-min=11 -x objective-c -fno-objc-arc
5- #cgo darwin LDFLAGS: -lobjc -framework Foundation -framework vmnet
6- # include "vmnet_darwin.h"
7- */
8- import "C"
1+ package datagram
2+
93import (
10- "errors"
114 "fmt"
125 "net"
136 "os"
147 "syscall"
15- "time"
8+
9+ "github.com/Code-Hex/vz/v3/vmnet"
10+ "github.com/Code-Hex/vz/v3/vmnet/fileadapter"
1611)
1712
18- // MARK: - DatagramFileAdaptorForInterface
13+ // MARK: - FileAdaptorForInterface
1914
20- // DatagramFileAdaptorForInterface returns a file for the given [Network ].
15+ // FileAdaptorForInterface returns a file for the given [vmnet.Interface ].
2116// - Invoke the returned function in a separate goroutine to start packet forwarding between the vmnet interface and the file.
2217// - The context can be used to stop the goroutines and the interface.
2318// - The returned error channel can be used to receive errors from the goroutines.
@@ -36,103 +31,74 @@ import (
3631//
3732// VZ:
3833//
39- // file, errCh, err := DatagramFileAdaptorForInterface (ctx, iface)
34+ // file, errCh, err := FileAdaptorForInterface (ctx, iface)
4035// attachment := NewFileHandleNetworkDeviceAttachment(file)
41- var DatagramFileAdaptorForInterface = FileAdaptorForInterface [* DatagramPacketForwarder , net.PacketConn ]
42-
43- // MARK: - DatagramPacketForwarder for datagram file adaptor
44-
45- // DatagramPacketForwarder implements PacketForwarder for datagram file descriptor.
46- type DatagramPacketForwarder struct {
47- readPktDescsManager * pktDescsManager
48- writePktDescsManager * pktDescsManager
49- }
50-
51- var _ PacketForwarder [net.PacketConn ] = (* DatagramPacketForwarder )(nil )
52-
53- // New creates a new DatagramPacketForwarder.
54- func (f * DatagramPacketForwarder ) New () PacketForwarder [net.PacketConn ] {
55- return & DatagramPacketForwarder {}
56- }
57-
58- // Sockopts returns socket options for the given Interface and user desired options.
59- // Default values are based on the following references:
60- // - https://developer.apple.com/documentation/virtualization/vzfilehandlenetworkdeviceattachment/maximumtransmissionunit?language=objc
61- func (* DatagramPacketForwarder ) Sockopts (iface * Interface , userOpts Sockopts ) Sockopts {
62- return sockoptsForPacketConn (iface , userOpts )
63- }
64-
65- // ConnAndFile creates a [net.PacketConn] and *[os.File] pair using [syscall.Socketpair].
66- func (f * DatagramPacketForwarder ) ConnAndFile (opts Sockopts ) (net.PacketConn , * os.File , error ) {
67- return packetConnAndFile (opts )
68- }
36+ var FileAdaptorForInterface = fileadapter.ForInterface [* PacketForwarder , net.PacketConn ]
6937
70- // AllocateBuffers allocates packet descriptor buffers for reading and writing packets.
71- func (f * DatagramPacketForwarder ) AllocateBuffers (iface * Interface ) error {
72- maxPacketSize := iface .MaxPacketSize
73- if iface .EnableVirtioHeader {
74- // Add virtio header size
75- maxPacketSize += virtioNetHdrSize
76- }
77- f .readPktDescsManager = newPktDescsManager (iface .MaxReadPacketCount , maxPacketSize )
78- f .writePktDescsManager = newPktDescsManager (iface .MaxWritePacketCount , maxPacketSize )
79- return nil
80- }
38+ // MARK: - PacketForwarder for datagram file adaptor
8139
82- // ReadPacketsFromInterface reads packets from the vmnet Interface.
83- func (f * DatagramPacketForwarder ) ReadPacketsFromInterface (iface * Interface , estimatedCount int ) (int , error ) {
84- f .readPktDescsManager .reset ()
85- return iface .ReadPackets (f .readPktDescsManager .pktDescs , estimatedCount )
40+ // PacketForwarder implements [fileadapter.PacketForwarder] for datagram file descriptor.
41+ type PacketForwarder struct {
8642}
8743
88- // WritePacketsToConn writes packets to the connection.
89- func (f * DatagramPacketForwarder ) WritePacketsToConn (conn net.PacketConn , packetCount int ) error {
90- return f .readPktDescsManager .writePacketsToPacketConn (conn , packetCount )
91- }
44+ var _ fileadapter.PacketForwarder [net.PacketConn ] = (* PacketForwarder )(nil )
9245
93- // ReadPacketsFromConn reads packets from the connection .
94- func (f * DatagramPacketForwarder ) ReadPacketsFromConn ( conn net.PacketConn ) ( int , error ) {
95- return f . writePktDescsManager . readPacketsFromPacketConn ( conn )
46+ // New creates a new [PacketForwarder] .
47+ func (f * PacketForwarder ) New () fileadapter. PacketForwarder [ net.PacketConn ] {
48+ return & PacketForwarder {}
9649}
9750
98- // WritePacketsToInterface writes packets to the vmnet Interface.
99- func (f * DatagramPacketForwarder ) WritePacketsToInterface (iface * Interface , packetCount int ) error {
100- return iface . WritePackets ( f . writePktDescsManager . pktDescs , packetCount )
51+ // Sockopts returns [fileadapter.Sockopts] for the given [vmnet. Interface] and user desired options .
52+ func (* PacketForwarder ) Sockopts (iface * vmnet. Interface , userOpts fileadapter. Sockopts ) fileadapter. Sockopts {
53+ return SockoptsForPacketConn ( iface , userOpts )
10154}
10255
103- // sockoptsForPacketConn returns socket options for the given [Interface] and user desired options for [net.PacketConn].
104- // Default values are based on the following references:
105- // - https://developer.apple.com/documentation/virtualization/vzfilehandlenetworkdeviceattachment/maximumtransmissionunit?language=objc
106- func sockoptsForPacketConn (iface * Interface , userOpts Sockopts ) Sockopts {
56+ // SockoptsForPacketConn returns [fileadapter.Sockopts] for the given [vmnet.Interface] and user desired options for [net.PacketConn].
57+ func SockoptsForPacketConn (iface * vmnet.Interface , userOpts fileadapter.Sockopts ) fileadapter.Sockopts {
10758 // Calculate minimum buffer sizes based on interface configuration
10859 packetSize := int (iface .MaxPacketSize )
10960 if iface .EnableVirtioHeader {
11061 // Add virtio header size
111- packetSize += virtioNetHdrSize
62+ packetSize += vmnet . VirtioNetHdrSize
11263 }
113- minPacketCount := max (iface .MaxReadPacketCount , iface .MaxWritePacketCount )
64+ maxPacketCount := max (iface .MaxReadPacketCount , iface .MaxWritePacketCount )
65+ // On datagram socket, send buffer size only needs to hold one packet.
11466 minSendBufSize := packetSize
115- minRecvBufSize := minSendBufSize * minPacketCount
67+ // Minimum receive buffer size is calculated to hold multiple packets that may handled at once by the vmnet interface.
68+ defaultRecvBufSize := minSendBufSize * maxPacketCount
69+ if ! iface .EnableTSO {
70+ // If TSO is disabled, receive buffer size calculated above is too small.
71+ // Increase receive buffer size to increase performance.
72+ defaultRecvBufSize *= 10
73+ }
11674
11775 // Default socket options
118- sockopts := Sockopts {
119- ReceiveBufferSize : minRecvBufSize * 4 * 10 ,
120- SendBufferSize : packetSize ,
76+ // When TSO is enabled, both receive buffer sizes will exceed the default maximum buffer size on macOS, it will be capped by the system.
77+ // default max buffer size on macOS 26.2:
78+ // kern.ipc.maxsockbuf: 8388608
79+ sockopts := fileadapter.Sockopts {
80+ ReceiveBufferSize : defaultRecvBufSize ,
81+ SendBufferSize : minSendBufSize ,
12182 }
122- // If user specified options, override with minimums as needed
12383 if userOpts .ReceiveBufferSize > 0 {
124- sockopts .ReceiveBufferSize = max ( userOpts . ReceiveBufferSize , minRecvBufSize )
84+ sockopts .ReceiveBufferSize = defaultRecvBufSize
12585 }
12686 if userOpts .SendBufferSize > 0 {
87+ // If user specified options, override with minimums as needed
12788 sockopts .SendBufferSize = max (userOpts .SendBufferSize , minSendBufSize )
12889 }
12990 return sockopts
13091}
13192
132- // packetConnAndFile creates a [net.PacketConn] and *[os.File] pair using [syscall.Socketpair].
133- func packetConnAndFile (opts Sockopts ) (net.PacketConn , * os.File , error ) {
93+ // ConnAndFile creates a [net.PacketConn] and *[os.File] pair using [syscall.Socketpair].
94+ func (f * PacketForwarder ) ConnAndFile (opts fileadapter.Sockopts ) (net.PacketConn , * os.File , error ) {
95+ return PacketConnAndFile (opts )
96+ }
97+
98+ // PacketConnAndFile creates a [net.PacketConn] and *[os.File] pair using [syscall.Socketpair].
99+ func PacketConnAndFile (opts fileadapter.Sockopts ) (net.PacketConn , * os.File , error ) {
134100 sendBufSize , recvBufSize := opts .SendBufferSize , opts .ReceiveBufferSize
135- connFile , file , err := filePair (syscall .SOCK_DGRAM , sendBufSize , recvBufSize )
101+ connFile , file , err := fileadapter . FilePair (syscall .SOCK_DGRAM , sendBufSize , recvBufSize )
136102 if err != nil {
137103 return nil , nil , fmt .Errorf ("ConnAndFile failed: %w" , err )
138104 }
@@ -150,102 +116,71 @@ func packetConnAndFile(opts Sockopts) (net.PacketConn, *os.File, error) {
150116 return conn , file , nil
151117}
152118
153- // MARK: - pktDescsManager methods for datagram file adaptor
154-
155- // buffersForWritingToPacketConn returns [net.Buffers] to write to the [net.PacketConn]
156- // adjusted their buffer sizes based vm_pkt_size in [VMPktDesc]s read from [Interface].
157- // The 4-byte header is excluded.
158- func (v * pktDescsManager ) buffersForWritingToPacketConn (packetCount int ) (net.Buffers , error ) {
159- for i , vmPktDesc := range v .iter (packetCount ) {
160- if uint64 (vmPktDesc .vm_pkt_size ) > v .maxPacketSize {
161- return nil , fmt .Errorf ("vm_pkt_size %d exceeds maxPacketSize %d" , vmPktDesc .vm_pkt_size , v .maxPacketSize )
162- }
163- // Resize buffer to exclude the 4-byte header
164- v .writingBuffers [i ] = v .packetBufferAt (i , 0 )
119+ // NewInterfaceToConnForwarder creates a new [fileadapter.InterfaceToConnForwarder] for [net.PacketConn].
120+ func (f * PacketForwarder ) NewInterfaceToConnForwarder (iface * vmnet.Interface ) fileadapter.InterfaceToConnForwarder [net.PacketConn ] {
121+ maxPacketSize := iface .MaxPacketSize
122+ if iface .EnableVirtioHeader {
123+ // Add virtio header size
124+ maxPacketSize += vmnet .VirtioNetHdrSize
125+ }
126+ return & InterfaceToPacketConnForwarder {
127+ readPktDescsManager : vmnet .NewPktDescsManager (iface .MaxReadPacketCount , maxPacketSize ),
165128 }
166- return v .writingBuffers [:packetCount ], nil
167129}
168130
169- // writePacketsToPacketConn writes packets from [VMPktDesc]s to the [net.PacketConn].
170- // - It returns an error if any occurs during sending packets.
171- func (v * pktDescsManager ) writePacketsToPacketConn (conn net.PacketConn , packetCount int ) error {
172- buffers , err := v .buffersForWritingToPacketConn (packetCount )
173- if err != nil {
174- return fmt .Errorf ("buffersForWritingToPacketConn failed: %w" , err )
175- }
176- // Get rawConn for syscall.Sendmsg
177- rawConn , _ := conn .(syscall.Conn ).SyscallConn ()
178- var sentCount int
179- var sendErr error
180- rawConnWriteErr := rawConn .Write (func (fd uintptr ) (done bool ) {
181- for sentCount < packetCount {
182- // send packet from buffer
183- if err := syscall .Sendmsg (int (fd ), buffers [sentCount ], nil , nil , 0 ); err != nil {
184- if errors .Is (err , syscall .EAGAIN ) {
185- return false // try again later
186- } else if errors .Is (err , syscall .ENOBUFS ) {
187- // Wait and try to send next packet
188- time .Sleep (100 * time .Microsecond )
189- continue
190- }
191- sendErr = fmt .Errorf ("syscall.Sendmsg failed: %w" , err )
192- return true
193- }
194- sentCount ++
195- }
196- return true
197- })
198- if rawConnWriteErr != nil {
199- return fmt .Errorf ("rawConn.Write failed: %w" , rawConnWriteErr )
131+ // NewConnToInterfaceForwarder creates a new [fileadapter.ConnToInterfaceForwarder] for [net.PacketConn].
132+ func (f * PacketForwarder ) NewConnToInterfaceForwarder (iface * vmnet.Interface ) fileadapter.ConnToInterfaceForwarder [net.PacketConn ] {
133+ maxPacketSize := iface .MaxPacketSize
134+ if iface .EnableVirtioHeader {
135+ // Add virtio header size
136+ maxPacketSize += vmnet .VirtioNetHdrSize
200137 }
201- if sendErr != nil {
202- return sendErr
138+ return & PacketConnToInterfaceForwarder {
139+ writePktDescsManager : vmnet . NewPktDescsManager ( iface . MaxWritePacketCount , maxPacketSize ),
203140 }
204- return nil
205141}
206142
207- // readPacketsFromPacketConn reads packets from the [net.PacketConn] into [VMPktDesc]s.
208- // - It returns the number of packets read.
209- // - The packets are expected to come one by one.
210- // - It receives all available packets until no more packets are available, packetCount reaches maxPacketCount, or an error occurs.
211- // - It waits for the connection to be ready for initial packet.
212- func (v * pktDescsManager ) readPacketsFromPacketConn (conn net.PacketConn ) (int , error ) {
213- var packetCount int
214- // Read the first packet (blocking)
215- n , _ , err := conn .ReadFrom (v .backingBuffers [packetCount ][headerSize :])
216- if n == 0 {
217- // normal closure. Will this happen in datagram socket?
218- return 0 , errors .New ("conn.ReadFrom: use of closed network connection" )
219- }
220- if err != nil {
221- return 0 , fmt .Errorf ("conn.ReadFrom failed: %w" , err )
222- }
223- v .at (packetCount ).SetPacketSize (n )
224- packetCount ++
225- // Get rawConn for syscall.Recvfrom
226- rawConn , _ := conn .(syscall.Conn ).SyscallConn ()
227- var recvErr error
228- rawConnReadErr := rawConn .Read (func (fd uintptr ) (done bool ) {
229- // Read available packets until no more packets are available or packetCount reaches maxPacketCount
230- for packetCount < v .maxPacketCount {
231- // receive packet into buffer
232- n , _ , err := syscall .Recvfrom (int (fd ), v .backingBuffers [packetCount ][headerSize :], 0 )
233- if err != nil {
234- if ! errors .Is (err , syscall .EAGAIN ) {
235- recvErr = fmt .Errorf ("syscall.Recvfrom failed: %w" , err )
236- }
237- return true // Do not retry on error
238- }
239- v .at (packetCount ).SetPacketSize (n )
240- packetCount ++
241- }
242- return true
243- })
244- if rawConnReadErr != nil {
245- return 0 , fmt .Errorf ("rawConn.Read failed: %w" , rawConnReadErr )
246- }
247- if recvErr != nil {
248- return 0 , recvErr
249- }
250- return packetCount , nil
143+ // MARK: - Interface -> Conn
144+
145+ // InterfaceToPacketConnForwarder forwards packets from [vmnet.Interface] to [net.PacketConn].
146+ type InterfaceToPacketConnForwarder struct {
147+ readPktDescsManager * vmnet.PktDescsManager
148+ packetCount int
149+ }
150+
151+ var _ fileadapter.InterfaceToConnForwarder [net.PacketConn ] = (* InterfaceToPacketConnForwarder )(nil )
152+
153+ // ReadPacketsFromInterface reads packets from the [vmnet.Interface].
154+ func (f * InterfaceToPacketConnForwarder ) ReadPacketsFromInterface (iface * vmnet.Interface , estimatedCount int ) (int , error ) {
155+ f .readPktDescsManager .Reset ()
156+ n , err := iface .ReadPackets (f .readPktDescsManager .PktDescs , estimatedCount )
157+ f .packetCount = n
158+ return n , err
159+ }
160+
161+ // WritePacketsToConn writes packets to the [net.PacketConn].
162+ func (f * InterfaceToPacketConnForwarder ) WritePacketsToConn (conn net.PacketConn ) error {
163+ return f .readPktDescsManager .WritePacketsToPacketConn (conn , f .packetCount )
164+ }
165+
166+ // MARK: - Conn -> Interface
167+
168+ // PacketConnToInterfaceForwarder forwards packets from [net.PacketConn] to [vmnet.Interface].
169+ type PacketConnToInterfaceForwarder struct {
170+ writePktDescsManager * vmnet.PktDescsManager
171+ packetCount int
172+ }
173+
174+ var _ fileadapter.ConnToInterfaceForwarder [net.PacketConn ] = (* PacketConnToInterfaceForwarder )(nil )
175+
176+ // ReadPacketsFromConn reads packets from the [net.PacketConn].
177+ func (f * PacketConnToInterfaceForwarder ) ReadPacketsFromConn (conn net.PacketConn ) error {
178+ n , err := f .writePktDescsManager .ReadPacketsFromPacketConn (conn )
179+ f .packetCount = n
180+ return err
181+ }
182+
183+ // WritePacketsToInterface writes packets to the [vmnet.Interface].
184+ func (f * PacketConnToInterfaceForwarder ) WritePacketsToInterface (iface * vmnet.Interface ) error {
185+ return iface .WritePackets (f .writePktDescsManager .PktDescs , f .packetCount )
251186}
0 commit comments