mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Extract network datagram endpoint common facilities
...from the UDP endpoint. Datagram-based transport endpoints (e.g. UDP, RAW IP) can share a lot of their write path due to the datagram-based nature of these endpoints. Extract the common facilities from UDP so they can be shared with other transport endpoints (in a later change). Test: UDP syscall tests. PiperOrigin-RevId: 394347774
This commit is contained in:
committed by
gVisor bot
parent
5032f4f57d
commit
ae3bd32011
@@ -48,6 +48,7 @@ go_library(
|
||||
"//pkg/tcpip/network/ipv4",
|
||||
"//pkg/tcpip/network/ipv6",
|
||||
"//pkg/tcpip/stack",
|
||||
"//pkg/tcpip/transport",
|
||||
"//pkg/tcpip/transport/tcp",
|
||||
"//pkg/tcpip/transport/udp",
|
||||
"//pkg/usermem",
|
||||
|
||||
@@ -59,8 +59,8 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/transport"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/transport/udp"
|
||||
"gvisor.dev/gvisor/pkg/usermem"
|
||||
"gvisor.dev/gvisor/pkg/waiter"
|
||||
)
|
||||
@@ -2045,7 +2045,7 @@ func setSockOptIPv6(t *kernel.Task, s socket.SocketOps, ep commonEndpoint, name
|
||||
|
||||
if isTCPSocket(skType, skProto) && tcp.EndpointState(ep.State()) != tcp.StateInitial {
|
||||
return syserr.ErrInvalidEndpointState
|
||||
} else if isUDPSocket(skType, skProto) && udp.EndpointState(ep.State()) != udp.StateInitial {
|
||||
} else if isUDPSocket(skType, skProto) && transport.DatagramEndpointState(ep.State()) != transport.DatagramEndpointStateInitial {
|
||||
return syserr.ErrInvalidEndpointState
|
||||
}
|
||||
|
||||
@@ -3331,10 +3331,10 @@ func (s *socketOpsCommon) State() uint32 {
|
||||
}
|
||||
case isUDPSocket(s.skType, s.protocol):
|
||||
// UDP socket.
|
||||
switch udp.EndpointState(s.Endpoint.State()) {
|
||||
case udp.StateInitial, udp.StateBound, udp.StateClosed:
|
||||
switch transport.DatagramEndpointState(s.Endpoint.State()) {
|
||||
case transport.DatagramEndpointStateInitial, transport.DatagramEndpointStateBound, transport.DatagramEndpointStateClosed:
|
||||
return linux.TCP_CLOSE
|
||||
case udp.StateConnected:
|
||||
case transport.DatagramEndpointStateConnected:
|
||||
return linux.TCP_ESTABLISHED
|
||||
default:
|
||||
return 0
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
load("//tools:defs.bzl", "go_library")
|
||||
|
||||
package(licenses = ["notice"])
|
||||
|
||||
go_library(
|
||||
name = "transport",
|
||||
srcs = [
|
||||
"datagram.go",
|
||||
"transport.go",
|
||||
],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = ["//pkg/tcpip"],
|
||||
)
|
||||
@@ -0,0 +1,49 @@
|
||||
// Copyright 2021 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package transport
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
)
|
||||
|
||||
// DatagramEndpointState is the state of a datagram-based endpoint.
|
||||
type DatagramEndpointState tcpip.EndpointState
|
||||
|
||||
// The states a datagram-based endpoint may be in.
|
||||
const (
|
||||
_ DatagramEndpointState = iota
|
||||
DatagramEndpointStateInitial
|
||||
DatagramEndpointStateBound
|
||||
DatagramEndpointStateConnected
|
||||
DatagramEndpointStateClosed
|
||||
)
|
||||
|
||||
// String implements fmt.Stringer.
|
||||
func (s DatagramEndpointState) String() string {
|
||||
switch s {
|
||||
case DatagramEndpointStateInitial:
|
||||
return "INITIAL"
|
||||
case DatagramEndpointStateBound:
|
||||
return "BOUND"
|
||||
case DatagramEndpointStateConnected:
|
||||
return "CONNECTED"
|
||||
case DatagramEndpointStateClosed:
|
||||
return "CLOSED"
|
||||
default:
|
||||
panic(fmt.Sprintf("unhandled %[1]T variant = %[1]d", s))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
load("//tools:defs.bzl", "go_library", "go_test")
|
||||
|
||||
package(licenses = ["notice"])
|
||||
|
||||
go_library(
|
||||
name = "network",
|
||||
srcs = [
|
||||
"endpoint.go",
|
||||
"endpoint_state.go",
|
||||
],
|
||||
visibility = [
|
||||
"//pkg/tcpip/transport/udp:__pkg__",
|
||||
],
|
||||
deps = [
|
||||
"//pkg/sync",
|
||||
"//pkg/tcpip",
|
||||
"//pkg/tcpip/header",
|
||||
"//pkg/tcpip/stack",
|
||||
"//pkg/tcpip/transport",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "network_test",
|
||||
size = "small",
|
||||
srcs = ["endpoint_test.go"],
|
||||
deps = [
|
||||
":network",
|
||||
"//pkg/tcpip",
|
||||
"//pkg/tcpip/buffer",
|
||||
"//pkg/tcpip/checker",
|
||||
"//pkg/tcpip/faketime",
|
||||
"//pkg/tcpip/header",
|
||||
"//pkg/tcpip/link/channel",
|
||||
"//pkg/tcpip/network/ipv4",
|
||||
"//pkg/tcpip/network/ipv6",
|
||||
"//pkg/tcpip/stack",
|
||||
"//pkg/tcpip/testutil",
|
||||
"//pkg/tcpip/transport",
|
||||
"//pkg/tcpip/transport/udp",
|
||||
"@com_github_google_go_cmp//cmp:go_default_library",
|
||||
],
|
||||
)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,56 @@
|
||||
// Copyright 2021 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package network
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/transport"
|
||||
)
|
||||
|
||||
// Resume implements tcpip.ResumableEndpoint.Resume.
|
||||
func (e *Endpoint) Resume(s *stack.Stack) {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
|
||||
e.stack = s
|
||||
|
||||
for m := range e.multicastMemberships {
|
||||
if err := e.stack.JoinGroup(e.netProto, m.nicID, m.multicastAddr); err != nil {
|
||||
panic(fmt.Sprintf("e.stack.JoinGroup(%d, %d, %s): %s", e.netProto, m.nicID, m.multicastAddr, err))
|
||||
}
|
||||
}
|
||||
|
||||
switch state := e.State(); state {
|
||||
case transport.DatagramEndpointStateInitial, transport.DatagramEndpointStateClosed:
|
||||
case transport.DatagramEndpointStateBound:
|
||||
if len(e.info.ID.LocalAddress) != 0 && !e.isBroadcastOrMulticast(e.info.RegisterNICID, e.effectiveNetProto, e.info.ID.LocalAddress) {
|
||||
if e.stack.CheckLocalAddress(e.info.RegisterNICID, e.effectiveNetProto, e.info.ID.LocalAddress) == 0 {
|
||||
panic(fmt.Sprintf("got e.stack.CheckLocalAddress(%d, %d, %s) = 0, want != 0", e.info.RegisterNICID, e.effectiveNetProto, e.info.ID.LocalAddress))
|
||||
}
|
||||
}
|
||||
case transport.DatagramEndpointStateConnected:
|
||||
var err tcpip.Error
|
||||
multicastLoop := e.ops.GetMulticastLoop()
|
||||
e.connectedRoute, err = e.stack.FindRoute(e.info.RegisterNICID, e.info.ID.LocalAddress, e.info.ID.RemoteAddress, e.effectiveNetProto, multicastLoop)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("e.stack.FindRoute(%d, %s, %s, %d, %t): %s", e.info.RegisterNICID, e.info.ID.LocalAddress, e.info.ID.RemoteAddress, e.effectiveNetProto, multicastLoop, err))
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("unhandled state = %s", state))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
// Copyright 2021 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package network_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/buffer"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/checker"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/faketime"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/link/channel"
|
||||
"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/testutil"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/transport"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/transport/internal/network"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/transport/udp"
|
||||
)
|
||||
|
||||
func TestEndpointStateTransitions(t *testing.T) {
|
||||
const (
|
||||
nicID = 1
|
||||
)
|
||||
|
||||
var (
|
||||
ipv4NICAddr = testutil.MustParse4("1.2.3.4")
|
||||
ipv6NICAddr = testutil.MustParse6("a::1")
|
||||
ipv4RemoteAddr = testutil.MustParse4("6.7.8.9")
|
||||
ipv6RemoteAddr = testutil.MustParse6("b::1")
|
||||
)
|
||||
|
||||
data := buffer.View([]byte{1, 2, 4, 5})
|
||||
v4Checker := func(t *testing.T, b buffer.View) {
|
||||
checker.IPv4(t, b,
|
||||
checker.SrcAddr(ipv4NICAddr),
|
||||
checker.DstAddr(ipv4RemoteAddr),
|
||||
checker.IPPayload(data),
|
||||
)
|
||||
}
|
||||
|
||||
v6Checker := func(t *testing.T, b buffer.View) {
|
||||
checker.IPv6(t, b,
|
||||
checker.SrcAddr(ipv6NICAddr),
|
||||
checker.DstAddr(ipv6RemoteAddr),
|
||||
checker.IPPayload(data),
|
||||
)
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
netProto tcpip.NetworkProtocolNumber
|
||||
expectedMaxHeaderLength uint16
|
||||
expectedNetProto tcpip.NetworkProtocolNumber
|
||||
expectedLocalAddr tcpip.Address
|
||||
bindAddr tcpip.Address
|
||||
expectedBoundAddr tcpip.Address
|
||||
remoteAddr tcpip.Address
|
||||
expectedRemoteAddr tcpip.Address
|
||||
checker func(*testing.T, buffer.View)
|
||||
}{
|
||||
{
|
||||
name: "IPv4",
|
||||
netProto: ipv4.ProtocolNumber,
|
||||
expectedMaxHeaderLength: header.IPv4MaximumHeaderSize,
|
||||
expectedNetProto: ipv4.ProtocolNumber,
|
||||
expectedLocalAddr: ipv4NICAddr,
|
||||
bindAddr: header.IPv4AllSystems,
|
||||
expectedBoundAddr: header.IPv4AllSystems,
|
||||
remoteAddr: ipv4RemoteAddr,
|
||||
expectedRemoteAddr: ipv4RemoteAddr,
|
||||
checker: v4Checker,
|
||||
},
|
||||
{
|
||||
name: "IPv6",
|
||||
netProto: ipv6.ProtocolNumber,
|
||||
expectedMaxHeaderLength: header.IPv6FixedHeaderSize,
|
||||
expectedNetProto: ipv6.ProtocolNumber,
|
||||
expectedLocalAddr: ipv6NICAddr,
|
||||
bindAddr: header.IPv6AllNodesMulticastAddress,
|
||||
expectedBoundAddr: header.IPv6AllNodesMulticastAddress,
|
||||
remoteAddr: ipv6RemoteAddr,
|
||||
expectedRemoteAddr: ipv6RemoteAddr,
|
||||
checker: v6Checker,
|
||||
},
|
||||
{
|
||||
name: "IPv4-mapped-IPv6",
|
||||
netProto: ipv6.ProtocolNumber,
|
||||
expectedMaxHeaderLength: header.IPv4MaximumHeaderSize,
|
||||
expectedNetProto: ipv4.ProtocolNumber,
|
||||
expectedLocalAddr: ipv4NICAddr,
|
||||
bindAddr: testutil.MustParse6("::ffff:e000:0001"),
|
||||
expectedBoundAddr: header.IPv4AllSystems,
|
||||
remoteAddr: testutil.MustParse6("::ffff:0607:0809"),
|
||||
expectedRemoteAddr: ipv4RemoteAddr,
|
||||
checker: v4Checker,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
s := stack.New(stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol, ipv6.NewProtocol},
|
||||
TransportProtocols: []stack.TransportProtocolFactory{udp.NewProtocol},
|
||||
Clock: &faketime.NullClock{},
|
||||
})
|
||||
e := channel.New(1, header.IPv6MinimumMTU, "")
|
||||
if err := s.CreateNIC(nicID, e); err != nil {
|
||||
t.Fatalf("s.CreateNIC(%d, _): %s", nicID, err)
|
||||
}
|
||||
|
||||
if err := s.AddAddress(nicID, ipv4.ProtocolNumber, ipv4NICAddr); err != nil {
|
||||
t.Fatalf("s.AddAddress(%d, %d, %s): %s", nicID, ipv4.ProtocolNumber, ipv4NICAddr, err)
|
||||
}
|
||||
if err := s.AddAddress(nicID, ipv6.ProtocolNumber, ipv6NICAddr); err != nil {
|
||||
t.Fatalf("s.AddAddress(%d, %d, %s): %s", nicID, ipv6.ProtocolNumber, ipv6NICAddr, err)
|
||||
}
|
||||
|
||||
s.SetRouteTable([]tcpip.Route{
|
||||
{Destination: ipv4RemoteAddr.WithPrefix().Subnet(), NIC: nicID},
|
||||
{Destination: ipv6RemoteAddr.WithPrefix().Subnet(), NIC: nicID},
|
||||
})
|
||||
|
||||
var ops tcpip.SocketOptions
|
||||
var ep network.Endpoint
|
||||
ep.Init(s, test.netProto, udp.ProtocolNumber, &ops)
|
||||
if state := ep.State(); state != transport.DatagramEndpointStateInitial {
|
||||
t.Fatalf("got ep.State() = %s, want = %s", state, transport.DatagramEndpointStateInitial)
|
||||
}
|
||||
|
||||
bindAddr := tcpip.FullAddress{Addr: test.bindAddr}
|
||||
if err := ep.Bind(bindAddr); err != nil {
|
||||
t.Fatalf("ep.Bind(%#v): %s", bindAddr, err)
|
||||
}
|
||||
if state := ep.State(); state != transport.DatagramEndpointStateBound {
|
||||
t.Fatalf("got ep.State() = %s, want = %s", state, transport.DatagramEndpointStateBound)
|
||||
}
|
||||
if diff := cmp.Diff(ep.GetLocalAddress(), tcpip.FullAddress{Addr: test.expectedBoundAddr}); diff != "" {
|
||||
t.Errorf("ep.GetLocalAddress() mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
if addr, connected := ep.GetRemoteAddress(); connected {
|
||||
t.Errorf("got ep.GetRemoteAddress() = (true, %#v), want = (false, _)", addr)
|
||||
}
|
||||
|
||||
connectAddr := tcpip.FullAddress{Addr: test.remoteAddr}
|
||||
if err := ep.Connect(connectAddr); err != nil {
|
||||
t.Fatalf("ep.Connect(%#v): %s", connectAddr, err)
|
||||
}
|
||||
if state := ep.State(); state != transport.DatagramEndpointStateConnected {
|
||||
t.Fatalf("got ep.State() = %s, want = %s", state, transport.DatagramEndpointStateConnected)
|
||||
}
|
||||
if diff := cmp.Diff(ep.GetLocalAddress(), tcpip.FullAddress{Addr: test.expectedLocalAddr}); diff != "" {
|
||||
t.Errorf("ep.GetLocalAddress() mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
if addr, connected := ep.GetRemoteAddress(); !connected {
|
||||
t.Errorf("got ep.GetRemoteAddress() = (false, _), want = (true, %#v)", connectAddr)
|
||||
} else if diff := cmp.Diff(addr, tcpip.FullAddress{Addr: test.expectedRemoteAddr}); diff != "" {
|
||||
t.Errorf("remote address mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
|
||||
ctx, err := ep.AcquireContextForWrite(tcpip.WriteOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("ep.AcquireContexForWrite({}): %s", err)
|
||||
}
|
||||
defer ctx.Release()
|
||||
info := ctx.PacketInfo()
|
||||
if diff := cmp.Diff(network.WritePacketInfo{
|
||||
NetProto: test.expectedNetProto,
|
||||
LocalAddress: test.expectedLocalAddr,
|
||||
RemoteAddress: test.expectedRemoteAddr,
|
||||
MaxHeaderLength: test.expectedMaxHeaderLength,
|
||||
RequiresTXTransportChecksum: true,
|
||||
}, info); diff != "" {
|
||||
t.Errorf("write packet info mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
if err := ctx.WritePacket(stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
ReserveHeaderBytes: int(info.MaxHeaderLength),
|
||||
Data: data.ToVectorisedView(),
|
||||
}), false /* headerIncluded */); err != nil {
|
||||
t.Fatalf("ctx.WritePacket(_, false): %s", err)
|
||||
}
|
||||
if pkt, ok := e.Read(); !ok {
|
||||
t.Fatalf("expected packet to be read from link endpoint")
|
||||
} else {
|
||||
test.checker(t, stack.PayloadSince(pkt.Pkt.NetworkHeader()))
|
||||
}
|
||||
|
||||
ep.Close()
|
||||
if state := ep.State(); state != transport.DatagramEndpointStateClosed {
|
||||
t.Fatalf("got ep.State() = %s, want = %s", state, transport.DatagramEndpointStateClosed)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright 2021 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package transport supports transport protocols.
|
||||
package transport
|
||||
@@ -35,6 +35,8 @@ go_library(
|
||||
"//pkg/tcpip/header/parse",
|
||||
"//pkg/tcpip/ports",
|
||||
"//pkg/tcpip/stack",
|
||||
"//pkg/tcpip/transport",
|
||||
"//pkg/tcpip/transport/internal/network",
|
||||
"//pkg/tcpip/transport/raw",
|
||||
"//pkg/waiter",
|
||||
],
|
||||
|
||||
+216
-652
File diff suppressed because it is too large
Load Diff
@@ -15,12 +15,13 @@
|
||||
package udp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/buffer"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/transport"
|
||||
)
|
||||
|
||||
// saveReceivedAt is invoked by stateify.
|
||||
@@ -66,50 +67,28 @@ func (e *endpoint) Resume(s *stack.Stack) {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
|
||||
e.net.Resume(s)
|
||||
|
||||
e.stack = s
|
||||
e.ops.InitHandler(e, e.stack, tcpip.GetStackSendBufferLimits, tcpip.GetStackReceiveBufferLimits)
|
||||
|
||||
for m := range e.multicastMemberships {
|
||||
if err := e.stack.JoinGroup(e.NetProto, m.nicID, m.multicastAddr); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
state := e.EndpointState()
|
||||
if state != StateBound && state != StateConnected {
|
||||
return
|
||||
}
|
||||
|
||||
netProto := e.effectiveNetProtos[0]
|
||||
// Connect() and bindLocked() both assert
|
||||
//
|
||||
// netProto == header.IPv6ProtocolNumber
|
||||
//
|
||||
// before creating a multi-entry effectiveNetProtos.
|
||||
if len(e.effectiveNetProtos) > 1 {
|
||||
netProto = header.IPv6ProtocolNumber
|
||||
}
|
||||
|
||||
var err tcpip.Error
|
||||
if state == StateConnected {
|
||||
e.route, err = e.stack.FindRoute(e.RegisterNICID, e.ID.LocalAddress, e.ID.RemoteAddress, netProto, e.ops.GetMulticastLoop())
|
||||
switch state := e.net.State(); state {
|
||||
case transport.DatagramEndpointStateInitial, transport.DatagramEndpointStateClosed:
|
||||
case transport.DatagramEndpointStateBound, transport.DatagramEndpointStateConnected:
|
||||
// Our saved state had a port, but we don't actually have a
|
||||
// reservation. We need to remove the port from our state, but still
|
||||
// pass it to the reservation machinery.
|
||||
var err tcpip.Error
|
||||
id := e.net.Info().ID
|
||||
id.LocalPort = e.localPort
|
||||
id.RemotePort = e.remotePort
|
||||
id, e.boundBindToDevice, err = e.registerWithStack(e.effectiveNetProtos, id)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
} else if len(e.ID.LocalAddress) != 0 && !e.isBroadcastOrMulticast(e.RegisterNICID, netProto, e.ID.LocalAddress) { // stateBound
|
||||
// A local unicast address is specified, verify that it's valid.
|
||||
if e.stack.CheckLocalAddress(e.RegisterNICID, netProto, e.ID.LocalAddress) == 0 {
|
||||
panic(&tcpip.ErrBadLocalAddress{})
|
||||
}
|
||||
}
|
||||
|
||||
// Our saved state had a port, but we don't actually have a
|
||||
// reservation. We need to remove the port from our state, but still
|
||||
// pass it to the reservation machinery.
|
||||
id := e.ID
|
||||
e.ID.LocalPort = 0
|
||||
e.ID, e.boundBindToDevice, err = e.registerWithStack(e.effectiveNetProtos, id)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
e.localPort = id.LocalPort
|
||||
e.remotePort = id.RemotePort
|
||||
default:
|
||||
panic(fmt.Sprintf("unhandled state = %s", state))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,28 +70,29 @@ func (r *ForwarderRequest) ID() stack.TransportEndpointID {
|
||||
|
||||
// CreateEndpoint creates a connected UDP endpoint for the session request.
|
||||
func (r *ForwarderRequest) CreateEndpoint(queue *waiter.Queue) (tcpip.Endpoint, tcpip.Error) {
|
||||
ep := newEndpoint(r.stack, r.pkt.NetworkProtocolNumber, queue)
|
||||
ep.mu.Lock()
|
||||
defer ep.mu.Unlock()
|
||||
|
||||
netHdr := r.pkt.Network()
|
||||
route, err := r.stack.FindRoute(r.pkt.NICID, netHdr.DestinationAddress(), netHdr.SourceAddress(), r.pkt.NetworkProtocolNumber, false /* multicastLoop */)
|
||||
if err != nil {
|
||||
if err := ep.net.Bind(tcpip.FullAddress{NIC: r.pkt.NICID, Addr: netHdr.DestinationAddress(), Port: r.id.LocalPort}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := ep.net.Connect(tcpip.FullAddress{NIC: r.pkt.NICID, Addr: netHdr.SourceAddress(), Port: r.id.RemotePort}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ep := newEndpoint(r.stack, r.pkt.NetworkProtocolNumber, queue)
|
||||
if err := r.stack.RegisterTransportEndpoint([]tcpip.NetworkProtocolNumber{r.pkt.NetworkProtocolNumber}, ProtocolNumber, r.id, ep, ep.portFlags, tcpip.NICID(ep.ops.GetBindToDevice())); err != nil {
|
||||
ep.Close()
|
||||
route.Release()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ep.ID = r.id
|
||||
ep.route = route
|
||||
ep.dstPort = r.id.RemotePort
|
||||
ep.localPort = r.id.LocalPort
|
||||
ep.remotePort = r.id.RemotePort
|
||||
ep.effectiveNetProtos = []tcpip.NetworkProtocolNumber{r.pkt.NetworkProtocolNumber}
|
||||
ep.RegisterNICID = r.pkt.NICID
|
||||
ep.boundPortFlags = ep.portFlags
|
||||
|
||||
ep.state = uint32(StateConnected)
|
||||
|
||||
ep.rcvMu.Lock()
|
||||
ep.rcvReady = true
|
||||
ep.rcvMu.Unlock()
|
||||
|
||||
Reference in New Issue
Block a user