Implement RTM_NEWROUTE in netstack to create/replace a route.

PiperOrigin-RevId: 650814137
This commit is contained in:
Jing Chen
2024-07-09 18:04:19 -07:00
committed by gVisor bot
parent 16ebae768c
commit 35309c96c0
13 changed files with 403 additions and 1 deletions
+116
View File
@@ -742,6 +742,122 @@ func (s *Stack) RouteTable() []inet.Route {
return routeTable
}
// NewRoute implements inet.Stack.NewRoute.
func (s *Stack) NewRoute(ctx context.Context, msg *nlmsg.Message) *syserr.Error {
var routeMsg linux.RouteMessage
attrs, ok := msg.GetData(&routeMsg)
if !ok {
return syserr.ErrInvalidArgument
}
route := inet.Route{
Family: routeMsg.Family,
DstLen: routeMsg.DstLen,
SrcLen: routeMsg.SrcLen,
TOS: routeMsg.TOS,
Table: routeMsg.Table,
Protocol: routeMsg.Protocol,
Scope: routeMsg.Scope,
Type: routeMsg.Type,
Flags: routeMsg.Flags,
}
for !attrs.Empty() {
ahdr, value, rest, ok := attrs.ParseFirst()
if !ok {
return syserr.ErrInvalidArgument
}
attrs = rest
switch ahdr.Type {
case linux.RTA_DST:
if len(value) < 1 {
return syserr.ErrInvalidArgument
}
route.DstAddr = value
case linux.RTA_SRC:
if len(value) < 1 {
return syserr.ErrInvalidArgument
}
route.SrcAddr = value
case linux.RTA_OIF:
oif := nlmsg.BytesView(value)
outputInterface, ok := oif.Int32()
if !ok {
return syserr.ErrInvalidArgument
}
if _, exist := s.Interfaces()[outputInterface]; !exist {
return syserr.ErrNoDevice
}
route.OutputInterface = outputInterface
case linux.RTA_GATEWAY:
if len(value) < 1 {
return syserr.ErrInvalidArgument
}
route.GatewayAddr = value
case linux.RTA_PRIORITY:
default:
ctx.Warningf("Unknown attribute: %v", ahdr.Type)
return syserr.ErrNotSupported
}
}
var dest tcpip.Subnet
// When no destination address is provided, the new route might be the default route.
if route.DstAddr == nil {
if route.GatewayAddr == nil {
return syserr.ErrInvalidArgument
}
switch len(route.GatewayAddr) {
case header.IPv4AddressSize:
subnet, err := tcpip.NewSubnet(tcpip.AddrFromSlice(tcpip.IPv4Zero), tcpip.MaskFromBytes(tcpip.IPv4Zero))
if err != nil {
return syserr.ErrInvalidArgument
}
dest = subnet
case header.IPv6AddressSize:
subnet, err := tcpip.NewSubnet(tcpip.AddrFromSlice(tcpip.IPv6Zero), tcpip.MaskFromBytes(tcpip.IPv6Zero))
if err != nil {
return syserr.ErrInvalidArgument
}
dest = subnet
default:
return syserr.ErrInvalidArgument
}
} else {
dest = tcpip.AddressWithPrefix{
Address: tcpip.AddrFromSlice(route.DstAddr),
PrefixLen: int(route.DstLen)}.Subnet()
}
localRoute := tcpip.Route{
Destination: dest,
Gateway: tcpip.AddrFromSlice(route.GatewayAddr),
NIC: tcpip.NICID(route.OutputInterface),
}
if len(route.SrcAddr) != 0 {
localRoute.SourceHint = tcpip.AddrFromSlice(route.SrcAddr)
}
found := false
for _, rt := range s.Stack.GetRouteTable() {
if localRoute.Equal(rt) {
found = true
break
}
}
flags := msg.Header().Flags
switch {
case !found && flags&linux.NLM_F_CREATE == linux.NLM_F_CREATE:
s.Stack.AddRoute(localRoute)
case found && flags&linux.NLM_F_REPLACE != linux.NLM_F_REPLACE:
return syserr.ErrExists
}
if flags&linux.NLM_F_REPLACE == linux.NLM_F_REPLACE {
s.Stack.ReplaceRoute(localRoute)
}
return nil
}
// IPTables returns the stack's iptables.
func (s *Stack) IPTables() (*stack.IPTables, error) {
return s.Stack.IPTables(), nil