From b48c22e4994381554c8e5520f3e5909c420d8bca Mon Sep 17 00:00:00 2001 From: breken-ai <312387581+breken-ai@users.noreply.github.com> Date: Fri, 25 Sep 2026 02:57:02 -0700 Subject: [PATCH 1/2] cli/command/container: reject --ip and --ip6 values of the wrong family Since the move to netip (v29.0.0), applyContainerOptions converted the --ip value with To4() and silently skipped it when the conversion failed. Passing an IPv6 address to --ip, for example: docker run --network mynet --ip 2001:db8::5 alpine no longer produced an error; the address was dropped, and the container started with a dynamically assigned address instead of failing. Before v29 the value was sent as-is, and the daemon rejected it. Return an invalid-parameter error for an --ip value that is not IPv4, and for an --ip6 value that is IPv4 (which was previously sent as an IPv4-mapped IPv6 address). Assisted-By: Claude Code Signed-off-by: breken-ai <312387581+breken-ai@users.noreply.github.com> --- cli/command/container/opts.go | 12 ++++++++---- cli/command/container/opts_test.go | 10 ++++++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/cli/command/container/opts.go b/cli/command/container/opts.go index 94b8dffa34a6..5c9e4a43151e 100644 --- a/cli/command/container/opts.go +++ b/cli/command/container/opts.go @@ -843,14 +843,18 @@ func applyContainerOptions(n *opts.NetworkAttachmentOpts, copts *containerOption copy(n.Links, copts.links.GetSlice()) } if copts.ipv4Address != nil { - if ipv4, ok := netip.AddrFromSlice(copts.ipv4Address.To4()); ok { - n.IPv4Address = ipv4 + ipv4, ok := netip.AddrFromSlice(copts.ipv4Address.To4()) + if !ok { + return invalidParameter(fmt.Errorf("invalid IPv4 address for --ip: %s", copts.ipv4Address)) } + n.IPv4Address = ipv4 } if copts.ipv6Address != nil { - if ipv6, ok := netip.AddrFromSlice(copts.ipv6Address.To16()); ok { - n.IPv6Address = ipv6 + ipv6, ok := netip.AddrFromSlice(copts.ipv6Address.To16()) + if !ok || copts.ipv6Address.To4() != nil { + return invalidParameter(fmt.Errorf("invalid IPv6 address for --ip6: %s", copts.ipv6Address)) } + n.IPv6Address = ipv6 } if copts.macAddress != "" { n.MacAddress = copts.macAddress diff --git a/cli/command/container/opts_test.go b/cli/command/container/opts_test.go index 556e013066fa..789851c58585 100644 --- a/cli/command/container/opts_test.go +++ b/cli/command/container/opts_test.go @@ -756,6 +756,16 @@ func TestParseNetworkConfig(t *testing.T) { flags: []string{"--network", "name=net1,mac-address=foobar"}, expectedErr: "foobar is not a valid mac address", }, + { + name: "invalid-ip-is-ipv6", + flags: []string{"--network", "net1", "--ip", "2001:db8::8822"}, + expectedErr: "invalid IPv4 address for --ip: 2001:db8::8822", + }, + { + name: "invalid-ip6-is-ipv4", + flags: []string{"--network", "net1", "--ip6", "172.20.88.22"}, + expectedErr: "invalid IPv6 address for --ip6: 172.20.88.22", + }, } for _, tc := range tests { From db8f7b09da2901700a8a13ccfaa4920e655bbf71 Mon Sep 17 00:00:00 2001 From: breken-ai <312387581+breken-ai@users.noreply.github.com> Date: Fri, 25 Sep 2026 02:57:03 -0700 Subject: [PATCH 2/2] cli/command/container: validate --link-local-ip values The --link-local-ip flag had no validator, and since the move to netip (v29.0.0) its values are converted with toNetipAddrSlice, which skips any value it cannot parse. A mistyped address, for example: docker run --network mynet --link-local-ip 169.254.1.1000 alpine was silently dropped, and the container was created without it. Before v29 the value was sent as-is, and the daemon rejected it. Validate the flag with opts.ValidateIPAddress, the same validator the --dns flag uses, so an invalid value fails when the flags are parsed. Assisted-By: Claude Code Signed-off-by: breken-ai <312387581+breken-ai@users.noreply.github.com> --- cli/command/container/opts.go | 2 +- cli/command/container/opts_test.go | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/cli/command/container/opts.go b/cli/command/container/opts.go index 5c9e4a43151e..0f187cf15271 100644 --- a/cli/command/container/opts.go +++ b/cli/command/container/opts.go @@ -170,7 +170,7 @@ func addFlags(flags *pflag.FlagSet) *containerOptions { groupAdd: opts.NewListOpts(nil), labels: opts.NewListOpts(opts.ValidateLabel), labelsFile: opts.NewListOpts(nil), - linkLocalIPs: opts.NewListOpts(nil), + linkLocalIPs: opts.NewListOpts(opts.ValidateIPAddress), links: opts.NewListOpts(opts.ValidateLink), loggingOpts: opts.NewListOpts(nil), publish: opts.NewListOpts(nil), diff --git a/cli/command/container/opts_test.go b/cli/command/container/opts_test.go index 789851c58585..cf529d5e862f 100644 --- a/cli/command/container/opts_test.go +++ b/cli/command/container/opts_test.go @@ -766,6 +766,11 @@ func TestParseNetworkConfig(t *testing.T) { flags: []string{"--network", "net1", "--ip6", "172.20.88.22"}, expectedErr: "invalid IPv6 address for --ip6: 172.20.88.22", }, + { + name: "invalid-link-local-ip", + flags: []string{"--network", "net1", "--link-local-ip", "foobar"}, + expectedErr: `invalid argument "foobar" for "--link-local-ip" flag: IP address is not correctly formatted: foobar`, + }, } for _, tc := range tests {