netstack: choose route by longest prefix

It has to return the most specific route.

PiperOrigin-RevId: 647421535
This commit is contained in:
Andrei Vagin
2024-06-27 13:12:45 -07:00
committed by gVisor bot
parent c6d16988a9
commit 3546fab741
9 changed files with 90 additions and 38 deletions
+13
View File
@@ -19,10 +19,23 @@ go_template_instance(
},
)
go_template_instance(
name = "route_list",
out = "route_list.go",
package = "tcpip",
prefix = "Route",
template = "//pkg/ilist:generic_list",
types = {
"Element": "*Route",
"Linker": "*Route",
},
)
go_library(
name = "tcpip",
srcs = [
"errors.go",
"route_list.go",
"sock_err_list.go",
"socketops.go",
"stdclock.go",
+3
View File
@@ -178,6 +178,9 @@ var (
// IPv4AllRoutersGroup is a multicast address for all routers.
IPv4AllRoutersGroup = tcpip.AddrFrom4([4]byte{0xe0, 0x00, 0x00, 0x02})
// IPv4Loopback is the loopback IPv4 address.
IPv4Loopback = tcpip.AddrFrom4([4]byte{0x7f, 0x00, 0x00, 0x01})
)
// Flags that may be set in an IPv4 packet.
+54 -20
View File
@@ -88,8 +88,9 @@ type Stack struct {
// routeMu protects annotated fields below.
routeMu routeStackRWMutex `state:"nosave"`
// routeTable is a list of routes sorted by prefix length, longest (most specific) first.
// +checklocks:routeMu
routeTable []tcpip.Route
routeTable tcpip.RouteList
mu stackRWMutex `state:"nosave"`
// +checklocks:mu
@@ -280,7 +281,7 @@ type TransportEndpointInfo struct {
// incompatible with the receiver.
//
// Preconditon: the parent endpoint mu must be held while calling this method.
func (t *TransportEndpointInfo) AddrNetProtoLocked(addr tcpip.FullAddress, v6only bool) (tcpip.FullAddress, tcpip.NetworkProtocolNumber, tcpip.Error) {
func (t *TransportEndpointInfo) AddrNetProtoLocked(addr tcpip.FullAddress, v6only bool, bind bool) (tcpip.FullAddress, tcpip.NetworkProtocolNumber, tcpip.Error) {
netProto := t.NetProto
switch addr.Addr.BitLen() {
case header.IPv4AddressSizeBits:
@@ -306,6 +307,22 @@ func (t *TransportEndpointInfo) AddrNetProtoLocked(addr tcpip.FullAddress, v6onl
}
}
if !bind && addr.Addr.Unspecified() {
// If the destination address isn't set, Linux sets it to the
// source address. If a source address isn't set either, it
// sets both to the loopback address.
if t.ID.LocalAddress.Unspecified() {
switch netProto {
case header.IPv4ProtocolNumber:
addr.Addr = header.IPv4Loopback
case header.IPv6ProtocolNumber:
addr.Addr = header.IPv6Loopback
}
} else {
addr.Addr = t.ID.LocalAddress
}
}
switch {
case netProto == t.NetProto:
case netProto == header.IPv4ProtocolNumber && t.NetProto == header.IPv6ProtocolNumber:
@@ -729,21 +746,41 @@ func (s *Stack) SetPortRange(start uint16, end uint16) tcpip.Error {
func (s *Stack) SetRouteTable(table []tcpip.Route) {
s.routeMu.Lock()
defer s.routeMu.Unlock()
s.routeTable = table
s.routeTable.Reset()
for _, r := range table {
s.addRouteLocked(&r)
}
}
// GetRouteTable returns the route table which is currently in use.
func (s *Stack) GetRouteTable() []tcpip.Route {
s.routeMu.RLock()
defer s.routeMu.RUnlock()
return append([]tcpip.Route(nil), s.routeTable...)
table := make([]tcpip.Route, 0)
for r := s.routeTable.Front(); r != nil; r = r.Next() {
table = append(table, *r)
}
return table
}
// AddRoute appends a route to the route table.
func (s *Stack) AddRoute(route tcpip.Route) {
s.routeMu.Lock()
defer s.routeMu.Unlock()
s.routeTable = append(s.routeTable, route)
s.addRouteLocked(&route)
}
// +checklocks:s.routeMu
func (s *Stack) addRouteLocked(route *tcpip.Route) {
routePrefix := route.Destination.Prefix()
n := s.routeTable.Front()
for ; n != nil; n = n.Next() {
if n.Destination.Prefix() < routePrefix {
s.routeTable.InsertBefore(n, route)
return
}
}
s.routeTable.PushBack(route)
}
// RemoveRoutes removes matching routes from the route table.
@@ -751,13 +788,13 @@ func (s *Stack) RemoveRoutes(match func(tcpip.Route) bool) {
s.routeMu.Lock()
defer s.routeMu.Unlock()
var filteredRoutes []tcpip.Route
for _, route := range s.routeTable {
if !match(route) {
filteredRoutes = append(filteredRoutes, route)
for route := s.routeTable.Front(); route != nil; {
next := route.Next()
if match(*route) {
s.routeTable.Remove(route)
}
route = next
}
s.routeTable = filteredRoutes
}
// NewEndpoint creates a new transport layer endpoint of the given protocol.
@@ -970,16 +1007,13 @@ func (s *Stack) removeNICLocked(id tcpip.NICID) tcpip.Error {
// Remove routes in-place. n tracks the number of routes written.
s.routeMu.Lock()
n := 0
for _, r := range s.routeTable {
if r.NIC != id {
// Keep this route.
s.routeTable[n] = r
n++
for r := s.routeTable.Front(); r != nil; {
next := r.Next()
if r.NIC == id {
s.routeTable.Remove(r)
}
r = next
}
clear(s.routeTable[n:])
s.routeTable = s.routeTable[:n]
s.routeMu.Unlock()
return nic.remove()
@@ -1425,7 +1459,7 @@ func (s *Stack) FindRoute(id tcpip.NICID, localAddr, remoteAddr tcpip.Address, n
s.routeMu.RLock()
defer s.routeMu.RUnlock()
for _, route := range s.routeTable {
for route := s.routeTable.Front(); route != nil; route = route.Next() {
if remoteAddr.BitLen() != 0 && !route.Destination.Contains(remoteAddr) {
continue
}
@@ -1464,7 +1498,7 @@ func (s *Stack) FindRoute(id tcpip.NICID, localAddr, remoteAddr tcpip.Address, n
locallyGenerated := (id != 0 || localAddr != tcpip.Address{})
if onlyGlobalAddresses && chosenRoute.Equal(tcpip.Route{}) && isNICForwarding(nic, netProto) {
if locallyGenerated {
chosenRoute = route
chosenRoute = *route
continue
}
+3 -3
View File
@@ -4402,14 +4402,14 @@ func TestAddRoute(t *testing.T) {
t.Fatal(err)
}
subnet2, err := tcpip.NewSubnet(tcpip.AddrFromSlice([]byte("\x01\x00\x00\x00")), tcpip.MaskFrom("\x01\x00\x00\x00"))
subnet2, err := tcpip.NewSubnet(tcpip.AddrFromSlice([]byte("\x01\x00\x00\x00")), tcpip.MaskFrom("\xff\x00\x00\x00"))
if err != nil {
t.Fatal(err)
}
expected := []tcpip.Route{
{Destination: subnet1, Gateway: tcpip.AddrFromSlice([]byte("\x00\x00\x00\x00")), NIC: 1},
{Destination: subnet2, Gateway: tcpip.AddrFromSlice([]byte("\x00\x00\x00\x00")), NIC: 1},
{Destination: subnet1, Gateway: tcpip.AddrFromSlice([]byte("\x00\x00\x00\x00")), NIC: 1},
}
// Initialize the route table with one route.
@@ -4423,7 +4423,7 @@ func TestAddRoute(t *testing.T) {
t.Fatalf("Unexpected route table length got = %d, want = %d", got, want)
}
for i, route := range rt {
if got, want := route, expected[i]; got != want {
if got, want := route, expected[i]; !got.Equal(want) {
t.Fatalf("Unexpected route got = %#v, want = %#v", got, want)
}
}
+2
View File
@@ -1513,6 +1513,8 @@ func GetStackReceiveBufferLimits(so StackHandler) ReceiveBufferSizeOption {
//
// +stateify savable
type Route struct {
RouteEntry
// Destination must contain the target address for this row to be viable.
Destination Subnet
+4 -4
View File
@@ -131,7 +131,7 @@ func TestLocalPing(t *testing.T) {
netProto: ipv4.ProtocolNumber,
linkEndpoint: loopback.New,
icmpBuf: ipv4ICMPBuf,
expectedConnectErr: &tcpip.ErrHostUnreachable{},
expectedConnectErr: &tcpip.ErrNetworkUnreachable{},
checkLinkEndpoint: func(*testing.T, stack.LinkEndpoint) {},
},
{
@@ -140,7 +140,7 @@ func TestLocalPing(t *testing.T) {
netProto: ipv6.ProtocolNumber,
linkEndpoint: loopback.New,
icmpBuf: ipv6ICMPBuf,
expectedConnectErr: &tcpip.ErrHostUnreachable{},
expectedConnectErr: &tcpip.ErrBadLocalAddress{},
checkLinkEndpoint: func(*testing.T, stack.LinkEndpoint) {},
},
{
@@ -149,7 +149,7 @@ func TestLocalPing(t *testing.T) {
netProto: ipv4.ProtocolNumber,
linkEndpoint: channelEP,
icmpBuf: ipv4ICMPBuf,
expectedConnectErr: &tcpip.ErrHostUnreachable{},
expectedConnectErr: &tcpip.ErrNetworkUnreachable{},
checkLinkEndpoint: channelEPCheck,
},
{
@@ -158,7 +158,7 @@ func TestLocalPing(t *testing.T) {
netProto: ipv6.ProtocolNumber,
linkEndpoint: channelEP,
icmpBuf: ipv6ICMPBuf,
expectedConnectErr: &tcpip.ErrHostUnreachable{},
expectedConnectErr: &tcpip.ErrBadLocalAddress{},
checkLinkEndpoint: channelEPCheck,
},
}
@@ -516,7 +516,7 @@ func (e *Endpoint) AcquireContextForWrite(opts tcpip.WriteOptions) (WriteContext
}
}
dst, netProto, err := e.checkV4Mapped(*to)
dst, netProto, err := e.checkV4Mapped(*to, false /* bind */)
if err != nil {
return WriteContext{}, err
}
@@ -656,7 +656,7 @@ func (e *Endpoint) ConnectAndThen(addr tcpip.FullAddress, f func(netProto tcpip.
return &tcpip.ErrInvalidEndpointState{}
}
addr, netProto, err := e.checkV4Mapped(addr)
addr, netProto, err := e.checkV4Mapped(addr, false /* bind */)
if err != nil {
return err
}
@@ -710,9 +710,9 @@ func (e *Endpoint) Shutdown() tcpip.Error {
// checkV4MappedRLocked determines the effective network protocol and converts
// addr to its canonical form.
func (e *Endpoint) checkV4Mapped(addr tcpip.FullAddress) (tcpip.FullAddress, tcpip.NetworkProtocolNumber, tcpip.Error) {
func (e *Endpoint) checkV4Mapped(addr tcpip.FullAddress, bind bool) (tcpip.FullAddress, tcpip.NetworkProtocolNumber, tcpip.Error) {
info := e.Info()
unwrapped, netProto, err := info.AddrNetProtoLocked(addr, e.ops.GetV6Only())
unwrapped, netProto, err := info.AddrNetProtoLocked(addr, e.ops.GetV6Only(), bind)
if err != nil {
return tcpip.FullAddress{}, 0, err
}
@@ -747,7 +747,7 @@ func (e *Endpoint) BindAndThen(addr tcpip.FullAddress, f func(tcpip.NetworkProto
return &tcpip.ErrInvalidEndpointState{}
}
addr, netProto, err := e.checkV4Mapped(addr)
addr, netProto, err := e.checkV4Mapped(addr, true /* bind */)
if err != nil {
return err
}
@@ -907,7 +907,7 @@ func (e *Endpoint) SetSockOpt(opt tcpip.SettableSocketOption) tcpip.Error {
defer e.mu.Unlock()
fa := tcpip.FullAddress{Addr: v.InterfaceAddr}
fa, netProto, err := e.checkV4Mapped(fa)
fa, netProto, err := e.checkV4Mapped(fa, true /* bind */)
if err != nil {
return err
}
+4 -4
View File
@@ -2220,8 +2220,8 @@ func (e *Endpoint) GetSockOpt(opt tcpip.GettableSocketOption) tcpip.Error {
// checkV4MappedLocked determines the effective network protocol and converts
// addr to its canonical form.
// +checklocks:e.mu
func (e *Endpoint) checkV4MappedLocked(addr tcpip.FullAddress) (tcpip.FullAddress, tcpip.NetworkProtocolNumber, tcpip.Error) {
unwrapped, netProto, err := e.TransportEndpointInfo.AddrNetProtoLocked(addr, e.ops.GetV6Only())
func (e *Endpoint) checkV4MappedLocked(addr tcpip.FullAddress, bind bool) (tcpip.FullAddress, tcpip.NetworkProtocolNumber, tcpip.Error) {
unwrapped, netProto, err := e.TransportEndpointInfo.AddrNetProtoLocked(addr, e.ops.GetV6Only(), bind)
if err != nil {
return tcpip.FullAddress{}, 0, err
}
@@ -2388,7 +2388,7 @@ func (e *Endpoint) registerEndpoint(addr tcpip.FullAddress, netProto tcpip.Netwo
func (e *Endpoint) connect(addr tcpip.FullAddress, handshake bool) tcpip.Error {
connectingAddr := addr.Addr
addr, netProto, err := e.checkV4MappedLocked(addr)
addr, netProto, err := e.checkV4MappedLocked(addr, false /* bind */)
if err != nil {
return err
}
@@ -2740,7 +2740,7 @@ func (e *Endpoint) bindLocked(addr tcpip.FullAddress) (err tcpip.Error) {
}
e.BindAddr = addr.Addr
addr, netProto, err := e.checkV4MappedLocked(addr)
addr, netProto, err := e.checkV4MappedLocked(addr, true /* bind */)
if err != nil {
return err
}
+1 -1
View File
@@ -140,7 +140,7 @@ func (e *Endpoint) Restore(s *stack.Stack) {
bind := func() {
e.mu.Lock()
defer e.mu.Unlock()
addr, _, err := e.checkV4MappedLocked(tcpip.FullAddress{Addr: e.BindAddr, Port: e.TransportEndpointInfo.ID.LocalPort})
addr, _, err := e.checkV4MappedLocked(tcpip.FullAddress{Addr: e.BindAddr, Port: e.TransportEndpointInfo.ID.LocalPort}, true /* bind */)
if err != nil {
panic("unable to parse BindAddr: " + err.String())
}