mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Prevent UDP sockets from binding to bound ports
PiperOrigin-RevId: 212653818 Change-Id: Ib4e1d754d9cdddeaa428a066cb675e6ec44d91ad
This commit is contained in:
committed by
Shentubot
parent
b4aed01bf2
commit
cbf3980464
@@ -16,6 +16,7 @@ package header
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"strings"
|
||||
|
||||
"gvisor.googlesource.com/gvisor/pkg/tcpip"
|
||||
)
|
||||
@@ -190,12 +191,5 @@ func IsV4MappedAddress(addr tcpip.Address) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
const prefix = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff"
|
||||
for i := 0; i < len(prefix); i++ {
|
||||
if prefix[i] != addr[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
return strings.HasPrefix(string(addr), "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff")
|
||||
}
|
||||
|
||||
+12
-13
@@ -27,7 +27,7 @@ const (
|
||||
// firstEphemeral is the first ephemeral port.
|
||||
firstEphemeral uint16 = 16000
|
||||
|
||||
anyIPAddress = tcpip.Address("")
|
||||
anyIPAddress tcpip.Address = ""
|
||||
)
|
||||
|
||||
type portDescriptor struct {
|
||||
@@ -95,14 +95,14 @@ func (s *PortManager) PickEphemeralPort(testPort func(p uint16) (bool, *tcpip.Er
|
||||
// reserved by another endpoint. If port is zero, ReservePort will search for
|
||||
// an unreserved ephemeral port and reserve it, returning its value in the
|
||||
// "port" return value.
|
||||
func (s *PortManager) ReservePort(network []tcpip.NetworkProtocolNumber, transport tcpip.TransportProtocolNumber, addr tcpip.Address, port uint16) (reservedPort uint16, err *tcpip.Error) {
|
||||
func (s *PortManager) ReservePort(networks []tcpip.NetworkProtocolNumber, transport tcpip.TransportProtocolNumber, addr tcpip.Address, port uint16) (reservedPort uint16, err *tcpip.Error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
// If a port is specified, just try to reserve it for all network
|
||||
// protocols.
|
||||
if port != 0 {
|
||||
if !s.reserveSpecificPort(network, transport, addr, port) {
|
||||
if !s.reserveSpecificPort(networks, transport, addr, port) {
|
||||
return 0, tcpip.ErrPortInUse
|
||||
}
|
||||
return port, nil
|
||||
@@ -110,16 +110,15 @@ func (s *PortManager) ReservePort(network []tcpip.NetworkProtocolNumber, transpo
|
||||
|
||||
// A port wasn't specified, so try to find one.
|
||||
return s.PickEphemeralPort(func(p uint16) (bool, *tcpip.Error) {
|
||||
return s.reserveSpecificPort(network, transport, addr, p), nil
|
||||
return s.reserveSpecificPort(networks, transport, addr, p), nil
|
||||
})
|
||||
}
|
||||
|
||||
// reserveSpecificPort tries to reserve the given port on all given protocols.
|
||||
func (s *PortManager) reserveSpecificPort(network []tcpip.NetworkProtocolNumber, transport tcpip.TransportProtocolNumber, addr tcpip.Address, port uint16) bool {
|
||||
func (s *PortManager) reserveSpecificPort(networks []tcpip.NetworkProtocolNumber, transport tcpip.TransportProtocolNumber, addr tcpip.Address, port uint16) bool {
|
||||
// Check that the port is available on all network protocols.
|
||||
desc := portDescriptor{0, transport, port}
|
||||
for _, n := range network {
|
||||
desc.network = n
|
||||
for _, network := range networks {
|
||||
desc := portDescriptor{network, transport, port}
|
||||
if addrs, ok := s.allocatedPorts[desc]; ok {
|
||||
if !addrs.isAvailable(addr) {
|
||||
return false
|
||||
@@ -128,8 +127,8 @@ func (s *PortManager) reserveSpecificPort(network []tcpip.NetworkProtocolNumber,
|
||||
}
|
||||
|
||||
// Reserve port on all network protocols.
|
||||
for _, n := range network {
|
||||
desc.network = n
|
||||
for _, network := range networks {
|
||||
desc := portDescriptor{network, transport, port}
|
||||
m, ok := s.allocatedPorts[desc]
|
||||
if !ok {
|
||||
m = make(bindAddresses)
|
||||
@@ -143,12 +142,12 @@ func (s *PortManager) reserveSpecificPort(network []tcpip.NetworkProtocolNumber,
|
||||
|
||||
// ReleasePort releases the reservation on a port/IP combination so that it can
|
||||
// be reserved by other endpoints.
|
||||
func (s *PortManager) ReleasePort(network []tcpip.NetworkProtocolNumber, transport tcpip.TransportProtocolNumber, addr tcpip.Address, port uint16) {
|
||||
func (s *PortManager) ReleasePort(networks []tcpip.NetworkProtocolNumber, transport tcpip.TransportProtocolNumber, addr tcpip.Address, port uint16) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
for _, n := range network {
|
||||
desc := portDescriptor{n, transport, port}
|
||||
for _, network := range networks {
|
||||
desc := portDescriptor{network, transport, port}
|
||||
m := s.allocatedPorts[desc]
|
||||
delete(m, addr)
|
||||
if len(m) == 0 {
|
||||
|
||||
@@ -132,6 +132,7 @@ func (e *endpoint) Close() {
|
||||
switch e.state {
|
||||
case stateBound, stateConnected:
|
||||
e.stack.UnregisterTransportEndpoint(e.regNICID, e.effectiveNetProtos, ProtocolNumber, e.id)
|
||||
e.stack.ReleasePort(e.effectiveNetProtos, ProtocolNumber, e.id.LocalAddress, e.id.LocalPort)
|
||||
}
|
||||
|
||||
// Close the receive list and drain it.
|
||||
@@ -496,7 +497,7 @@ func (e *endpoint) Connect(addr tcpip.FullAddress) *tcpip.Error {
|
||||
defer e.mu.Unlock()
|
||||
|
||||
nicid := addr.NIC
|
||||
localPort := uint16(0)
|
||||
var localPort uint16
|
||||
switch e.state {
|
||||
case stateInitial:
|
||||
case stateBound, stateConnected:
|
||||
@@ -537,7 +538,7 @@ func (e *endpoint) Connect(addr tcpip.FullAddress) *tcpip.Error {
|
||||
// packets on a different network protocol, so we register both even if
|
||||
// v6only is set to false and this is an ipv6 endpoint.
|
||||
netProtos := []tcpip.NetworkProtocolNumber{netProto}
|
||||
if e.netProto == header.IPv6ProtocolNumber && !e.v6only {
|
||||
if netProto == header.IPv6ProtocolNumber && !e.v6only {
|
||||
netProtos = []tcpip.NetworkProtocolNumber{
|
||||
header.IPv4ProtocolNumber,
|
||||
header.IPv6ProtocolNumber,
|
||||
@@ -611,27 +612,18 @@ func (*endpoint) Accept() (tcpip.Endpoint, *waiter.Queue, *tcpip.Error) {
|
||||
}
|
||||
|
||||
func (e *endpoint) registerWithStack(nicid tcpip.NICID, netProtos []tcpip.NetworkProtocolNumber, id stack.TransportEndpointID) (stack.TransportEndpointID, *tcpip.Error) {
|
||||
if id.LocalPort != 0 {
|
||||
// The endpoint already has a local port, just attempt to
|
||||
// register it.
|
||||
err := e.stack.RegisterTransportEndpoint(nicid, netProtos, ProtocolNumber, id, e)
|
||||
return id, err
|
||||
if e.id.LocalPort == 0 {
|
||||
port, err := e.stack.ReservePort(netProtos, ProtocolNumber, id.LocalAddress, id.LocalPort)
|
||||
if err != nil {
|
||||
return id, err
|
||||
}
|
||||
id.LocalPort = port
|
||||
}
|
||||
|
||||
// We need to find a port for the endpoint.
|
||||
_, err := e.stack.PickEphemeralPort(func(p uint16) (bool, *tcpip.Error) {
|
||||
id.LocalPort = p
|
||||
err := e.stack.RegisterTransportEndpoint(nicid, netProtos, ProtocolNumber, id, e)
|
||||
switch err {
|
||||
case nil:
|
||||
return true, nil
|
||||
case tcpip.ErrPortInUse:
|
||||
return false, nil
|
||||
default:
|
||||
return false, err
|
||||
}
|
||||
})
|
||||
|
||||
err := e.stack.RegisterTransportEndpoint(nicid, netProtos, ProtocolNumber, id, e)
|
||||
if err != nil {
|
||||
e.stack.ReleasePort(netProtos, ProtocolNumber, id.LocalAddress, id.LocalPort)
|
||||
}
|
||||
return id, err
|
||||
}
|
||||
|
||||
@@ -677,6 +669,7 @@ func (e *endpoint) bindLocked(addr tcpip.FullAddress, commit func() *tcpip.Error
|
||||
if err := commit(); err != nil {
|
||||
// Unregister, the commit failed.
|
||||
e.stack.UnregisterTransportEndpoint(addr.NIC, netProtos, ProtocolNumber, id)
|
||||
e.stack.ReleasePort(netProtos, ProtocolNumber, id.LocalAddress, id.LocalPort)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,7 +94,12 @@ func (e *endpoint) afterLoad() {
|
||||
}
|
||||
}
|
||||
|
||||
e.id, err = e.registerWithStack(e.regNICID, e.effectiveNetProtos, e.id)
|
||||
// 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, err = e.registerWithStack(e.regNICID, e.effectiveNetProtos, id)
|
||||
if err != nil {
|
||||
panic(*err)
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ func (c *testContext) cleanup() {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *testContext) createV6Endpoint(v4only bool) {
|
||||
func (c *testContext) createV6Endpoint(v6only bool) {
|
||||
var err *tcpip.Error
|
||||
c.ep, err = c.s.NewEndpoint(udp.ProtocolNumber, ipv6.ProtocolNumber, &c.wq)
|
||||
if err != nil {
|
||||
@@ -120,7 +120,7 @@ func (c *testContext) createV6Endpoint(v4only bool) {
|
||||
}
|
||||
|
||||
var v tcpip.V6OnlyOption
|
||||
if v4only {
|
||||
if v6only {
|
||||
v = 1
|
||||
}
|
||||
if err := c.ep.SetSockOpt(v); err != nil {
|
||||
@@ -296,6 +296,76 @@ func testV4Read(c *testContext) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestBindEphemeralPort(t *testing.T) {
|
||||
c := newDualTestContext(t, defaultMTU)
|
||||
defer c.cleanup()
|
||||
|
||||
c.createV6Endpoint(false)
|
||||
|
||||
if err := c.ep.Bind(tcpip.FullAddress{}, nil); err != nil {
|
||||
t.Fatalf("ep.Bind(...) failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBindReservedPort(t *testing.T) {
|
||||
c := newDualTestContext(t, defaultMTU)
|
||||
defer c.cleanup()
|
||||
|
||||
c.createV6Endpoint(false)
|
||||
|
||||
if err := c.ep.Connect(tcpip.FullAddress{Addr: testV6Addr, Port: testPort}); err != nil {
|
||||
c.t.Fatalf("Connect failed: %v", err)
|
||||
}
|
||||
|
||||
addr, err := c.ep.GetLocalAddress()
|
||||
if err != nil {
|
||||
t.Fatalf("GetLocalAddress failed: %v", err)
|
||||
}
|
||||
|
||||
// We can't bind the address reserved by the connected endpoint above.
|
||||
{
|
||||
ep, err := c.s.NewEndpoint(udp.ProtocolNumber, ipv6.ProtocolNumber, &c.wq)
|
||||
if err != nil {
|
||||
t.Fatalf("NewEndpoint failed: %v", err)
|
||||
}
|
||||
defer ep.Close()
|
||||
if got, want := ep.Bind(addr, nil), tcpip.ErrPortInUse; got != want {
|
||||
t.Fatalf("got ep.Bind(...) = %v, want = %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func() {
|
||||
ep, err := c.s.NewEndpoint(udp.ProtocolNumber, ipv4.ProtocolNumber, &c.wq)
|
||||
if err != nil {
|
||||
t.Fatalf("NewEndpoint failed: %v", err)
|
||||
}
|
||||
defer ep.Close()
|
||||
// We can't bind ipv4-any on the port reserved by the connected endpoint
|
||||
// above, since the endpoint is dual-stack.
|
||||
if got, want := ep.Bind(tcpip.FullAddress{Port: addr.Port}, nil), tcpip.ErrPortInUse; got != want {
|
||||
t.Fatalf("got ep.Bind(...) = %v, want = %v", got, want)
|
||||
}
|
||||
// We can bind an ipv4 address on this port, though.
|
||||
if err := ep.Bind(tcpip.FullAddress{Addr: stackAddr, Port: addr.Port}, nil); err != nil {
|
||||
t.Fatalf("ep.Bind(...) failed: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Once the connected endpoint releases its port reservation, we are able to
|
||||
// bind ipv4-any once again.
|
||||
c.ep.Close()
|
||||
func() {
|
||||
ep, err := c.s.NewEndpoint(udp.ProtocolNumber, ipv4.ProtocolNumber, &c.wq)
|
||||
if err != nil {
|
||||
t.Fatalf("NewEndpoint failed: %v", err)
|
||||
}
|
||||
defer ep.Close()
|
||||
if err := ep.Bind(tcpip.FullAddress{Port: addr.Port}, nil); err != nil {
|
||||
t.Fatalf("ep.Bind(...) failed: %v", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func TestV4ReadOnV6(t *testing.T) {
|
||||
c := newDualTestContext(t, defaultMTU)
|
||||
defer c.cleanup()
|
||||
|
||||
Reference in New Issue
Block a user