Skip to content

Commit e096ea2

Browse files
committed
re-package
Signed-off-by: Norio Nomura <norio.nomura@gmail.com>
1 parent f883358 commit e096ea2

6 files changed

Lines changed: 766 additions & 488 deletions

File tree

Lines changed: 107 additions & 172 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,18 @@
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+
93
import (
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

Comments
 (0)