diff options
-rw-r--r-- | pkg/abi/linux/netlink_route.go | 5 | ||||
-rw-r--r-- | pkg/sentry/inet/inet.go | 3 | ||||
-rw-r--r-- | pkg/sentry/socket/epsocket/epsocket.go | 52 | ||||
-rw-r--r-- | pkg/sentry/socket/epsocket/stack.go | 12 | ||||
-rw-r--r-- | pkg/sentry/socket/netlink/route/protocol.go | 15 | ||||
-rw-r--r-- | pkg/sentry/socket/netlink/socket.go | 64 | ||||
-rw-r--r-- | pkg/tcpip/link/loopback/loopback.go | 2 | ||||
-rw-r--r-- | pkg/tcpip/stack/nic.go | 7 | ||||
-rw-r--r-- | pkg/tcpip/stack/registration.go | 1 | ||||
-rw-r--r-- | pkg/tcpip/stack/stack.go | 36 | ||||
-rw-r--r-- | runsc/boot/network.go | 5 |
11 files changed, 138 insertions, 64 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. diff --git a/runsc/boot/network.go b/runsc/boot/network.go index d702ae74e..0e43c91be 100644 --- a/runsc/boot/network.go +++ b/runsc/boot/network.go @@ -133,15 +133,16 @@ func (n *Network) CreateLinksAndRoutes(args *CreateLinksAndRoutesArgs, _ *struct return fmt.Errorf("failed to dup FD %v: %v", oldFD, err) } + mac := tcpip.LinkAddress(generateRndMac()) linkEP := fdbased.New(&fdbased.Options{ FD: newFD, MTU: uint32(link.MTU), EthernetHeader: true, HandleLocal: true, - Address: tcpip.LinkAddress(generateRndMac()), + Address: mac, }) - log.Infof("Enabling interface %q with id %d on addresses %+v", link.Name, nicID, link.Addresses) + log.Infof("Enabling interface %q with id %d on addresses %+v (%v)", link.Name, nicID, link.Addresses, mac) if err := n.createNICWithAddrs(nicID, link.Name, linkEP, link.Addresses); err != nil { return err } |