Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide NetworkEndpoints with an NetworkInterface interface #3789

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 17 additions & 6 deletions pkg/tcpip/network/arp/arp.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,23 @@ const (

// endpoint implements stack.NetworkEndpoint.
type endpoint struct {
stack.AddressableEndpoint

protocol *protocol
nicID tcpip.NICID
linkEP stack.LinkEndpoint
linkAddrCache stack.LinkAddressCache
nud stack.NUDHandler
}

func (*endpoint) Enable() *tcpip.Error {
return nil
}

func (*endpoint) Disable() *tcpip.Error {
return nil
}

// DefaultTTL is unused for ARP. It implements stack.NetworkEndpoint.
func (e *endpoint) DefaultTTL() uint8 {
return 0
Expand Down Expand Up @@ -168,13 +178,14 @@ func (*protocol) ParseAddresses(v buffer.View) (src, dst tcpip.Address) {
return tcpip.Address(h.ProtocolAddressSender()), ProtocolAddress
}

func (p *protocol) NewEndpoint(nicID tcpip.NICID, linkAddrCache stack.LinkAddressCache, nud stack.NUDHandler, dispatcher stack.TransportDispatcher, sender stack.LinkEndpoint, st *stack.Stack) stack.NetworkEndpoint {
func (p *protocol) NewEndpoint(nic stack.NetworkInterface, linkAddrCache stack.LinkAddressCache, nud stack.NUDHandler, dispatcher stack.TransportDispatcher, sender stack.LinkEndpoint, st *stack.Stack) stack.NetworkEndpoint {
return &endpoint{
protocol: p,
nicID: nicID,
linkEP: sender,
linkAddrCache: linkAddrCache,
nud: nud,
AddressableEndpoint: stack.NewAddressableEndpoint(),
protocol: p,
nicID: nic.ID(),
linkEP: sender,
linkAddrCache: linkAddrCache,
nud: nud,
}
}

Expand Down
26 changes: 19 additions & 7 deletions pkg/tcpip/network/ip_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,10 +247,22 @@ func buildDummyStack(t *testing.T) *stack.Stack {
return s
}

var testNIC stack.NetworkInterface = (*testInterface)(nil)

type testInterface struct{}

func (*testInterface) ID() tcpip.NICID {
return nicID
}

func (*testInterface) IsLoopback() bool {
return false
}

func TestIPv4Send(t *testing.T) {
o := testObject{t: t, v4: true}
proto := ipv4.NewProtocol()
ep := proto.NewEndpoint(nicID, nil, nil, nil, &o, buildDummyStack(t))
ep := proto.NewEndpoint(testNIC, nil, nil, nil, &o, buildDummyStack(t))
defer ep.Close()

// Allocate and initialize the payload view.
Expand Down Expand Up @@ -287,7 +299,7 @@ func TestIPv4Send(t *testing.T) {
func TestIPv4Receive(t *testing.T) {
o := testObject{t: t, v4: true}
proto := ipv4.NewProtocol()
ep := proto.NewEndpoint(nicID, nil, nil, &o, nil, buildDummyStack(t))
ep := proto.NewEndpoint(testNIC, nil, nil, &o, nil, buildDummyStack(t))
defer ep.Close()

totalLen := header.IPv4MinimumSize + 30
Expand Down Expand Up @@ -357,7 +369,7 @@ func TestIPv4ReceiveControl(t *testing.T) {
t.Run(c.name, func(t *testing.T) {
o := testObject{t: t}
proto := ipv4.NewProtocol()
ep := proto.NewEndpoint(nicID, nil, nil, &o, nil, buildDummyStack(t))
ep := proto.NewEndpoint(testNIC, nil, nil, &o, nil, buildDummyStack(t))
defer ep.Close()

const dataOffset = header.IPv4MinimumSize*2 + header.ICMPv4MinimumSize
Expand Down Expand Up @@ -418,7 +430,7 @@ func TestIPv4ReceiveControl(t *testing.T) {
func TestIPv4FragmentationReceive(t *testing.T) {
o := testObject{t: t, v4: true}
proto := ipv4.NewProtocol()
ep := proto.NewEndpoint(nicID, nil, nil, &o, nil, buildDummyStack(t))
ep := proto.NewEndpoint(testNIC, nil, nil, &o, nil, buildDummyStack(t))
defer ep.Close()

totalLen := header.IPv4MinimumSize + 24
Expand Down Expand Up @@ -495,7 +507,7 @@ func TestIPv4FragmentationReceive(t *testing.T) {
func TestIPv6Send(t *testing.T) {
o := testObject{t: t}
proto := ipv6.NewProtocol()
ep := proto.NewEndpoint(nicID, nil, nil, &o, channel.New(0, 1280, ""), buildDummyStack(t))
ep := proto.NewEndpoint(testNIC, nil, nil, &o, channel.New(0, 1280, ""), buildDummyStack(t))
defer ep.Close()

// Allocate and initialize the payload view.
Expand Down Expand Up @@ -532,7 +544,7 @@ func TestIPv6Send(t *testing.T) {
func TestIPv6Receive(t *testing.T) {
o := testObject{t: t}
proto := ipv6.NewProtocol()
ep := proto.NewEndpoint(nicID, nil, nil, &o, nil, buildDummyStack(t))
ep := proto.NewEndpoint(testNIC, nil, nil, &o, nil, buildDummyStack(t))
defer ep.Close()

totalLen := header.IPv6MinimumSize + 30
Expand Down Expand Up @@ -611,7 +623,7 @@ func TestIPv6ReceiveControl(t *testing.T) {
t.Run(c.name, func(t *testing.T) {
o := testObject{t: t}
proto := ipv6.NewProtocol()
ep := proto.NewEndpoint(nicID, nil, nil, &o, nil, buildDummyStack(t))
ep := proto.NewEndpoint(testNIC, nil, nil, &o, nil, buildDummyStack(t))
defer ep.Close()

dataOffset := header.IPv6MinimumSize*2 + header.ICMPv6MinimumSize
Expand Down
1 change: 1 addition & 0 deletions pkg/tcpip/network/ipv4/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ go_library(
],
visibility = ["//visibility:public"],
deps = [
"//pkg/sync",
"//pkg/tcpip",
"//pkg/tcpip/buffer",
"//pkg/tcpip/header",
Expand Down
197 changes: 191 additions & 6 deletions pkg/tcpip/network/ipv4/ipv4.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ package ipv4
import (
"sync/atomic"

"gvisor.dev/gvisor/pkg/sync"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/buffer"
"gvisor.dev/gvisor/pkg/tcpip/header"
Expand Down Expand Up @@ -50,23 +51,84 @@ const (
fragmentblockSize = 8
)

var _ stack.GroupAddressableEndpoint = (*endpoint)(nil)
var _ stack.AddressableEndpoint = (*endpoint)(nil)
var _ stack.NetworkEndpoint = (*endpoint)(nil)

type endpoint struct {
nicID tcpip.NICID
nic stack.NetworkInterface
linkEP stack.LinkEndpoint
dispatcher stack.TransportDispatcher
protocol *protocol
stack *stack.Stack

mu struct {
sync.RWMutex
ep stack.AddressableEndpoint
gep stack.GroupAddressableEndpoint
}
}

// NewEndpoint creates a new ipv4 endpoint.
func (p *protocol) NewEndpoint(nicID tcpip.NICID, _ stack.LinkAddressCache, _ stack.NUDHandler, dispatcher stack.TransportDispatcher, linkEP stack.LinkEndpoint, st *stack.Stack) stack.NetworkEndpoint {
return &endpoint{
nicID: nicID,
func (p *protocol) NewEndpoint(nic stack.NetworkInterface, _ stack.LinkAddressCache, _ stack.NUDHandler, dispatcher stack.TransportDispatcher, linkEP stack.LinkEndpoint, st *stack.Stack) stack.NetworkEndpoint {
e := &endpoint{
nic: nic,
linkEP: linkEP,
dispatcher: dispatcher,
protocol: p,
stack: st,
}
e.mu.ep = stack.NewAddressableEndpointWithLock(&e.mu)
e.mu.gep = stack.NewGroupAddressableEndpoint(e.mu.ep)
return e
}

var ipv4BroadcastAddr = tcpip.AddressWithPrefix{
Address: header.IPv4Broadcast,
PrefixLen: 8 * header.IPv4AddressSize,
}

// Enable implements stack.NetworkEndpoint.
func (e *endpoint) Enable() *tcpip.Error {
e.mu.Lock()
defer e.mu.Unlock()

// Create an endpoint to receive broadcast packets on this interface.
if _, err := e.mu.ep.AddAddress(ipv4BroadcastAddr, stack.AddAddressOptions{
Deprecated: false,
ConfigType: stack.AddressConfigStatic,
Kind: stack.Permanent,
PEB: stack.NeverPrimaryEndpoint,
}); err != nil {
return err
}

// As per RFC 1122 section 3.3.7, all hosts should join the all-hosts
// multicast group. Note, the IANA calls the all-hosts multicast group the
// all-systems multicast group.
if _, err := e.mu.gep.JoinGroup(header.IPv4AllSystems); err != nil {
return err
}

return nil
}

// Disable implements stack.NetworkEndpoint.
func (e *endpoint) Disable() *tcpip.Error {
e.mu.Lock()
defer e.mu.Unlock()

// The NIC may have already left the multicast group.
if _, err := e.mu.gep.LeaveGroup(header.IPv4AllSystems, false /* force */); err != nil && err != tcpip.ErrBadLocalAddress {
return err
}

// The address may have already been removed.o
if err := e.mu.ep.RemoveAddress(ipv4BroadcastAddr.Address); err != nil && err != tcpip.ErrBadLocalAddress {
return err
}

return nil
}

// DefaultTTL is the default time-to-live value for this endpoint.
Expand All @@ -80,14 +142,14 @@ func (e *endpoint) MTU() uint32 {
return calculateMTU(e.linkEP.MTU())
}

// Capabilities implements stack.NetworkEndpoint.Capabilities.
// Capabilities implements stack.NetworkEndpoint.
func (e *endpoint) Capabilities() stack.LinkEndpointCapabilities {
return e.linkEP.Capabilities()
}

// NICID returns the ID of the NIC this endpoint belongs to.
func (e *endpoint) NICID() tcpip.NICID {
return e.nicID
return e.nic.ID()
}

// MaxHeaderLength returns the maximum length needed by ipv4 headers (and
Expand Down Expand Up @@ -452,6 +514,129 @@ func (e *endpoint) HandlePacket(r *stack.Route, pkt *stack.PacketBuffer) {
// Close cleans up resources associated with the endpoint.
func (e *endpoint) Close() {}

// AddAddress implements stack.AddressableEndpoint.
func (e *endpoint) AddAddress(addr tcpip.AddressWithPrefix, opts stack.AddAddressOptions) (stack.AddressEndpoint, *tcpip.Error) {
e.mu.Lock()
defer e.mu.Unlock()
return e.mu.ep.AddAddress(addr, opts)
}

// RemoveAddress implements stack.AddressableEndpoint.
func (e *endpoint) RemoveAddress(addr tcpip.Address) *tcpip.Error {
e.mu.Lock()
defer e.mu.Unlock()
return e.mu.ep.RemoveAddress(addr)
}

// HasAddress implements stack.AddressableEndpoint.
func (e *endpoint) HasAddress(addr tcpip.Address) bool {
e.mu.RLock()
defer e.mu.RUnlock()
return e.mu.ep.HasAddress(addr)
}

// PrimaryEndpoints implements stack.AddressableEndpoint.
func (e *endpoint) PrimaryEndpoints() []stack.AddressEndpoint {
e.mu.RLock()
defer e.mu.RUnlock()
return e.mu.ep.PrimaryEndpoints()
}

// AllEndpoints implements stack.AddressableEndpoint.
func (e *endpoint) AllEndpoints() []stack.AddressEndpoint {
e.mu.RLock()
defer e.mu.RUnlock()
return e.mu.ep.AllEndpoints()
}

// GetEndpoint implements stack.AddressableEndpoint.
func (e *endpoint) GetEndpoint(localAddr tcpip.Address) stack.AddressEndpoint {
e.mu.RLock()
defer e.mu.RUnlock()
return e.mu.ep.GetEndpoint(localAddr)
}

// GetAssignedEndpoint implements stack.AddressableEndpoint.
func (e *endpoint) GetAssignedEndpoint(localAddr tcpip.Address, allowAnyInSubnet, allowTemp bool, tempPEB stack.PrimaryEndpointBehavior) stack.AddressEndpoint {
e.mu.Lock()
defer e.mu.Unlock()

if r := e.mu.ep.GetAssignedEndpoint(localAddr, allowAnyInSubnet, allowTemp, tempPEB); r != nil {
return r
}

eps := e.mu.ep.AllEndpoints()
for _, r := range eps {
addr := r.AddressWithPrefix()
subnet := addr.Subnet()
if subnet.IsBroadcast(localAddr) && r.IsAssigned(allowTemp) && r.IncRef() {
return r
}
}

return nil
}

// PrimaryEndpoint implements stack.AddressableEndpoint.
func (e *endpoint) PrimaryEndpoint(remoteAddr tcpip.Address, spoofingOrPromiscuous bool) stack.AddressEndpoint {
e.mu.RLock()
defer e.mu.RUnlock()
return e.mu.ep.PrimaryEndpoint(remoteAddr, spoofingOrPromiscuous)
}

// PrimaryAddresses implements stack.AddressableEndpoint.
func (e *endpoint) PrimaryAddresses() []tcpip.AddressWithPrefix {
e.mu.RLock()
defer e.mu.RUnlock()
return e.mu.ep.PrimaryAddresses()
}

// AllAddresses implements stack.AddressableEndpoint.
func (e *endpoint) AllAddresses() []tcpip.AddressWithPrefix {
e.mu.RLock()
defer e.mu.RUnlock()
return e.mu.ep.AllAddresses()
}

// RemoveAllAddresses implements stack.AddressableEndpoint.
func (e *endpoint) RemoveAllAddresses() *tcpip.Error {
e.mu.RLock()
defer e.mu.RUnlock()
return e.mu.ep.RemoveAllAddresses()
}

// JoinGroup implements stack.GroupAddressableEndpoint.
func (e *endpoint) JoinGroup(addr tcpip.Address) (bool, *tcpip.Error) {
if !header.IsV4MulticastAddress(addr) {
return false, tcpip.ErrBadAddress
}

e.mu.Lock()
defer e.mu.Unlock()
return e.mu.gep.JoinGroup(addr)
}

// LeaveGroup implements stack.GroupAddressableEndpoint.
func (e *endpoint) LeaveGroup(addr tcpip.Address, force bool) (bool, *tcpip.Error) {
e.mu.Lock()
defer e.mu.Unlock()
return e.mu.gep.LeaveGroup(addr, force)
}

// IsInGroup implements stack.GroupAddressableEndpoint.
func (e *endpoint) IsInGroup(addr tcpip.Address) bool {
e.mu.RLock()
defer e.mu.RUnlock()
return e.mu.gep.IsInGroup(addr)
}

// LeaveAllGroups implements stack.GroupAddressableEndpoint.
func (e *endpoint) LeaveAllGroups() *tcpip.Error {
e.mu.Lock()
defer e.mu.Unlock()
return e.mu.gep.LeaveAllGroups()
}

type protocol struct {
ids []uint32
hashIV uint32
Expand Down
1 change: 1 addition & 0 deletions pkg/tcpip/network/ipv6/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ go_library(
],
visibility = ["//visibility:public"],
deps = [
"//pkg/sync",
"//pkg/tcpip",
"//pkg/tcpip/buffer",
"//pkg/tcpip/header",
Expand Down
Loading