mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
AF_PACKET support for netstack (aka epsocket).
Like (AF_INET, SOCK_RAW) sockets, AF_PACKET sockets require CAP_NET_RAW. With runsc, you'll need to pass `--net-raw=true` to enable them. Binding isn't supported yet. PiperOrigin-RevId: 275909366
This commit is contained in:
committed by
gVisor bot
parent
652f7b1d0f
commit
12235d533a
@@ -256,6 +256,17 @@ type SockAddrInet6 struct {
|
||||
Scope_id uint32
|
||||
}
|
||||
|
||||
// SockAddrLink is a struct sockaddr_ll, from uapi/linux/if_packet.h.
|
||||
type SockAddrLink struct {
|
||||
Family uint16
|
||||
Protocol uint16
|
||||
InterfaceIndex int32
|
||||
ARPHardwareType uint16
|
||||
PacketType byte
|
||||
HardwareAddrLen byte
|
||||
HardwareAddr [8]byte
|
||||
}
|
||||
|
||||
// UnixPathMax is the maximum length of the path in an AF_UNIX socket.
|
||||
//
|
||||
// From uapi/linux/un.h.
|
||||
@@ -278,6 +289,7 @@ type SockAddr interface {
|
||||
|
||||
func (s *SockAddrInet) implementsSockAddr() {}
|
||||
func (s *SockAddrInet6) implementsSockAddr() {}
|
||||
func (s *SockAddrLink) implementsSockAddr() {}
|
||||
func (s *SockAddrUnix) implementsSockAddr() {}
|
||||
func (s *SockAddrNetlink) implementsSockAddr() {}
|
||||
|
||||
|
||||
@@ -53,6 +53,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/syserror"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/buffer"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/transport/udp"
|
||||
@@ -296,6 +297,7 @@ func New(t *kernel.Task, family int, skType linux.SockType, protocol int, queue
|
||||
|
||||
var sockAddrInetSize = int(binary.Size(linux.SockAddrInet{}))
|
||||
var sockAddrInet6Size = int(binary.Size(linux.SockAddrInet6{}))
|
||||
var sockAddrLinkSize = int(binary.Size(linux.SockAddrLink{}))
|
||||
|
||||
// bytesToIPAddress converts an IPv4 or IPv6 address from the user to the
|
||||
// netstack representation taking any addresses into account.
|
||||
@@ -307,12 +309,12 @@ func bytesToIPAddress(addr []byte) tcpip.Address {
|
||||
}
|
||||
|
||||
// AddressAndFamily reads an sockaddr struct from the given address and
|
||||
// converts it to the FullAddress format. It supports AF_UNIX, AF_INET and
|
||||
// AF_INET6 addresses.
|
||||
// converts it to the FullAddress format. It supports AF_UNIX, AF_INET,
|
||||
// AF_INET6, and AF_PACKET addresses.
|
||||
//
|
||||
// strict indicates whether addresses with the AF_UNSPEC family are accepted of not.
|
||||
//
|
||||
// AddressAndFamily returns an address, its family.
|
||||
// AddressAndFamily returns an address and its family.
|
||||
func AddressAndFamily(sfamily int, addr []byte, strict bool) (tcpip.FullAddress, uint16, *syserr.Error) {
|
||||
// Make sure we have at least 2 bytes for the address family.
|
||||
if len(addr) < 2 {
|
||||
@@ -371,6 +373,22 @@ func AddressAndFamily(sfamily int, addr []byte, strict bool) (tcpip.FullAddress,
|
||||
}
|
||||
return out, family, nil
|
||||
|
||||
case linux.AF_PACKET:
|
||||
var a linux.SockAddrLink
|
||||
if len(addr) < sockAddrLinkSize {
|
||||
return tcpip.FullAddress{}, family, syserr.ErrInvalidArgument
|
||||
}
|
||||
binary.Unmarshal(addr[:sockAddrLinkSize], usermem.ByteOrder, &a)
|
||||
if a.Family != linux.AF_PACKET || a.HardwareAddrLen != header.EthernetAddressSize {
|
||||
return tcpip.FullAddress{}, family, syserr.ErrInvalidArgument
|
||||
}
|
||||
|
||||
// TODO(b/129292371): Return protocol too.
|
||||
return tcpip.FullAddress{
|
||||
NIC: tcpip.NICID(a.InterfaceIndex),
|
||||
Addr: tcpip.Address(a.HardwareAddr[:header.EthernetAddressSize]),
|
||||
}, family, nil
|
||||
|
||||
case linux.AF_UNSPEC:
|
||||
return tcpip.FullAddress{}, family, nil
|
||||
|
||||
@@ -1951,12 +1969,14 @@ func ConvertAddress(family int, addr tcpip.FullAddress) (linux.SockAddr, uint32)
|
||||
return &out, uint32(2 + l)
|
||||
}
|
||||
return &out, uint32(3 + l)
|
||||
|
||||
case linux.AF_INET:
|
||||
var out linux.SockAddrInet
|
||||
copy(out.Addr[:], addr.Addr)
|
||||
out.Family = linux.AF_INET
|
||||
out.Port = htons(addr.Port)
|
||||
return &out, uint32(binary.Size(out))
|
||||
return &out, uint32(sockAddrInetSize)
|
||||
|
||||
case linux.AF_INET6:
|
||||
var out linux.SockAddrInet6
|
||||
if len(addr.Addr) == 4 {
|
||||
@@ -1972,7 +1992,17 @@ func ConvertAddress(family int, addr tcpip.FullAddress) (linux.SockAddr, uint32)
|
||||
if isLinkLocal(addr.Addr) {
|
||||
out.Scope_id = uint32(addr.NIC)
|
||||
}
|
||||
return &out, uint32(binary.Size(out))
|
||||
return &out, uint32(sockAddrInet6Size)
|
||||
|
||||
case linux.AF_PACKET:
|
||||
// TODO(b/129292371): Return protocol too.
|
||||
var out linux.SockAddrLink
|
||||
out.Family = linux.AF_PACKET
|
||||
out.InterfaceIndex = int32(addr.NIC)
|
||||
out.HardwareAddrLen = header.EthernetAddressSize
|
||||
copy(out.HardwareAddr[:], addr.Addr)
|
||||
return &out, uint32(sockAddrLinkSize)
|
||||
|
||||
default:
|
||||
return nil, 0
|
||||
}
|
||||
|
||||
@@ -62,6 +62,10 @@ func getTransportProtocol(ctx context.Context, stype linux.SockType, protocol in
|
||||
}
|
||||
|
||||
case linux.SOCK_RAW:
|
||||
// TODO(b/142504697): "In order to create a raw socket, a
|
||||
// process must have the CAP_NET_RAW capability in the user
|
||||
// namespace that governs its network namespace." - raw(7)
|
||||
|
||||
// Raw sockets require CAP_NET_RAW.
|
||||
creds := auth.CredentialsFromContext(ctx)
|
||||
if !creds.HasCapability(linux.CAP_NET_RAW) {
|
||||
@@ -85,7 +89,8 @@ func getTransportProtocol(ctx context.Context, stype linux.SockType, protocol in
|
||||
return 0, true, syserr.ErrProtocolNotSupported
|
||||
}
|
||||
|
||||
// Socket creates a new socket object for the AF_INET or AF_INET6 family.
|
||||
// Socket creates a new socket object for the AF_INET, AF_INET6, or AF_PACKET
|
||||
// family.
|
||||
func (p *provider) Socket(t *kernel.Task, stype linux.SockType, protocol int) (*fs.File, *syserr.Error) {
|
||||
// Fail right away if we don't have a stack.
|
||||
stack := t.NetworkContext()
|
||||
@@ -99,6 +104,12 @@ func (p *provider) Socket(t *kernel.Task, stype linux.SockType, protocol int) (*
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Packet sockets are handled separately, since they are neither INET
|
||||
// nor INET6 specific.
|
||||
if p.family == linux.AF_PACKET {
|
||||
return packetSocket(t, eps, stype, protocol)
|
||||
}
|
||||
|
||||
// Figure out the transport protocol.
|
||||
transProto, associated, err := getTransportProtocol(t, stype, protocol)
|
||||
if err != nil {
|
||||
@@ -121,12 +132,47 @@ func (p *provider) Socket(t *kernel.Task, stype linux.SockType, protocol int) (*
|
||||
return New(t, p.family, stype, int(transProto), wq, ep)
|
||||
}
|
||||
|
||||
func packetSocket(t *kernel.Task, epStack *Stack, stype linux.SockType, protocol int) (*fs.File, *syserr.Error) {
|
||||
// TODO(b/142504697): "In order to create a packet socket, a process
|
||||
// must have the CAP_NET_RAW capability in the user namespace that
|
||||
// governs its network namespace." - packet(7)
|
||||
|
||||
// Packet sockets require CAP_NET_RAW.
|
||||
creds := auth.CredentialsFromContext(t)
|
||||
if !creds.HasCapability(linux.CAP_NET_RAW) {
|
||||
return nil, syserr.ErrNotPermitted
|
||||
}
|
||||
|
||||
// "cooked" packets don't contain link layer information.
|
||||
var cooked bool
|
||||
switch stype {
|
||||
case linux.SOCK_DGRAM:
|
||||
cooked = true
|
||||
case linux.SOCK_RAW:
|
||||
cooked = false
|
||||
default:
|
||||
return nil, syserr.ErrProtocolNotSupported
|
||||
}
|
||||
|
||||
// protocol is passed in network byte order, but netstack wants it in
|
||||
// host order.
|
||||
netProto := tcpip.NetworkProtocolNumber(ntohs(uint16(protocol)))
|
||||
|
||||
wq := &waiter.Queue{}
|
||||
ep, err := epStack.Stack.NewPacketEndpoint(cooked, netProto, wq)
|
||||
if err != nil {
|
||||
return nil, syserr.TranslateNetstackError(err)
|
||||
}
|
||||
|
||||
return New(t, linux.AF_PACKET, stype, protocol, wq, ep)
|
||||
}
|
||||
|
||||
// Pair just returns nil sockets (not supported).
|
||||
func (*provider) Pair(*kernel.Task, linux.SockType, int) (*fs.File, *fs.File, *syserr.Error) {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
// init registers socket providers for AF_INET and AF_INET6.
|
||||
// init registers socket providers for AF_INET, AF_INET6, and AF_PACKET.
|
||||
func init() {
|
||||
// Providers backed by netstack.
|
||||
p := []provider{
|
||||
@@ -138,6 +184,9 @@ func init() {
|
||||
family: linux.AF_INET6,
|
||||
netProto: ipv6.ProtocolNumber,
|
||||
},
|
||||
{
|
||||
family: linux.AF_PACKET,
|
||||
},
|
||||
}
|
||||
|
||||
for i := range p {
|
||||
|
||||
@@ -50,6 +50,24 @@ const (
|
||||
EthernetAddressSize = 6
|
||||
)
|
||||
|
||||
const (
|
||||
// EthernetProtocolAll is a catch-all for all protocols carried inside
|
||||
// an ethernet frame. It is mainly used to create packet sockets that
|
||||
// capture all traffic.
|
||||
EthernetProtocolAll tcpip.NetworkProtocolNumber = 0x0003
|
||||
|
||||
// EthernetProtocolPUP is the PARC Universial Packet protocol ethertype.
|
||||
EthernetProtocolPUP tcpip.NetworkProtocolNumber = 0x0200
|
||||
)
|
||||
|
||||
// Ethertypes holds the protocol numbers describing the payload of an ethernet
|
||||
// frame. These types aren't necessarily supported by netstack, but can be used
|
||||
// to catch all traffic of a type via packet endpoints.
|
||||
var Ethertypes = []tcpip.NetworkProtocolNumber{
|
||||
EthernetProtocolAll,
|
||||
EthernetProtocolPUP,
|
||||
}
|
||||
|
||||
// SourceAddress returns the "MAC source" field of the ethernet frame header.
|
||||
func (b Ethernet) SourceAddress() tcpip.LinkAddress {
|
||||
return tcpip.LinkAddress(b[srcMAC:][:EthernetAddressSize])
|
||||
|
||||
@@ -72,7 +72,7 @@ func (e *Endpoint) Inject(protocol tcpip.NetworkProtocolNumber, vv buffer.Vector
|
||||
|
||||
// InjectLinkAddr injects an inbound packet with a remote link address.
|
||||
func (e *Endpoint) InjectLinkAddr(protocol tcpip.NetworkProtocolNumber, remote tcpip.LinkAddress, vv buffer.VectorisedView) {
|
||||
e.dispatcher.DeliverNetworkPacket(e, remote, "" /* local */, protocol, vv.Clone(nil))
|
||||
e.dispatcher.DeliverNetworkPacket(e, remote, "" /* local */, protocol, vv.Clone(nil), nil /* linkHeader */)
|
||||
}
|
||||
|
||||
// Attach saves the stack network-layer dispatcher for use later when packets
|
||||
@@ -134,5 +134,22 @@ func (e *Endpoint) WritePacket(_ *stack.Route, gso *stack.GSO, hdr buffer.Prepen
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteRawPacket implements stack.LinkEndpoint.WriteRawPacket.
|
||||
func (e *Endpoint) WriteRawPacket(packet buffer.VectorisedView) *tcpip.Error {
|
||||
p := PacketInfo{
|
||||
Header: packet.ToView(),
|
||||
Proto: 0,
|
||||
Payload: buffer.View{},
|
||||
GSO: nil,
|
||||
}
|
||||
|
||||
select {
|
||||
case e.C <- p:
|
||||
default:
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Wait implements stack.LinkEndpoint.Wait.
|
||||
func (*Endpoint) Wait() {}
|
||||
|
||||
@@ -430,8 +430,13 @@ func (e *endpoint) WritePacket(r *stack.Route, gso *stack.GSO, hdr buffer.Prepen
|
||||
return rawfile.NonBlockingWrite3(e.fds[0], hdr.View(), payload.ToView(), nil)
|
||||
}
|
||||
|
||||
// WriteRawPacket writes a raw packet directly to the file descriptor.
|
||||
func (e *endpoint) WriteRawPacket(dest tcpip.Address, packet []byte) *tcpip.Error {
|
||||
// WriteRawPacket implements stack.LinkEndpoint.WriteRawPacket.
|
||||
func (e *endpoint) WriteRawPacket(packet buffer.VectorisedView) *tcpip.Error {
|
||||
return rawfile.NonBlockingWrite(e.fds[0], packet.ToView())
|
||||
}
|
||||
|
||||
// InjectOutobund implements stack.InjectableEndpoint.InjectOutbound.
|
||||
func (e *endpoint) InjectOutbound(dest tcpip.Address, packet []byte) *tcpip.Error {
|
||||
return rawfile.NonBlockingWrite(e.fds[0], packet)
|
||||
}
|
||||
|
||||
@@ -468,9 +473,9 @@ func (e *InjectableEndpoint) Attach(dispatcher stack.NetworkDispatcher) {
|
||||
e.dispatcher = dispatcher
|
||||
}
|
||||
|
||||
// Inject injects an inbound packet.
|
||||
func (e *InjectableEndpoint) Inject(protocol tcpip.NetworkProtocolNumber, vv buffer.VectorisedView) {
|
||||
e.dispatcher.DeliverNetworkPacket(e, "" /* remote */, "" /* local */, protocol, vv)
|
||||
// InjectInbound injects an inbound packet.
|
||||
func (e *InjectableEndpoint) InjectInbound(protocol tcpip.NetworkProtocolNumber, vv buffer.VectorisedView) {
|
||||
e.dispatcher.DeliverNetworkPacket(e, "" /* remote */, "" /* local */, protocol, vv, nil /* linkHeader */)
|
||||
}
|
||||
|
||||
// NewInjectable creates a new fd-based InjectableEndpoint.
|
||||
|
||||
@@ -43,9 +43,10 @@ const (
|
||||
)
|
||||
|
||||
type packetInfo struct {
|
||||
raddr tcpip.LinkAddress
|
||||
proto tcpip.NetworkProtocolNumber
|
||||
contents buffer.View
|
||||
raddr tcpip.LinkAddress
|
||||
proto tcpip.NetworkProtocolNumber
|
||||
contents buffer.View
|
||||
linkHeader buffer.View
|
||||
}
|
||||
|
||||
type context struct {
|
||||
@@ -92,8 +93,8 @@ func (c *context) cleanup() {
|
||||
syscall.Close(c.fds[1])
|
||||
}
|
||||
|
||||
func (c *context) DeliverNetworkPacket(linkEP stack.LinkEndpoint, remote tcpip.LinkAddress, local tcpip.LinkAddress, protocol tcpip.NetworkProtocolNumber, vv buffer.VectorisedView) {
|
||||
c.ch <- packetInfo{remote, protocol, vv.ToView()}
|
||||
func (c *context) DeliverNetworkPacket(linkEP stack.LinkEndpoint, remote tcpip.LinkAddress, local tcpip.LinkAddress, protocol tcpip.NetworkProtocolNumber, vv buffer.VectorisedView, linkHeader buffer.View) {
|
||||
c.ch <- packetInfo{remote, protocol, vv.ToView(), linkHeader}
|
||||
}
|
||||
|
||||
func TestNoEthernetProperties(t *testing.T) {
|
||||
@@ -293,11 +294,12 @@ func TestDeliverPacket(t *testing.T) {
|
||||
b[i] = uint8(rand.Intn(256))
|
||||
}
|
||||
|
||||
var hdr header.Ethernet
|
||||
if !eth {
|
||||
// So that it looks like an IPv4 packet.
|
||||
b[0] = 0x40
|
||||
} else {
|
||||
hdr := make(header.Ethernet, header.EthernetMinimumSize)
|
||||
hdr = make(header.Ethernet, header.EthernetMinimumSize)
|
||||
hdr.Encode(&header.EthernetFields{
|
||||
SrcAddr: raddr,
|
||||
DstAddr: laddr,
|
||||
@@ -315,9 +317,10 @@ func TestDeliverPacket(t *testing.T) {
|
||||
select {
|
||||
case pi := <-c.ch:
|
||||
want := packetInfo{
|
||||
raddr: raddr,
|
||||
proto: proto,
|
||||
contents: b,
|
||||
raddr: raddr,
|
||||
proto: proto,
|
||||
contents: b,
|
||||
linkHeader: buffer.View(hdr),
|
||||
}
|
||||
if !eth {
|
||||
want.proto = header.IPv4ProtocolNumber
|
||||
|
||||
@@ -169,9 +169,10 @@ func (d *packetMMapDispatcher) dispatch() (bool, *tcpip.Error) {
|
||||
var (
|
||||
p tcpip.NetworkProtocolNumber
|
||||
remote, local tcpip.LinkAddress
|
||||
eth header.Ethernet
|
||||
)
|
||||
if d.e.hdrSize > 0 {
|
||||
eth := header.Ethernet(pkt)
|
||||
eth = header.Ethernet(pkt)
|
||||
p = eth.Type()
|
||||
remote = eth.SourceAddress()
|
||||
local = eth.DestinationAddress()
|
||||
@@ -189,6 +190,6 @@ func (d *packetMMapDispatcher) dispatch() (bool, *tcpip.Error) {
|
||||
}
|
||||
|
||||
pkt = pkt[d.e.hdrSize:]
|
||||
d.e.dispatcher.DeliverNetworkPacket(d.e, remote, local, p, buffer.NewVectorisedView(len(pkt), []buffer.View{buffer.View(pkt)}))
|
||||
d.e.dispatcher.DeliverNetworkPacket(d.e, remote, local, p, buffer.NewVectorisedView(len(pkt), []buffer.View{buffer.View(pkt)}), buffer.View(eth))
|
||||
return true, nil
|
||||
}
|
||||
|
||||
@@ -118,9 +118,10 @@ func (d *readVDispatcher) dispatch() (bool, *tcpip.Error) {
|
||||
var (
|
||||
p tcpip.NetworkProtocolNumber
|
||||
remote, local tcpip.LinkAddress
|
||||
eth header.Ethernet
|
||||
)
|
||||
if d.e.hdrSize > 0 {
|
||||
eth := header.Ethernet(d.views[0])
|
||||
eth = header.Ethernet(d.views[0][:header.EthernetMinimumSize])
|
||||
p = eth.Type()
|
||||
remote = eth.SourceAddress()
|
||||
local = eth.DestinationAddress()
|
||||
@@ -141,7 +142,7 @@ func (d *readVDispatcher) dispatch() (bool, *tcpip.Error) {
|
||||
vv := buffer.NewVectorisedView(n, d.views[:used])
|
||||
vv.TrimFront(d.e.hdrSize)
|
||||
|
||||
d.e.dispatcher.DeliverNetworkPacket(d.e, remote, local, p, vv)
|
||||
d.e.dispatcher.DeliverNetworkPacket(d.e, remote, local, p, vv, buffer.View(eth))
|
||||
|
||||
// Prepare e.views for another packet: release used views.
|
||||
for i := 0; i < used; i++ {
|
||||
@@ -271,9 +272,10 @@ func (d *recvMMsgDispatcher) dispatch() (bool, *tcpip.Error) {
|
||||
var (
|
||||
p tcpip.NetworkProtocolNumber
|
||||
remote, local tcpip.LinkAddress
|
||||
eth header.Ethernet
|
||||
)
|
||||
if d.e.hdrSize > 0 {
|
||||
eth := header.Ethernet(d.views[k][0])
|
||||
eth = header.Ethernet(d.views[k][0])
|
||||
p = eth.Type()
|
||||
remote = eth.SourceAddress()
|
||||
local = eth.DestinationAddress()
|
||||
@@ -293,7 +295,7 @@ func (d *recvMMsgDispatcher) dispatch() (bool, *tcpip.Error) {
|
||||
used := d.capViews(k, int(n), BufConfig)
|
||||
vv := buffer.NewVectorisedView(int(n), d.views[k][:used])
|
||||
vv.TrimFront(d.e.hdrSize)
|
||||
d.e.dispatcher.DeliverNetworkPacket(d.e, remote, local, p, vv)
|
||||
d.e.dispatcher.DeliverNetworkPacket(d.e, remote, local, p, vv, buffer.View(eth))
|
||||
|
||||
// Prepare e.views for another packet: release used views.
|
||||
for i := 0; i < used; i++ {
|
||||
|
||||
@@ -10,6 +10,7 @@ go_library(
|
||||
deps = [
|
||||
"//pkg/tcpip",
|
||||
"//pkg/tcpip/buffer",
|
||||
"//pkg/tcpip/header",
|
||||
"//pkg/tcpip/stack",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -23,6 +23,7 @@ package loopback
|
||||
import (
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/buffer"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
)
|
||||
|
||||
@@ -70,6 +71,9 @@ func (*endpoint) LinkAddress() tcpip.LinkAddress {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Wait implements stack.LinkEndpoint.Wait.
|
||||
func (*endpoint) Wait() {}
|
||||
|
||||
// WritePacket implements stack.LinkEndpoint.WritePacket. It delivers outbound
|
||||
// packets to the network-layer dispatcher.
|
||||
func (e *endpoint) WritePacket(_ *stack.Route, _ *stack.GSO, hdr buffer.Prependable, payload buffer.VectorisedView, protocol tcpip.NetworkProtocolNumber) *tcpip.Error {
|
||||
@@ -81,10 +85,22 @@ func (e *endpoint) WritePacket(_ *stack.Route, _ *stack.GSO, hdr buffer.Prependa
|
||||
// Because we're immediately turning around and writing the packet back to the
|
||||
// rx path, we intentionally don't preserve the remote and local link
|
||||
// addresses from the stack.Route we're passed.
|
||||
e.dispatcher.DeliverNetworkPacket(e, "" /* remote */, "" /* local */, protocol, vv)
|
||||
e.dispatcher.DeliverNetworkPacket(e, "" /* remote */, "" /* local */, protocol, vv, nil /* linkHeader */)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Wait implements stack.LinkEndpoint.Wait.
|
||||
func (*endpoint) Wait() {}
|
||||
// WriteRawPacket implements stack.LinkEndpoint.WriteRawPacket.
|
||||
func (e *endpoint) WriteRawPacket(packet buffer.VectorisedView) *tcpip.Error {
|
||||
// Reject the packet if it's shorter than an ethernet header.
|
||||
if packet.Size() < header.EthernetMinimumSize {
|
||||
return tcpip.ErrBadAddress
|
||||
}
|
||||
|
||||
// There should be an ethernet header at the beginning of packet.
|
||||
linkHeader := header.Ethernet(packet.First()[:header.EthernetMinimumSize])
|
||||
packet.TrimFront(len(linkHeader))
|
||||
e.dispatcher.DeliverNetworkPacket(e, "" /* remote */, "" /* local */, linkHeader.Type(), packet, buffer.View(linkHeader))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -79,9 +79,9 @@ func (m *InjectableEndpoint) IsAttached() bool {
|
||||
return m.dispatcher != nil
|
||||
}
|
||||
|
||||
// Inject implements stack.InjectableLinkEndpoint.
|
||||
func (m *InjectableEndpoint) Inject(protocol tcpip.NetworkProtocolNumber, vv buffer.VectorisedView) {
|
||||
m.dispatcher.DeliverNetworkPacket(m, "" /* remote */, "" /* local */, protocol, vv)
|
||||
// InjectInbound implements stack.InjectableLinkEndpoint.
|
||||
func (m *InjectableEndpoint) InjectInbound(protocol tcpip.NetworkProtocolNumber, vv buffer.VectorisedView) {
|
||||
m.dispatcher.DeliverNetworkPacket(m, "" /* remote */, "" /* local */, protocol, vv, nil /* linkHeader */)
|
||||
}
|
||||
|
||||
// WritePacket writes outbound packets to the appropriate LinkInjectableEndpoint
|
||||
@@ -94,14 +94,21 @@ func (m *InjectableEndpoint) WritePacket(r *stack.Route, _ *stack.GSO, hdr buffe
|
||||
return tcpip.ErrNoRoute
|
||||
}
|
||||
|
||||
// WriteRawPacket writes outbound packets to the appropriate
|
||||
// WriteRawPacket implements stack.LinkEndpoint.WriteRawPacket.
|
||||
func (m *InjectableEndpoint) WriteRawPacket(packet buffer.VectorisedView) *tcpip.Error {
|
||||
// WriteRawPacket doesn't get a route or network address, so there's
|
||||
// nowhere to write this.
|
||||
return tcpip.ErrNoRoute
|
||||
}
|
||||
|
||||
// InjectOutbound writes outbound packets to the appropriate
|
||||
// LinkInjectableEndpoint based on the dest address.
|
||||
func (m *InjectableEndpoint) WriteRawPacket(dest tcpip.Address, packet []byte) *tcpip.Error {
|
||||
func (m *InjectableEndpoint) InjectOutbound(dest tcpip.Address, packet []byte) *tcpip.Error {
|
||||
endpoint, ok := m.routes[dest]
|
||||
if !ok {
|
||||
return tcpip.ErrNoRoute
|
||||
}
|
||||
return endpoint.WriteRawPacket(dest, packet)
|
||||
return endpoint.InjectOutbound(dest, packet)
|
||||
}
|
||||
|
||||
// Wait implements stack.LinkEndpoint.Wait.
|
||||
|
||||
@@ -31,7 +31,7 @@ import (
|
||||
func TestInjectableEndpointRawDispatch(t *testing.T) {
|
||||
endpoint, sock, dstIP := makeTestInjectableEndpoint(t)
|
||||
|
||||
endpoint.WriteRawPacket(dstIP, []byte{0xFA})
|
||||
endpoint.InjectOutbound(dstIP, []byte{0xFA})
|
||||
|
||||
buf := make([]byte, ipv4.MaxTotalSize)
|
||||
bytesRead, err := sock.Read(buf)
|
||||
|
||||
@@ -212,6 +212,21 @@ func (e *endpoint) WritePacket(r *stack.Route, _ *stack.GSO, hdr buffer.Prependa
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteRawPacket implements stack.LinkEndpoint.WriteRawPacket.
|
||||
func (e *endpoint) WriteRawPacket(packet buffer.VectorisedView) *tcpip.Error {
|
||||
v := packet.ToView()
|
||||
// Transmit the packet.
|
||||
e.mu.Lock()
|
||||
ok := e.tx.transmit(v, buffer.View{})
|
||||
e.mu.Unlock()
|
||||
|
||||
if !ok {
|
||||
return tcpip.ErrWouldBlock
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// dispatchLoop reads packets from the rx queue in a loop and dispatches them
|
||||
// to the network stack.
|
||||
func (e *endpoint) dispatchLoop(d stack.NetworkDispatcher) {
|
||||
@@ -254,7 +269,7 @@ func (e *endpoint) dispatchLoop(d stack.NetworkDispatcher) {
|
||||
|
||||
// Send packet up the stack.
|
||||
eth := header.Ethernet(b)
|
||||
d.DeliverNetworkPacket(e, eth.SourceAddress(), eth.DestinationAddress(), eth.Type(), buffer.View(b[header.EthernetMinimumSize:]).ToVectorisedView())
|
||||
d.DeliverNetworkPacket(e, eth.SourceAddress(), eth.DestinationAddress(), eth.Type(), buffer.View(b[header.EthernetMinimumSize:]).ToVectorisedView(), buffer.View(eth))
|
||||
}
|
||||
|
||||
// Clean state.
|
||||
|
||||
@@ -78,9 +78,10 @@ func (q *queueBuffers) cleanup() {
|
||||
}
|
||||
|
||||
type packetInfo struct {
|
||||
addr tcpip.LinkAddress
|
||||
proto tcpip.NetworkProtocolNumber
|
||||
vv buffer.VectorisedView
|
||||
addr tcpip.LinkAddress
|
||||
proto tcpip.NetworkProtocolNumber
|
||||
vv buffer.VectorisedView
|
||||
linkHeader buffer.View
|
||||
}
|
||||
|
||||
type testContext struct {
|
||||
@@ -130,12 +131,13 @@ func newTestContext(t *testing.T, mtu, bufferSize uint32, addr tcpip.LinkAddress
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *testContext) DeliverNetworkPacket(_ stack.LinkEndpoint, remoteLinkAddr, localLinkAddr tcpip.LinkAddress, proto tcpip.NetworkProtocolNumber, vv buffer.VectorisedView) {
|
||||
func (c *testContext) DeliverNetworkPacket(_ stack.LinkEndpoint, remoteLinkAddr, localLinkAddr tcpip.LinkAddress, proto tcpip.NetworkProtocolNumber, vv buffer.VectorisedView, linkHeader buffer.View) {
|
||||
c.mu.Lock()
|
||||
c.packets = append(c.packets, packetInfo{
|
||||
addr: remoteLinkAddr,
|
||||
proto: proto,
|
||||
vv: vv.Clone(nil),
|
||||
addr: remoteLinkAddr,
|
||||
proto: proto,
|
||||
vv: vv.Clone(nil),
|
||||
linkHeader: linkHeader,
|
||||
})
|
||||
c.mu.Unlock()
|
||||
|
||||
|
||||
@@ -116,7 +116,7 @@ func NewWithFile(lower stack.LinkEndpoint, file *os.File, snapLen uint32) (stack
|
||||
// DeliverNetworkPacket implements the stack.NetworkDispatcher interface. It is
|
||||
// called by the link-layer endpoint being wrapped when a packet arrives, and
|
||||
// logs the packet before forwarding to the actual dispatcher.
|
||||
func (e *endpoint) DeliverNetworkPacket(linkEP stack.LinkEndpoint, remote, local tcpip.LinkAddress, protocol tcpip.NetworkProtocolNumber, vv buffer.VectorisedView) {
|
||||
func (e *endpoint) DeliverNetworkPacket(linkEP stack.LinkEndpoint, remote, local tcpip.LinkAddress, protocol tcpip.NetworkProtocolNumber, vv buffer.VectorisedView, linkHeader buffer.View) {
|
||||
if atomic.LoadUint32(&LogPackets) == 1 && e.file == nil {
|
||||
logPacket("recv", protocol, vv.First(), nil)
|
||||
}
|
||||
@@ -147,7 +147,7 @@ func (e *endpoint) DeliverNetworkPacket(linkEP stack.LinkEndpoint, remote, local
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
e.dispatcher.DeliverNetworkPacket(e, remote, local, protocol, vv)
|
||||
e.dispatcher.DeliverNetworkPacket(e, remote, local, protocol, vv, linkHeader)
|
||||
}
|
||||
|
||||
// Attach implements the stack.LinkEndpoint interface. It saves the dispatcher
|
||||
@@ -218,21 +218,7 @@ func (e *endpoint) WritePacket(r *stack.Route, gso *stack.GSO, hdr buffer.Prepen
|
||||
panic(err)
|
||||
}
|
||||
length -= len(hdrBuf)
|
||||
if length > 0 {
|
||||
for _, v := range payload.Views() {
|
||||
if len(v) > length {
|
||||
v = v[:length]
|
||||
}
|
||||
n, err := buf.Write(v)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
length -= n
|
||||
if length == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
logVectorisedView(payload, length, buf)
|
||||
if _, err := e.file.Write(buf.Bytes()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -240,6 +226,48 @@ func (e *endpoint) WritePacket(r *stack.Route, gso *stack.GSO, hdr buffer.Prepen
|
||||
return e.lower.WritePacket(r, gso, hdr, payload, protocol)
|
||||
}
|
||||
|
||||
// WriteRawPacket implements stack.LinkEndpoint.WriteRawPacket.
|
||||
func (e *endpoint) WriteRawPacket(packet buffer.VectorisedView) *tcpip.Error {
|
||||
if atomic.LoadUint32(&LogPackets) == 1 && e.file == nil {
|
||||
logPacket("send", 0, buffer.View("[raw packet, no header available]"), nil /* gso */)
|
||||
}
|
||||
if e.file != nil && atomic.LoadUint32(&LogPacketsToFile) == 1 {
|
||||
length := packet.Size()
|
||||
if length > int(e.maxPCAPLen) {
|
||||
length = int(e.maxPCAPLen)
|
||||
}
|
||||
|
||||
buf := bytes.NewBuffer(make([]byte, 0, pcapPacketHeaderLen+length))
|
||||
if err := binary.Write(buf, binary.BigEndian, newPCAPPacketHeader(uint32(length), uint32(packet.Size()))); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
logVectorisedView(packet, length, buf)
|
||||
if _, err := e.file.Write(buf.Bytes()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
return e.lower.WriteRawPacket(packet)
|
||||
}
|
||||
|
||||
func logVectorisedView(vv buffer.VectorisedView, length int, buf *bytes.Buffer) {
|
||||
if length <= 0 {
|
||||
return
|
||||
}
|
||||
for _, v := range vv.Views() {
|
||||
if len(v) > length {
|
||||
v = v[:length]
|
||||
}
|
||||
n, err := buf.Write(v)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
length -= n
|
||||
if length == 0 {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Wait implements stack.LinkEndpoint.Wait.
|
||||
func (*endpoint) Wait() {}
|
||||
|
||||
|
||||
@@ -50,12 +50,12 @@ func New(lower stack.LinkEndpoint) *Endpoint {
|
||||
// It is called by the link-layer endpoint being wrapped when a packet arrives,
|
||||
// and only forwards to the actual dispatcher if Wait or WaitDispatch haven't
|
||||
// been called.
|
||||
func (e *Endpoint) DeliverNetworkPacket(linkEP stack.LinkEndpoint, remote, local tcpip.LinkAddress, protocol tcpip.NetworkProtocolNumber, vv buffer.VectorisedView) {
|
||||
func (e *Endpoint) DeliverNetworkPacket(linkEP stack.LinkEndpoint, remote, local tcpip.LinkAddress, protocol tcpip.NetworkProtocolNumber, vv buffer.VectorisedView, linkHeader buffer.View) {
|
||||
if !e.dispatchGate.Enter() {
|
||||
return
|
||||
}
|
||||
|
||||
e.dispatcher.DeliverNetworkPacket(e, remote, local, protocol, vv)
|
||||
e.dispatcher.DeliverNetworkPacket(e, remote, local, protocol, vv, linkHeader)
|
||||
e.dispatchGate.Leave()
|
||||
}
|
||||
|
||||
@@ -109,6 +109,17 @@ func (e *Endpoint) WritePacket(r *stack.Route, gso *stack.GSO, hdr buffer.Prepen
|
||||
return err
|
||||
}
|
||||
|
||||
// WriteRawPacket implements stack.LinkEndpoint.WriteRawPacket.
|
||||
func (e *Endpoint) WriteRawPacket(packet buffer.VectorisedView) *tcpip.Error {
|
||||
if !e.writeGate.Enter() {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := e.lower.WriteRawPacket(packet)
|
||||
e.writeGate.Leave()
|
||||
return err
|
||||
}
|
||||
|
||||
// WaitWrite prevents new calls to WritePacket from reaching the lower endpoint,
|
||||
// and waits for inflight ones to finish before returning.
|
||||
func (e *Endpoint) WaitWrite() {
|
||||
|
||||
@@ -35,7 +35,7 @@ type countedEndpoint struct {
|
||||
dispatcher stack.NetworkDispatcher
|
||||
}
|
||||
|
||||
func (e *countedEndpoint) DeliverNetworkPacket(linkEP stack.LinkEndpoint, remote, local tcpip.LinkAddress, protocol tcpip.NetworkProtocolNumber, vv buffer.VectorisedView) {
|
||||
func (e *countedEndpoint) DeliverNetworkPacket(linkEP stack.LinkEndpoint, remote, local tcpip.LinkAddress, protocol tcpip.NetworkProtocolNumber, vv buffer.VectorisedView, linkHeader buffer.View) {
|
||||
e.dispatchCount++
|
||||
}
|
||||
|
||||
@@ -70,6 +70,11 @@ func (e *countedEndpoint) WritePacket(r *stack.Route, _ *stack.GSO, hdr buffer.P
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *countedEndpoint) WriteRawPacket(packet buffer.VectorisedView) *tcpip.Error {
|
||||
e.writeCount++
|
||||
return nil
|
||||
}
|
||||
|
||||
// Wait implements stack.LinkEndpoint.Wait.
|
||||
func (*countedEndpoint) Wait() {}
|
||||
|
||||
@@ -109,21 +114,21 @@ func TestWaitDispatch(t *testing.T) {
|
||||
}
|
||||
|
||||
// Dispatch and check that it goes through.
|
||||
ep.dispatcher.DeliverNetworkPacket(ep, "", "", 0, buffer.VectorisedView{})
|
||||
ep.dispatcher.DeliverNetworkPacket(ep, "", "", 0, buffer.VectorisedView{}, buffer.View{})
|
||||
if want := 1; ep.dispatchCount != want {
|
||||
t.Fatalf("Unexpected dispatchCount: got=%v, want=%v", ep.dispatchCount, want)
|
||||
}
|
||||
|
||||
// Wait on writes, then try to dispatch. It must go through.
|
||||
wep.WaitWrite()
|
||||
ep.dispatcher.DeliverNetworkPacket(ep, "", "", 0, buffer.VectorisedView{})
|
||||
ep.dispatcher.DeliverNetworkPacket(ep, "", "", 0, buffer.VectorisedView{}, buffer.View{})
|
||||
if want := 2; ep.dispatchCount != want {
|
||||
t.Fatalf("Unexpected dispatchCount: got=%v, want=%v", ep.dispatchCount, want)
|
||||
}
|
||||
|
||||
// Wait on dispatches, then try to dispatch. It must not go through.
|
||||
wep.WaitDispatch()
|
||||
ep.dispatcher.DeliverNetworkPacket(ep, "", "", 0, buffer.VectorisedView{})
|
||||
ep.dispatcher.DeliverNetworkPacket(ep, "", "", 0, buffer.VectorisedView{}, buffer.View{})
|
||||
if want := 2; ep.dispatchCount != want {
|
||||
t.Fatalf("Unexpected dispatchCount: got=%v, want=%v", ep.dispatchCount, want)
|
||||
}
|
||||
|
||||
@@ -171,6 +171,10 @@ func (t *testObject) WritePacket(_ *stack.Route, _ *stack.GSO, hdr buffer.Prepen
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *testObject) WriteRawPacket(_ buffer.VectorisedView) *tcpip.Error {
|
||||
return tcpip.ErrNotSupported
|
||||
}
|
||||
|
||||
func buildIPv4Route(local, remote tcpip.Address) (stack.Route, *tcpip.Error) {
|
||||
s := stack.New(stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocol{ipv4.NewProtocol()},
|
||||
|
||||
+70
-3
@@ -40,6 +40,9 @@ type NIC struct {
|
||||
endpoints map[NetworkEndpointID]*referencedNetworkEndpoint
|
||||
addressRanges []tcpip.Subnet
|
||||
mcastJoins map[NetworkEndpointID]int32
|
||||
// packetEPs is protected by mu, but the contained PacketEndpoint
|
||||
// values are not.
|
||||
packetEPs map[tcpip.NetworkProtocolNumber][]PacketEndpoint
|
||||
|
||||
stats NICStats
|
||||
|
||||
@@ -78,7 +81,7 @@ const (
|
||||
)
|
||||
|
||||
func newNIC(stack *Stack, id tcpip.NICID, name string, ep LinkEndpoint, loopback bool) *NIC {
|
||||
return &NIC{
|
||||
nic := &NIC{
|
||||
stack: stack,
|
||||
id: id,
|
||||
name: name,
|
||||
@@ -87,6 +90,7 @@ func newNIC(stack *Stack, id tcpip.NICID, name string, ep LinkEndpoint, loopback
|
||||
primary: make(map[tcpip.NetworkProtocolNumber][]*referencedNetworkEndpoint),
|
||||
endpoints: make(map[NetworkEndpointID]*referencedNetworkEndpoint),
|
||||
mcastJoins: make(map[NetworkEndpointID]int32),
|
||||
packetEPs: make(map[tcpip.NetworkProtocolNumber][]PacketEndpoint),
|
||||
stats: NICStats{
|
||||
Tx: DirectionStats{
|
||||
Packets: &tcpip.StatCounter{},
|
||||
@@ -101,6 +105,16 @@ func newNIC(stack *Stack, id tcpip.NICID, name string, ep LinkEndpoint, loopback
|
||||
dad: make(map[tcpip.Address]dadState),
|
||||
},
|
||||
}
|
||||
|
||||
// Register supported packet endpoint protocols.
|
||||
for _, netProto := range header.Ethertypes {
|
||||
nic.packetEPs[netProto] = []PacketEndpoint{}
|
||||
}
|
||||
for _, netProto := range stack.networkProtocols {
|
||||
nic.packetEPs[netProto.Number()] = []PacketEndpoint{}
|
||||
}
|
||||
|
||||
return nic
|
||||
}
|
||||
|
||||
// enable enables the NIC. enable will attach the link to its LinkEndpoint and
|
||||
@@ -631,7 +645,7 @@ func handlePacket(protocol tcpip.NetworkProtocolNumber, dst, src tcpip.Address,
|
||||
// Note that the ownership of the slice backing vv is retained by the caller.
|
||||
// This rule applies only to the slice itself, not to the items of the slice;
|
||||
// the ownership of the items is not retained by the caller.
|
||||
func (n *NIC) DeliverNetworkPacket(linkEP LinkEndpoint, remote, _ tcpip.LinkAddress, protocol tcpip.NetworkProtocolNumber, vv buffer.VectorisedView) {
|
||||
func (n *NIC) DeliverNetworkPacket(linkEP LinkEndpoint, remote, local tcpip.LinkAddress, protocol tcpip.NetworkProtocolNumber, vv buffer.VectorisedView, linkHeader buffer.View) {
|
||||
n.stats.Rx.Packets.Increment()
|
||||
n.stats.Rx.Bytes.IncrementBy(uint64(vv.Size()))
|
||||
|
||||
@@ -641,6 +655,26 @@ func (n *NIC) DeliverNetworkPacket(linkEP LinkEndpoint, remote, _ tcpip.LinkAddr
|
||||
return
|
||||
}
|
||||
|
||||
// If no local link layer address is provided, assume it was sent
|
||||
// directly to this NIC.
|
||||
if local == "" {
|
||||
local = n.linkEP.LinkAddress()
|
||||
}
|
||||
|
||||
// Are any packet sockets listening for this network protocol?
|
||||
n.mu.RLock()
|
||||
packetEPs := n.packetEPs[protocol]
|
||||
// Check whether there are packet sockets listening for every protocol.
|
||||
// If we received a packet with protocol EthernetProtocolAll, then the
|
||||
// previous for loop will have handled it.
|
||||
if protocol != header.EthernetProtocolAll {
|
||||
packetEPs = append(packetEPs, n.packetEPs[header.EthernetProtocolAll]...)
|
||||
}
|
||||
n.mu.RUnlock()
|
||||
for _, ep := range packetEPs {
|
||||
ep.HandlePacket(n.id, local, protocol, vv, linkHeader)
|
||||
}
|
||||
|
||||
if netProto.Number() == header.IPv4ProtocolNumber || netProto.Number() == header.IPv6ProtocolNumber {
|
||||
n.stack.stats.IP.PacketsReceived.Increment()
|
||||
}
|
||||
@@ -700,7 +734,10 @@ func (n *NIC) DeliverNetworkPacket(linkEP LinkEndpoint, remote, _ tcpip.LinkAddr
|
||||
return
|
||||
}
|
||||
|
||||
n.stack.stats.IP.InvalidAddressesReceived.Increment()
|
||||
// If a packet socket handled the packet, don't treat it as invalid.
|
||||
if len(packetEPs) == 0 {
|
||||
n.stack.stats.IP.InvalidAddressesReceived.Increment()
|
||||
}
|
||||
}
|
||||
|
||||
// DeliverTransportPacket delivers the packets to the appropriate transport
|
||||
@@ -856,6 +893,36 @@ const (
|
||||
temporary
|
||||
)
|
||||
|
||||
func (n *NIC) registerPacketEndpoint(netProto tcpip.NetworkProtocolNumber, ep PacketEndpoint) *tcpip.Error {
|
||||
n.mu.Lock()
|
||||
defer n.mu.Unlock()
|
||||
|
||||
eps, ok := n.packetEPs[netProto]
|
||||
if !ok {
|
||||
return tcpip.ErrNotSupported
|
||||
}
|
||||
n.packetEPs[netProto] = append(eps, ep)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *NIC) unregisterPacketEndpoint(netProto tcpip.NetworkProtocolNumber, ep PacketEndpoint) {
|
||||
n.mu.Lock()
|
||||
defer n.mu.Unlock()
|
||||
|
||||
eps, ok := n.packetEPs[netProto]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
for i, epOther := range eps {
|
||||
if epOther == ep {
|
||||
n.packetEPs[netProto] = append(eps[:i], eps[i+1:]...)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type referencedNetworkEndpoint struct {
|
||||
ep NetworkEndpoint
|
||||
nic *NIC
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user