Add loopback interface as an ethernet-based device

...to match Linux behaviour.

We can see evidence of Linux representing loopback as an ethernet-based
device below:
```
# EUI-48 based MAC addresses.
$ ip link show lo
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00

# tcpdump showing ethernet frames when sniffing loopback and logging the
# link-type as EN10MB (Ethernet).
$ sudo tcpdump -i lo -e -c 2 -n
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on lo, link-type EN10MB (Ethernet), snapshot length 262144 bytes
03:09:05.002034 00:00:00:00:00:00 > 00:00:00:00:00:00, ethertype IPv4 (0x0800), length 66: 127.0.0.1.9557 > 127.0.0.1.36828: Flags [.], ack 3562800815, win 15342, options [nop,nop,TS val 843174495 ecr 843159493], length 0
03:09:05.002094 00:00:00:00:00:00 > 00:00:00:00:00:00, ethertype IPv4 (0x0800), length 66: 127.0.0.1.36828 > 127.0.0.1.9557: Flags [.], ack 1, win 6160, options [nop,nop,TS val 843174496 ecr 843159493], length 0
2 packets captured
116 packets received by filter
0 packets dropped by kernel
```

Wireshark shows a similar result as the tcpdump example above.

Linux's loopback setup: 5bfc75d92e/drivers/net/loopback.c (L162)

PiperOrigin-RevId: 391836719
This commit is contained in:
Ghanan Gowripalan 2021-08-19 13:51:40 -07:00 committed by gVisor bot
parent 50ed6b2e09
commit 3b4bb94751
7 changed files with 43 additions and 27 deletions

View File

@ -49,9 +49,9 @@ const (
// EthernetAddressSize is the size, in bytes, of an ethernet address.
EthernetAddressSize = 6
// unspecifiedEthernetAddress is the unspecified ethernet address
// UnspecifiedEthernetAddress is the unspecified ethernet address
// (all bits set to 0).
unspecifiedEthernetAddress = tcpip.LinkAddress("\x00\x00\x00\x00\x00\x00")
UnspecifiedEthernetAddress = tcpip.LinkAddress("\x00\x00\x00\x00\x00\x00")
// EthernetBroadcastAddress is an ethernet address that addresses every node
// on a local link.
@ -134,7 +134,7 @@ func IsValidUnicastEthernetAddress(addr tcpip.LinkAddress) bool {
return false
}
if addr == unspecifiedEthernetAddress {
if addr == UnspecifiedEthernetAddress {
return false
}

View File

@ -44,7 +44,7 @@ func TestIsValidUnicastEthernetAddress(t *testing.T) {
},
{
"Unspecified",
unspecifiedEthernetAddress,
UnspecifiedEthernetAddress,
false,
},
{
@ -91,7 +91,7 @@ func TestIsMulticastEthernetAddress(t *testing.T) {
},
{
"Unspecified",
unspecifiedEthernetAddress,
UnspecifiedEthernetAddress,
false,
},
{

View File

@ -42,6 +42,14 @@ type Endpoint struct {
nested.Endpoint
}
// LinkAddress implements stack.LinkEndpoint.
func (e *Endpoint) LinkAddress() tcpip.LinkAddress {
if l := e.Endpoint.LinkAddress(); len(l) != 0 {
return l
}
return header.UnspecifiedEthernetAddress
}
// DeliverNetworkPacket implements stack.NetworkDispatcher.
func (e *Endpoint) DeliverNetworkPacket(_, _ tcpip.LinkAddress, _ tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) {
hdr, ok := pkt.LinkHeader().Consume(header.EthernetMinimumSize)
@ -57,18 +65,22 @@ func (e *Endpoint) DeliverNetworkPacket(_, _ tcpip.LinkAddress, _ tcpip.NetworkP
// Capabilities implements stack.LinkEndpoint.
func (e *Endpoint) Capabilities() stack.LinkEndpointCapabilities {
return stack.CapabilityResolutionRequired | e.Endpoint.Capabilities()
c := e.Endpoint.Capabilities()
if c&stack.CapabilityLoopback == 0 {
c |= stack.CapabilityResolutionRequired
}
return c
}
// WritePacket implements stack.LinkEndpoint.
func (e *Endpoint) WritePacket(r stack.RouteInfo, proto tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) tcpip.Error {
e.AddHeader(e.Endpoint.LinkAddress(), r.RemoteLinkAddress, proto, pkt)
e.AddHeader(e.LinkAddress(), r.RemoteLinkAddress, proto, pkt)
return e.Endpoint.WritePacket(r, proto, pkt)
}
// WritePackets implements stack.LinkEndpoint.
func (e *Endpoint) WritePackets(r stack.RouteInfo, pkts stack.PacketBufferList, proto tcpip.NetworkProtocolNumber) (int, tcpip.Error) {
linkAddr := e.Endpoint.LinkAddress()
linkAddr := e.LinkAddress()
for pkt := pkts.Front(); pkt != nil; pkt = pkt.Next() {
e.AddHeader(linkAddr, r.RemoteLinkAddress, proto, pkt)
@ -83,7 +95,10 @@ func (e *Endpoint) MaxHeaderLength() uint16 {
}
// ARPHardwareType implements stack.LinkEndpoint.
func (*Endpoint) ARPHardwareType() header.ARPHardwareType {
func (e *Endpoint) ARPHardwareType() header.ARPHardwareType {
if a := e.Endpoint.ARPHardwareType(); a != header.ARPHardwareNone {
return a
}
return header.ARPHardwareEther
}

View File

@ -321,28 +321,26 @@ func (e *endpoint) write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, tcp
}
defer route.Release()
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
ReserveHeaderBytes: int(route.MaxHeaderLength()),
Data: buffer.View(payloadBytes).ToVectorisedView(),
})
pkt.Owner = owner
if e.ops.GetHeaderIncluded() {
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
Data: buffer.View(payloadBytes).ToVectorisedView(),
})
if err := route.WriteHeaderIncludedPacket(pkt); err != nil {
return 0, err
}
} else {
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
ReserveHeaderBytes: int(route.MaxHeaderLength()),
Data: buffer.View(payloadBytes).ToVectorisedView(),
})
pkt.Owner = owner
if err := route.WritePacket(stack.NetworkHeaderParams{
Protocol: e.TransProto,
TTL: route.DefaultTTL(),
TOS: stack.DefaultTOS,
}, pkt); err != nil {
return 0, err
}
return int64(len(payloadBytes)), nil
}
if err := route.WritePacket(stack.NetworkHeaderParams{
Protocol: e.TransProto,
TTL: route.DefaultTTL(),
TOS: stack.DefaultTOS,
}, pkt); err != nil {
return 0, err
}
return int64(len(payloadBytes)), nil
}

View File

@ -97,6 +97,7 @@ go_library(
"//pkg/sentry/watchdog",
"//pkg/sync",
"//pkg/tcpip",
"//pkg/tcpip/link/ethernet",
"//pkg/tcpip/link/fdbased",
"//pkg/tcpip/link/loopback",
"//pkg/tcpip/link/packetsocket",

View File

@ -58,6 +58,7 @@ import (
"gvisor.dev/gvisor/pkg/sentry/watchdog"
"gvisor.dev/gvisor/pkg/sync"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/link/ethernet"
"gvisor.dev/gvisor/pkg/tcpip/link/loopback"
"gvisor.dev/gvisor/pkg/tcpip/link/sniffer"
"gvisor.dev/gvisor/pkg/tcpip/network/arp"
@ -1174,7 +1175,7 @@ func (f *sandboxNetstackCreator) CreateStack() (inet.Stack, error) {
n := &Network{Stack: s.(*netstack.Stack).Stack}
nicID := tcpip.NICID(f.uniqueID.UniqueID())
link := DefaultLoopbackLink
linkEP := loopback.New()
linkEP := ethernet.New(loopback.New())
if err := n.createNICWithAddrs(nicID, link.Name, linkEP, link.Addresses); err != nil {
return nil, err
}

View File

@ -23,6 +23,7 @@ import (
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/link/ethernet"
"gvisor.dev/gvisor/pkg/tcpip/link/fdbased"
"gvisor.dev/gvisor/pkg/tcpip/link/loopback"
"gvisor.dev/gvisor/pkg/tcpip/link/packetsocket"
@ -169,7 +170,7 @@ func (n *Network) CreateLinksAndRoutes(args *CreateLinksAndRoutesArgs, _ *struct
nicID++
nicids[link.Name] = nicID
linkEP := loopback.New()
linkEP := ethernet.New(loopback.New())
log.Infof("Enabling loopback interface %q with id %d on addresses %+v", link.Name, nicID, link.Addresses)
if err := n.createNICWithAddrs(nicID, link.Name, linkEP, link.Addresses); err != nil {