fix(network): match each gateway to its subnet for dual-stack#5010
fix(network): match each gateway to its subnet for dual-stack#5010mayur-tolexo wants to merge 1 commit into
Conversation
|
Ran the integration test locally (containerd v1.7.24, linux/arm64). The new dual-stack subtest plus the existing |
3217eb9 to
d8307e8
Compare
|
Checked this against Docker (29.4.0) to make sure the dual-stack behaviour lines up. Docker: nerdctl with the same flags now gives the same result: One thing worth noting: Docker matches each gateway to whichever subnet contains it, not by position. Passing the gateways in reverse order still pairs them correctly, and this PR does the same (it is And a gateway that is not inside any subnet errors the same way on both: |
| cmd.Flags().StringArray("ipam-opt", nil, "Set IPAM driver specific options") | ||
| cmd.Flags().StringArray("subnet", nil, `Subnet in CIDR format that represents a network segment, e.g. "10.5.0.0/16"`) | ||
| cmd.Flags().String("gateway", "", `Gateway for the master subnet`) | ||
| cmd.Flags().StringArray("gateway", nil, `Gateway for the subnet; repeat for a dual-stack network, e.g. "10.5.0.1" and "fd00::1"`) |
There was a problem hiding this comment.
Is this compatible with Docker?
If yes, the description should correspond to it.
There was a problem hiding this comment.
Yes, it matches Docker. docker network create --gateway is repeatable and pairs each gateway with the subnet that contains it (verified on Docker 29.4.0, including reverse order and the no-matching-subnet error). I updated the flag help and the docs to Docker's wording, "IPv4 or IPv6 Gateway for the master subnet".
d8307e8 to
25e9bd1
Compare
| }, | ||
| Expected: func(data test.Data, helpers test.Helpers) *test.Expected { | ||
| return &test.Expected{ | ||
| ExitCode: 0, |
There was a problem hiding this comment.
you can use the Tigron frameworks exit code constants
There was a problem hiding this comment.
Done, using expect.ExitCodeSuccess now.
| gateway := "" | ||
| for j, g := range gateways { | ||
| gw := net.ParseIP(g) | ||
| if gw == nil { |
There was a problem hiding this comment.
you could refactor the code like this for more readability
if gw := net.ParseIP(g); gw != nil && subnet.Contains(gw) {
gateway, used[j] = g, true
break
} else {
return nil, findIPv4, fmt.Errorf("failed to parse gateway
}
There was a problem hiding this comment.
Pulled the parse out of the loop into a single up-front pass for readability. Kept it separate from the Contains check on purpose though: a valid gateway that is not in this subnet still belongs to another subnet on a dual-stack net, so an else error there would reject correct input.
| } | ||
| // A gateway that matched no subnet is an error. | ||
| for j, g := range gateways { | ||
| if !used[j] { |
There was a problem hiding this comment.
why do we do this check here can't we kind of refactor this logic to the above group somehow i feel like this part is not kind of needed
There was a problem hiding this comment.
This one has to stay outside the loop, a gateway only counts as "no matching subnet" once every subnet has been checked. Docker errors the same way, so I kept it and tightened the comment.
| } | ||
|
|
||
| // Windows is single-subnet, so use at most one gateway. | ||
| gatewayStr := "" |
There was a problem hiding this comment.
is this used somewhere ?
There was a problem hiding this comment.
Yep, just below at parseIPAMRange(subnet, gatewayStr, ipRangeStr). Windows is single-subnet so it takes at most the first gateway.
network create paired a single --gateway with every subnet, so on a dual-stack network the IPv6 gateway was checked against the IPv4 subnet and creation failed with "no matching subnet". Accept --gateway more than once and match each gateway to the subnet that contains it. Signed-off-by: Mayur Das <mayur.das@neevcloud.com>
25e9bd1 to
31a40e9
Compare
A single
--gatewaygets applied to every--subnet, so creating a dual-stack network and passing both gateways fails — the IPv6 gateway ends up checked against the IPv4 subnet:This makes
--gatewayrepeatable (--subnetalready is) and matches each gateway to the subnet it belongs to, so the v4 gateway goes with the v4 subnet and the v6 one with the v6 subnet. A gateway that isn't inside any subnet still errors out.Same command after the change:
--ip-rangehas the same single-value-per-network limitation on dual-stack (Docker takes it per subnet too). I kept this PR to--gatewayto stay focused on #4951; happy to do--ip-rangethe same way as a follow-up.Closes #4951.