mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Use unicast source for ICMP echo replies
Packets MUST NOT use a non-unicast source address for ICMP Echo Replies. Test: integration_test.TestPingMulticastBroadcast PiperOrigin-RevId: 325634380
This commit is contained in:
committed by
gVisor bot
parent
13a8ae81b2
commit
b404b5c255
@@ -96,6 +96,26 @@ func (e *endpoint) handleICMP(r *stack.Route, pkt *stack.PacketBuffer) {
|
||||
NetworkHeader: append(buffer.View(nil), pkt.NetworkHeader...),
|
||||
})
|
||||
|
||||
remoteLinkAddr := r.RemoteLinkAddress
|
||||
|
||||
// As per RFC 1122 section 3.2.1.3, when a host sends any datagram, the IP
|
||||
// source address MUST be one of its own IP addresses (but not a broadcast
|
||||
// or multicast address).
|
||||
localAddr := r.LocalAddress
|
||||
if r.IsInboundBroadcast() || header.IsV4MulticastAddress(r.LocalAddress) {
|
||||
localAddr = ""
|
||||
}
|
||||
|
||||
r, err := r.Stack().FindRoute(e.NICID(), localAddr, r.RemoteAddress, ProtocolNumber, false /* multicastLoop */)
|
||||
if err != nil {
|
||||
// If we cannot find a route to the destination, silently drop the packet.
|
||||
return
|
||||
}
|
||||
defer r.Release()
|
||||
|
||||
// Use the remote link address from the incoming packet.
|
||||
r.ResolveWith(remoteLinkAddr)
|
||||
|
||||
vv := pkt.Data.Clone(nil)
|
||||
vv.TrimFront(header.ICMPv4MinimumSize)
|
||||
hdr := buffer.NewPrependable(int(r.MaxHeaderLength()) + header.ICMPv4MinimumSize)
|
||||
|
||||
@@ -389,6 +389,26 @@ func (e *endpoint) handleICMP(r *stack.Route, pkt *stack.PacketBuffer, hasFragme
|
||||
received.Invalid.Increment()
|
||||
return
|
||||
}
|
||||
|
||||
remoteLinkAddr := r.RemoteLinkAddress
|
||||
|
||||
// As per RFC 4291 section 2.7, multicast addresses must not be used as
|
||||
// source addresses in IPv6 packets.
|
||||
localAddr := r.LocalAddress
|
||||
if header.IsV6MulticastAddress(r.LocalAddress) {
|
||||
localAddr = ""
|
||||
}
|
||||
|
||||
r, err := r.Stack().FindRoute(e.NICID(), localAddr, r.RemoteAddress, ProtocolNumber, false /* multicastLoop */)
|
||||
if err != nil {
|
||||
// If we cannot find a route to the destination, silently drop the packet.
|
||||
return
|
||||
}
|
||||
defer r.Release()
|
||||
|
||||
// Use the link address from the source of the original packet.
|
||||
r.ResolveWith(remoteLinkAddr)
|
||||
|
||||
pkt.Data.TrimFront(header.ICMPv6EchoMinimumSize)
|
||||
hdr := buffer.NewPrependable(int(r.MaxHeaderLength()) + header.ICMPv6EchoMinimumSize)
|
||||
packet := header.ICMPv6(hdr.Prepend(header.ICMPv6EchoMinimumSize))
|
||||
|
||||
@@ -121,6 +121,7 @@ go_test(
|
||||
"//pkg/tcpip/header",
|
||||
"//pkg/tcpip/link/channel",
|
||||
"//pkg/tcpip/link/loopback",
|
||||
"//pkg/tcpip/network/arp",
|
||||
"//pkg/tcpip/network/ipv4",
|
||||
"//pkg/tcpip/network/ipv6",
|
||||
"//pkg/tcpip/ports",
|
||||
|
||||
@@ -110,6 +110,12 @@ func (r *Route) GSOMaxSize() uint32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// ResolveWith immediately resolves a route with the specified remote link
|
||||
// address.
|
||||
func (r *Route) ResolveWith(addr tcpip.LinkAddress) {
|
||||
r.RemoteLinkAddress = addr
|
||||
}
|
||||
|
||||
// Resolve attempts to resolve the link address if necessary. Returns ErrWouldBlock in
|
||||
// case address resolution requires blocking, e.g. wait for ARP reply. Waker is
|
||||
// notified when address resolution is complete (success or not).
|
||||
@@ -279,12 +285,26 @@ func (r *Route) Stack() *Stack {
|
||||
return r.ref.stack()
|
||||
}
|
||||
|
||||
// IsBroadcast returns true if the route is to send a broadcast packet.
|
||||
func (r *Route) IsBroadcast() bool {
|
||||
// IsOutboundBroadcast returns true if the route is for an outbound broadcast
|
||||
// packet.
|
||||
func (r *Route) IsOutboundBroadcast() bool {
|
||||
// Only IPv4 has a notion of broadcast.
|
||||
return r.directedBroadcast || r.RemoteAddress == header.IPv4Broadcast
|
||||
}
|
||||
|
||||
// IsInboundBroadcast returns true if the route is for an inbound broadcast
|
||||
// packet.
|
||||
func (r *Route) IsInboundBroadcast() bool {
|
||||
// Only IPv4 has a notion of broadcast.
|
||||
if r.LocalAddress == header.IPv4Broadcast {
|
||||
return true
|
||||
}
|
||||
|
||||
addr := r.ref.addrWithPrefix()
|
||||
subnet := addr.Subnet()
|
||||
return subnet.IsBroadcast(r.LocalAddress)
|
||||
}
|
||||
|
||||
// ReverseRoute returns new route with given source and destination address.
|
||||
func (r *Route) ReverseRoute(src tcpip.Address, dst tcpip.Address) Route {
|
||||
return Route{
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math"
|
||||
"net"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -34,6 +35,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/link/channel"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/link/loopback"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/network/arp"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/network/ipv6"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
@@ -3694,3 +3696,49 @@ func TestOutgoingSubnetBroadcast(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveWith(t *testing.T) {
|
||||
const (
|
||||
unspecifiedNICID = 0
|
||||
nicID = 1
|
||||
)
|
||||
|
||||
s := stack.New(stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocol{ipv4.NewProtocol(), arp.NewProtocol()},
|
||||
})
|
||||
ep := channel.New(0, defaultMTU, "")
|
||||
ep.LinkEPCapabilities |= stack.CapabilityResolutionRequired
|
||||
if err := s.CreateNIC(nicID, ep); err != nil {
|
||||
t.Fatalf("CreateNIC(%d, _): %s", nicID, err)
|
||||
}
|
||||
addr := tcpip.ProtocolAddress{
|
||||
Protocol: header.IPv4ProtocolNumber,
|
||||
AddressWithPrefix: tcpip.AddressWithPrefix{
|
||||
Address: tcpip.Address(net.ParseIP("192.168.1.58").To4()),
|
||||
PrefixLen: 24,
|
||||
},
|
||||
}
|
||||
if err := s.AddProtocolAddress(nicID, addr); err != nil {
|
||||
t.Fatalf("AddProtocolAddress(%d, %+v): %s", nicID, addr, err)
|
||||
}
|
||||
|
||||
s.SetRouteTable([]tcpip.Route{{Destination: header.IPv4EmptySubnet, NIC: nicID}})
|
||||
|
||||
remoteAddr := tcpip.Address(net.ParseIP("192.168.1.59").To4())
|
||||
r, err := s.FindRoute(unspecifiedNICID, "" /* localAddr */, remoteAddr, header.IPv4ProtocolNumber, false /* multicastLoop */)
|
||||
if err != nil {
|
||||
t.Fatalf("FindRoute(%d, '', %s, %d): %s", unspecifiedNICID, remoteAddr, header.IPv4ProtocolNumber, err)
|
||||
}
|
||||
defer r.Release()
|
||||
|
||||
// Should initially require resolution.
|
||||
if !r.IsResolutionRequired() {
|
||||
t.Fatal("got r.IsResolutionRequired() = false, want = true")
|
||||
}
|
||||
|
||||
// Manually resolving the route should no longer require resolution.
|
||||
r.ResolveWith("\x01")
|
||||
if r.IsResolutionRequired() {
|
||||
t.Fatal("got r.IsResolutionRequired() = true, want = false")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ go_test(
|
||||
"//pkg/tcpip/network/ipv4",
|
||||
"//pkg/tcpip/network/ipv6",
|
||||
"//pkg/tcpip/stack",
|
||||
"//pkg/tcpip/transport/icmp",
|
||||
"//pkg/tcpip/transport/udp",
|
||||
"//pkg/waiter",
|
||||
"@com_github_google_go_cmp//cmp:go_default_library",
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package integration_test
|
||||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
@@ -25,11 +26,195 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/network/ipv6"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/transport/icmp"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/transport/udp"
|
||||
"gvisor.dev/gvisor/pkg/waiter"
|
||||
)
|
||||
|
||||
const defaultMTU = 1280
|
||||
const (
|
||||
defaultMTU = 1280
|
||||
ttl = 255
|
||||
)
|
||||
|
||||
var (
|
||||
ipv4Addr = tcpip.AddressWithPrefix{
|
||||
Address: tcpip.Address(net.ParseIP("192.168.1.58").To4()),
|
||||
PrefixLen: 24,
|
||||
}
|
||||
ipv4Subnet = ipv4Addr.Subnet()
|
||||
ipv4SubnetBcast = ipv4Subnet.Broadcast()
|
||||
|
||||
ipv6Addr = tcpip.AddressWithPrefix{
|
||||
Address: tcpip.Address(net.ParseIP("200a::1").To16()),
|
||||
PrefixLen: 64,
|
||||
}
|
||||
ipv6Subnet = ipv6Addr.Subnet()
|
||||
ipv6SubnetBcast = ipv6Subnet.Broadcast()
|
||||
|
||||
// Remote addrs.
|
||||
remoteIPv4Addr = tcpip.Address(net.ParseIP("10.0.0.1").To4())
|
||||
remoteIPv6Addr = tcpip.Address(net.ParseIP("200b::1").To16())
|
||||
)
|
||||
|
||||
// TestPingMulticastBroadcast tests that responding to an Echo Request destined
|
||||
// to a multicast or broadcast address uses a unicast source address for the
|
||||
// reply.
|
||||
func TestPingMulticastBroadcast(t *testing.T) {
|
||||
const nicID = 1
|
||||
|
||||
rxIPv4ICMP := func(e *channel.Endpoint, dst tcpip.Address) {
|
||||
totalLen := header.IPv4MinimumSize + header.ICMPv4MinimumSize
|
||||
hdr := buffer.NewPrependable(totalLen)
|
||||
pkt := header.ICMPv4(hdr.Prepend(header.ICMPv4MinimumSize))
|
||||
pkt.SetType(header.ICMPv4Echo)
|
||||
pkt.SetCode(0)
|
||||
pkt.SetChecksum(0)
|
||||
pkt.SetChecksum(^header.Checksum(pkt, 0))
|
||||
ip := header.IPv4(hdr.Prepend(header.IPv4MinimumSize))
|
||||
ip.Encode(&header.IPv4Fields{
|
||||
IHL: header.IPv4MinimumSize,
|
||||
TotalLength: uint16(totalLen),
|
||||
Protocol: uint8(icmp.ProtocolNumber4),
|
||||
TTL: ttl,
|
||||
SrcAddr: remoteIPv4Addr,
|
||||
DstAddr: dst,
|
||||
})
|
||||
|
||||
e.InjectInbound(header.IPv4ProtocolNumber, &stack.PacketBuffer{
|
||||
Data: hdr.View().ToVectorisedView(),
|
||||
})
|
||||
}
|
||||
|
||||
rxIPv6ICMP := func(e *channel.Endpoint, dst tcpip.Address) {
|
||||
totalLen := header.IPv6MinimumSize + header.ICMPv6MinimumSize
|
||||
hdr := buffer.NewPrependable(totalLen)
|
||||
pkt := header.ICMPv6(hdr.Prepend(header.ICMPv6MinimumSize))
|
||||
pkt.SetType(header.ICMPv6EchoRequest)
|
||||
pkt.SetCode(0)
|
||||
pkt.SetChecksum(0)
|
||||
pkt.SetChecksum(header.ICMPv6Checksum(pkt, remoteIPv6Addr, dst, buffer.VectorisedView{}))
|
||||
ip := header.IPv6(hdr.Prepend(header.IPv6MinimumSize))
|
||||
ip.Encode(&header.IPv6Fields{
|
||||
PayloadLength: header.ICMPv6MinimumSize,
|
||||
NextHeader: uint8(icmp.ProtocolNumber6),
|
||||
HopLimit: ttl,
|
||||
SrcAddr: remoteIPv6Addr,
|
||||
DstAddr: dst,
|
||||
})
|
||||
|
||||
e.InjectInbound(header.IPv6ProtocolNumber, &stack.PacketBuffer{
|
||||
Data: hdr.View().ToVectorisedView(),
|
||||
})
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
dstAddr tcpip.Address
|
||||
}{
|
||||
{
|
||||
name: "IPv4 unicast",
|
||||
dstAddr: ipv4Addr.Address,
|
||||
},
|
||||
{
|
||||
name: "IPv4 directed broadcast",
|
||||
dstAddr: ipv4SubnetBcast,
|
||||
},
|
||||
{
|
||||
name: "IPv4 broadcast",
|
||||
dstAddr: header.IPv4Broadcast,
|
||||
},
|
||||
{
|
||||
name: "IPv4 all-systems multicast",
|
||||
dstAddr: header.IPv4AllSystems,
|
||||
},
|
||||
{
|
||||
name: "IPv6 unicast",
|
||||
dstAddr: ipv6Addr.Address,
|
||||
},
|
||||
{
|
||||
name: "IPv6 all-nodes multicast",
|
||||
dstAddr: header.IPv6AllNodesMulticastAddress,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
ipv4Proto := ipv4.NewProtocol()
|
||||
ipv6Proto := ipv6.NewProtocol()
|
||||
s := stack.New(stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocol{ipv4Proto, ipv6Proto},
|
||||
TransportProtocols: []stack.TransportProtocol{icmp.NewProtocol4(), icmp.NewProtocol6()},
|
||||
})
|
||||
// We only expect a single packet in response to our ICMP Echo Request.
|
||||
e := channel.New(1, defaultMTU, "")
|
||||
if err := s.CreateNIC(nicID, e); err != nil {
|
||||
t.Fatalf("CreateNIC(%d, _): %s", nicID, err)
|
||||
}
|
||||
ipv4ProtoAddr := tcpip.ProtocolAddress{Protocol: header.IPv4ProtocolNumber, AddressWithPrefix: ipv4Addr}
|
||||
if err := s.AddProtocolAddress(nicID, ipv4ProtoAddr); err != nil {
|
||||
t.Fatalf("AddProtocolAddress(%d, %+v): %s", nicID, ipv4ProtoAddr, err)
|
||||
}
|
||||
ipv6ProtoAddr := tcpip.ProtocolAddress{Protocol: header.IPv6ProtocolNumber, AddressWithPrefix: ipv6Addr}
|
||||
if err := s.AddProtocolAddress(nicID, ipv6ProtoAddr); err != nil {
|
||||
t.Fatalf("AddProtocolAddress(%d, %+v): %s", nicID, ipv6ProtoAddr, err)
|
||||
}
|
||||
|
||||
// Default routes for IPv4 and IPv6 so ICMP can find a route to the remote
|
||||
// node when attempting to send the ICMP Echo Reply.
|
||||
s.SetRouteTable([]tcpip.Route{
|
||||
tcpip.Route{
|
||||
Destination: header.IPv6EmptySubnet,
|
||||
NIC: nicID,
|
||||
},
|
||||
tcpip.Route{
|
||||
Destination: header.IPv4EmptySubnet,
|
||||
NIC: nicID,
|
||||
},
|
||||
})
|
||||
|
||||
var rxICMP func(*channel.Endpoint, tcpip.Address)
|
||||
var expectedSrc tcpip.Address
|
||||
var expectedDst tcpip.Address
|
||||
var proto stack.NetworkProtocol
|
||||
switch l := len(test.dstAddr); l {
|
||||
case header.IPv4AddressSize:
|
||||
rxICMP = rxIPv4ICMP
|
||||
expectedSrc = ipv4Addr.Address
|
||||
expectedDst = remoteIPv4Addr
|
||||
proto = ipv4Proto
|
||||
case header.IPv6AddressSize:
|
||||
rxICMP = rxIPv6ICMP
|
||||
expectedSrc = ipv6Addr.Address
|
||||
expectedDst = remoteIPv6Addr
|
||||
proto = ipv6Proto
|
||||
default:
|
||||
t.Fatalf("got unexpected address length = %d bytes", l)
|
||||
}
|
||||
|
||||
rxICMP(e, test.dstAddr)
|
||||
pkt, ok := e.Read()
|
||||
if !ok {
|
||||
t.Fatal("expected ICMP response")
|
||||
}
|
||||
|
||||
if pkt.Route.LocalAddress != expectedSrc {
|
||||
t.Errorf("got pkt.Route.LocalAddress = %s, want = %s", pkt.Route.LocalAddress, expectedSrc)
|
||||
}
|
||||
if pkt.Route.RemoteAddress != expectedDst {
|
||||
t.Errorf("got pkt.Route.RemoteAddress = %s, want = %s", pkt.Route.RemoteAddress, expectedDst)
|
||||
}
|
||||
|
||||
src, dst := proto.ParseAddresses(pkt.Pkt.NetworkHeader)
|
||||
if src != expectedSrc {
|
||||
t.Errorf("got pkt source = %s, want = %s", src, expectedSrc)
|
||||
}
|
||||
if dst != expectedDst {
|
||||
t.Errorf("got pkt destination = %s, want = %s", dst, expectedDst)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// TestIncomingMulticastAndBroadcast tests receiving a packet destined to some
|
||||
// multicast or broadcast address.
|
||||
@@ -38,31 +223,10 @@ func TestIncomingMulticastAndBroadcast(t *testing.T) {
|
||||
nicID = 1
|
||||
remotePort = 5555
|
||||
localPort = 80
|
||||
ttl = 255
|
||||
)
|
||||
|
||||
data := []byte{1, 2, 3, 4}
|
||||
|
||||
// Local IPv4 subnet: 192.168.1.58/24
|
||||
ipv4Addr := tcpip.AddressWithPrefix{
|
||||
Address: "\xc0\xa8\x01\x3a",
|
||||
PrefixLen: 24,
|
||||
}
|
||||
ipv4Subnet := ipv4Addr.Subnet()
|
||||
ipv4SubnetBcast := ipv4Subnet.Broadcast()
|
||||
|
||||
// Local IPv6 subnet: 200a::1/64
|
||||
ipv6Addr := tcpip.AddressWithPrefix{
|
||||
Address: "\x20\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01",
|
||||
PrefixLen: 64,
|
||||
}
|
||||
ipv6Subnet := ipv6Addr.Subnet()
|
||||
ipv6SubnetBcast := ipv6Subnet.Broadcast()
|
||||
|
||||
// Remote addrs.
|
||||
remoteIPv4Addr := tcpip.Address("\x64\x0a\x7b\x18")
|
||||
remoteIPv6Addr := tcpip.Address("\x20\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02")
|
||||
|
||||
rxIPv4UDP := func(e *channel.Endpoint, dst tcpip.Address) {
|
||||
payloadLen := header.UDPMinimumSize + len(data)
|
||||
totalLen := header.IPv4MinimumSize + payloadLen
|
||||
|
||||
@@ -499,7 +499,7 @@ func (e *endpoint) write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, <-c
|
||||
resolve = route.Resolve
|
||||
}
|
||||
|
||||
if !e.broadcast && route.IsBroadcast() {
|
||||
if !e.broadcast && route.IsOutboundBroadcast() {
|
||||
return 0, nil, tcpip.ErrBroadcastDisabled
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user