From 138118919562bc2bed4fcf8c5fabcff20efa6b80 Mon Sep 17 00:00:00 2001 From: Lucas Manning Date: Mon, 13 Jan 2025 10:18:45 -0800 Subject: [PATCH] Create the interfaces for PACKET_MMAP endpoints to implement. PiperOrigin-RevId: 715019134 --- pkg/abi/linux/socket.go | 66 ++++++++++++++++++++++++++ pkg/sentry/socket/netstack/BUILD | 1 + pkg/sentry/socket/netstack/netstack.go | 23 +++++++++ pkg/syserr/netstack.go | 3 ++ pkg/tcpip/errors.go | 18 +++++++ pkg/tcpip/stack/registration.go | 52 ++++++++++++++++++++ pkg/tcpip/tcpip.go | 13 +++++ 7 files changed, 176 insertions(+) diff --git a/pkg/abi/linux/socket.go b/pkg/abi/linux/socket.go index 2905f73eda..0df19a34d8 100644 --- a/pkg/abi/linux/socket.go +++ b/pkg/abi/linux/socket.go @@ -144,6 +144,72 @@ const ( PACKET_OUTGOING = 4 // Outgoing of any type ) +// Packet socket options from +const ( + PACKET_RX_RING = 5 +) + +// Statuses for a frame in a packet_mmap ring buffer from . +const ( + TP_STATUS_KERNEL = 0 + TP_STATUS_USER = 0x1 + TP_STATUS_COPY = 0x2 + TP_STATUS_LOSING = 0x4 + TP_STATUS_CSUM_NOT_READY = 0x8 + TP_STATUS_VLAN_VALID = 0x10 + TP_STATUS_BLK_TMO = 0x20 + TP_STATUS_VLAN_TPID_VALID = 0x40 + TP_STATUS_CSUM_VALID = 0x80 + TP_STATUS_GSO_TCP = 0x100 +) + +// TpacketReq is the request for a packet_mmap ring buffer from +// . +// +// +marshal +type TpacketReq struct { + TpBlockSize uint32 + TpBlockNr uint32 + TpFrameSize uint32 + TpFrameNr uint32 +} + +// TpacketHdr is the header for a frame in a packet_mmap ring buffer from +// . +// +// +marshal +type TpacketHdr struct { + TpStatus uint64 + TpLen uint32 + TpSnaplen uint32 + TpMac uint16 + TpNet uint16 + TpSec uint32 + TpUsec uint32 `marshal:"unaligned"` +} + +// TpacketAlignment is the alignment of a frame in a packet_mmap ring buffer +// from . +const ( + TPACKET_ALIGNMENT = 16 +) + +// TPACKET_V1 is the version of a packet_mmap ring buffer from +// that is implemented in gVisor. +const ( + TPACKET_V1 = iota +) + +// TPACKET_HDRLEN is the length of a TpacketHdr from . +var ( + TPACKET_HDRLEN = uint32((*TpacketHdr)(nil).SizeBytes()) + uint32((*SockAddrLink)(nil).SizeBytes()) +) + +// TPacketAlign aligns a value to the alignment of a TPacket. +func TPacketAlign(x uint32) uint32 { + return (x + TPACKET_ALIGNMENT - 1) &^ (TPACKET_ALIGNMENT - 1) +} + // Socket options from socket.h. const ( SO_DEBUG = 1 diff --git a/pkg/sentry/socket/netstack/BUILD b/pkg/sentry/socket/netstack/BUILD index f801bb501e..3dcf8cc147 100644 --- a/pkg/sentry/socket/netstack/BUILD +++ b/pkg/sentry/socket/netstack/BUILD @@ -41,6 +41,7 @@ go_library( "//pkg/sentry/kernel", "//pkg/sentry/kernel/auth", "//pkg/sentry/ktime", + "//pkg/sentry/memmap", "//pkg/sentry/socket", "//pkg/sentry/socket/netfilter", "//pkg/sentry/socket/netlink/nlmsg", diff --git a/pkg/sentry/socket/netstack/netstack.go b/pkg/sentry/socket/netstack/netstack.go index 808fe1d2da..2f0709266e 100644 --- a/pkg/sentry/socket/netstack/netstack.go +++ b/pkg/sentry/socket/netstack/netstack.go @@ -51,6 +51,7 @@ import ( "gvisor.dev/gvisor/pkg/sentry/kernel" "gvisor.dev/gvisor/pkg/sentry/kernel/auth" "gvisor.dev/gvisor/pkg/sentry/ktime" + "gvisor.dev/gvisor/pkg/sentry/memmap" "gvisor.dev/gvisor/pkg/sentry/socket" "gvisor.dev/gvisor/pkg/sentry/socket/netfilter" epb "gvisor.dev/gvisor/pkg/sentry/socket/netstack/events_go_proto" @@ -318,6 +319,28 @@ const sizeOfInt32 int = 4 var errStackType = syserr.New("expected but did not receive a netstack.Stack", errno.EINVAL) +// mappableEndpoint is the interface implemented by endpoints that support +// mmap. +type mappableEndpoint interface { + // ConfigureMMap implements vfs.FileDescriptionImpl.ConfigureMMap. + ConfigureMMap(ctx context.Context, opts *memmap.MMapOpts) error + + // AddMapping implements memmap.Mappable.AddMapping. + AddMapping(context.Context, memmap.MappingSpace, hostarch.AddrRange, uint64, bool) error + + // RemoveMapping implements memmap.Mappable.RemoveMapping. + RemoveMapping(context.Context, memmap.MappingSpace, hostarch.AddrRange, uint64, bool) + + // CopyMapping implements memmap.Mappable.CopyMapping. + CopyMapping(context.Context, memmap.MappingSpace, hostarch.AddrRange, hostarch.AddrRange, uint64, bool) error + + // Translate implements memmap.Mappable.Translate. + Translate(context.Context, memmap.MappableRange, memmap.MappableRange, hostarch.AccessType) ([]memmap.Translation, error) + + // InvalidateUnsavable implements memmap.Mappable.InvalidateUnsavable. + InvalidateUnsavable(context.Context) error +} + // commonEndpoint represents the intersection of a tcpip.Endpoint and a // transport.Endpoint. type commonEndpoint interface { diff --git a/pkg/syserr/netstack.go b/pkg/syserr/netstack.go index 3928106da0..0cbcdd5957 100644 --- a/pkg/syserr/netstack.go +++ b/pkg/syserr/netstack.go @@ -55,6 +55,7 @@ var ( ErrMulticastInputCannotBeOutput = New((&tcpip.ErrMulticastInputCannotBeOutput{}).String(), errno.EINVAL) ErrMissingRequiredFields = New((&tcpip.ErrMissingRequiredFields{}).String(), errno.EINVAL) ErrNoNet = New((&tcpip.ErrNoNet{}).String(), errno.ENONET) + ErrEndpointBusy = New((&tcpip.ErrEndpointBusy{}).String(), errno.EBUSY) ) // TranslateNetstackError converts an error from the tcpip package to a sentry @@ -149,6 +150,8 @@ func TranslateNetstackError(err tcpip.Error) *Error { return ErrMulticastInputCannotBeOutput case *tcpip.ErrMissingRequiredFields: return ErrMissingRequiredFields + case *tcpip.ErrEndpointBusy: + return ErrEndpointBusy default: panic(fmt.Sprintf("unknown error %T", err)) } diff --git a/pkg/tcpip/errors.go b/pkg/tcpip/errors.go index 0df3d8857d..f6ed361766 100644 --- a/pkg/tcpip/errors.go +++ b/pkg/tcpip/errors.go @@ -620,4 +620,22 @@ func (*ErrMulticastInputCannotBeOutput) IgnoreStats() bool { } func (*ErrMulticastInputCannotBeOutput) String() string { return "output cannot contain input" } +// ErrEndpointBusy indicates that the operation cannot be completed because the +// endpoint is busy. +// +// +stateify savable +type ErrEndpointBusy struct{} + +// isError implements Error. +func (*ErrEndpointBusy) isError() {} + +// IgnoreStats implements Error. +func (*ErrEndpointBusy) IgnoreStats() bool { + return true +} + +func (*ErrEndpointBusy) String() string { + return "operation cannot be completed because the endpoint is busy" +} + // LINT.ThenChange(../syserr/netstack.go) diff --git a/pkg/tcpip/stack/registration.go b/pkg/tcpip/stack/registration.go index ee5952f9b9..a6583e91d8 100644 --- a/pkg/tcpip/stack/registration.go +++ b/pkg/tcpip/stack/registration.go @@ -171,6 +171,58 @@ type PacketEndpoint interface { HandlePacket(nicID tcpip.NICID, netProto tcpip.NetworkProtocolNumber, pkt *PacketBuffer) } +// MappablePacketEndpoint is a packet endpoint that supports forwarding its +// packets to a PacketMMapEndpoint. +type MappablePacketEndpoint interface { + PacketEndpoint + + // ConfigurePacketMMap configures the endpoint to use a memory mapped endpoint + // handler ep. All packets sent or received via this endpoint will be handled + // by the PacketMMapEndpoint. + ConfigurePacketMMap(ep PacketMMapEndpoint) +} + +// PacketMMapCopyHandler is a function that is called when a packet received is +// too large for the buffer size specified for the memory mapped endpoint. In +// this case, the packet is copied and passed to the PacketMMapCopyHandler. +type PacketMMapCopyHandler func(nicID tcpip.NICID, netProto tcpip.NetworkProtocolNumber, pkt *PacketBuffer) + +// InitPacketMMapOpts are the options for initializing a PacketMMapEndpoint. +type InitPacketMMapOpts struct { + RxReq, TxReq *tcpip.TpacketReq + Cooked bool + Stack *Stack + Stats *tcpip.TransportEndpointStats + Wq *waiter.Queue + CopyHandler PacketMMapCopyHandler +} + +// PacketMMapEndpoint is the interface implemented by endpoints to handle memory +// mapped packets over the packet transport protocol (PACKET_MMAP). +type PacketMMapEndpoint interface { + // HandlePacket is called by the stack when new packets arrive that + // match the endpoint. + // + // Implementers should treat packet as immutable and should copy it + // before before modification. + // + // linkHeader may have a length of 0, in which case the PacketEndpoint + // should construct its own ethernet header for applications. + // + // HandlePacket may modify pkt. + HandlePacket(nicID tcpip.NICID, netProto tcpip.NetworkProtocolNumber, pkt *PacketBuffer) + + // Close releases any resources associated with the endpoint. + Close() + + // Readiness returns the events that the endpoint is ready for. + Readiness(mask waiter.EventMask) waiter.EventMask + + // InitPacketMMap initializes the endpoint. It must be called before any other + // methods. + InitPacketMMap(opts InitPacketMMapOpts) tcpip.Error +} + // UnknownDestinationPacketDisposition enumerates the possible return values from // HandleUnknownDestinationPacket(). type UnknownDestinationPacketDisposition int diff --git a/pkg/tcpip/tcpip.go b/pkg/tcpip/tcpip.go index cea7530c2a..970fe21d25 100644 --- a/pkg/tcpip/tcpip.go +++ b/pkg/tcpip/tcpip.go @@ -1185,6 +1185,19 @@ func (*ICMPv6Filter) isGettableSocketOption() {} func (*ICMPv6Filter) isSettableSocketOption() {} +// TpacketReq is the tpacket_req structure as described in +// https://www.kernel.org/doc/Documentation/networking/packet_mmap.txt +// +// +stateify savable +type TpacketReq struct { + TpBlockSize uint32 + TpBlockNr uint32 + TpFrameSize uint32 + TpFrameNr uint32 +} + +func (*TpacketReq) isSettableSocketOption() {} + // EndpointState represents the state of an endpoint. type EndpointState uint8