Basic support for 'ip route'
Implements support for RTM_GETROUTE requests for netlink sockets. Fixes #507 PiperOrigin-RevId: 261051045
This commit is contained in:
parent
77833ece3b
commit
0a246fab80
|
@ -189,3 +189,139 @@ const (
|
|||
const (
|
||||
ARPHRD_LOOPBACK = 772
|
||||
)
|
||||
|
||||
// RouteMessage struct rtmsg, from uapi/linux/rtnetlink.h.
|
||||
type RouteMessage struct {
|
||||
Family uint8
|
||||
DstLen uint8
|
||||
SrcLen uint8
|
||||
TOS uint8
|
||||
|
||||
Table uint8
|
||||
Protocol uint8
|
||||
Scope uint8
|
||||
Type uint8
|
||||
|
||||
Flags uint32
|
||||
}
|
||||
|
||||
// Route types, from uapi/linux/rtnetlink.h.
|
||||
const (
|
||||
// RTN_UNSPEC represents an unspecified route type.
|
||||
RTN_UNSPEC = 0
|
||||
|
||||
// RTN_UNICAST represents a unicast route.
|
||||
RTN_UNICAST = 1
|
||||
|
||||
// RTN_LOCAL represents a route that is accepted locally.
|
||||
RTN_LOCAL = 2
|
||||
|
||||
// RTN_BROADCAST represents a broadcast route (Traffic is accepted locally
|
||||
// as broadcast, and sent as broadcast).
|
||||
RTN_BROADCAST = 3
|
||||
|
||||
// RTN_ANYCAST represents a anycast route (Traffic is accepted locally as
|
||||
// broadcast but sent as unicast).
|
||||
RTN_ANYCAST = 6
|
||||
|
||||
// RTN_MULTICAST represents a multicast route.
|
||||
RTN_MULTICAST = 5
|
||||
|
||||
// RTN_BLACKHOLE represents a route where all traffic is dropped.
|
||||
RTN_BLACKHOLE = 6
|
||||
|
||||
// RTN_UNREACHABLE represents a route where the destination is unreachable.
|
||||
RTN_UNREACHABLE = 7
|
||||
|
||||
RTN_PROHIBIT = 8
|
||||
RTN_THROW = 9
|
||||
RTN_NAT = 10
|
||||
RTN_XRESOLVE = 11
|
||||
)
|
||||
|
||||
// Route protocols/origins, from uapi/linux/rtnetlink.h.
|
||||
const (
|
||||
RTPROT_UNSPEC = 0
|
||||
RTPROT_REDIRECT = 1
|
||||
RTPROT_KERNEL = 2
|
||||
RTPROT_BOOT = 3
|
||||
RTPROT_STATIC = 4
|
||||
RTPROT_GATED = 8
|
||||
RTPROT_RA = 9
|
||||
RTPROT_MRT = 10
|
||||
RTPROT_ZEBRA = 11
|
||||
RTPROT_BIRD = 12
|
||||
RTPROT_DNROUTED = 13
|
||||
RTPROT_XORP = 14
|
||||
RTPROT_NTK = 15
|
||||
RTPROT_DHCP = 16
|
||||
RTPROT_MROUTED = 17
|
||||
RTPROT_BABEL = 42
|
||||
RTPROT_BGP = 186
|
||||
RTPROT_ISIS = 187
|
||||
RTPROT_OSPF = 188
|
||||
RTPROT_RIP = 189
|
||||
RTPROT_EIGRP = 192
|
||||
)
|
||||
|
||||
// Route scopes, from uapi/linux/rtnetlink.h.
|
||||
const (
|
||||
RT_SCOPE_UNIVERSE = 0
|
||||
RT_SCOPE_SITE = 200
|
||||
RT_SCOPE_LINK = 253
|
||||
RT_SCOPE_HOST = 254
|
||||
RT_SCOPE_NOWHERE = 255
|
||||
)
|
||||
|
||||
// Route flags, from uapi/linux/rtnetlink.h.
|
||||
const (
|
||||
RTM_F_NOTIFY = 0x100
|
||||
RTM_F_CLONED = 0x200
|
||||
RTM_F_EQUALIZE = 0x400
|
||||
RTM_F_PREFIX = 0x800
|
||||
RTM_F_LOOKUP_TABLE = 0x1000
|
||||
RTM_F_FIB_MATCH = 0x2000
|
||||
)
|
||||
|
||||
// Route tables, from uapi/linux/rtnetlink.h.
|
||||
const (
|
||||
RT_TABLE_UNSPEC = 0
|
||||
RT_TABLE_COMPAT = 252
|
||||
RT_TABLE_DEFAULT = 253
|
||||
RT_TABLE_MAIN = 254
|
||||
RT_TABLE_LOCAL = 255
|
||||
)
|
||||
|
||||
// Route attributes, from uapi/linux/rtnetlink.h.
|
||||
const (
|
||||
RTA_UNSPEC = 0
|
||||
RTA_DST = 1
|
||||
RTA_SRC = 2
|
||||
RTA_IIF = 3
|
||||
RTA_OIF = 4
|
||||
RTA_GATEWAY = 5
|
||||
RTA_PRIORITY = 6
|
||||
RTA_PREFSRC = 7
|
||||
RTA_METRICS = 8
|
||||
RTA_MULTIPATH = 9
|
||||
RTA_PROTOINFO = 10
|
||||
RTA_FLOW = 11
|
||||
RTA_CACHEINFO = 12
|
||||
RTA_SESSION = 13
|
||||
RTA_MP_ALGO = 14
|
||||
RTA_TABLE = 15
|
||||
RTA_MARK = 16
|
||||
RTA_MFC_STATS = 17
|
||||
RTA_VIA = 18
|
||||
RTA_NEWDST = 19
|
||||
RTA_PREF = 20
|
||||
RTA_ENCAP_TYPE = 21
|
||||
RTA_ENCAP = 22
|
||||
RTA_EXPIRES = 23
|
||||
RTA_PAD = 24
|
||||
RTA_UID = 25
|
||||
RTA_TTL_PROPAGATE = 26
|
||||
RTA_IP_PROTO = 27
|
||||
RTA_SPORT = 28
|
||||
RTA_DPORT = 29
|
||||
)
|
||||
|
|
|
@ -52,12 +52,13 @@ type Stack interface {
|
|||
|
||||
// Statistics reports stack statistics.
|
||||
Statistics(stat interface{}, arg string) error
|
||||
|
||||
// RouteTable returns the network stack's route table.
|
||||
RouteTable() []Route
|
||||
}
|
||||
|
||||
// Interface contains information about a network interface.
|
||||
type Interface struct {
|
||||
// Keep these fields sorted in the order they appear in rtnetlink(7).
|
||||
|
||||
// DeviceType is the device type, a Linux ARPHRD_* constant.
|
||||
DeviceType uint16
|
||||
|
||||
|
@ -77,8 +78,6 @@ type Interface struct {
|
|||
|
||||
// InterfaceAddr contains information about a network interface address.
|
||||
type InterfaceAddr struct {
|
||||
// Keep these fields sorted in the order they appear in rtnetlink(7).
|
||||
|
||||
// Family is the address family, a Linux AF_* constant.
|
||||
Family uint8
|
||||
|
||||
|
@ -109,3 +108,45 @@ type TCPBufferSize struct {
|
|||
// StatDev describes one line of /proc/net/dev, i.e., stats for one network
|
||||
// interface.
|
||||
type StatDev [16]uint64
|
||||
|
||||
// Route contains information about a network route.
|
||||
type Route struct {
|
||||
// Family is the address family, a Linux AF_* constant.
|
||||
Family uint8
|
||||
|
||||
// DstLen is the length of the destination address.
|
||||
DstLen uint8
|
||||
|
||||
// SrcLen is the length of the source address.
|
||||
SrcLen uint8
|
||||
|
||||
// TOS is the Type of Service filter.
|
||||
TOS uint8
|
||||
|
||||
// Table is the routing table ID.
|
||||
Table uint8
|
||||
|
||||
// Protocol is the route origin, a Linux RTPROT_* constant.
|
||||
Protocol uint8
|
||||
|
||||
// Scope is the distance to destination, a Linux RT_SCOPE_* constant.
|
||||
Scope uint8
|
||||
|
||||
// Type is the route origin, a Linux RTN_* constant.
|
||||
Type uint8
|
||||
|
||||
// Flags are route flags. See rtnetlink(7) under "rtm_flags".
|
||||
Flags uint32
|
||||
|
||||
// DstAddr is the route destination address (RTA_DST).
|
||||
DstAddr []byte
|
||||
|
||||
// SrcAddr is the route source address (RTA_SRC).
|
||||
SrcAddr []byte
|
||||
|
||||
// OutputInterface is the output interface index (RTA_OIF).
|
||||
OutputInterface int32
|
||||
|
||||
// GatewayAddr is the route gateway address (RTA_GATEWAY).
|
||||
GatewayAddr []byte
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ package inet
|
|||
type TestStack struct {
|
||||
InterfacesMap map[int32]Interface
|
||||
InterfaceAddrsMap map[int32][]InterfaceAddr
|
||||
RouteList []Route
|
||||
SupportsIPv6Flag bool
|
||||
TCPRecvBufSize TCPBufferSize
|
||||
TCPSendBufSize TCPBufferSize
|
||||
|
@ -86,3 +87,8 @@ func (s *TestStack) SetTCPSACKEnabled(enabled bool) error {
|
|||
func (s *TestStack) Statistics(stat interface{}, arg string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// RouteTable implements Stack.RouteTable.
|
||||
func (s *TestStack) RouteTable() []Route {
|
||||
return s.RouteList
|
||||
}
|
||||
|
|
|
@ -2252,19 +2252,19 @@ func interfaceIoctl(ctx context.Context, io usermem.IO, arg int, ifr *linux.IFRe
|
|||
|
||||
case syscall.SIOCGIFMAP:
|
||||
// Gets the hardware parameters of the device.
|
||||
// TODO(b/71872867): Implement.
|
||||
// TODO(gvisor.dev/issue/505): Implement.
|
||||
|
||||
case syscall.SIOCGIFTXQLEN:
|
||||
// Gets the transmit queue length of the device.
|
||||
// TODO(b/71872867): Implement.
|
||||
// TODO(gvisor.dev/issue/505): Implement.
|
||||
|
||||
case syscall.SIOCGIFDSTADDR:
|
||||
// Gets the destination address of a point-to-point device.
|
||||
// TODO(b/71872867): Implement.
|
||||
// TODO(gvisor.dev/issue/505): Implement.
|
||||
|
||||
case syscall.SIOCGIFBRDADDR:
|
||||
// Gets the broadcast address of a device.
|
||||
// TODO(b/71872867): Implement.
|
||||
// TODO(gvisor.dev/issue/505): Implement.
|
||||
|
||||
case syscall.SIOCGIFNETMASK:
|
||||
// Gets the network mask of a device.
|
||||
|
|
|
@ -19,6 +19,8 @@ import (
|
|||
"gvisor.dev/gvisor/pkg/log"
|
||||
"gvisor.dev/gvisor/pkg/sentry/inet"
|
||||
"gvisor.dev/gvisor/pkg/syserr"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/network/ipv6"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
|
@ -143,3 +145,46 @@ func (s *Stack) SetTCPSACKEnabled(enabled bool) error {
|
|||
func (s *Stack) Statistics(stat interface{}, arg string) error {
|
||||
return syserr.ErrEndpointOperation.ToError()
|
||||
}
|
||||
|
||||
// RouteTable implements inet.Stack.RouteTable.
|
||||
func (s *Stack) RouteTable() []inet.Route {
|
||||
var routeTable []inet.Route
|
||||
|
||||
for _, rt := range s.Stack.GetRouteTable() {
|
||||
var family uint8
|
||||
switch len(rt.Destination) {
|
||||
case header.IPv4AddressSize:
|
||||
family = linux.AF_INET
|
||||
case header.IPv6AddressSize:
|
||||
family = linux.AF_INET6
|
||||
default:
|
||||
log.Warningf("Unknown network protocol in route %+v", rt)
|
||||
continue
|
||||
}
|
||||
|
||||
dstSubnet, err := tcpip.NewSubnet(rt.Destination, rt.Mask)
|
||||
if err != nil {
|
||||
log.Warningf("Invalid destination & mask in route: %s(%s): %v", rt.Destination, rt.Mask, err)
|
||||
continue
|
||||
}
|
||||
routeTable = append(routeTable, inet.Route{
|
||||
Family: family,
|
||||
DstLen: uint8(dstSubnet.Prefix()), // The CIDR prefix for the destination.
|
||||
|
||||
// Always return unspecified protocol since we have no notion of
|
||||
// protocol for routes.
|
||||
Protocol: linux.RTPROT_UNSPEC,
|
||||
// Set statically to LINK scope for now.
|
||||
//
|
||||
// TODO(gvisor.dev/issue/595): Set scope for routes.
|
||||
Scope: linux.RT_SCOPE_LINK,
|
||||
Type: linux.RTN_UNICAST,
|
||||
|
||||
DstAddr: []byte(rt.Destination),
|
||||
OutputInterface: int32(rt.NIC),
|
||||
GatewayAddr: []byte(rt.Gateway),
|
||||
})
|
||||
}
|
||||
|
||||
return routeTable
|
||||
}
|
||||
|
|
|
@ -46,6 +46,7 @@ type Stack struct {
|
|||
// Stack is immutable.
|
||||
interfaces map[int32]inet.Interface
|
||||
interfaceAddrs map[int32][]inet.InterfaceAddr
|
||||
routes []inet.Route
|
||||
supportsIPv6 bool
|
||||
tcpRecvBufSize inet.TCPBufferSize
|
||||
tcpSendBufSize inet.TCPBufferSize
|
||||
|
@ -66,6 +67,10 @@ func (s *Stack) Configure() error {
|
|||
return err
|
||||
}
|
||||
|
||||
if err := addHostRoutes(s); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := os.Stat("/proc/net/if_inet6"); err == nil {
|
||||
s.supportsIPv6 = true
|
||||
}
|
||||
|
@ -161,6 +166,54 @@ func ExtractHostInterfaces(links []syscall.NetlinkMessage, addrs []syscall.Netli
|
|||
return nil
|
||||
}
|
||||
|
||||
// ExtractHostRoutes populates the given routes slice with the data from the
|
||||
// host route table.
|
||||
func ExtractHostRoutes(routeMsgs []syscall.NetlinkMessage) ([]inet.Route, error) {
|
||||
var routes []inet.Route
|
||||
for _, routeMsg := range routeMsgs {
|
||||
if routeMsg.Header.Type != syscall.RTM_NEWROUTE {
|
||||
continue
|
||||
}
|
||||
|
||||
var ifRoute syscall.RtMsg
|
||||
binary.Unmarshal(routeMsg.Data[:syscall.SizeofRtMsg], usermem.ByteOrder, &ifRoute)
|
||||
inetRoute := inet.Route{
|
||||
Family: ifRoute.Family,
|
||||
DstLen: ifRoute.Dst_len,
|
||||
SrcLen: ifRoute.Src_len,
|
||||
TOS: ifRoute.Tos,
|
||||
Table: ifRoute.Table,
|
||||
Protocol: ifRoute.Protocol,
|
||||
Scope: ifRoute.Scope,
|
||||
Type: ifRoute.Type,
|
||||
Flags: ifRoute.Flags,
|
||||
}
|
||||
|
||||
// Not clearly documented: syscall.ParseNetlinkRouteAttr will check the
|
||||
// syscall.NetlinkMessage.Header.Type and skip the struct rtmsg
|
||||
// accordingly.
|
||||
attrs, err := syscall.ParseNetlinkRouteAttr(&routeMsg)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("RTM_GETROUTE returned RTM_NEWROUTE message with invalid rtattrs: %v", err)
|
||||
}
|
||||
|
||||
for _, attr := range attrs {
|
||||
switch attr.Attr.Type {
|
||||
case syscall.RTA_DST:
|
||||
inetRoute.DstAddr = attr.Value
|
||||
case syscall.RTA_SRC:
|
||||
inetRoute.SrcAddr = attr.Value
|
||||
case syscall.RTA_OIF:
|
||||
inetRoute.GatewayAddr = attr.Value
|
||||
}
|
||||
}
|
||||
|
||||
routes = append(routes, inetRoute)
|
||||
}
|
||||
|
||||
return routes, nil
|
||||
}
|
||||
|
||||
func addHostInterfaces(s *Stack) error {
|
||||
links, err := doNetlinkRouteRequest(syscall.RTM_GETLINK)
|
||||
if err != nil {
|
||||
|
@ -175,6 +228,20 @@ func addHostInterfaces(s *Stack) error {
|
|||
return ExtractHostInterfaces(links, addrs, s.interfaces, s.interfaceAddrs)
|
||||
}
|
||||
|
||||
func addHostRoutes(s *Stack) error {
|
||||
routes, err := doNetlinkRouteRequest(syscall.RTM_GETROUTE)
|
||||
if err != nil {
|
||||
return fmt.Errorf("RTM_GETROUTE failed: %v", err)
|
||||
}
|
||||
|
||||
s.routes, err = ExtractHostRoutes(routes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func doNetlinkRouteRequest(req int) ([]syscall.NetlinkMessage, error) {
|
||||
data, err := syscall.NetlinkRIB(req, syscall.AF_UNSPEC)
|
||||
if err != nil {
|
||||
|
@ -202,12 +269,20 @@ func readTCPBufferSizeFile(filename string) (inet.TCPBufferSize, error) {
|
|||
|
||||
// Interfaces implements inet.Stack.Interfaces.
|
||||
func (s *Stack) Interfaces() map[int32]inet.Interface {
|
||||
return s.interfaces
|
||||
interfaces := make(map[int32]inet.Interface)
|
||||
for k, v := range s.interfaces {
|
||||
interfaces[k] = v
|
||||
}
|
||||
return interfaces
|
||||
}
|
||||
|
||||
// InterfaceAddrs implements inet.Stack.InterfaceAddrs.
|
||||
func (s *Stack) InterfaceAddrs() map[int32][]inet.InterfaceAddr {
|
||||
return s.interfaceAddrs
|
||||
addrs := make(map[int32][]inet.InterfaceAddr)
|
||||
for k, v := range s.interfaceAddrs {
|
||||
addrs[k] = append([]inet.InterfaceAddr(nil), v...)
|
||||
}
|
||||
return addrs
|
||||
}
|
||||
|
||||
// SupportsIPv6 implements inet.Stack.SupportsIPv6.
|
||||
|
@ -249,3 +324,8 @@ func (s *Stack) SetTCPSACKEnabled(enabled bool) error {
|
|||
func (s *Stack) Statistics(stat interface{}, arg string) error {
|
||||
return syserror.EOPNOTSUPP
|
||||
}
|
||||
|
||||
// RouteTable implements inet.Stack.RouteTable.
|
||||
func (s *Stack) RouteTable() []inet.Route {
|
||||
return append([]inet.Route(nil), s.routes...)
|
||||
}
|
||||
|
|
|
@ -110,7 +110,7 @@ func (p *Protocol) dumpLinks(ctx context.Context, hdr linux.NetlinkMessageHeader
|
|||
m.PutAttr(linux.IFLA_ADDRESS, mac)
|
||||
m.PutAttr(linux.IFLA_BROADCAST, brd)
|
||||
|
||||
// TODO(b/68878065): There are many more attributes.
|
||||
// TODO(gvisor.dev/issue/578): There are many more attributes.
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -151,13 +151,69 @@ func (p *Protocol) dumpAddrs(ctx context.Context, hdr linux.NetlinkMessageHeader
|
|||
|
||||
m.PutAttr(linux.IFA_ADDRESS, []byte(a.Addr))
|
||||
|
||||
// TODO(b/68878065): There are many more attributes.
|
||||
// TODO(gvisor.dev/issue/578): There are many more attributes.
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// dumpRoutes handles RTM_GETROUTE + NLM_F_DUMP requests.
|
||||
func (p *Protocol) dumpRoutes(ctx context.Context, hdr linux.NetlinkMessageHeader, data []byte, ms *netlink.MessageSet) *syserr.Error {
|
||||
// RTM_GETROUTE dump requests need not contain anything more than the
|
||||
// netlink header and 1 byte protocol family common to all
|
||||
// NETLINK_ROUTE requests.
|
||||
|
||||
// We always send back an NLMSG_DONE.
|
||||
ms.Multi = true
|
||||
|
||||
stack := inet.StackFromContext(ctx)
|
||||
if stack == nil {
|
||||
// No network routes.
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, rt := range stack.RouteTable() {
|
||||
m := ms.AddMessage(linux.NetlinkMessageHeader{
|
||||
Type: linux.RTM_NEWROUTE,
|
||||
})
|
||||
|
||||
m.Put(linux.RouteMessage{
|
||||
Family: rt.Family,
|
||||
DstLen: rt.DstLen,
|
||||
SrcLen: rt.SrcLen,
|
||||
TOS: rt.TOS,
|
||||
|
||||
// Always return the main table since we don't have multiple
|
||||
// routing tables.
|
||||
Table: linux.RT_TABLE_MAIN,
|
||||
Protocol: rt.Protocol,
|
||||
Scope: rt.Scope,
|
||||
Type: rt.Type,
|
||||
|
||||
Flags: rt.Flags,
|
||||
})
|
||||
|
||||
m.PutAttr(254, []byte{123})
|
||||
if rt.DstLen > 0 {
|
||||
m.PutAttr(linux.RTA_DST, rt.DstAddr)
|
||||
}
|
||||
if rt.SrcLen > 0 {
|
||||
m.PutAttr(linux.RTA_SRC, rt.SrcAddr)
|
||||
}
|
||||
if rt.OutputInterface != 0 {
|
||||
m.PutAttr(linux.RTA_OIF, rt.OutputInterface)
|
||||
}
|
||||
if len(rt.GatewayAddr) > 0 {
|
||||
m.PutAttr(linux.RTA_GATEWAY, rt.GatewayAddr)
|
||||
}
|
||||
|
||||
// TODO(gvisor.dev/issue/578): There are many more attributes.
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ProcessMessage implements netlink.Protocol.ProcessMessage.
|
||||
func (p *Protocol) ProcessMessage(ctx context.Context, hdr linux.NetlinkMessageHeader, data []byte, ms *netlink.MessageSet) *syserr.Error {
|
||||
// All messages start with a 1 byte protocol family.
|
||||
|
@ -186,6 +242,8 @@ func (p *Protocol) ProcessMessage(ctx context.Context, hdr linux.NetlinkMessageH
|
|||
return p.dumpLinks(ctx, hdr, data, ms)
|
||||
case linux.RTM_GETADDR:
|
||||
return p.dumpAddrs(ctx, hdr, data, ms)
|
||||
case linux.RTM_GETROUTE:
|
||||
return p.dumpRoutes(ctx, hdr, data, ms)
|
||||
default:
|
||||
return syserr.ErrNotSupported
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ import (
|
|||
type Stack struct {
|
||||
interfaces map[int32]inet.Interface
|
||||
interfaceAddrs map[int32][]inet.InterfaceAddr
|
||||
routes []inet.Route
|
||||
rpcConn *conn.RPCConnection
|
||||
notifier *notifier.Notifier
|
||||
}
|
||||
|
@ -69,6 +70,16 @@ func NewStack(fd int32) (*Stack, error) {
|
|||
return nil, e
|
||||
}
|
||||
|
||||
routes, err := stack.DoNetlinkRouteRequest(syscall.RTM_GETROUTE)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("RTM_GETROUTE failed: %v", err)
|
||||
}
|
||||
|
||||
stack.routes, e = hostinet.ExtractHostRoutes(routes)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
|
||||
return stack, nil
|
||||
}
|
||||
|
||||
|
@ -89,12 +100,20 @@ func (s *Stack) RPCWriteFile(path string, data []byte) (int64, *syserr.Error) {
|
|||
|
||||
// Interfaces implements inet.Stack.Interfaces.
|
||||
func (s *Stack) Interfaces() map[int32]inet.Interface {
|
||||
return s.interfaces
|
||||
interfaces := make(map[int32]inet.Interface)
|
||||
for k, v := range s.interfaces {
|
||||
interfaces[k] = v
|
||||
}
|
||||
return interfaces
|
||||
}
|
||||
|
||||
// InterfaceAddrs implements inet.Stack.InterfaceAddrs.
|
||||
func (s *Stack) InterfaceAddrs() map[int32][]inet.InterfaceAddr {
|
||||
return s.interfaceAddrs
|
||||
addrs := make(map[int32][]inet.InterfaceAddr)
|
||||
for k, v := range s.interfaceAddrs {
|
||||
addrs[k] = append([]inet.InterfaceAddr(nil), v...)
|
||||
}
|
||||
return addrs
|
||||
}
|
||||
|
||||
// SupportsIPv6 implements inet.Stack.SupportsIPv6.
|
||||
|
@ -138,3 +157,8 @@ func (s *Stack) SetTCPSACKEnabled(enabled bool) error {
|
|||
func (s *Stack) Statistics(stat interface{}, arg string) error {
|
||||
return syserr.ErrEndpointOperation.ToError()
|
||||
}
|
||||
|
||||
// RouteTable implements inet.Stack.RouteTable.
|
||||
func (s *Stack) RouteTable() []inet.Route {
|
||||
return append([]inet.Route(nil), s.routes...)
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <ifaddrs.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
|
@ -425,6 +426,78 @@ TEST(NetlinkRouteTest, LookupAll) {
|
|||
ASSERT_GT(count, 0);
|
||||
}
|
||||
|
||||
// GetRouteDump tests a RTM_GETROUTE + NLM_F_DUMP request.
|
||||
TEST(NetlinkRouteTest, GetRouteDump) {
|
||||
FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket());
|
||||
uint32_t port = ASSERT_NO_ERRNO_AND_VALUE(NetlinkPortID(fd.get()));
|
||||
|
||||
struct request {
|
||||
struct nlmsghdr hdr;
|
||||
struct rtmsg rtm;
|
||||
};
|
||||
|
||||
constexpr uint32_t kSeq = 12345;
|
||||
|
||||
struct request req = {};
|
||||
req.hdr.nlmsg_len = sizeof(req);
|
||||
req.hdr.nlmsg_type = RTM_GETROUTE;
|
||||
req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
|
||||
req.hdr.nlmsg_seq = kSeq;
|
||||
req.rtm.rtm_family = AF_UNSPEC;
|
||||
|
||||
bool routeFound = false;
|
||||
bool dstFound = true;
|
||||
ASSERT_NO_ERRNO(NetlinkRequestResponse(
|
||||
fd, &req, sizeof(req), [&](const struct nlmsghdr* hdr) {
|
||||
// Validate the reponse to RTM_GETROUTE + NLM_F_DUMP.
|
||||
EXPECT_THAT(hdr->nlmsg_type, AnyOf(Eq(RTM_NEWROUTE), Eq(NLMSG_DONE)));
|
||||
|
||||
EXPECT_TRUE((hdr->nlmsg_flags & NLM_F_MULTI) == NLM_F_MULTI)
|
||||
<< std::hex << hdr->nlmsg_flags;
|
||||
|
||||
EXPECT_EQ(hdr->nlmsg_seq, kSeq);
|
||||
EXPECT_EQ(hdr->nlmsg_pid, port);
|
||||
|
||||
// The test should not proceed if it's not a RTM_NEWROUTE message.
|
||||
if (hdr->nlmsg_type != RTM_NEWROUTE) {
|
||||
return;
|
||||
}
|
||||
|
||||
// RTM_NEWROUTE contains at least the header and rtmsg.
|
||||
ASSERT_GE(hdr->nlmsg_len, NLMSG_SPACE(sizeof(struct rtmsg)));
|
||||
const struct rtmsg* msg =
|
||||
reinterpret_cast<const struct rtmsg*>(NLMSG_DATA(hdr));
|
||||
// NOTE: rtmsg fields are char fields.
|
||||
std::cout << "Found route table=" << static_cast<int>(msg->rtm_table)
|
||||
<< ", protocol=" << static_cast<int>(msg->rtm_protocol)
|
||||
<< ", scope=" << static_cast<int>(msg->rtm_scope)
|
||||
<< ", type=" << static_cast<int>(msg->rtm_type);
|
||||
|
||||
int len = RTM_PAYLOAD(hdr);
|
||||
bool rtDstFound = false;
|
||||
for (struct rtattr* attr = RTM_RTA(msg); RTA_OK(attr, len);
|
||||
attr = RTA_NEXT(attr, len)) {
|
||||
if (attr->rta_type == RTA_DST) {
|
||||
char address[INET_ADDRSTRLEN] = {};
|
||||
inet_ntop(AF_INET, RTA_DATA(attr), address, sizeof(address));
|
||||
std::cout << ", dst=" << address;
|
||||
rtDstFound = true;
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << std::endl;
|
||||
|
||||
if (msg->rtm_table == RT_TABLE_MAIN) {
|
||||
routeFound = true;
|
||||
dstFound = rtDstFound && dstFound;
|
||||
}
|
||||
}));
|
||||
// At least one route found in main route table.
|
||||
EXPECT_TRUE(routeFound);
|
||||
// Found RTA_DST for each route in main table.
|
||||
EXPECT_TRUE(dstFound);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace testing
|
||||
|
|
Loading…
Reference in New Issue