2019-04-29 21:25:05 +00:00
|
|
|
// Copyright 2018 The gVisor Authors.
|
2018-07-09 21:03:03 +00:00
|
|
|
//
|
|
|
|
// 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.
|
2018-04-27 17:37:02 +00:00
|
|
|
|
2018-08-22 20:29:57 +00:00
|
|
|
// +build linux
|
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
// Package fdbased provides the implemention of data-link layer endpoints
|
|
|
|
// backed by boundary-preserving file descriptors (e.g., TUN devices,
|
|
|
|
// seqpacket/datagram sockets).
|
|
|
|
//
|
|
|
|
// FD based endpoints can be used in the networking stack by calling New() to
|
|
|
|
// create a new endpoint, and then passing it as an argument to
|
|
|
|
// Stack.CreateNIC().
|
2019-06-06 15:05:46 +00:00
|
|
|
//
|
|
|
|
// FD based endpoints can use more than one file descriptor to read incoming
|
|
|
|
// packets. If there are more than one FDs specified and the underlying FD is an
|
|
|
|
// AF_PACKET then the endpoint will enable FANOUT mode on the socket so that the
|
|
|
|
// host kernel will consistently hash the packets to the sockets. This ensures
|
|
|
|
// that packets for the same TCP streams are not reordered.
|
|
|
|
//
|
|
|
|
// Similarly if more than one FD's are specified where the underlying FD is not
|
|
|
|
// AF_PACKET then it's the caller's responsibility to ensure that all inbound
|
|
|
|
// packets on the descriptors are consistently 5 tuple hashed to one of the
|
|
|
|
// descriptors to prevent TCP reordering.
|
|
|
|
//
|
|
|
|
// Since netstack today does not compute 5 tuple hashes for outgoing packets we
|
|
|
|
// only use the first FD to write outbound packets. Once 5 tuple hashes for
|
|
|
|
// all outbound packets are available we will make use of all underlying FD's to
|
|
|
|
// write outbound packets.
|
2018-04-27 17:37:02 +00:00
|
|
|
package fdbased
|
|
|
|
|
|
|
|
import (
|
2019-01-29 09:37:54 +00:00
|
|
|
"fmt"
|
2018-04-27 17:37:02 +00:00
|
|
|
"syscall"
|
|
|
|
|
2019-06-06 15:05:46 +00:00
|
|
|
"golang.org/x/sys/unix"
|
2019-06-13 23:49:09 +00:00
|
|
|
"gvisor.dev/gvisor/pkg/tcpip"
|
|
|
|
"gvisor.dev/gvisor/pkg/tcpip/buffer"
|
|
|
|
"gvisor.dev/gvisor/pkg/tcpip/header"
|
|
|
|
"gvisor.dev/gvisor/pkg/tcpip/link/rawfile"
|
|
|
|
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
2018-04-27 17:37:02 +00:00
|
|
|
)
|
|
|
|
|
2019-01-29 09:37:54 +00:00
|
|
|
// linkDispatcher reads packets from the link FD and dispatches them to the
|
|
|
|
// NetworkDispatcher.
|
2019-05-21 22:23:12 +00:00
|
|
|
type linkDispatcher interface {
|
|
|
|
dispatch() (bool, *tcpip.Error)
|
|
|
|
}
|
2019-01-29 09:37:54 +00:00
|
|
|
|
2019-02-13 22:52:06 +00:00
|
|
|
// PacketDispatchMode are the various supported methods of receiving and
|
|
|
|
// dispatching packets from the underlying FD.
|
|
|
|
type PacketDispatchMode int
|
|
|
|
|
|
|
|
const (
|
|
|
|
// Readv is the default dispatch mode and is the least performant of the
|
|
|
|
// dispatch options but the one that is supported by all underlying FD
|
|
|
|
// types.
|
|
|
|
Readv PacketDispatchMode = iota
|
|
|
|
// RecvMMsg enables use of recvmmsg() syscall instead of readv() to
|
|
|
|
// read inbound packets. This reduces # of syscalls needed to process
|
|
|
|
// packets.
|
|
|
|
//
|
|
|
|
// NOTE: recvmmsg() is only supported for sockets, so if the underlying
|
|
|
|
// FD is not a socket then the code will still fall back to the readv()
|
|
|
|
// path.
|
|
|
|
RecvMMsg
|
|
|
|
// PacketMMap enables use of PACKET_RX_RING to receive packets from the
|
|
|
|
// NIC. PacketMMap requires that the underlying FD be an AF_PACKET. The
|
|
|
|
// primary use-case for this is runsc which uses an AF_PACKET FD to
|
|
|
|
// receive packets from the veth device.
|
|
|
|
PacketMMap
|
|
|
|
)
|
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
type endpoint struct {
|
2019-06-06 15:05:46 +00:00
|
|
|
// fds is the set of file descriptors each identifying one inbound/outbound
|
|
|
|
// channel. The endpoint will dispatch from all inbound channels as well as
|
|
|
|
// hash outbound packets to specific channels based on the packet hash.
|
|
|
|
fds []int
|
2018-04-27 17:37:02 +00:00
|
|
|
|
|
|
|
// mtu (maximum transmission unit) is the maximum size of a packet.
|
|
|
|
mtu uint32
|
|
|
|
|
|
|
|
// hdrSize specifies the link-layer header size. If set to 0, no header
|
|
|
|
// is added/removed; otherwise an ethernet header is used.
|
|
|
|
hdrSize int
|
|
|
|
|
|
|
|
// addr is the address of the endpoint.
|
|
|
|
addr tcpip.LinkAddress
|
|
|
|
|
|
|
|
// caps holds the endpoint capabilities.
|
|
|
|
caps stack.LinkEndpointCapabilities
|
|
|
|
|
|
|
|
// closed is a function to be called when the FD's peer (if any) closes
|
|
|
|
// its end of the communication pipe.
|
|
|
|
closed func(*tcpip.Error)
|
|
|
|
|
2019-06-06 15:05:46 +00:00
|
|
|
inboundDispatchers []linkDispatcher
|
|
|
|
dispatcher stack.NetworkDispatcher
|
2018-08-09 05:02:09 +00:00
|
|
|
|
2019-02-13 22:52:06 +00:00
|
|
|
// packetDispatchMode controls the packet dispatcher used by this
|
|
|
|
// endpoint.
|
|
|
|
packetDispatchMode PacketDispatchMode
|
|
|
|
|
2019-03-28 18:02:23 +00:00
|
|
|
// gsoMaxSize is the maximum GSO packet size. It is zero if GSO is
|
|
|
|
// disabled.
|
|
|
|
gsoMaxSize uint32
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Options specify the details about the fd-based endpoint to be created.
|
|
|
|
type Options struct {
|
2019-06-06 15:05:46 +00:00
|
|
|
// FDs is a set of FDs used to read/write packets.
|
|
|
|
FDs []int
|
|
|
|
|
|
|
|
// MTU is the mtu to use for this endpoint.
|
|
|
|
MTU uint32
|
|
|
|
|
|
|
|
// EthernetHeader if true, indicates that the endpoint should read/write
|
|
|
|
// ethernet frames instead of IP packets.
|
|
|
|
EthernetHeader bool
|
|
|
|
|
|
|
|
// ClosedFunc is a function to be called when an endpoint's peer (if
|
|
|
|
// any) closes its end of the communication pipe.
|
|
|
|
ClosedFunc func(*tcpip.Error)
|
|
|
|
|
|
|
|
// Address is the link address for this endpoint. Only used if
|
|
|
|
// EthernetHeader is true.
|
|
|
|
Address tcpip.LinkAddress
|
|
|
|
|
|
|
|
// SaveRestore if true, indicates that this NIC capability set should
|
|
|
|
// include CapabilitySaveRestore
|
|
|
|
SaveRestore bool
|
|
|
|
|
|
|
|
// DisconnectOk if true, indicates that this NIC capability set should
|
|
|
|
// include CapabilityDisconnectOk.
|
|
|
|
DisconnectOk bool
|
|
|
|
|
|
|
|
// GSOMaxSize is the maximum GSO packet size. It is zero if GSO is
|
|
|
|
// disabled.
|
|
|
|
GSOMaxSize uint32
|
|
|
|
|
|
|
|
// PacketDispatchMode specifies the type of inbound dispatcher to be
|
|
|
|
// used for this endpoint.
|
2019-02-13 22:52:06 +00:00
|
|
|
PacketDispatchMode PacketDispatchMode
|
2019-06-06 15:05:46 +00:00
|
|
|
|
|
|
|
// TXChecksumOffload if true, indicates that this endpoints capability
|
|
|
|
// set should include CapabilityTXChecksumOffload.
|
|
|
|
TXChecksumOffload bool
|
|
|
|
|
|
|
|
// RXChecksumOffload if true, indicates that this endpoints capability
|
|
|
|
// set should include CapabilityRXChecksumOffload.
|
|
|
|
RXChecksumOffload bool
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// New creates a new fd-based endpoint.
|
|
|
|
//
|
|
|
|
// Makes fd non-blocking, but does not take ownership of fd, which must remain
|
|
|
|
// open for the lifetime of the returned endpoint.
|
2019-04-17 18:14:24 +00:00
|
|
|
func New(opts *Options) (tcpip.LinkEndpointID, error) {
|
2018-04-27 17:37:02 +00:00
|
|
|
caps := stack.LinkEndpointCapabilities(0)
|
2019-04-09 18:22:28 +00:00
|
|
|
if opts.RXChecksumOffload {
|
|
|
|
caps |= stack.CapabilityRXChecksumOffload
|
|
|
|
}
|
|
|
|
|
|
|
|
if opts.TXChecksumOffload {
|
|
|
|
caps |= stack.CapabilityTXChecksumOffload
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
hdrSize := 0
|
|
|
|
if opts.EthernetHeader {
|
|
|
|
hdrSize = header.EthernetMinimumSize
|
|
|
|
caps |= stack.CapabilityResolutionRequired
|
|
|
|
}
|
|
|
|
|
2018-07-30 22:42:01 +00:00
|
|
|
if opts.SaveRestore {
|
|
|
|
caps |= stack.CapabilitySaveRestore
|
|
|
|
}
|
|
|
|
|
|
|
|
if opts.DisconnectOk {
|
|
|
|
caps |= stack.CapabilityDisconnectOk
|
|
|
|
}
|
|
|
|
|
2019-06-06 15:05:46 +00:00
|
|
|
if len(opts.FDs) == 0 {
|
|
|
|
return 0, fmt.Errorf("opts.FD is empty, at least one FD must be specified")
|
|
|
|
}
|
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
e := &endpoint{
|
2019-06-06 15:05:46 +00:00
|
|
|
fds: opts.FDs,
|
2019-02-13 22:52:06 +00:00
|
|
|
mtu: opts.MTU,
|
|
|
|
caps: caps,
|
|
|
|
closed: opts.ClosedFunc,
|
|
|
|
addr: opts.Address,
|
|
|
|
hdrSize: hdrSize,
|
|
|
|
packetDispatchMode: opts.PacketDispatchMode,
|
|
|
|
}
|
|
|
|
|
2019-06-06 15:05:46 +00:00
|
|
|
// Create per channel dispatchers.
|
|
|
|
for i := 0; i < len(e.fds); i++ {
|
|
|
|
fd := e.fds[i]
|
|
|
|
if err := syscall.SetNonblock(fd, true); err != nil {
|
|
|
|
return 0, fmt.Errorf("syscall.SetNonblock(%v) failed: %v", fd, err)
|
2019-02-13 22:52:06 +00:00
|
|
|
}
|
2019-06-06 15:05:46 +00:00
|
|
|
|
|
|
|
isSocket, err := isSocketFD(fd)
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
if isSocket {
|
|
|
|
if opts.GSOMaxSize != 0 {
|
|
|
|
e.caps |= stack.CapabilityGSO
|
|
|
|
e.gsoMaxSize = opts.GSOMaxSize
|
|
|
|
}
|
|
|
|
}
|
|
|
|
inboundDispatcher, err := createInboundDispatcher(e, fd, isSocket)
|
|
|
|
if err != nil {
|
|
|
|
return 0, fmt.Errorf("createInboundDispatcher(...) = %v", err)
|
|
|
|
}
|
|
|
|
e.inboundDispatchers = append(e.inboundDispatchers, inboundDispatcher)
|
2019-05-21 22:23:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return stack.RegisterLinkEndpoint(e), nil
|
|
|
|
}
|
2019-02-13 22:52:06 +00:00
|
|
|
|
2019-06-06 15:05:46 +00:00
|
|
|
func createInboundDispatcher(e *endpoint, fd int, isSocket bool) (linkDispatcher, error) {
|
2019-05-21 22:23:12 +00:00
|
|
|
// By default use the readv() dispatcher as it works with all kinds of
|
|
|
|
// FDs (tap/tun/unix domain sockets and af_packet).
|
2019-06-06 15:05:46 +00:00
|
|
|
inboundDispatcher, err := newReadVDispatcher(fd, e)
|
2019-05-21 22:23:12 +00:00
|
|
|
if err != nil {
|
2019-06-06 15:05:46 +00:00
|
|
|
return nil, fmt.Errorf("newReadVDispatcher(%d, %+v) = %v", fd, e, err)
|
2019-05-21 22:23:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if isSocket {
|
2019-06-06 15:05:46 +00:00
|
|
|
sa, err := unix.Getsockname(fd)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("unix.Getsockname(%d) = %v", fd, err)
|
|
|
|
}
|
|
|
|
switch sa.(type) {
|
|
|
|
case *unix.SockaddrLinklayer:
|
|
|
|
// enable PACKET_FANOUT mode is the underlying socket is
|
|
|
|
// of type AF_PACKET.
|
|
|
|
const fanoutID = 1
|
|
|
|
const fanoutType = 0x8000 // PACKET_FANOUT_HASH | PACKET_FANOUT_FLAG_DEFRAG
|
|
|
|
fanoutArg := fanoutID | fanoutType<<16
|
|
|
|
if err := syscall.SetsockoptInt(fd, syscall.SOL_PACKET, unix.PACKET_FANOUT, fanoutArg); err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to enable PACKET_FANOUT option: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-17 18:14:24 +00:00
|
|
|
switch e.packetDispatchMode {
|
|
|
|
case PacketMMap:
|
2019-06-06 15:05:46 +00:00
|
|
|
inboundDispatcher, err = newPacketMMapDispatcher(fd, e)
|
2019-05-21 22:23:12 +00:00
|
|
|
if err != nil {
|
2019-06-06 15:05:46 +00:00
|
|
|
return nil, fmt.Errorf("newPacketMMapDispatcher(%d, %+v) = %v", fd, e, err)
|
2019-04-17 18:14:24 +00:00
|
|
|
}
|
|
|
|
case RecvMMsg:
|
2019-04-26 19:50:38 +00:00
|
|
|
// If the provided FD is a socket then we optimize
|
|
|
|
// packet reads by using recvmmsg() instead of read() to
|
|
|
|
// read packets in a batch.
|
2019-06-06 15:05:46 +00:00
|
|
|
inboundDispatcher, err = newRecvMMsgDispatcher(fd, e)
|
2019-05-21 22:23:12 +00:00
|
|
|
if err != nil {
|
2019-06-06 15:05:46 +00:00
|
|
|
return nil, fmt.Errorf("newRecvMMsgDispatcher(%d, %+v) = %v", fd, e, err)
|
2019-05-21 22:23:12 +00:00
|
|
|
}
|
2019-04-17 18:14:24 +00:00
|
|
|
}
|
2019-01-29 09:37:54 +00:00
|
|
|
}
|
2019-05-21 22:23:12 +00:00
|
|
|
return inboundDispatcher, nil
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
2019-04-17 18:14:24 +00:00
|
|
|
func isSocketFD(fd int) (bool, error) {
|
2019-01-29 09:37:54 +00:00
|
|
|
var stat syscall.Stat_t
|
|
|
|
if err := syscall.Fstat(fd, &stat); err != nil {
|
2019-04-17 18:14:24 +00:00
|
|
|
return false, fmt.Errorf("syscall.Fstat(%v,...) failed: %v", fd, err)
|
2019-01-29 09:37:54 +00:00
|
|
|
}
|
2019-04-17 18:14:24 +00:00
|
|
|
return (stat.Mode & syscall.S_IFSOCK) == syscall.S_IFSOCK, nil
|
2019-01-29 09:37:54 +00:00
|
|
|
}
|
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
// Attach launches the goroutine that reads packets from the file descriptor and
|
|
|
|
// dispatches them via the provided dispatcher.
|
|
|
|
func (e *endpoint) Attach(dispatcher stack.NetworkDispatcher) {
|
2018-08-09 05:02:09 +00:00
|
|
|
e.dispatcher = dispatcher
|
2018-07-17 17:13:57 +00:00
|
|
|
// Link endpoints are not savable. When transportation endpoints are
|
|
|
|
// saved, they stop sending outgoing packets and all incoming packets
|
|
|
|
// are rejected.
|
2019-06-06 15:05:46 +00:00
|
|
|
for i := range e.inboundDispatchers {
|
|
|
|
go e.dispatchLoop(e.inboundDispatchers[i]) // S/R-SAFE: See above.
|
|
|
|
}
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
2018-05-22 20:46:37 +00:00
|
|
|
// IsAttached implements stack.LinkEndpoint.IsAttached.
|
|
|
|
func (e *endpoint) IsAttached() bool {
|
2018-08-09 05:02:09 +00:00
|
|
|
return e.dispatcher != nil
|
2018-05-22 20:46:37 +00:00
|
|
|
}
|
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
// MTU implements stack.LinkEndpoint.MTU. It returns the value initialized
|
|
|
|
// during construction.
|
|
|
|
func (e *endpoint) MTU() uint32 {
|
|
|
|
return e.mtu
|
|
|
|
}
|
|
|
|
|
|
|
|
// Capabilities implements stack.LinkEndpoint.Capabilities.
|
|
|
|
func (e *endpoint) Capabilities() stack.LinkEndpointCapabilities {
|
|
|
|
return e.caps
|
|
|
|
}
|
|
|
|
|
|
|
|
// MaxHeaderLength returns the maximum size of the link-layer header.
|
|
|
|
func (e *endpoint) MaxHeaderLength() uint16 {
|
|
|
|
return uint16(e.hdrSize)
|
|
|
|
}
|
|
|
|
|
|
|
|
// LinkAddress returns the link address of this endpoint.
|
|
|
|
func (e *endpoint) LinkAddress() tcpip.LinkAddress {
|
|
|
|
return e.addr
|
|
|
|
}
|
|
|
|
|
2019-03-28 18:02:23 +00:00
|
|
|
// virtioNetHdr is declared in linux/virtio_net.h.
|
|
|
|
type virtioNetHdr struct {
|
|
|
|
flags uint8
|
|
|
|
gsoType uint8
|
|
|
|
hdrLen uint16
|
|
|
|
gsoSize uint16
|
|
|
|
csumStart uint16
|
|
|
|
csumOffset uint16
|
|
|
|
}
|
|
|
|
|
|
|
|
// These constants are declared in linux/virtio_net.h.
|
|
|
|
const (
|
|
|
|
_VIRTIO_NET_HDR_F_NEEDS_CSUM = 1
|
|
|
|
|
|
|
|
_VIRTIO_NET_HDR_GSO_TCPV4 = 1
|
|
|
|
_VIRTIO_NET_HDR_GSO_TCPV6 = 4
|
|
|
|
)
|
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
// WritePacket writes outbound packets to the file descriptor. If it is not
|
|
|
|
// currently writable, the packet is dropped.
|
2019-03-28 18:02:23 +00:00
|
|
|
func (e *endpoint) WritePacket(r *stack.Route, gso *stack.GSO, hdr buffer.Prependable, payload buffer.VectorisedView, protocol tcpip.NetworkProtocolNumber) *tcpip.Error {
|
2018-04-27 17:37:02 +00:00
|
|
|
if e.hdrSize > 0 {
|
|
|
|
// Add ethernet header if needed.
|
|
|
|
eth := header.Ethernet(hdr.Prepend(header.EthernetMinimumSize))
|
2018-09-19 20:42:55 +00:00
|
|
|
ethHdr := &header.EthernetFields{
|
2018-04-27 17:37:02 +00:00
|
|
|
DstAddr: r.RemoteLinkAddress,
|
|
|
|
Type: protocol,
|
2018-09-19 20:42:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Preserve the src address if it's set in the route.
|
|
|
|
if r.LocalLinkAddress != "" {
|
|
|
|
ethHdr.SrcAddr = r.LocalLinkAddress
|
|
|
|
} else {
|
|
|
|
ethHdr.SrcAddr = e.addr
|
|
|
|
}
|
|
|
|
eth.Encode(ethHdr)
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
2019-03-28 18:02:23 +00:00
|
|
|
if e.Capabilities()&stack.CapabilityGSO != 0 {
|
|
|
|
vnetHdr := virtioNetHdr{}
|
|
|
|
vnetHdrBuf := vnetHdrToByteSlice(&vnetHdr)
|
|
|
|
if gso != nil {
|
|
|
|
vnetHdr.hdrLen = uint16(hdr.UsedLength())
|
|
|
|
if gso.NeedsCsum {
|
|
|
|
vnetHdr.flags = _VIRTIO_NET_HDR_F_NEEDS_CSUM
|
|
|
|
vnetHdr.csumStart = header.EthernetMinimumSize + gso.L3HdrLen
|
|
|
|
vnetHdr.csumOffset = gso.CsumOffset
|
|
|
|
}
|
|
|
|
if gso.Type != stack.GSONone && uint16(payload.Size()) > gso.MSS {
|
|
|
|
switch gso.Type {
|
|
|
|
case stack.GSOTCPv4:
|
|
|
|
vnetHdr.gsoType = _VIRTIO_NET_HDR_GSO_TCPV4
|
|
|
|
case stack.GSOTCPv6:
|
|
|
|
vnetHdr.gsoType = _VIRTIO_NET_HDR_GSO_TCPV6
|
|
|
|
default:
|
|
|
|
panic(fmt.Sprintf("Unknown gso type: %v", gso.Type))
|
|
|
|
}
|
|
|
|
vnetHdr.gsoSize = gso.MSS
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-06 15:05:46 +00:00
|
|
|
return rawfile.NonBlockingWrite3(e.fds[0], vnetHdrBuf, hdr.View(), payload.ToView())
|
2019-03-28 18:02:23 +00:00
|
|
|
}
|
|
|
|
|
2018-09-06 00:33:18 +00:00
|
|
|
if payload.Size() == 0 {
|
2019-06-06 15:05:46 +00:00
|
|
|
return rawfile.NonBlockingWrite(e.fds[0], hdr.View())
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
2019-06-06 15:05:46 +00:00
|
|
|
return rawfile.NonBlockingWrite3(e.fds[0], hdr.View(), payload.ToView(), nil)
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
2019-02-26 22:57:27 +00:00
|
|
|
// WriteRawPacket writes a raw packet directly to the file descriptor.
|
|
|
|
func (e *endpoint) WriteRawPacket(dest tcpip.Address, packet []byte) *tcpip.Error {
|
2019-06-06 15:05:46 +00:00
|
|
|
return rawfile.NonBlockingWrite(e.fds[0], packet)
|
2019-02-26 22:57:27 +00:00
|
|
|
}
|
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
// dispatchLoop reads packets from the file descriptor in a loop and dispatches
|
|
|
|
// them to the network stack.
|
2019-06-06 15:05:46 +00:00
|
|
|
func (e *endpoint) dispatchLoop(inboundDispatcher linkDispatcher) *tcpip.Error {
|
2018-04-27 17:37:02 +00:00
|
|
|
for {
|
2019-06-06 15:05:46 +00:00
|
|
|
cont, err := inboundDispatcher.dispatch()
|
2018-04-27 17:37:02 +00:00
|
|
|
if err != nil || !cont {
|
|
|
|
if e.closed != nil {
|
|
|
|
e.closed(err)
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-28 18:02:23 +00:00
|
|
|
// GSOMaxSize returns the maximum GSO packet size.
|
|
|
|
func (e *endpoint) GSOMaxSize() uint32 {
|
|
|
|
return e.gsoMaxSize
|
|
|
|
}
|
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
// InjectableEndpoint is an injectable fd-based endpoint. The endpoint writes
|
|
|
|
// to the FD, but does not read from it. All reads come from injected packets.
|
|
|
|
type InjectableEndpoint struct {
|
|
|
|
endpoint
|
|
|
|
|
|
|
|
dispatcher stack.NetworkDispatcher
|
|
|
|
}
|
|
|
|
|
|
|
|
// Attach saves the stack network-layer dispatcher for use later when packets
|
|
|
|
// are injected.
|
|
|
|
func (e *InjectableEndpoint) Attach(dispatcher stack.NetworkDispatcher) {
|
|
|
|
e.dispatcher = dispatcher
|
|
|
|
}
|
|
|
|
|
|
|
|
// Inject injects an inbound packet.
|
2018-09-13 04:57:04 +00:00
|
|
|
func (e *InjectableEndpoint) Inject(protocol tcpip.NetworkProtocolNumber, vv buffer.VectorisedView) {
|
2018-11-14 22:45:29 +00:00
|
|
|
e.dispatcher.DeliverNetworkPacket(e, "" /* remote */, "" /* local */, protocol, vv)
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewInjectable creates a new fd-based InjectableEndpoint.
|
2019-04-09 18:22:28 +00:00
|
|
|
func NewInjectable(fd int, mtu uint32, capabilities stack.LinkEndpointCapabilities) (tcpip.LinkEndpointID, *InjectableEndpoint) {
|
2018-04-27 17:37:02 +00:00
|
|
|
syscall.SetNonblock(fd, true)
|
|
|
|
|
|
|
|
e := &InjectableEndpoint{endpoint: endpoint{
|
2019-06-06 15:05:46 +00:00
|
|
|
fds: []int{fd},
|
2019-04-09 18:22:28 +00:00
|
|
|
mtu: mtu,
|
|
|
|
caps: capabilities,
|
2018-04-27 17:37:02 +00:00
|
|
|
}}
|
|
|
|
|
|
|
|
return stack.RegisterLinkEndpoint(e), e
|
|
|
|
}
|