mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Basic support for ip link/addr and ifconfig
Closes #94 PiperOrigin-RevId: 207997580 Change-Id: I19b426f1586b5ec12f8b0cd5884d5b401d334924
This commit is contained in:
committed by
Shentubot
parent
48b5b35b2b
commit
4e171f7590
@@ -184,3 +184,8 @@ const (
|
||||
IFA_MULTICAST = 7
|
||||
IFA_FLAGS = 8
|
||||
)
|
||||
|
||||
// Device types, from uapi/linux/if_arp.h.
|
||||
const (
|
||||
ARPHRD_LOOPBACK = 772
|
||||
)
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
// TODO: There are many more attributes, such as
|
||||
// MAC address.
|
||||
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.
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -205,6 +205,7 @@ const (
|
||||
CapabilityResolutionRequired
|
||||
CapabilitySaveRestore
|
||||
CapabilityDisconnectOk
|
||||
CapabilityLoopback
|
||||
)
|
||||
|
||||
// LinkEndpoint is the interface implemented by data link layer protocols (e.g.,
|
||||
|
||||
+16
-20
@@ -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.
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user