diff --git a/pkg/sentry/socket/epsocket/epsocket.go b/pkg/sentry/socket/epsocket/epsocket.go index 2495ba459..9ff9af0bc 100644 --- a/pkg/sentry/socket/epsocket/epsocket.go +++ b/pkg/sentry/socket/epsocket/epsocket.go @@ -48,12 +48,15 @@ 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/transport/unix" "gvisor.googlesource.com/gvisor/pkg/waiter" ) const sizeOfInt32 int = 4 +var errStackType = syserr.New("expected but did not receive an epsocket.Stack", linux.EINVAL) + // ntohs converts a 16-bit number from network byte order to host byte order. It // assumes that the host is little endian. func ntohs(v uint16) uint16 { @@ -1177,9 +1180,11 @@ func interfaceIoctl(ctx context.Context, io usermem.IO, arg int, ifr *linux.IFRe usermem.ByteOrder.PutUint16(ifr.Data[:2], uint16(n)) case syscall.SIOCGIFFLAGS: - // TODO: Implement. For now, return only that the - // device is up so that ifconfig prints it. - usermem.ByteOrder.PutUint16(ifr.Data[:2], linux.IFF_UP) + f, err := interfaceStatusFlags(stack, iface.Name) + if err != nil { + return err + } + usermem.ByteOrder.PutUint16(ifr.Data[:2], f) case syscall.SIOCGIFADDR: // Copy the IPv4 address out. @@ -1288,3 +1293,49 @@ func ifconfIoctl(ctx context.Context, io usermem.IO, ifc *linux.IFConf) error { } return nil } + +// 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) { + // epsocket should only ever be passed an epsocket.Stack. + epstack, ok := stack.(*Stack) + if !ok { + return 0, errStackType + } + + // 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() { + if info.Name == name { + found = true + break + } + } + if !found { + return 0, syserr.ErrNoDevice + } + + // Set flags based on NIC state. + nicFlags, err := ns.NICFlags(nicid) + if err != nil { + return 0, syserr.TranslateNetstackError(err) + } + + var retFlags uint16 + if nicFlags.Up { + retFlags |= linux.IFF_UP + } + if nicFlags.Running { + retFlags |= linux.IFF_RUNNING + } + if nicFlags.Promiscuous { + retFlags |= linux.IFF_PROMISC + } + return retFlags, nil +} diff --git a/pkg/tcpip/link/channel/channel.go b/pkg/tcpip/link/channel/channel.go index cebc34553..3f5440cc1 100644 --- a/pkg/tcpip/link/channel/channel.go +++ b/pkg/tcpip/link/channel/channel.go @@ -67,6 +67,11 @@ func (e *Endpoint) Attach(dispatcher stack.NetworkDispatcher) { e.dispatcher = dispatcher } +// IsAttached implements stack.LinkEndpoint.IsAttached. +func (e *Endpoint) IsAttached() bool { + return e.dispatcher != nil +} + // MTU implements stack.LinkEndpoint.MTU. It returns the value initialized // during construction. func (e *Endpoint) MTU() uint32 { diff --git a/pkg/tcpip/link/fdbased/endpoint.go b/pkg/tcpip/link/fdbased/endpoint.go index da74cd644..668514454 100644 --- a/pkg/tcpip/link/fdbased/endpoint.go +++ b/pkg/tcpip/link/fdbased/endpoint.go @@ -45,9 +45,10 @@ type endpoint struct { // its end of the communication pipe. closed func(*tcpip.Error) - vv *buffer.VectorisedView - iovecs []syscall.Iovec - views []buffer.View + vv *buffer.VectorisedView + iovecs []syscall.Iovec + views []buffer.View + attached bool } // Options specify the details about the fd-based endpoint to be created. @@ -96,9 +97,15 @@ func New(opts *Options) tcpip.LinkEndpointID { // Attach launches the goroutine that reads packets from the file descriptor and // dispatches them via the provided dispatcher. func (e *endpoint) Attach(dispatcher stack.NetworkDispatcher) { + e.attached = true go e.dispatchLoop(dispatcher) // S/R-FIXME } +// IsAttached implements stack.LinkEndpoint.IsAttached. +func (e *endpoint) IsAttached() bool { + return e.attached +} + // MTU implements stack.LinkEndpoint.MTU. It returns the value initialized // during construction. func (e *endpoint) MTU() uint32 { diff --git a/pkg/tcpip/link/loopback/loopback.go b/pkg/tcpip/link/loopback/loopback.go index 1a9cd09d7..f38847949 100644 --- a/pkg/tcpip/link/loopback/loopback.go +++ b/pkg/tcpip/link/loopback/loopback.go @@ -32,6 +32,11 @@ func (e *endpoint) Attach(dispatcher stack.NetworkDispatcher) { e.dispatcher = dispatcher } +// IsAttached implements stack.LinkEndpoint.IsAttached. +func (e *endpoint) IsAttached() bool { + return e.dispatcher != nil +} + // MTU implements stack.LinkEndpoint.MTU. It returns a constant that matches the // linux loopback interface. func (*endpoint) MTU() uint32 { diff --git a/pkg/tcpip/link/sharedmem/sharedmem.go b/pkg/tcpip/link/sharedmem/sharedmem.go index 2c0f1b294..5369ebc68 100644 --- a/pkg/tcpip/link/sharedmem/sharedmem.go +++ b/pkg/tcpip/link/sharedmem/sharedmem.go @@ -137,6 +137,13 @@ func (e *endpoint) Attach(dispatcher stack.NetworkDispatcher) { e.mu.Unlock() } +// IsAttached implements stack.LinkEndpoint.IsAttached. +func (e *endpoint) IsAttached() bool { + e.mu.Lock() + defer e.mu.Unlock() + return e.workerStarted +} + // MTU implements stack.LinkEndpoint.MTU. It returns the value initialized // during construction. func (e *endpoint) MTU() uint32 { diff --git a/pkg/tcpip/link/sniffer/sniffer.go b/pkg/tcpip/link/sniffer/sniffer.go index 72d9a0f1c..3a40081c0 100644 --- a/pkg/tcpip/link/sniffer/sniffer.go +++ b/pkg/tcpip/link/sniffer/sniffer.go @@ -143,6 +143,11 @@ func (e *endpoint) Attach(dispatcher stack.NetworkDispatcher) { e.lower.Attach(e) } +// IsAttached implements stack.LinkEndpoint.IsAttached. +func (e *endpoint) IsAttached() bool { + return e.dispatcher != nil +} + // MTU implements stack.LinkEndpoint.MTU. It just forwards the request to the // lower endpoint. func (e *endpoint) MTU() uint32 { diff --git a/pkg/tcpip/link/waitable/waitable.go b/pkg/tcpip/link/waitable/waitable.go index 2c6e73f22..91aed7a12 100644 --- a/pkg/tcpip/link/waitable/waitable.go +++ b/pkg/tcpip/link/waitable/waitable.go @@ -58,6 +58,11 @@ func (e *Endpoint) Attach(dispatcher stack.NetworkDispatcher) { e.lower.Attach(e) } +// IsAttached implements stack.LinkEndpoint.IsAttached. +func (e *Endpoint) IsAttached() bool { + return e.dispatcher != nil +} + // MTU implements stack.LinkEndpoint.MTU. It just forwards the request to the // lower endpoint. func (e *Endpoint) MTU() uint32 { diff --git a/pkg/tcpip/link/waitable/waitable_test.go b/pkg/tcpip/link/waitable/waitable_test.go index cb433dc19..188049322 100644 --- a/pkg/tcpip/link/waitable/waitable_test.go +++ b/pkg/tcpip/link/waitable/waitable_test.go @@ -34,6 +34,11 @@ func (e *countedEndpoint) Attach(dispatcher stack.NetworkDispatcher) { e.dispatcher = dispatcher } +// IsAttached implements stack.LinkEndpoint.IsAttached. +func (e *countedEndpoint) IsAttached() bool { + return e.dispatcher != nil +} + func (e *countedEndpoint) MTU() uint32 { return e.mtu } diff --git a/pkg/tcpip/network/ip_test.go b/pkg/tcpip/network/ip_test.go index 797501858..c5f8714da 100644 --- a/pkg/tcpip/network/ip_test.go +++ b/pkg/tcpip/network/ip_test.go @@ -90,6 +90,11 @@ func (t *testObject) DeliverTransportControlPacket(local, remote tcpip.Address, // Attach is only implemented to satisfy the LinkEndpoint interface. func (*testObject) Attach(stack.NetworkDispatcher) {} +// IsAttached implements stack.LinkEndpoint.IsAttached. +func (*testObject) IsAttached() bool { + return true +} + // MTU implements stack.LinkEndpoint.MTU. It just returns a constant that // matches the linux loopback MTU. func (*testObject) MTU() uint32 { diff --git a/pkg/tcpip/stack/registration.go b/pkg/tcpip/stack/registration.go index e7e6381ac..15b2418ad 100644 --- a/pkg/tcpip/stack/registration.go +++ b/pkg/tcpip/stack/registration.go @@ -224,6 +224,10 @@ type LinkEndpoint interface { // Attach attaches the data link layer endpoint to the network-layer // dispatcher of the stack. Attach(dispatcher NetworkDispatcher) + + // IsAttached returns whether a NetworkDispatcher is attached to the + // endpoint. + IsAttached() bool } // A LinkAddressResolver is an extension to a NetworkProtocol that diff --git a/pkg/tcpip/stack/stack.go b/pkg/tcpip/stack/stack.go index f0fbd8aad..3976f585c 100644 --- a/pkg/tcpip/stack/stack.go +++ b/pkg/tcpip/stack/stack.go @@ -541,6 +541,39 @@ func (s *Stack) NICInfo() map[tcpip.NICID]NICInfo { return nics } +// NICStateFlags holds information about the state of an NIC. +type NICStateFlags struct { + // Up indicates whether the interface is running. + Up bool + + // Running indicates whether resources are allocated. + Running bool + + // 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 +} + // AddAddress adds a new network-layer address to the specified NIC. func (s *Stack) AddAddress(id tcpip.NICID, protocol tcpip.NetworkProtocolNumber, addr tcpip.Address) *tcpip.Error { s.mu.RLock()