diff options
Diffstat (limited to 'pkg')
-rw-r--r-- | pkg/abi/linux/netlink_route.go | 64 | ||||
-rw-r--r-- | pkg/sentry/inet/inet.go | 14 | ||||
-rw-r--r-- | pkg/sentry/socket/hostinet/stack.go | 15 | ||||
-rw-r--r-- | pkg/sentry/socket/netlink/route/protocol.go | 130 | ||||
-rw-r--r-- | pkg/sentry/socket/netstack/stack.go | 93 |
5 files changed, 316 insertions, 0 deletions
diff --git a/pkg/abi/linux/netlink_route.go b/pkg/abi/linux/netlink_route.go index 581a11b24..932a06bac 100644 --- a/pkg/abi/linux/netlink_route.go +++ b/pkg/abi/linux/netlink_route.go @@ -352,3 +352,67 @@ type RtAttr struct { // SizeOfRtAttr is the size of RtAttr. const SizeOfRtAttr = 4 + +// NeighborMessage is struct ifaddrmsg, from uapi/linux/if_addr.h. +// +// +marshal +type NeighborMessage struct { + Family uint8 + Pad1 uint8 + Pad2 uint16 + IfIndex int32 + State uint16 + Flags uint8 + Type uint8 +} + +// Neighbor attributes, from uapi/linux/if_addr.h. +const ( + NDA_UNSPEC = 0 + NDA_DST = 1 + NDA_LLADDR = 2 + NDA_CACHEINFO = 3 + NDA_PROBES = 4 + NDA_VLAN = 5 + NDA_PORT = 6 + NDA_VNI = 7 + NDA_IFINDEX = 8 + NDA_MASTER = 9 + NDA_LINK_NETNSID = 10 + NDA_SRC_VNI = 11 + NDA_PROTOCOL = 12 /* Originator of entry */ +) + +// Neighbor Cache Entry Flags, from uapi/linux/neighbour.h +const ( + NTF_USE = 0x01 + NTF_SELF = 0x02 + NTF_MASTER = 0x04 + NTF_PROXY = 0x08 /* == ATF_PUBL */ + NTF_EXT_LEARNED = 0x10 + NTF_OFFLOADED = 0x20 + NTF_STICKY = 0x40 + NTF_ROUTER = 0x80 +) + +// Neighbor Cache Entry States. +const ( + NUD_INCOMPLETE = 0x01 + NUD_REACHABLE = 0x02 + NUD_STALE = 0x04 + NUD_DELAY = 0x08 + NUD_PROBE = 0x10 + NUD_FAILED = 0x20 + + /* Dummy states */ + NUD_NOARP = 0x40 + NUD_PERMANENT = 0x80 + NUD_NONE = 0x00 +) + +type NeighborCacheInfo struct { + Confirmed uint32 + Used uint32 + Updated uint32 + RefCount uint32 +} diff --git a/pkg/sentry/inet/inet.go b/pkg/sentry/inet/inet.go index b121fc1b4..f92706ab7 100644 --- a/pkg/sentry/inet/inet.go +++ b/pkg/sentry/inet/inet.go @@ -42,6 +42,12 @@ type Stack interface { // identified by idx. RemoveInterfaceAddr(idx int32, addr InterfaceAddr) error + AddNeighbor(neighbor Neighbor) error + + RemoveNeighbor(neighbor Neighbor) error + + Neighbors() ([]Neighbor, error) + // SupportsIPv6 returns true if the stack supports IPv6 connectivity. SupportsIPv6() bool @@ -137,6 +143,14 @@ type InterfaceAddr struct { Addr []byte } +type Neighbor struct { + Family uint8 + Idx int32 + State uint16 + Addr []byte + LinkAddr []byte +} + // TCPBufferSize contains settings controlling TCP buffer sizing. // // +stateify savable diff --git a/pkg/sentry/socket/hostinet/stack.go b/pkg/sentry/socket/hostinet/stack.go index 61111ac6c..b80d64c9a 100644 --- a/pkg/sentry/socket/hostinet/stack.go +++ b/pkg/sentry/socket/hostinet/stack.go @@ -333,6 +333,21 @@ func (*Stack) RemoveInterfaceAddr(int32, inet.InterfaceAddr) error { return linuxerr.EACCES } +// Neighbors implements inet.Stack.Neighbors. +func (s *Stack) Neighbors() ([]inet.Neighbor, error) { + return nil, linuxerr.EACCES +} + +// AddNeighbor implements inet.Stack.AddNeighbor. +func (s *Stack) AddNeighbor(inet.Neighbor) error { + return linuxerr.EACCES +} + +// RemoveNeighbor implements inet.Stack.RemoveNeighbor. +func (s *Stack) RemoveNeighbor(inet.Neighbor) error { + return linuxerr.EACCES +} + // SupportsIPv6 implements inet.Stack.SupportsIPv6. func (s *Stack) SupportsIPv6() bool { return s.supportsIPv6 diff --git a/pkg/sentry/socket/netlink/route/protocol.go b/pkg/sentry/socket/netlink/route/protocol.go index d526acb73..38f714c48 100644 --- a/pkg/sentry/socket/netlink/route/protocol.go +++ b/pkg/sentry/socket/netlink/route/protocol.go @@ -275,6 +275,130 @@ func (p *Protocol) dumpAddrs(ctx context.Context, msg *netlink.Message, ms *netl return nil } +// dumpNeighbors handles RTM_GETNEIGH dump requests. +func (p *Protocol) dumpNeighbors(ctx context.Context, msg *netlink.Message, ms *netlink.MessageSet) *syserr.Error { + // RTM_GETNEIGH dump requests need not contain anything more than the + // netlink header and 1 byte protocol family common to all + // NETLINK_ROUTE requests. + + // The RTM_GETNEIGH dump response is a set of RTM_NEWNEIGH messages each + // containing an NeighborMessage followed by a set of netlink + // attributes. + + // We always send back an NLMSG_DONE. + ms.Multi = true + + stack := inet.StackFromContext(ctx) + if stack == nil { + // No network devices. + return nil + } + + nas, err := stack.Neighbors(); if err != nil { + return syserr.FromError(err) + } + + for _, na := range nas { + m := ms.AddMessage(linux.NetlinkMessageHeader{ + Type: linux.RTM_NEWNEIGH, + }) + + m.Put(&linux.NeighborMessage{ + Family: na.Family, + IfIndex: na.Idx, + State: na.State, + }) + + m.PutAttr(linux.NDA_DST, primitive.AsByteSlice(na.Addr)) + if len(na.LinkAddr) > 0 { + m.PutAttr(linux.NDA_LLADDR, primitive.AsByteSlice(na.LinkAddr)) + } + } + + return nil +} + +// newNeigh handles RTM_NEWNEIGH requests. +func (p *Protocol) newNeigh(ctx context.Context, msg *netlink.Message, ms *netlink.MessageSet) *syserr.Error { + stack := inet.StackFromContext(ctx) + if stack == nil { + // No network stack. + return syserr.ErrProtocolNotSupported + } + + var nm linux.NeighborMessage + attrs, ok := msg.GetData(&nm) + if !ok { + return syserr.ErrInvalidArgument + } + + var addr []byte + var llAddr []byte + + for !attrs.Empty() { + ahdr, value, rest, ok := attrs.ParseFirst() + if !ok { + return syserr.ErrInvalidArgument + } + attrs = rest + + switch ahdr.Type { + case linux.NDA_DST: + addr = value + case linux.NDA_LLADDR: + llAddr = value + default: + return syserr.ErrNotSupported + } + } + + return syserr.FromError(stack.AddNeighbor(inet.Neighbor{ + Family: nm.Family, + Idx: nm.IfIndex, + Addr: addr, + State: nm.State, + LinkAddr: llAddr, + })) +} + +// delNeigh handles RTM_DELNEIGH requests. +func (p *Protocol) delNeigh(ctx context.Context, msg *netlink.Message, ms *netlink.MessageSet) *syserr.Error { + stack := inet.StackFromContext(ctx) + if stack == nil { + // No network stack. + return syserr.ErrProtocolNotSupported + } + + var nm linux.NeighborMessage + attrs, ok := msg.GetData(&nm) + if !ok { + return syserr.ErrInvalidArgument + } + + var addr []byte + + for !attrs.Empty() { + ahdr, value, rest, ok := attrs.ParseFirst() + if !ok { + return syserr.ErrInvalidArgument + } + attrs = rest + + switch ahdr.Type { + case linux.NDA_DST: + addr = value + default: + return syserr.ErrNotSupported + } + } + + return syserr.FromError(stack.RemoveNeighbor(inet.Neighbor{ + Family: nm.Family, + Idx: nm.IfIndex, + Addr: addr, + })) +} + // commonPrefixLen reports the length of the longest IP address prefix. // This is a simplied version from Golang's src/net/addrselect.go. func commonPrefixLen(a, b []byte) (cpl int) { @@ -571,6 +695,8 @@ func (p *Protocol) ProcessMessage(ctx context.Context, msg *netlink.Message, ms return p.dumpAddrs(ctx, msg, ms) case linux.RTM_GETROUTE: return p.dumpRoutes(ctx, msg, ms) + case linux.RTM_GETNEIGH: + return p.dumpNeighbors(ctx, msg, ms) default: return syserr.ErrNotSupported } @@ -586,6 +712,10 @@ func (p *Protocol) ProcessMessage(ctx context.Context, msg *netlink.Message, ms return p.newAddr(ctx, msg, ms) case linux.RTM_DELADDR: return p.delAddr(ctx, msg, ms) + case linux.RTM_NEWNEIGH: + return p.newNeigh(ctx, msg, ms) + case linux.RTM_DELNEIGH: + return p.delNeigh(ctx, msg, ms) default: return syserr.ErrNotSupported } diff --git a/pkg/sentry/socket/netstack/stack.go b/pkg/sentry/socket/netstack/stack.go index ea199f223..3fceb4cd5 100644 --- a/pkg/sentry/socket/netstack/stack.go +++ b/pkg/sentry/socket/netstack/stack.go @@ -204,6 +204,99 @@ func (s *Stack) RemoveInterfaceAddr(idx int32, addr inet.InterfaceAddr) error { return nil } +func (s *Stack) appendNeighbor(ns []inet.Neighbor, id tcpip.NICID, family uint8, protocol tcpip.NetworkProtocolNumber) ([]inet.Neighbor, error) { + ne, err := s.Stack.Neighbors(id, protocol); if _, ok := err.(*tcpip.ErrNotSupported); ok { + return ns, nil + } else if err != nil { + return ns, syserr.TranslateNetstackError(err).ToError() + } + + for _, ni := range ne { + var State uint16 = 0 + + switch ni.State { + case stack.Unknown: + State = linux.NUD_NONE + case stack.Incomplete: + State = linux.NUD_INCOMPLETE + case stack.Reachable: + State = linux.NUD_REACHABLE + case stack.Stale: + State = linux.NUD_STALE + case stack.Delay: + State = linux.NUD_DELAY + case stack.Probe: + State = linux.NUD_PROBE + case stack.Static: + State = linux.NUD_PERMANENT + case stack.Unreachable: + State = linux.NUD_FAILED + } + + ns = append(ns, inet.Neighbor{ + Family: family, + Idx: int32(id), + Addr: []byte(ni.Addr), + LinkAddr: []byte(ni.LinkAddr), + State: State, + }) + } + + return ns, nil +} + +// Neighbors implements inet.Stack.Neighbors. +func (s *Stack) Neighbors() ([]inet.Neighbor, error) { + var err error + ns := []inet.Neighbor{} + for id, _ := range s.Stack.NICInfo() { + ns, err = s.appendNeighbor(ns, id, linux.AF_INET, ipv4.ProtocolNumber); if err != nil { + return nil, err + } + ns, err = s.appendNeighbor(ns, id, linux.AF_INET6, ipv6.ProtocolNumber); if err != nil { + return nil, err + } + } + + return ns, nil +} + +func (s *Stack) AddNeighbor(n inet.Neighbor) error { + var protocol tcpip.NetworkProtocolNumber + + switch n.Family { + case linux.AF_INET: + protocol = ipv4.ProtocolNumber + case linux.AF_INET6: + protocol = ipv6.ProtocolNumber + default: + panic(fmt.Sprintf("AddStaticNeighbor(%v) failed: unsupported family", n.Family)) + } + + if n.State != linux.NUD_PERMANENT { + return syserr.ErrInvalidArgument.ToError() + } + + err := s.Stack.AddStaticNeighbor(tcpip.NICID(n.Idx), protocol, tcpip.Address(n.Addr), tcpip.LinkAddress(n.LinkAddr)) + return syserr.TranslateNetstackError(err).ToError() +} + +func (s *Stack) RemoveNeighbor(n inet.Neighbor) error { + var protocol tcpip.NetworkProtocolNumber + + switch n.Family { + case linux.AF_INET: + protocol = ipv4.ProtocolNumber + case linux.AF_INET6: + protocol = ipv6.ProtocolNumber + default: + panic(fmt.Sprintf("RemoveNeighbor(%v) failed: unsupported family", n.Family)) + } + + err := s.Stack.RemoveNeighbor(tcpip.NICID(n.Idx), protocol, tcpip.Address(n.Addr)) + return syserr.TranslateNetstackError(err).ToError() +} + // TCPReceiveBufferSize implements inet.Stack.TCPReceiveBufferSize. func (s *Stack) TCPReceiveBufferSize() (inet.TCPBufferSize, error) { var rs tcpip.TCPReceiveBufferSizeRangeOption |