Skip to content

Commit e0d7aa9

Browse files
committed
Validate existing bridge subnet mask against config
1 parent 1e54854 commit e0d7aa9

2 files changed

Lines changed: 79 additions & 7 deletions

File tree

lib/network/bridge_linux.go

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,37 @@ func listBridgeAddrsWithRetry(link netlink.Link) ([]netlink.Addr, error) {
5151
return nil, err
5252
}
5353

54+
func bridgeAddrMatchesGatewayAndMask(addrs []netlink.Addr, expectedGateway net.IP, expectedMask net.IPMask) (bool, bool, []string, []string) {
55+
expectedOnes, expectedBits := expectedMask.Size()
56+
hasGateway := false
57+
hasGatewayWithMask := false
58+
actualIPs := make([]string, 0, len(addrs))
59+
gatewayCIDRs := make([]string, 0, 1)
60+
61+
for _, addr := range addrs {
62+
actualCIDR := "<nil>"
63+
if addr.IPNet != nil {
64+
actualCIDR = addr.IPNet.String()
65+
}
66+
actualIPs = append(actualIPs, actualCIDR)
67+
if !addr.IP.Equal(expectedGateway) {
68+
continue
69+
}
70+
hasGateway = true
71+
gatewayCIDRs = append(gatewayCIDRs, actualCIDR)
72+
if addr.IPNet == nil {
73+
continue
74+
}
75+
76+
ones, bits := addr.IPNet.Mask.Size()
77+
if ones == expectedOnes && bits == expectedBits {
78+
hasGatewayWithMask = true
79+
}
80+
}
81+
82+
return hasGateway, hasGatewayWithMask, actualIPs, gatewayCIDRs
83+
}
84+
5485
// checkSubnetConflicts checks if the configured subnet conflicts with existing routes.
5586
// Returns an error if a conflict is detected, with guidance on how to resolve it.
5687
func (m *manager) checkSubnetConflicts(ctx context.Context, subnet string) error {
@@ -128,14 +159,10 @@ func (m *manager) createBridge(ctx context.Context, name, gateway, subnet string
128159
}
129160

130161
expectedGW := net.ParseIP(gateway)
131-
hasExpectedIP := false
132-
var actualIPs []string
133-
for _, addr := range addrs {
134-
actualIPs = append(actualIPs, addr.IPNet.String())
135-
if addr.IP.Equal(expectedGW) {
136-
hasExpectedIP = true
137-
}
162+
if expectedGW == nil {
163+
return fmt.Errorf("invalid gateway IP: %s", gateway)
138164
}
165+
hasExpectedIP, hasExpectedMask, actualIPs, gatewayCIDRs := bridgeAddrMatchesGatewayAndMask(addrs, expectedGW, ipNet.Mask)
139166

140167
if !hasExpectedIP {
141168
ones, _ := ipNet.Mask.Size()
@@ -145,6 +172,14 @@ func (m *manager) createBridge(ctx context.Context, name, gateway, subnet string
145172
"or (3) delete the bridge with: sudo ip link delete %s",
146173
name, actualIPs, gateway, ones, name)
147174
}
175+
if !hasExpectedMask {
176+
ones, _ := ipNet.Mask.Size()
177+
return fmt.Errorf("bridge %s exists with gateway %s but mask does not match expected /%d (gateway addresses: %v). "+
178+
"Options: (1) update SUBNET_CIDR and SUBNET_GATEWAY to match the existing bridge, "+
179+
"(2) use a different BRIDGE_NAME, "+
180+
"or (3) delete the bridge with: sudo ip link delete %s",
181+
name, gateway, ones, gatewayCIDRs, name)
182+
}
148183

149184
// Bridge exists with correct IP, verify it's up
150185
if err := netlink.LinkSetUp(existing); err != nil {

lib/network/bridge_linux_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@
33
package network
44

55
import (
6+
"net"
67
"testing"
78

89
"github.com/stretchr/testify/assert"
10+
"github.com/stretchr/testify/require"
11+
"github.com/vishvananda/netlink"
912
)
1013

1114
func TestParseBridgeFilters(t *testing.T) {
@@ -66,3 +69,37 @@ func TestPlanOrphanedBridgeTCBailsWhenNoRTIIFParses(t *testing.T) {
6669
assert.Nil(t, staleFilters)
6770
assert.Nil(t, staleClasses)
6871
}
72+
73+
func TestBridgeAddrMatchesGatewayAndMask(t *testing.T) {
74+
addrs := []netlink.Addr{
75+
{IPNet: &net.IPNet{IP: net.ParseIP("10.244.0.1"), Mask: net.CIDRMask(24, 32)}},
76+
{IPNet: &net.IPNet{IP: net.ParseIP("10.244.0.2"), Mask: net.CIDRMask(24, 32)}},
77+
}
78+
79+
hasGateway, hasMask, actualIPs, gatewayCIDRs := bridgeAddrMatchesGatewayAndMask(
80+
addrs,
81+
net.ParseIP("10.244.0.1"),
82+
net.CIDRMask(24, 32),
83+
)
84+
85+
require.True(t, hasGateway)
86+
require.True(t, hasMask)
87+
assert.Equal(t, []string{"10.244.0.1/24", "10.244.0.2/24"}, actualIPs)
88+
assert.Equal(t, []string{"10.244.0.1/24"}, gatewayCIDRs)
89+
}
90+
91+
func TestBridgeAddrMatchesGatewayAndMaskDetectsMaskMismatch(t *testing.T) {
92+
addrs := []netlink.Addr{
93+
{IPNet: &net.IPNet{IP: net.ParseIP("10.244.0.1"), Mask: net.CIDRMask(16, 32)}},
94+
}
95+
96+
hasGateway, hasMask, _, gatewayCIDRs := bridgeAddrMatchesGatewayAndMask(
97+
addrs,
98+
net.ParseIP("10.244.0.1"),
99+
net.CIDRMask(24, 32),
100+
)
101+
102+
require.True(t, hasGateway)
103+
require.False(t, hasMask)
104+
assert.Equal(t, []string{"10.244.0.1/16"}, gatewayCIDRs)
105+
}

0 commit comments

Comments
 (0)