diff options
Diffstat (limited to 'pkg/sentry')
-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 |
4 files changed, 252 insertions, 0 deletions
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 |