mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
netstack: choose route by longest prefix
It has to return the most specific route. PiperOrigin-RevId: 647421535
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user