summaryrefslogtreecommitdiffhomepage
path: root/pkg
diff options
context:
space:
mode:
authorFabricio Voznika <fvoznika@google.com>2018-08-08 22:38:41 -0700
committerShentubot <shentubot@google.com>2018-08-08 22:39:58 -0700
commit4e171f7590284c1f4cedf90c92204873961b2e97 (patch)
tree98fef86694288ce258985ce7ff4772ee23566874 /pkg
parent48b5b35b2bd46ecd043f95d5f470da71046af760 (diff)
Basic support for ip link/addr and ifconfig
Closes #94 PiperOrigin-RevId: 207997580 Change-Id: I19b426f1586b5ec12f8b0cd5884d5b401d334924
Diffstat (limited to 'pkg')
-rw-r--r--pkg/abi/linux/netlink_route.go5
-rw-r--r--pkg/sentry/inet/inet.go3
-rw-r--r--pkg/sentry/socket/epsocket/epsocket.go52
-rw-r--r--pkg/sentry/socket/epsocket/stack.go12
-rw-r--r--pkg/sentry/socket/netlink/route/protocol.go15
-rw-r--r--pkg/sentry/socket/netlink/socket.go64
-rw-r--r--pkg/tcpip/link/loopback/loopback.go2
-rw-r--r--pkg/tcpip/stack/nic.go7
-rw-r--r--pkg/tcpip/stack/registration.go1
-rw-r--r--pkg/tcpip/stack/stack.go36
10 files changed, 135 insertions, 62 deletions
diff --git a/pkg/abi/linux/netlink_route.go b/pkg/abi/linux/netlink_route.go
index 0d88bc5c5..a5d778748 100644
--- a/pkg/abi/linux/netlink_route.go
+++ b/pkg/abi/linux/netlink_route.go
@@ -184,3 +184,8 @@ const (
IFA_MULTICAST = 7
IFA_FLAGS = 8
)
+
+// Device types, from uapi/linux/if_arp.h.
+const (
+ ARPHRD_LOOPBACK = 772
+)
diff --git a/pkg/sentry/inet/inet.go b/pkg/sentry/inet/inet.go
index e54a61196..30ca4e0c0 100644
--- a/pkg/sentry/inet/inet.go
+++ b/pkg/sentry/inet/inet.go
@@ -67,6 +67,9 @@ type Interface struct {
// Addr is the hardware device address.
Addr []byte
+
+ // MTU is the maximum transmission unit.
+ MTU uint32
}
// InterfaceAddr contains information about a network interface address.
diff --git a/pkg/sentry/socket/epsocket/epsocket.go b/pkg/sentry/socket/epsocket/epsocket.go
index f969a1d7c..b32eda96f 100644
--- a/pkg/sentry/socket/epsocket/epsocket.go
+++ b/pkg/sentry/socket/epsocket/epsocket.go
@@ -48,7 +48,7 @@ import (
"gvisor.googlesource.com/gvisor/pkg/syserror"
"gvisor.googlesource.com/gvisor/pkg/tcpip"
"gvisor.googlesource.com/gvisor/pkg/tcpip/buffer"
- nstack "gvisor.googlesource.com/gvisor/pkg/tcpip/stack"
+ "gvisor.googlesource.com/gvisor/pkg/tcpip/stack"
"gvisor.googlesource.com/gvisor/pkg/tcpip/transport/unix"
"gvisor.googlesource.com/gvisor/pkg/waiter"
)
@@ -452,7 +452,7 @@ func (s *SocketOperations) GetSockOpt(t *kernel.Task, level, name, outLen int) (
// sockets backed by a commonEndpoint.
func GetSockOpt(t *kernel.Task, s socket.Socket, ep commonEndpoint, family int, skType unix.SockType, level, name, outLen int) (interface{}, *syserr.Error) {
switch level {
- case syscall.SOL_SOCKET:
+ case linux.SOL_SOCKET:
switch name {
case linux.SO_TYPE:
if outLen < sizeOfInt32 {
@@ -634,7 +634,7 @@ func (s *SocketOperations) SetSockOpt(t *kernel.Task, level int, name int, optVa
// sockets backed by a commonEndpoint.
func SetSockOpt(t *kernel.Task, s socket.Socket, ep commonEndpoint, level int, name int, optVal []byte) *syserr.Error {
switch level {
- case syscall.SOL_SOCKET:
+ case linux.SOL_SOCKET:
switch name {
case linux.SO_SNDBUF:
if len(optVal) < sizeOfInt32 {
@@ -1191,7 +1191,9 @@ func interfaceIoctl(ctx context.Context, io usermem.IO, arg int, ifr *linux.IFRe
if err != nil {
return err
}
- usermem.ByteOrder.PutUint16(ifr.Data[:2], f)
+ // Drop the flags that don't fit in the size that we need to return. This
+ // matches Linux behavior.
+ usermem.ByteOrder.PutUint16(ifr.Data[:2], uint16(f))
case syscall.SIOCGIFADDR:
// Copy the IPv4 address out.
@@ -1304,7 +1306,7 @@ func ifconfIoctl(ctx context.Context, io usermem.IO, ifc *linux.IFConf) error {
// interfaceStatusFlags returns status flags for an interface in the stack.
// Flag values and meanings are described in greater detail in netdevice(7) in
// the SIOCGIFFLAGS section.
-func interfaceStatusFlags(stack inet.Stack, name string) (uint16, *syserr.Error) {
+func interfaceStatusFlags(stack inet.Stack, name string) (uint32, *syserr.Error) {
// epsocket should only ever be passed an epsocket.Stack.
epstack, ok := stack.(*Stack)
if !ok {
@@ -1312,37 +1314,27 @@ func interfaceStatusFlags(stack inet.Stack, name string) (uint16, *syserr.Error)
}
// Find the NIC corresponding to this interface.
- var (
- nicid tcpip.NICID
- info nstack.NICInfo
- found bool
- )
- ns := epstack.Stack
- for nicid, info = range ns.NICInfo() {
+ for _, info := range epstack.Stack.NICInfo() {
if info.Name == name {
- found = true
- break
+ return nicStateFlagsToLinux(info.Flags), nil
}
}
- if !found {
- return 0, syserr.ErrNoDevice
- }
+ return 0, syserr.ErrNoDevice
+}
- // Set flags based on NIC state.
- nicFlags, err := ns.NICFlags(nicid)
- if err != nil {
- return 0, syserr.TranslateNetstackError(err)
+func nicStateFlagsToLinux(f stack.NICStateFlags) uint32 {
+ var rv uint32
+ if f.Up {
+ rv |= linux.IFF_UP | linux.IFF_LOWER_UP
}
-
- var retFlags uint16
- if nicFlags.Up {
- retFlags |= linux.IFF_UP
+ if f.Running {
+ rv |= linux.IFF_RUNNING
}
- if nicFlags.Running {
- retFlags |= linux.IFF_RUNNING
+ if f.Promiscuous {
+ rv |= linux.IFF_PROMISC
}
- if nicFlags.Promiscuous {
- retFlags |= linux.IFF_PROMISC
+ if f.Loopback {
+ rv |= linux.IFF_LOOPBACK
}
- return retFlags, nil
+ return rv
}
diff --git a/pkg/sentry/socket/epsocket/stack.go b/pkg/sentry/socket/epsocket/stack.go
index 12b4b4767..e4ed52fc8 100644
--- a/pkg/sentry/socket/epsocket/stack.go
+++ b/pkg/sentry/socket/epsocket/stack.go
@@ -41,10 +41,16 @@ func (s *Stack) SupportsIPv6() bool {
func (s *Stack) Interfaces() map[int32]inet.Interface {
is := make(map[int32]inet.Interface)
for id, ni := range s.Stack.NICInfo() {
+ var devType uint16
+ if ni.Flags.Loopback {
+ devType = linux.ARPHRD_LOOPBACK
+ }
is[int32(id)] = inet.Interface{
- Name: ni.Name,
- Addr: []byte(ni.LinkAddress),
- // TODO: Other fields.
+ Name: ni.Name,
+ Addr: []byte(ni.LinkAddress),
+ Flags: uint32(nicStateFlagsToLinux(ni.Flags)),
+ DeviceType: devType,
+ MTU: ni.MTU,
}
}
return is
diff --git a/pkg/sentry/socket/netlink/route/protocol.go b/pkg/sentry/socket/netlink/route/protocol.go
index 55a76e916..70322b9ed 100644
--- a/pkg/sentry/socket/netlink/route/protocol.go
+++ b/pkg/sentry/socket/netlink/route/protocol.go
@@ -16,6 +16,8 @@
package route
import (
+ "bytes"
+
"gvisor.googlesource.com/gvisor/pkg/abi/linux"
"gvisor.googlesource.com/gvisor/pkg/sentry/context"
"gvisor.googlesource.com/gvisor/pkg/sentry/inet"
@@ -97,9 +99,18 @@ func (p *Protocol) dumpLinks(ctx context.Context, hdr linux.NetlinkMessageHeader
})
m.PutAttrString(linux.IFLA_IFNAME, i.Name)
+ m.PutAttr(linux.IFLA_MTU, i.MTU)
+
+ mac := make([]byte, 6)
+ brd := mac
+ if len(i.Addr) > 0 {
+ mac = i.Addr
+ brd = bytes.Repeat([]byte{0xff}, len(i.Addr))
+ }
+ m.PutAttr(linux.IFLA_ADDRESS, mac)
+ m.PutAttr(linux.IFLA_BROADCAST, brd)
- // TODO: There are many more attributes, such as
- // MAC address.
+ // TODO: There are many more attributes.
}
return nil
diff --git a/pkg/sentry/socket/netlink/socket.go b/pkg/sentry/socket/netlink/socket.go
index e15d1546c..f3b2c7256 100644
--- a/pkg/sentry/socket/netlink/socket.go
+++ b/pkg/sentry/socket/netlink/socket.go
@@ -16,6 +16,7 @@
package netlink
import (
+ "math"
"sync"
"gvisor.googlesource.com/gvisor/pkg/abi/linux"
@@ -39,8 +40,18 @@ import (
"gvisor.googlesource.com/gvisor/pkg/waiter"
)
-// defaultSendBufferSize is the default size for the send buffer.
-const defaultSendBufferSize = 16 * 1024
+const sizeOfInt32 int = 4
+
+const (
+ // minBufferSize is the smallest size of a send buffer.
+ minSendBufferSize = 4 << 10 // 4096 bytes.
+
+ // defaultSendBufferSize is the default size for the send buffer.
+ defaultSendBufferSize = 16 * 1024
+
+ // maxBufferSize is the largest size a send buffer can grow to.
+ maxSendBufferSize = 4 << 20 // 4MB
+)
// netlinkSocketDevice is the netlink socket virtual device.
var netlinkSocketDevice = device.NewAnonDevice()
@@ -86,7 +97,7 @@ type Socket struct {
// sendBufferSize is the send buffer "size". We don't actually have a
// fixed buffer but only consume this many bytes.
- sendBufferSize uint64
+ sendBufferSize uint32
}
var _ socket.Socket = (*Socket)(nil)
@@ -273,13 +284,54 @@ func (s *Socket) Shutdown(t *kernel.Task, how int) *syserr.Error {
// GetSockOpt implements socket.Socket.GetSockOpt.
func (s *Socket) GetSockOpt(t *kernel.Task, level int, name int, outLen int) (interface{}, *syserr.Error) {
- // TODO: no sockopts supported.
+ switch level {
+ case linux.SOL_SOCKET:
+ switch name {
+ case linux.SO_SNDBUF:
+ if outLen < sizeOfInt32 {
+ return nil, syserr.ErrInvalidArgument
+ }
+ return int32(s.sendBufferSize), nil
+
+ case linux.SO_RCVBUF:
+ if outLen < sizeOfInt32 {
+ return nil, syserr.ErrInvalidArgument
+ }
+ // We don't have limit on receiving size.
+ return math.MaxInt32, nil
+ }
+ }
+ // TODO: other sockopts are not supported.
return nil, syserr.ErrProtocolNotAvailable
}
// SetSockOpt implements socket.Socket.SetSockOpt.
func (s *Socket) SetSockOpt(t *kernel.Task, level int, name int, opt []byte) *syserr.Error {
- // TODO: no sockopts supported.
+ switch level {
+ case linux.SOL_SOCKET:
+ switch name {
+ case linux.SO_SNDBUF:
+ if len(opt) < sizeOfInt32 {
+ return syserr.ErrInvalidArgument
+ }
+ size := usermem.ByteOrder.Uint32(opt)
+ if size < minSendBufferSize {
+ size = minSendBufferSize
+ } else if size > maxSendBufferSize {
+ size = maxSendBufferSize
+ }
+ s.sendBufferSize = size
+ return nil
+ case linux.SO_RCVBUF:
+ if len(opt) < sizeOfInt32 {
+ return syserr.ErrInvalidArgument
+ }
+ // We don't have limit on receiving size. So just accept anything as
+ // valid for compatibility.
+ return nil
+ }
+ }
+ // TODO: other sockopts are not supported.
return syserr.ErrProtocolNotAvailable
}
@@ -489,7 +541,7 @@ func (s *Socket) sendMsg(ctx context.Context, src usermem.IOSequence, to []byte,
// For simplicity, and consistency with Linux, we copy in the entire
// message up front.
- if uint64(src.NumBytes()) > s.sendBufferSize {
+ if src.NumBytes() > int64(s.sendBufferSize) {
return 0, syserr.ErrMessageTooLong
}
diff --git a/pkg/tcpip/link/loopback/loopback.go b/pkg/tcpip/link/loopback/loopback.go
index b4dc4833c..015275721 100644
--- a/pkg/tcpip/link/loopback/loopback.go
+++ b/pkg/tcpip/link/loopback/loopback.go
@@ -56,7 +56,7 @@ func (*endpoint) MTU() uint32 {
// Capabilities implements stack.LinkEndpoint.Capabilities. Loopback advertises
// itself as supporting checksum offload, but in reality it's just omitted.
func (*endpoint) Capabilities() stack.LinkEndpointCapabilities {
- return stack.CapabilityChecksumOffload | stack.CapabilitySaveRestore
+ return stack.CapabilityChecksumOffload | stack.CapabilitySaveRestore | stack.CapabilityLoopback
}
// MaxHeaderLength implements stack.LinkEndpoint.MaxHeaderLength. Given that the
diff --git a/pkg/tcpip/stack/nic.go b/pkg/tcpip/stack/nic.go
index c1480f97b..592006a32 100644
--- a/pkg/tcpip/stack/nic.go
+++ b/pkg/tcpip/stack/nic.go
@@ -67,6 +67,13 @@ func (n *NIC) setPromiscuousMode(enable bool) {
n.mu.Unlock()
}
+func (n *NIC) isPromiscuousMode() bool {
+ n.mu.RLock()
+ rv := n.promiscuous
+ n.mu.RUnlock()
+ return rv
+}
+
// setSpoofing enables or disables address spoofing.
func (n *NIC) setSpoofing(enable bool) {
n.mu.Lock()
diff --git a/pkg/tcpip/stack/registration.go b/pkg/tcpip/stack/registration.go
index 01a29689d..bbe887144 100644
--- a/pkg/tcpip/stack/registration.go
+++ b/pkg/tcpip/stack/registration.go
@@ -205,6 +205,7 @@ const (
CapabilityResolutionRequired
CapabilitySaveRestore
CapabilityDisconnectOk
+ CapabilityLoopback
)
// LinkEndpoint is the interface implemented by data link layer protocols (e.g.,
diff --git a/pkg/tcpip/stack/stack.go b/pkg/tcpip/stack/stack.go
index 6c4aa7cc5..e2b9dc2c0 100644
--- a/pkg/tcpip/stack/stack.go
+++ b/pkg/tcpip/stack/stack.go
@@ -563,6 +563,12 @@ type NICInfo struct {
Name string
LinkAddress tcpip.LinkAddress
ProtocolAddresses []tcpip.ProtocolAddress
+
+ // Flags indicate the state of the NIC.
+ Flags NICStateFlags
+
+ // MTU is the maximum transmission unit.
+ MTU uint32
}
// NICInfo returns a map of NICIDs to their associated information.
@@ -572,10 +578,18 @@ func (s *Stack) NICInfo() map[tcpip.NICID]NICInfo {
nics := make(map[tcpip.NICID]NICInfo)
for id, nic := range s.nics {
+ flags := NICStateFlags{
+ Up: true, // Netstack interfaces are always up.
+ Running: nic.linkEP.IsAttached(),
+ Promiscuous: nic.isPromiscuousMode(),
+ Loopback: nic.linkEP.Capabilities()&CapabilityLoopback != 0,
+ }
nics[id] = NICInfo{
Name: nic.name,
LinkAddress: nic.linkEP.LinkAddress(),
ProtocolAddresses: nic.Addresses(),
+ Flags: flags,
+ MTU: nic.linkEP.MTU(),
}
}
return nics
@@ -591,27 +605,9 @@ type NICStateFlags struct {
// Promiscuous indicates whether the interface is in promiscuous mode.
Promiscuous bool
-}
-
-// NICFlags returns flags about the state of the NIC. It returns an error if
-// the NIC corresponding to id cannot be found.
-func (s *Stack) NICFlags(id tcpip.NICID) (NICStateFlags, *tcpip.Error) {
- s.mu.RLock()
- defer s.mu.RUnlock()
- nic := s.nics[id]
- if nic == nil {
- return NICStateFlags{}, tcpip.ErrUnknownNICID
- }
-
- ret := NICStateFlags{
- // Netstack interfaces are always up.
- Up: true,
-
- Running: nic.linkEP.IsAttached(),
- Promiscuous: nic.promiscuous,
- }
- return ret, nil
+ // Loopback indicates whether the interface is a loopback.
+ Loopback bool
}
// AddAddress adds a new network-layer address to the specified NIC.