Skip to content

Commit

Permalink
Merge pull request moby#48567 from akerouanton/add-SO_REUSEADDR-to-do…
Browse files Browse the repository at this point in the history
…cker-proxy

cmd/docker-proxy: re-add SO_REUSEADDR
  • Loading branch information
akerouanton authored Oct 8, 2024
2 parents 60a624c + 77e5165 commit aafdd33
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 0 deletions.
53 changes: 53 additions & 0 deletions integration/networking/port_mapping_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -407,3 +407,56 @@ func TestAccessPublishedPortFromRemoteHost(t *testing.T) {
}
}
}

// TestRestartUserlandProxyUnder2MSL checks that a container can be restarted
// while previous connections to the proxy are still in TIME_WAIT state.
func TestRestartUserlandProxyUnder2MSL(t *testing.T) {
skip.If(t, testEnv.IsRootless())

ctx := setupTest(t)

d := daemon.New(t)
d.StartWithBusybox(ctx, t)
defer d.Stop(t)

c := d.NewClientT(t)
defer c.Close()

const netName = "nat-time-wait"
network.CreateNoError(ctx, t, c, netName,
network.WithDriver("bridge"),
network.WithOption(bridge.BridgeName, netName))
defer network.RemoveNoError(ctx, t, c, netName)

ctrName := sanitizeCtrName(t.Name() + "-server")
ctrOpts := []func(*container.TestContainerConfig){
container.WithName(ctrName),
container.WithExposedPorts("80/tcp"),
container.WithPortMap(nat.PortMap{"80/tcp": {{HostPort: "1780"}}}),
container.WithCmd("httpd", "-f"),
container.WithNetworkMode(netName),
}

container.Run(ctx, t, c, ctrOpts...)
defer c.ContainerRemove(ctx, ctrName, containertypes.RemoveOptions{Force: true})

// Make an HTTP request to open a TCP connection to the proxy. We don't
// care about the HTTP response, just that the connection is established.
// So, check that we receive a 404 to make sure we've a working full-duplex
// TCP connection.
httpClient := &http.Client{Timeout: 3 * time.Second}
resp, err := httpClient.Get("http://127.0.0.1:1780")
assert.NilError(t, err)
assert.Check(t, is.Equal(resp.StatusCode, 404))

// Removing the container will kill the userland proxy, and the connection
// opened by the previous HTTP request will be properly closed (ie. on both
// sides). Thus, that connection will transition to the TIME_WAIT state.
assert.NilError(t, c.ContainerRemove(ctx, ctrName, containertypes.RemoveOptions{Force: true}))

// Make sure the container can be restarted. [container.Run] checks that
// the ContainerStart API call doesn't return an error. We don't need to
// make another TCP connection either, that's out of scope. Hence, we don't
// need to check anything after this call.
container.Run(ctx, t, c, ctrOpts...)
}
4 changes: 4 additions & 0 deletions libnetwork/drivers/bridge/port_mapping_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,10 @@ func bindTCPOrUDP(cfg portBindingReq, port, typ, proto int) (_ portBinding, retE
}
}()

if err := syscall.SetsockoptInt(sd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil {
return portBinding{}, fmt.Errorf("failed to setsockopt(SO_REUSEADDR) for %s: %w", cfg, err)
}

if domain == syscall.AF_INET6 {
syscall.SetsockoptInt(sd, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 1)
}
Expand Down

0 comments on commit aafdd33

Please sign in to comment.