Browse code

libnetwork: respect gw priority per address family

Signed-off-by: Varun Hotani <varunhotani@gmail.com>

Varun Hotani authored on 2026/04/09 19:40:44
Showing 2 changed files
... ...
@@ -190,26 +190,19 @@ func (sb *Sandbox) getGatewayEndpoint() (ep4, ep6 *Endpoint) {
190 190
 			continue
191 191
 		}
192 192
 		gw4, gw6 := ep.hasGatewayOrDefaultRoute()
193
-		if gw4 && gw6 {
194
-			// The first dual-stack endpoint is the gateway, no need to search further.
195
-			//
196
-			// FIXME(robmry) - this means a dual-stack gateway is preferred over single-stack
197
-			// gateways with higher gateway-priorities. A dual-stack network should probably
198
-			// be preferred over two single-stack networks, if they all have equal priorities.
199
-			// It'd probably also be better to use a dual-stack endpoint as the gateway for
200
-			// a single address family, if there's a higher-priority single-stack gateway for
201
-			// the other address family. (But, priority is currently a Sandbox property, not
202
-			// an Endpoint property. So, this function doesn't have access to priorities.)
203
-			return ep, ep
204
-		}
205 193
 		if gw4 && ep4 == nil {
206
-			// Found the best IPv4-only gateway, keep searching for an IPv6 or dual-stack gateway.
194
+			// Endpoints are already sorted according to Endpoint.Less(). The
195
+			// first endpoint with IPv4 connectivity is the IPv4 gateway.
207 196
 			ep4 = ep
208 197
 		}
209 198
 		if gw6 && ep6 == nil {
210
-			// Found the best IPv6-only gateway, keep searching for an IPv4 or dual-stack gateway.
199
+			// The first endpoint with IPv6 connectivity is the IPv6 gateway.
211 200
 			ep6 = ep
212 201
 		}
202
+		if ep4 != nil && ep6 != nil {
203
+			// Found both; we're done.
204
+			break
205
+		}
213 206
 	}
214 207
 	return ep4, ep6
215 208
 }
... ...
@@ -206,6 +206,51 @@ func TestSandboxAddMultiPrio(t *testing.T) {
206 206
 	}
207 207
 }
208 208
 
209
+func TestGatewayEndpointRespectsPriorityPerAddressFamily(t *testing.T) {
210
+	defer netnsutils.SetupTestOSContext(t)()
211
+	ctx := t.Context()
212
+
213
+	opts := [][]NetworkOption{
214
+		{
215
+			NetworkOptionEnableIPv4(true),
216
+			NetworkOptionEnableIPv6(true),
217
+			NetworkOptionIpam(defaultipam.DriverName, "",
218
+				[]*IpamConf{{PreferredPool: "172.30.0.0/24", Gateway: "172.30.0.1"}},
219
+				[]*IpamConf{{PreferredPool: "fe90::/64", Gateway: "fe90::1"}}, nil),
220
+		},
221
+		{
222
+			NetworkOptionEnableIPv4(true),
223
+			NetworkOptionIpam(defaultipam.DriverName, "",
224
+				[]*IpamConf{{PreferredPool: "172.31.0.0/24", Gateway: "172.31.0.1"}},
225
+				nil, nil),
226
+		},
227
+	}
228
+
229
+	ctrlr, nws := getTestEnv(t, opts...)
230
+
231
+	sbx, err := ctrlr.NewSandbox(ctx, "sandbox-prio-per-family")
232
+	assert.NilError(t, err)
233
+
234
+	epDual, err := nws[0].CreateEndpoint(ctx, "ep-dual")
235
+	assert.NilError(t, err)
236
+	epV4, err := nws[1].CreateEndpoint(ctx, "ep-v4")
237
+	assert.NilError(t, err)
238
+
239
+	err = epDual.Join(ctx, sbx)
240
+	assert.NilError(t, err)
241
+	err = epV4.Join(ctx, sbx, JoinOptionPriority(1000))
242
+	assert.NilError(t, err)
243
+
244
+	gwep4, gwep6 := sbx.getGatewayEndpoint()
245
+	assert.Assert(t, gwep4 != nil)
246
+	assert.Assert(t, gwep6 != nil)
247
+	assert.Check(t, is.Equal(gwep4.ID(), epV4.ID()))
248
+	assert.Check(t, is.Equal(gwep6.ID(), epDual.ID()))
249
+
250
+	err = sbx.Delete(ctx)
251
+	assert.NilError(t, err)
252
+}
253
+
209 254
 func TestSandboxAddSamePrio(t *testing.T) {
210 255
 	defer netnsutils.SetupTestOSContext(t)()
211 256