@@ -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.
5687func (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 {
0 commit comments