mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Support specifying local IPv6 adddress/interface
...for outgoing packets. This is the first step in supporting IPV6_PKTINFO in the send path. Note that the implementation enforces strong host semantics as gVisor currently follows the strong host model. This change does not support IPV6_PKTINFO in the send path from Linux applications (via CMSGS) yet as there is no need. The bindings to enable the IPV6_PKTINFO CMSG in the sendpath can be done in a followup. Bug: https://fxbug.dev/102222 PiperOrigin-RevId: 454898862
This commit is contained in:
committed by
gVisor bot
parent
82498d087e
commit
fa7c3d9c51
@@ -436,6 +436,12 @@ type SendableControlMessages struct {
|
||||
|
||||
// HopLimit is the IPv6 Hop Limit of the associated packet.
|
||||
HopLimit uint8
|
||||
|
||||
// HasIPv6PacketInfo indicates whether IPv6PacketInfo is set.
|
||||
HasIPv6PacketInfo bool
|
||||
|
||||
// IPv6PacketInfo holds interface and address data on an incoming packet.
|
||||
IPv6PacketInfo IPv6PacketInfo
|
||||
}
|
||||
|
||||
// ReceivableControlMessages contains socket control messages that can be
|
||||
|
||||
@@ -19,7 +19,9 @@ go_test(
|
||||
deps = [
|
||||
":transport",
|
||||
"//pkg/tcpip",
|
||||
"//pkg/tcpip/checker",
|
||||
"//pkg/tcpip/header",
|
||||
"//pkg/tcpip/link/channel",
|
||||
"//pkg/tcpip/link/loopback",
|
||||
"//pkg/tcpip/network/ipv4",
|
||||
"//pkg/tcpip/network/ipv6",
|
||||
|
||||
@@ -23,7 +23,9 @@ import (
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/checker"
|
||||
"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/ipv4"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/network/ipv6"
|
||||
@@ -664,3 +666,451 @@ func TestMulticastLoop(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIPv6PacketInfo(t *testing.T) {
|
||||
const (
|
||||
nicID1 = 1
|
||||
nicID2 = 2
|
||||
port = 12345
|
||||
)
|
||||
|
||||
type localNICAddr struct {
|
||||
nicID tcpip.NICID
|
||||
addr tcpip.AddressWithPrefix
|
||||
}
|
||||
|
||||
type testCase struct {
|
||||
name string
|
||||
boundNICID tcpip.NICID
|
||||
bindAddr tcpip.FullAddress
|
||||
connectAddr tcpip.FullAddress
|
||||
toAddr tcpip.FullAddress
|
||||
pktInfo tcpip.IPv6PacketInfo
|
||||
|
||||
expectedErr tcpip.Error
|
||||
expectedLocalAddr tcpip.Address
|
||||
expectedRemoteAddr tcpip.Address
|
||||
}
|
||||
|
||||
ipv6Addr1 := testutil.MustParse6("1::1")
|
||||
ipv6Addr2 := testutil.MustParse6("1::2")
|
||||
ipv6RemoteAddr1 := testutil.MustParse6("2::1")
|
||||
ipv6RemoteAddr2 := testutil.MustParse6("2::2")
|
||||
|
||||
localAddrs := []localNICAddr{
|
||||
{
|
||||
nicID: nicID1,
|
||||
addr: ipv6Addr1.WithPrefix(),
|
||||
},
|
||||
{
|
||||
nicID: nicID2,
|
||||
addr: ipv6Addr2.WithPrefix(),
|
||||
},
|
||||
}
|
||||
|
||||
tests := []testCase{
|
||||
// Bind and SendTo
|
||||
{
|
||||
name: "Bind wildcard & SendTo with packet info NIC",
|
||||
bindAddr: tcpip.FullAddress{
|
||||
Addr: "",
|
||||
Port: port,
|
||||
},
|
||||
toAddr: tcpip.FullAddress{
|
||||
Addr: ipv6RemoteAddr1,
|
||||
Port: port,
|
||||
},
|
||||
pktInfo: tcpip.IPv6PacketInfo{
|
||||
NIC: nicID1,
|
||||
},
|
||||
expectedLocalAddr: ipv6Addr1,
|
||||
expectedRemoteAddr: ipv6RemoteAddr1,
|
||||
},
|
||||
{
|
||||
name: "BindToDevice & Bind wildcard & SendTo with packet info NIC not matching",
|
||||
boundNICID: nicID2,
|
||||
bindAddr: tcpip.FullAddress{
|
||||
Addr: "",
|
||||
Port: port,
|
||||
},
|
||||
toAddr: tcpip.FullAddress{
|
||||
Addr: ipv6RemoteAddr1,
|
||||
Port: port,
|
||||
},
|
||||
pktInfo: tcpip.IPv6PacketInfo{
|
||||
NIC: nicID1,
|
||||
},
|
||||
expectedErr: &tcpip.ErrNoRoute{},
|
||||
},
|
||||
{
|
||||
name: "Bind wildcard and NIC & SendTo with packet info NIC matching",
|
||||
bindAddr: tcpip.FullAddress{
|
||||
NIC: nicID1,
|
||||
Addr: "",
|
||||
Port: port,
|
||||
},
|
||||
toAddr: tcpip.FullAddress{
|
||||
Addr: ipv6RemoteAddr1,
|
||||
Port: port,
|
||||
},
|
||||
pktInfo: tcpip.IPv6PacketInfo{
|
||||
NIC: nicID1,
|
||||
},
|
||||
expectedLocalAddr: ipv6Addr1,
|
||||
expectedRemoteAddr: ipv6RemoteAddr1,
|
||||
},
|
||||
{
|
||||
name: "Bind wildcard and NIC & SendTo with packet info NIC not matching",
|
||||
bindAddr: tcpip.FullAddress{
|
||||
NIC: nicID2,
|
||||
Addr: "",
|
||||
Port: port,
|
||||
},
|
||||
toAddr: tcpip.FullAddress{
|
||||
Addr: ipv6RemoteAddr1,
|
||||
Port: port,
|
||||
},
|
||||
pktInfo: tcpip.IPv6PacketInfo{
|
||||
NIC: nicID1,
|
||||
},
|
||||
expectedErr: &tcpip.ErrNoRoute{},
|
||||
},
|
||||
{
|
||||
name: "Bind specified & SendTo with packet info NIC not matching bound addr",
|
||||
bindAddr: tcpip.FullAddress{
|
||||
Addr: ipv6Addr2,
|
||||
Port: port,
|
||||
},
|
||||
toAddr: tcpip.FullAddress{
|
||||
Addr: ipv6RemoteAddr1,
|
||||
Port: port,
|
||||
},
|
||||
pktInfo: tcpip.IPv6PacketInfo{
|
||||
NIC: nicID1,
|
||||
},
|
||||
expectedErr: &tcpip.ErrBadLocalAddress{},
|
||||
},
|
||||
{
|
||||
name: "Bind specified and NIC & SendTo with packet info NIC not matching but local addr specified",
|
||||
bindAddr: tcpip.FullAddress{
|
||||
NIC: nicID2,
|
||||
Addr: ipv6Addr2,
|
||||
Port: port,
|
||||
},
|
||||
toAddr: tcpip.FullAddress{
|
||||
Addr: ipv6RemoteAddr1,
|
||||
Port: port,
|
||||
},
|
||||
pktInfo: tcpip.IPv6PacketInfo{
|
||||
NIC: nicID1,
|
||||
Addr: ipv6Addr1,
|
||||
},
|
||||
expectedLocalAddr: ipv6Addr1,
|
||||
expectedRemoteAddr: ipv6RemoteAddr1,
|
||||
},
|
||||
|
||||
// Bind and Connect
|
||||
{
|
||||
name: "Bind wildcard & Connect then Send with packet info NIC",
|
||||
bindAddr: tcpip.FullAddress{
|
||||
Addr: "",
|
||||
Port: port,
|
||||
},
|
||||
connectAddr: tcpip.FullAddress{
|
||||
Addr: ipv6RemoteAddr1,
|
||||
Port: port,
|
||||
},
|
||||
pktInfo: tcpip.IPv6PacketInfo{
|
||||
NIC: nicID1,
|
||||
},
|
||||
expectedLocalAddr: ipv6Addr1,
|
||||
expectedRemoteAddr: ipv6RemoteAddr1,
|
||||
},
|
||||
{
|
||||
name: "Bind wildcard and NIC & Connect then Send with packet info NIC matching",
|
||||
bindAddr: tcpip.FullAddress{
|
||||
NIC: nicID1,
|
||||
Addr: "",
|
||||
Port: port,
|
||||
},
|
||||
connectAddr: tcpip.FullAddress{
|
||||
Addr: ipv6RemoteAddr1,
|
||||
Port: port,
|
||||
},
|
||||
pktInfo: tcpip.IPv6PacketInfo{
|
||||
NIC: nicID1,
|
||||
},
|
||||
expectedLocalAddr: ipv6Addr1,
|
||||
expectedRemoteAddr: ipv6RemoteAddr1,
|
||||
},
|
||||
{
|
||||
name: "Bind wildcard and NIC & Connect then Send with packet info NIC not matching",
|
||||
bindAddr: tcpip.FullAddress{
|
||||
NIC: nicID2,
|
||||
Addr: "",
|
||||
Port: port,
|
||||
},
|
||||
connectAddr: tcpip.FullAddress{
|
||||
Addr: ipv6RemoteAddr1,
|
||||
Port: port,
|
||||
},
|
||||
pktInfo: tcpip.IPv6PacketInfo{
|
||||
NIC: nicID1,
|
||||
},
|
||||
expectedErr: &tcpip.ErrNoRoute{},
|
||||
},
|
||||
{
|
||||
name: "Bind wildcard & Connect with NIC then Send with packet info NIC matching",
|
||||
bindAddr: tcpip.FullAddress{
|
||||
Addr: "",
|
||||
Port: port,
|
||||
},
|
||||
connectAddr: tcpip.FullAddress{
|
||||
NIC: nicID1,
|
||||
Addr: ipv6RemoteAddr1,
|
||||
Port: port,
|
||||
},
|
||||
pktInfo: tcpip.IPv6PacketInfo{
|
||||
NIC: nicID1,
|
||||
},
|
||||
expectedLocalAddr: ipv6Addr1,
|
||||
expectedRemoteAddr: ipv6RemoteAddr1,
|
||||
},
|
||||
{
|
||||
name: "Bind wildcard & Connect with NIC then Send with packet info NIC not matching",
|
||||
bindAddr: tcpip.FullAddress{
|
||||
Addr: "",
|
||||
Port: port,
|
||||
},
|
||||
connectAddr: tcpip.FullAddress{
|
||||
NIC: nicID2,
|
||||
Addr: ipv6RemoteAddr1,
|
||||
Port: port,
|
||||
},
|
||||
pktInfo: tcpip.IPv6PacketInfo{
|
||||
NIC: nicID1,
|
||||
},
|
||||
expectedErr: &tcpip.ErrNoRoute{},
|
||||
},
|
||||
{
|
||||
name: "Bind specified & Connect then Send with packet info NIC not matching but local addr specified",
|
||||
bindAddr: tcpip.FullAddress{
|
||||
NIC: nicID2,
|
||||
Addr: ipv6Addr2,
|
||||
Port: port,
|
||||
},
|
||||
connectAddr: tcpip.FullAddress{
|
||||
Addr: ipv6RemoteAddr1,
|
||||
Port: port,
|
||||
},
|
||||
pktInfo: tcpip.IPv6PacketInfo{
|
||||
NIC: nicID1,
|
||||
Addr: ipv6Addr1,
|
||||
},
|
||||
expectedErr: &tcpip.ErrNoRoute{},
|
||||
},
|
||||
|
||||
// Connect
|
||||
{
|
||||
name: "Connect with NIC then Send with packet info NIC matching",
|
||||
connectAddr: tcpip.FullAddress{
|
||||
NIC: nicID1,
|
||||
Addr: ipv6RemoteAddr1,
|
||||
Port: port,
|
||||
},
|
||||
pktInfo: tcpip.IPv6PacketInfo{
|
||||
NIC: nicID1,
|
||||
},
|
||||
expectedLocalAddr: ipv6Addr1,
|
||||
expectedRemoteAddr: ipv6RemoteAddr1,
|
||||
},
|
||||
{
|
||||
// Because NIC2 is preferred over NIC1 for route selection, we pick a
|
||||
// local address on NIC2. Since the pktinfo does not specify a local
|
||||
// address but requests the packet to be sent out through NIC1 we fail
|
||||
// with err bad local address because NIC2's local address is not
|
||||
// available on NIC1.
|
||||
name: "Connect then Send with packet info NIC not matching",
|
||||
connectAddr: tcpip.FullAddress{
|
||||
Addr: ipv6RemoteAddr1,
|
||||
Port: port,
|
||||
},
|
||||
pktInfo: tcpip.IPv6PacketInfo{
|
||||
NIC: nicID1,
|
||||
},
|
||||
expectedErr: &tcpip.ErrBadLocalAddress{},
|
||||
},
|
||||
{
|
||||
name: "BindToDevice & Connect then Send with packet info NIC matching",
|
||||
boundNICID: nicID2,
|
||||
connectAddr: tcpip.FullAddress{
|
||||
Addr: ipv6RemoteAddr1,
|
||||
Port: port,
|
||||
},
|
||||
pktInfo: tcpip.IPv6PacketInfo{
|
||||
NIC: nicID1,
|
||||
},
|
||||
expectedErr: &tcpip.ErrNoRoute{},
|
||||
},
|
||||
{
|
||||
name: "Connect then Send with packet info NIC not matching",
|
||||
connectAddr: tcpip.FullAddress{
|
||||
NIC: nicID2,
|
||||
Addr: ipv6RemoteAddr1,
|
||||
Port: port,
|
||||
},
|
||||
pktInfo: tcpip.IPv6PacketInfo{
|
||||
NIC: nicID1,
|
||||
},
|
||||
expectedErr: &tcpip.ErrNoRoute{},
|
||||
},
|
||||
|
||||
// Connect and SendTo
|
||||
{
|
||||
name: "Connect with NIC then SendTo with different NIC with packet info NIC matching SendTo NIC",
|
||||
connectAddr: tcpip.FullAddress{
|
||||
NIC: nicID2,
|
||||
Addr: ipv6RemoteAddr2,
|
||||
Port: port,
|
||||
},
|
||||
toAddr: tcpip.FullAddress{
|
||||
NIC: nicID1,
|
||||
Addr: ipv6RemoteAddr1,
|
||||
Port: port,
|
||||
},
|
||||
pktInfo: tcpip.IPv6PacketInfo{
|
||||
Addr: ipv6Addr1,
|
||||
NIC: nicID1,
|
||||
},
|
||||
expectedLocalAddr: ipv6Addr1,
|
||||
expectedRemoteAddr: ipv6RemoteAddr1,
|
||||
},
|
||||
}
|
||||
|
||||
for _, transProto := range []struct {
|
||||
name string
|
||||
createEndpoint func(*stack.Stack, *waiter.Queue) (tcpip.Endpoint, error)
|
||||
}{
|
||||
{
|
||||
name: "UDP",
|
||||
createEndpoint: func(s *stack.Stack, wq *waiter.Queue) (tcpip.Endpoint, error) {
|
||||
ep, err := s.NewEndpoint(udp.ProtocolNumber, header.IPv6ProtocolNumber, wq)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("s.NewEndpoint(%d, %d, _) failed: %s", udp.ProtocolNumber, header.IPv6ProtocolNumber, err)
|
||||
}
|
||||
return ep, nil
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "RAW",
|
||||
createEndpoint: func(s *stack.Stack, wq *waiter.Queue) (tcpip.Endpoint, error) {
|
||||
ep, err := s.NewRawEndpoint(udp.ProtocolNumber, header.IPv6ProtocolNumber, wq, true /* associated */)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("s.NewRawEndpoint(%d, %d, _, true) failed: %s", udp.ProtocolNumber, header.IPv6ProtocolNumber, err)
|
||||
}
|
||||
return ep, nil
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(transProto.name, func(t *testing.T) {
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
s := stack.New(stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocolFactory{ipv6.NewProtocol, ipv4.NewProtocol},
|
||||
TransportProtocols: []stack.TransportProtocolFactory{udp.NewProtocol},
|
||||
RawFactory: &raw.EndpointFactory{},
|
||||
})
|
||||
e1 := channel.New(1, header.IPv6MinimumMTU, "")
|
||||
if err := s.CreateNIC(nicID1, e1); err != nil {
|
||||
t.Fatalf("s.CreateNIC(%d, _) failed: %s", nicID1, err)
|
||||
}
|
||||
e2 := channel.New(1, header.IPv6MinimumMTU, "")
|
||||
if err := s.CreateNIC(nicID2, e2); err != nil {
|
||||
t.Fatalf("s.CreateNIC(%d, _) failed: %s", nicID2, err)
|
||||
}
|
||||
|
||||
for _, localAddr := range localAddrs {
|
||||
addr := tcpip.ProtocolAddress{
|
||||
Protocol: header.IPv6ProtocolNumber,
|
||||
AddressWithPrefix: localAddr.addr,
|
||||
}
|
||||
if err := s.AddProtocolAddress(localAddr.nicID, addr, stack.AddressProperties{}); err != nil {
|
||||
t.Fatalf("AddProtocolAddress(%d, %#v, {}): %s", localAddr.nicID, addr, err)
|
||||
}
|
||||
}
|
||||
s.SetRouteTable([]tcpip.Route{
|
||||
// NIC2 before NIC1 to let NIC2 have preference.
|
||||
{
|
||||
Destination: header.IPv6EmptySubnet,
|
||||
NIC: nicID2,
|
||||
},
|
||||
{
|
||||
Destination: header.IPv6EmptySubnet,
|
||||
NIC: nicID1,
|
||||
},
|
||||
})
|
||||
|
||||
var wq waiter.Queue
|
||||
ep, err := transProto.createEndpoint(s, &wq)
|
||||
if err != nil {
|
||||
t.Fatalf("transProto.createEndpoint(_) failed: %s", err)
|
||||
}
|
||||
defer ep.Close()
|
||||
|
||||
if err := ep.SocketOptions().SetBindToDevice(int32(test.boundNICID)); err != nil {
|
||||
t.Fatalf("ep.SocketOptions().SetBindToDevice(int32(%d)): %s", test.boundNICID, err)
|
||||
}
|
||||
|
||||
if test.bindAddr != (tcpip.FullAddress{}) {
|
||||
if err := ep.Bind(test.bindAddr); err != nil {
|
||||
t.Fatalf("ep.Bind(%#v): %s", test.bindAddr, err)
|
||||
}
|
||||
}
|
||||
|
||||
if test.connectAddr != (tcpip.FullAddress{}) {
|
||||
if err := ep.Connect(test.connectAddr); err != nil {
|
||||
t.Fatalf("ep.Connect(%#v): %s", test.connectAddr, err)
|
||||
}
|
||||
}
|
||||
|
||||
buf := [...]byte{1, 2, 3, 4}
|
||||
var r bytes.Reader
|
||||
r.Reset(buf[:])
|
||||
opts := tcpip.WriteOptions{
|
||||
ControlMessages: tcpip.SendableControlMessages{
|
||||
HasIPv6PacketInfo: true,
|
||||
IPv6PacketInfo: test.pktInfo,
|
||||
},
|
||||
}
|
||||
if test.toAddr != (tcpip.FullAddress{}) {
|
||||
opts.To = &test.toAddr
|
||||
}
|
||||
|
||||
if n, err := ep.Write(&r, opts); !cmp.Equal(test.expectedErr, err) {
|
||||
t.Fatalf("got Write(_, %#v) = %s, want = %s", opts, err, test.expectedErr)
|
||||
} else if test.expectedErr != nil {
|
||||
return
|
||||
} else if want := int64(len(buf)); n != want {
|
||||
t.Fatalf("got Write(_, %#v) = %d, want = %d", opts, n, want)
|
||||
}
|
||||
|
||||
{
|
||||
p := e1.Read()
|
||||
if p == nil {
|
||||
t.Fatal("packet didn't arrive at ep1")
|
||||
}
|
||||
|
||||
checker.IPv6(t, stack.PayloadSince(p.NetworkHeader()),
|
||||
checker.SrcAddr(test.expectedLocalAddr),
|
||||
checker.DstAddr(test.expectedRemoteAddr),
|
||||
)
|
||||
}
|
||||
|
||||
if p := e2.Read(); p != nil {
|
||||
t.Errorf("unexpected packet from ep2 = %#v", p)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -392,40 +392,113 @@ func (e *Endpoint) AcquireContextForWrite(opts tcpip.WriteOptions) (WriteContext
|
||||
return WriteContext{}, &tcpip.ErrClosedForSend{}
|
||||
}
|
||||
|
||||
ipv6PktInfoValid := e.effectiveNetProto == header.IPv6ProtocolNumber && opts.ControlMessages.HasIPv6PacketInfo
|
||||
|
||||
route := e.connectedRoute
|
||||
if opts.To == nil {
|
||||
to := opts.To
|
||||
info := e.Info()
|
||||
switch {
|
||||
case to == nil:
|
||||
// If the user doesn't specify a destination, they should have
|
||||
// connected to another address.
|
||||
if e.State() != transport.DatagramEndpointStateConnected {
|
||||
return WriteContext{}, &tcpip.ErrDestinationRequired{}
|
||||
}
|
||||
|
||||
route.Acquire()
|
||||
} else {
|
||||
if !ipv6PktInfoValid {
|
||||
route.Acquire()
|
||||
break
|
||||
}
|
||||
|
||||
// We are connected and the caller did not specify the destination but
|
||||
// we have an IPv6 packet info structure which may change our local
|
||||
// interface/address used to send the packet so we need to construct
|
||||
// a new route instead of using the connected route.
|
||||
//
|
||||
// Contruct a destination matching the remote the endpoint is connected
|
||||
// to.
|
||||
to = &tcpip.FullAddress{
|
||||
// RegisterNICID is set when the endpoint is connected. It is usually
|
||||
// only set for link-local addresses or multicast addresses if the
|
||||
// multicast interface was specified (see e.multicastNICID,
|
||||
// e.connectRouteRLocked and e.ConnectAndThen).
|
||||
NIC: info.RegisterNICID,
|
||||
Addr: info.ID.RemoteAddress,
|
||||
}
|
||||
fallthrough
|
||||
default:
|
||||
// Reject destination address if it goes through a different
|
||||
// NIC than the endpoint was bound to.
|
||||
nicID := opts.To.NIC
|
||||
nicID := to.NIC
|
||||
if nicID == 0 {
|
||||
nicID = tcpip.NICID(e.ops.GetBindToDevice())
|
||||
}
|
||||
info := e.Info()
|
||||
if info.BindNICID != 0 {
|
||||
if nicID != 0 && nicID != info.BindNICID {
|
||||
return WriteContext{}, &tcpip.ErrNoRoute{}
|
||||
|
||||
var localAddr tcpip.Address
|
||||
if ipv6PktInfoValid {
|
||||
// Uphold strong-host semantics since (as of writing) the stack follows
|
||||
// the strong host model.
|
||||
|
||||
pktInfoNICID := opts.ControlMessages.IPv6PacketInfo.NIC
|
||||
pktInfoAddr := opts.ControlMessages.IPv6PacketInfo.Addr
|
||||
|
||||
if pktInfoNICID != 0 {
|
||||
// If we are bound to an interface or specified the destination
|
||||
// interface (usually when using link-local addresses), make sure the
|
||||
// interface matches the specified local interface.
|
||||
if nicID != 0 && nicID != pktInfoNICID {
|
||||
return WriteContext{}, &tcpip.ErrNoRoute{}
|
||||
}
|
||||
|
||||
// If a local address is not specified, then we need to make sure the
|
||||
// bound address belongs to the specified local interface.
|
||||
if len(pktInfoAddr) == 0 {
|
||||
// If the bound interface is different from the specified local
|
||||
// interface, the bound address obviously does not belong to the
|
||||
// specified local interface.
|
||||
//
|
||||
// The bound interface is usually only set for link-local addresses.
|
||||
if info.BindNICID != 0 && info.BindNICID != pktInfoNICID {
|
||||
return WriteContext{}, &tcpip.ErrNoRoute{}
|
||||
}
|
||||
if len(info.ID.LocalAddress) != 0 && e.stack.CheckLocalAddress(pktInfoNICID, header.IPv6ProtocolNumber, info.ID.LocalAddress) == 0 {
|
||||
return WriteContext{}, &tcpip.ErrBadLocalAddress{}
|
||||
}
|
||||
}
|
||||
|
||||
nicID = pktInfoNICID
|
||||
}
|
||||
|
||||
nicID = info.BindNICID
|
||||
}
|
||||
if nicID == 0 {
|
||||
nicID = info.RegisterNICID
|
||||
if len(pktInfoAddr) != 0 {
|
||||
// The local address must belong to the stack. If an outgoing interface
|
||||
// is specified as a result of binding the endpoint to a device, or
|
||||
// specifying the outgoing interface in the destination address/pkt info
|
||||
// structure, the address must belong to that interface.
|
||||
if e.stack.CheckLocalAddress(nicID, header.IPv6ProtocolNumber, pktInfoAddr) == 0 {
|
||||
return WriteContext{}, &tcpip.ErrBadLocalAddress{}
|
||||
}
|
||||
|
||||
localAddr = pktInfoAddr
|
||||
}
|
||||
} else {
|
||||
if info.BindNICID != 0 {
|
||||
if nicID != 0 && nicID != info.BindNICID {
|
||||
return WriteContext{}, &tcpip.ErrNoRoute{}
|
||||
}
|
||||
|
||||
nicID = info.BindNICID
|
||||
}
|
||||
if nicID == 0 {
|
||||
nicID = info.RegisterNICID
|
||||
}
|
||||
}
|
||||
|
||||
dst, netProto, err := e.checkV4Mapped(*opts.To)
|
||||
dst, netProto, err := e.checkV4Mapped(*to)
|
||||
if err != nil {
|
||||
return WriteContext{}, err
|
||||
}
|
||||
|
||||
route, _, err = e.connectRouteRLocked(nicID, dst, netProto)
|
||||
route, _, err = e.connectRouteRLocked(nicID, localAddr, dst, netProto)
|
||||
if err != nil {
|
||||
return WriteContext{}, err
|
||||
}
|
||||
@@ -496,19 +569,21 @@ func (e *Endpoint) Disconnect() {
|
||||
// specified address is a multicast address.
|
||||
//
|
||||
// +checklocksread:e.mu
|
||||
func (e *Endpoint) connectRouteRLocked(nicID tcpip.NICID, addr tcpip.FullAddress, netProto tcpip.NetworkProtocolNumber) (*stack.Route, tcpip.NICID, tcpip.Error) {
|
||||
localAddr := e.Info().ID.LocalAddress
|
||||
if e.isBroadcastOrMulticast(nicID, netProto, localAddr) {
|
||||
// A packet can only originate from a unicast address (i.e., an interface).
|
||||
localAddr = ""
|
||||
}
|
||||
|
||||
if header.IsV4MulticastAddress(addr.Addr) || header.IsV6MulticastAddress(addr.Addr) {
|
||||
if nicID == 0 {
|
||||
nicID = e.multicastNICID
|
||||
func (e *Endpoint) connectRouteRLocked(nicID tcpip.NICID, localAddr tcpip.Address, addr tcpip.FullAddress, netProto tcpip.NetworkProtocolNumber) (*stack.Route, tcpip.NICID, tcpip.Error) {
|
||||
if len(localAddr) == 0 {
|
||||
localAddr = e.Info().ID.LocalAddress
|
||||
if e.isBroadcastOrMulticast(nicID, netProto, localAddr) {
|
||||
// A packet can only originate from a unicast address (i.e., an interface).
|
||||
localAddr = ""
|
||||
}
|
||||
if localAddr == "" && nicID == 0 {
|
||||
localAddr = e.multicastAddr
|
||||
|
||||
if header.IsV4MulticastAddress(addr.Addr) || header.IsV6MulticastAddress(addr.Addr) {
|
||||
if nicID == 0 {
|
||||
nicID = e.multicastNICID
|
||||
}
|
||||
if localAddr == "" && nicID == 0 {
|
||||
localAddr = e.multicastAddr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -563,7 +638,7 @@ func (e *Endpoint) ConnectAndThen(addr tcpip.FullAddress, f func(netProto tcpip.
|
||||
return err
|
||||
}
|
||||
|
||||
r, nicID, err := e.connectRouteRLocked(nicID, addr, netProto)
|
||||
r, nicID, err := e.connectRouteRLocked(nicID, "", addr, netProto)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user