diff --git a/pkg/eventfd/BUILD b/pkg/eventfd/BUILD index f0bed8e49..2ff858d0a 100644 --- a/pkg/eventfd/BUILD +++ b/pkg/eventfd/BUILD @@ -8,7 +8,10 @@ go_library( "eventfd.go", "eventfd_unsafe.go", ], - visibility = ["//:sandbox"], + visibility = [ + "//:sandbox", + "//cloud/cluster/node/network/client/go:__pkg__", + ], deps = [ "//pkg/hostarch", "//pkg/tcpip/link/rawfile", diff --git a/pkg/tcpip/header/BUILD b/pkg/tcpip/header/BUILD index 01240f5d0..5b7c07aa7 100644 --- a/pkg/tcpip/header/BUILD +++ b/pkg/tcpip/header/BUILD @@ -26,6 +26,7 @@ go_library( "ndpoptionidentifier_string.go", "tcp.go", "udp.go", + "virtionet.go", ], visibility = ["//visibility:public"], deps = [ diff --git a/pkg/tcpip/header/virtionet.go b/pkg/tcpip/header/virtionet.go new file mode 100644 index 000000000..e6b0c71bb --- /dev/null +++ b/pkg/tcpip/header/virtionet.go @@ -0,0 +1,94 @@ +// Copyright 2021 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package header + +import "encoding/binary" + +// These constants are declared in linux/virtio_net.h. +const ( + _VIRTIO_NET_HDR_F_NEEDS_CSUM = 1 + _VIRTIO_NET_HDR_GSO_NONE = 0 + _VIRTIO_NET_HDR_GSO_TCPV4 = 1 + _VIRTIO_NET_HDR_GSO_TCPV6 = 4 +) + +const ( + // VirtioNetHeaderSize is the size of VirtioNetHeader in bytes. + VirtioNetHeaderSize = 10 +) + +// Offsets for fields in the virtio net header. +const ( + flags = 0 + gsoType = 1 + hdrLen = 2 + gsoSize = 4 + csumStart = 6 + csumOffset = 8 +) + +// VirtioNetHeaderFields is the Go equivalent of the struct declared in +// linux/virtio_net.h. +type VirtioNetHeaderFields struct { + Flags uint8 + GSOType uint8 + HdrLen uint16 + GSOSize uint16 + CSumStart uint16 + CSumOffset uint16 +} + +// VirtioNetHeader represents a virtio net header stored in a byte array. +type VirtioNetHeader []byte + +// Flags returns the "flags" field of the virtio net header. +func (v VirtioNetHeader) Flags() uint8 { + return uint8(v[flags]) +} + +// GSOType returns the "gsoType" field of the virtio net header. +func (v VirtioNetHeader) GSOType() uint8 { + return uint8(v[gsoType]) +} + +// HdrLen returns the "hdrLen" field of the virtio net header. +func (v VirtioNetHeader) HdrLen() uint16 { + return binary.BigEndian.Uint16(v[hdrLen:]) +} + +// GSOSize returns the "gsoSize" field of the virtio net header. +func (v VirtioNetHeader) GSOSize() uint16 { + return binary.BigEndian.Uint16(v[gsoSize:]) +} + +// CSumStart returns the "csumStart" field of the virtio net header. +func (v VirtioNetHeader) CSumStart() uint16 { + return binary.BigEndian.Uint16(v[csumStart:]) +} + +// CSumOffset returns the "csumOffset" field of the virtio net header. +func (v VirtioNetHeader) CSumOffset() uint16 { + return binary.BigEndian.Uint16(v[csumOffset:]) +} + +// Encode encodes all the fields of the virtio net header. +func (v VirtioNetHeader) Encode(f *VirtioNetHeaderFields) { + v[flags] = uint8(f.Flags) + v[gsoType] = uint8(f.GSOType) + binary.BigEndian.PutUint16(v[hdrLen:], f.HdrLen) + binary.BigEndian.PutUint16(v[gsoSize:], f.GSOSize) + binary.BigEndian.PutUint16(v[csumStart:], f.CSumStart) + binary.BigEndian.PutUint16(v[csumOffset:], f.CSumOffset) +} diff --git a/pkg/tcpip/link/sharedmem/BUILD b/pkg/tcpip/link/sharedmem/BUILD index af755473c..fe3ba2ed8 100644 --- a/pkg/tcpip/link/sharedmem/BUILD +++ b/pkg/tcpip/link/sharedmem/BUILD @@ -14,7 +14,9 @@ go_library( "sharedmem_unsafe.go", "tx.go", ], - visibility = ["//visibility:public"], + visibility = [ + "//visibility:public", + ], deps = [ "//pkg/cleanup", "//pkg/eventfd", diff --git a/pkg/tcpip/link/sharedmem/sharedmem.go b/pkg/tcpip/link/sharedmem/sharedmem.go index ed9c02e6d..c4f347cde 100644 --- a/pkg/tcpip/link/sharedmem/sharedmem.go +++ b/pkg/tcpip/link/sharedmem/sharedmem.go @@ -129,6 +129,10 @@ type Options struct { // RXChecksumOffload if true, indicates that this endpoints capability // set should include CapabilityRXChecksumOffload. RXChecksumOffload bool + + // VirtioNetHeaderRequired if true, indicates that all outbound packets should have + // a virtio header and inbound packets should have a virtio header as well. + VirtioNetHeaderRequired bool } type endpoint struct { @@ -156,6 +160,10 @@ type endpoint struct { // hdrSize is immutable. hdrSize uint32 + // virtioNetHeaderRequired if true indicates that a virtio header is expected + // in all inbound/outbound packets. + virtioNetHeaderRequired bool + // rx is the receive queue. rx rx @@ -186,11 +194,12 @@ type endpoint struct { // into buffers of "bufferSize" bytes. func New(opts Options) (stack.LinkEndpoint, error) { e := &endpoint{ - mtu: opts.MTU, - bufferSize: opts.BufferSize, - addr: opts.LinkAddress, - peerFD: opts.PeerFD, - onClosed: opts.OnClosed, + mtu: opts.MTU, + bufferSize: opts.BufferSize, + addr: opts.LinkAddress, + peerFD: opts.PeerFD, + onClosed: opts.OnClosed, + virtioNetHeaderRequired: opts.VirtioNetHeaderRequired, } if err := e.tx.init(opts.BufferSize, &opts.TX); err != nil { @@ -215,6 +224,11 @@ func New(opts Options) (stack.LinkEndpoint, error) { e.hdrSize = header.EthernetMinimumSize e.caps |= stack.CapabilityResolutionRequired } + + if opts.VirtioNetHeaderRequired { + e.hdrSize += header.VirtioNetHeaderSize + } + return e, nil } @@ -322,6 +336,11 @@ func (e *endpoint) AddHeader(local, remote tcpip.LinkAddress, protocol tcpip.Net eth.Encode(ethHdr) } +func (e *endpoint) AddVirtioNetHeader(pkt *stack.PacketBuffer) { + virtio := header.VirtioNetHeader(pkt.VirtioNetHeader().Push(header.VirtioNetHeaderSize)) + virtio.Encode(&header.VirtioNetHeaderFields{}) +} + // WriteRawPacket implements stack.LinkEndpoint. func (*endpoint) WriteRawPacket(*stack.PacketBuffer) tcpip.Error { return &tcpip.ErrNotSupported{} } @@ -330,6 +349,9 @@ func (e *endpoint) writePacketLocked(r stack.RouteInfo, protocol tcpip.NetworkPr if e.addr != "" { e.AddHeader(r.LocalLinkAddress, r.RemoteLinkAddress, protocol, pkt) } + if e.virtioNetHeaderRequired { + e.AddVirtioNetHeader(pkt) + } views := pkt.Views() // Transmit the packet. @@ -414,6 +436,14 @@ func (e *endpoint) dispatchLoop(d stack.NetworkDispatcher) { Data: buffer.View(b).ToVectorisedView(), }) + if e.virtioNetHeaderRequired { + _, ok := pkt.VirtioNetHeader().Consume(header.VirtioNetHeaderSize) + if !ok { + pkt.DecRef() + continue + } + } + var src, dst tcpip.LinkAddress var proto tcpip.NetworkProtocolNumber if e.addr != "" { diff --git a/pkg/tcpip/link/sharedmem/sharedmem_server.go b/pkg/tcpip/link/sharedmem/sharedmem_server.go index 00c8a6a3b..fb0b7b8d7 100644 --- a/pkg/tcpip/link/sharedmem/sharedmem_server.go +++ b/pkg/tcpip/link/sharedmem/sharedmem_server.go @@ -63,6 +63,10 @@ type serverEndpoint struct { // hdrSize is immutable. hdrSize uint32 + // virtioNetHeaderRequired if true indicates that a virtio header is expected + // in all inbound/outbound packets. + virtioNetHeaderRequired bool + // onClosed is a function to be called when the FD's peer (if any) closes its // end of the communication pipe. onClosed func(tcpip.Error) @@ -218,6 +222,11 @@ func (e *serverEndpoint) AddHeader(local, remote tcpip.LinkAddress, protocol tcp eth.Encode(ethHdr) } +func (e *serverEndpoint) AddVirtioNetHeader(pkt *stack.PacketBuffer) { + virtio := header.VirtioNetHeader(pkt.VirtioNetHeader().Push(header.VirtioNetHeaderSize)) + virtio.Encode(&header.VirtioNetHeaderFields{}) +} + // WriteRawPacket implements stack.LinkEndpoint.WriteRawPacket func (e *serverEndpoint) WriteRawPacket(pkt *stack.PacketBuffer) tcpip.Error { views := pkt.Views() @@ -237,6 +246,10 @@ func (e *serverEndpoint) writePacketLocked(r stack.RouteInfo, protocol tcpip.Net e.AddHeader(r.LocalLinkAddress, r.RemoteLinkAddress, protocol, pkt) } + if e.virtioNetHeaderRequired { + e.AddVirtioNetHeader(pkt) + } + views := pkt.Views() ok := e.tx.transmit(views) if !ok { @@ -306,6 +319,13 @@ func (e *serverEndpoint) dispatchLoop(d stack.NetworkDispatcher) { pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{ Data: buffer.View(b).ToVectorisedView(), }) + if e.virtioNetHeaderRequired { + _, ok := pkt.VirtioNetHeader().Consume(header.VirtioNetHeaderSize) + if !ok { + pkt.DecRef() + continue + } + } var src, dst tcpip.LinkAddress var proto tcpip.NetworkProtocolNumber if e.addr != "" { diff --git a/pkg/tcpip/link/sniffer/sniffer.go b/pkg/tcpip/link/sniffer/sniffer.go index 965cc994f..759ce8802 100644 --- a/pkg/tcpip/link/sniffer/sniffer.go +++ b/pkg/tcpip/link/sniffer/sniffer.go @@ -207,6 +207,7 @@ func logPacket(prefix string, dir direction, protocol tcpip.NetworkProtocolNumbe // We trim the link headers from the cloned buffer as the sniffer doesn't // handle link headers. vv := buffer.NewVectorisedView(pkt.Size(), pkt.Views()) + vv.TrimFront(len(pkt.VirtioNetHeader().View())) vv.TrimFront(len(pkt.LinkHeader().View())) pkt = stack.NewPacketBuffer(stack.PacketBufferOptions{Data: vv}) defer pkt.DecRef() diff --git a/pkg/tcpip/stack/headertype_string.go b/pkg/tcpip/stack/headertype_string.go index 5efddfaaf..cd80de0cd 100644 --- a/pkg/tcpip/stack/headertype_string.go +++ b/pkg/tcpip/stack/headertype_string.go @@ -21,13 +21,14 @@ func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} - _ = x[linkHeader-0] - _ = x[networkHeader-1] - _ = x[transportHeader-2] - _ = x[numHeaderType-3] + _ = x[virtioNetHeader-0] + _ = x[linkHeader-1] + _ = x[networkHeader-2] + _ = x[transportHeader-3] + _ = x[numHeaderType-4] } -const _headerType_name = "linkHeadernetworkHeadertransportHeadernumHeaderType" +const _headerType_name = "virtioNetHeaderlinkHeadernetworkHeadertransportHeadernumHeaderType" var _headerType_index = [...]uint8{0, 10, 23, 38, 51} diff --git a/pkg/tcpip/stack/packet_buffer.go b/pkg/tcpip/stack/packet_buffer.go index 9b391891f..2239ba5d5 100644 --- a/pkg/tcpip/stack/packet_buffer.go +++ b/pkg/tcpip/stack/packet_buffer.go @@ -27,7 +27,8 @@ import ( type headerType int const ( - linkHeader headerType = iota + virtioNetHeader headerType = iota + linkHeader networkHeader transportHeader numHeaderType @@ -216,6 +217,14 @@ func (pk *PacketBuffer) AvailableHeaderBytes() int { return pk.reserved - pk.pushed } +// VirtioNetHeader returns the handle to virtio-layer header. +func (pk *PacketBuffer) VirtioNetHeader() PacketHeader { + return PacketHeader{ + pk: pk, + typ: virtioNetHeader, + } +} + // LinkHeader returns the handle to link-layer header. func (pk *PacketBuffer) LinkHeader() PacketHeader { return PacketHeader{ diff --git a/pkg/tcpip/transport/icmp/BUILD b/pkg/tcpip/transport/icmp/BUILD index 4718ec4ec..56a917355 100644 --- a/pkg/tcpip/transport/icmp/BUILD +++ b/pkg/tcpip/transport/icmp/BUILD @@ -26,6 +26,7 @@ go_library( imports = ["gvisor.dev/gvisor/pkg/tcpip/buffer"], visibility = ["//visibility:public"], deps = [ + "//pkg/log", "//pkg/sleep", "//pkg/sync", "//pkg/tcpip", diff --git a/pkg/tcpip/transport/icmp/endpoint.go b/pkg/tcpip/transport/icmp/endpoint.go index 249588241..acdc56b32 100644 --- a/pkg/tcpip/transport/icmp/endpoint.go +++ b/pkg/tcpip/transport/icmp/endpoint.go @@ -19,6 +19,7 @@ import ( "io" "time" + "gvisor.dev/gvisor/pkg/log" "gvisor.dev/gvisor/pkg/sync" "gvisor.dev/gvisor/pkg/tcpip" "gvisor.dev/gvisor/pkg/tcpip/buffer" @@ -348,6 +349,7 @@ func (e *endpoint) GetSockOpt(opt tcpip.GettableSocketOption) tcpip.Error { func send4(s *stack.Stack, ctx *network.WriteContext, ident uint16, data buffer.View, maxHeaderLength uint16) tcpip.Error { if len(data) < header.ICMPv4MinimumSize { + log.Infof("len(data) is smaller than min size") return &tcpip.ErrInvalidEndpointState{} }