Skip to content

Commit 4d3e739

Browse files
committed
Fixed dual stack bridge
+ better error messages for dual stack objects
1 parent fdd7edb commit 4d3e739

File tree

8 files changed

+145
-43
lines changed

8 files changed

+145
-43
lines changed

Changes

+3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
{{$NEXT}}
22

3+
- Fixed dual stack bridge. IPv4 and IPv6 interfaces were accidently
4+
processed together.
5+
36
6.075 2024-12-19 11:53:37+01:00 Europe/Berlin
47

58
- Fix: Attribute 'policy_distribution_point' at dual stack router

go/pkg/pass1/check-ip.go

+23-20
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
package pass1
22

33
import (
4-
"maps"
54
"net/netip"
6-
"slices"
75
"strings"
86

97
"go4.org/netipx"
@@ -53,28 +51,31 @@ func (c *spoc) checkSubnetOf() {
5351
}
5452

5553
func (c *spoc) checkIPAddressesAndBridges() {
56-
prefix2net := make(map[string][]*network)
54+
type nameV46 struct {
55+
name string
56+
v6 bool
57+
}
58+
prefix2net := make(map[nameV46][]*network)
5759
for _, n := range c.allNetworks {
58-
// Group bridged networks by prefix of name.
60+
// Group bridged networks by IPv4/v6 type and prefix of name.
5961
if n.ipType == bridgedIP {
6062
prefix, _, _ := strings.Cut(n.name, "/")
61-
prefix2net[prefix] = append(prefix2net[prefix], n)
63+
prefixV46 := nameV46{prefix, n.ipV6}
64+
prefix2net[prefixV46] = append(prefix2net[prefixV46], n)
6265
} else if n.ipType == unnumberedIP {
6366
l := n.interfaces
6467
if len(l) > 2 {
6568
c.err(
6669
"Unnumbered %s is connected to more than two interfaces:\n%s",
67-
n.name, l.nameList())
70+
n.vxName(), l.nameList())
6871
}
6972
} else if !(n.ipType == tunnelIP || n.loopback) {
7073
c.checkIPAddr(n)
7174
}
7275
}
7376

7477
// Check address conflicts for collected parts of bridged networks.
75-
// Sort prefix names for deterministic error messages.
76-
for _, prefix := range slices.Sorted(maps.Keys(prefix2net)) {
77-
l := prefix2net[prefix]
78+
for prefixV46, l := range prefix2net {
7879
dummy := new(network)
7980
seen := make(map[*routerIntf]bool)
8081
for _, n := range l {
@@ -91,7 +92,7 @@ func (c *spoc) checkIPAddressesAndBridges() {
9192
c.checkIPAddr(dummy)
9293

9394
// Check collected parts of bridged networks.
94-
c.checkBridgedNetworks(prefix, l)
95+
c.checkBridgedNetworks(prefixV46.name, l)
9596
}
9697
}
9798

@@ -126,7 +127,8 @@ func (c *spoc) checkIPAddr(n *network) {
126127
ip := intf.ip
127128
if other, found := ip2name[ip]; found {
128129
if !(intf.redundant && redundant[other]) {
129-
c.err("Duplicate IP address for %s and %s", other, intf)
130+
c.err("Duplicate IP address for %s and %s",
131+
other, intf.vxName())
130132
}
131133
} else {
132134
ip2name[ip] = intf.name
@@ -139,7 +141,7 @@ func (c *spoc) checkIPAddr(n *network) {
139141
if shortIntf != nil && routeIntf != nil {
140142
c.err("Can't generate static routes for %s"+
141143
" because IP address is unknown for:\n%s",
142-
routeIntf, shortIntf.nameList())
144+
routeIntf.vxName(), shortIntf.nameList())
143145
}
144146

145147
// Optimization: No need to collect .routeInZone at bridge router.
@@ -156,7 +158,7 @@ func (c *spoc) checkIPAddr(n *network) {
156158
}
157159
rg := h.ipRange
158160
if other, found := range2name[rg]; found {
159-
c.err("Duplicate IP address for %s and %s", other, h)
161+
c.err("Duplicate IP address for %s and %s", other, h.vxName())
160162
} else {
161163
range2name[rg] = h.name
162164
}
@@ -170,15 +172,15 @@ func (c *spoc) checkIPAddr(n *network) {
170172
}
171173
for ip, other := range ip2name {
172174
if rg.Contains(ip) {
173-
c.err("Duplicate IP address for %s and %s", other, h)
175+
c.err("Duplicate IP address for %s and %s", other, h.vxName())
174176
}
175177
}
176178
}
177179

178180
for _, h := range n.hosts {
179181
if h.ip.IsValid() {
180182
if other, found := ip2name[h.ip]; found {
181-
c.err("Duplicate IP address for %s and %s", other, h)
183+
c.err("Duplicate IP address for %s and %s", other, h.vxName())
182184
} else {
183185
ip2name[h.ip] = h.name
184186
}
@@ -198,12 +200,12 @@ func (c *spoc) checkBridgedNetworks(prefix string, l netList) {
198200
if n, found := c.symTable.network[prefix[len("network:"):]]; found {
199201
c.err(
200202
"Must not define %s together with bridged networks of same name",
201-
n)
203+
n.vxName())
202204
}
203205
n1 := l[0]
204206
group := l[1:]
205207
if len(group) == 0 {
206-
c.warn("Bridged %s must not be used solitary", n1)
208+
c.warn("Bridged %s must not be used solitary", n1.vxName())
207209
}
208210
seen := make(map[*router]bool)
209211
connected := make(map[*network]bool)
@@ -214,7 +216,8 @@ func (c *spoc) checkBridgedNetworks(prefix string, l netList) {
214216
n2 := next[0]
215217
next = next[1:]
216218
if n1.ipp != n2.ipp {
217-
c.err("%s and %s must have identical address", n1, n2)
219+
c.err("%s and %s must have identical address",
220+
n1.vxName(), n2.vxName())
218221
}
219222
connected[n2] = true
220223
for _, in := range n2.interfaces {
@@ -230,7 +233,7 @@ func (c *spoc) checkBridgedNetworks(prefix string, l netList) {
230233
if l3 := in.layer3Intf; l3 != nil {
231234
if !n1.ipp.Contains(l3.ip) {
232235
c.err("%s's IP doesn't match address of bridged networks",
233-
l3)
236+
l3.vxName())
234237
}
235238
}
236239
for _, out := range r.interfaces {
@@ -246,7 +249,7 @@ func (c *spoc) checkBridgedNetworks(prefix string, l netList) {
246249
}
247250
for _, n2 := range group {
248251
if !connected[n2] {
249-
c.err("%s and %s must be connected by bridge", n2, n1)
252+
c.err("%s and %s must be connected by bridge", n2.vxName(), n1.vxName())
250253
}
251254
}
252255
}

go/pkg/pass1/find-subnets.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ func (c *spoc) findSubnetsInZoneCluster0(z0 *zone) {
122122
// Found two different networks with identical address.
123123
if other := ipMap[ipp.Addr()]; other != nil {
124124
c.err("%s and %s have identical address in %s",
125-
other.name, n.name, z0.name)
125+
other.vxName(), n.vxName(), z0.name)
126126
} else {
127127

128128
// Store original network under its address.
@@ -422,7 +422,7 @@ func (c *spoc) findSubnetsInNatDomain0(domains []*natDomain, networks netList) {
422422
for _, z := range domain.zones {
423423
if z.cluster[0] == cl {
424424
c.err("%s and %s have identical address in %s",
425-
n, other, z)
425+
n.vxName(), other.vxName(), z)
426426
break
427427
}
428428
}

go/pkg/pass1/setup-objects.go

+19-12
Original file line numberDiff line numberDiff line change
@@ -431,15 +431,6 @@ func (c *spoc) getAndCheckLayer3(r *router, a *ast.Router) string {
431431
bName = a1.Name
432432
}
433433
}
434-
if l3Name != "" {
435-
// Check existence of layer3 interface.
436-
if !slices.ContainsFunc(a.Interfaces, func(a1 *ast.Attribute) bool {
437-
return a1.Name == l3Name
438-
}) {
439-
c.err("Must define %s at %s for corresponding bridge interfaces",
440-
l3Name, a.Name)
441-
}
442-
}
443434
}
444435
return l3Name
445436
}
@@ -1820,6 +1811,7 @@ func (c *spoc) setupRouter2(r *router) {
18201811
}
18211812

18221813
isCryptoHub := false
1814+
var layer3Intf *routerIntf
18231815
for _, intf := range r.interfaces {
18241816
if intf.hub != nil || intf.spoke != nil {
18251817
if r.model.crypto == "" {
@@ -1833,9 +1825,24 @@ func (c *spoc) setupRouter2(r *router) {
18331825
// Link bridged interfaces with corresponding layer3 device.
18341826
// Used in findAutoInterfaces.
18351827
if intf.ipType == bridgedIP {
1836-
layer3Name := intf.name[len("interface:"):]
1837-
layer3Name, _, _ = strings.Cut(layer3Name, "/")
1838-
intf.layer3Intf = c.symTable.routerIntf[layer3Name]
1828+
if layer3Intf == nil {
1829+
i := slices.IndexFunc(r.interfaces, func(intf *routerIntf) bool {
1830+
if intf.isLayer3 {
1831+
return true
1832+
}
1833+
return false
1834+
})
1835+
if i == -1 {
1836+
l3Name, _, _ := strings.Cut(intf.vxName(), "/")
1837+
c.err("Must define %s for corresponding bridge interfaces",
1838+
l3Name)
1839+
// Prevent further errors.
1840+
layer3Intf = intf
1841+
} else {
1842+
layer3Intf = r.interfaces[i]
1843+
}
1844+
}
1845+
intf.layer3Intf = layer3Intf
18391846
}
18401847
}
18411848
if r.model.doAuth {

go/pkg/pass1/types.go

+20-7
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,19 @@ func (x *network) getUp() someObj {
184184
}
185185
func (x *network) intfList() intfList { return x.interfaces }
186186
func (x network) isCombined46() bool { return x.combined46 != nil }
187+
func (x network) vxName() string {
188+
return vxName(x.name, x.ipV6, x.combined46 != nil)
189+
}
190+
191+
func vxName(name string, ipV6, isCombined46 bool) string {
192+
if isCombined46 {
193+
if ipV6 {
194+
return "IPv6 " + name
195+
}
196+
return "IPv4 " + name
197+
}
198+
return name
199+
}
187200

188201
type netList []*network
189202

@@ -225,6 +238,9 @@ type host struct {
225238
}
226239

227240
func (x host) isCombined46() bool { return x.combined46 != nil }
241+
func (x host) vxName() string {
242+
return vxName(x.name, x.ipV6, x.combined46 != nil)
243+
}
228244

229245
type model struct {
230246
commentChar string
@@ -388,6 +404,9 @@ func (intf routerIntf) getCrypto() *crypto {
388404
return intf.realIntf.spoke
389405
}
390406
func (x routerIntf) isCombined46() bool { return x.combined46 != nil }
407+
func (x routerIntf) vxName() string {
408+
return vxName(x.name, x.ipV6, x.combined46 != nil)
409+
}
391410

392411
type intfList []*routerIntf
393412

@@ -536,13 +555,7 @@ type area struct {
536555

537556
func (x area) String() string { return x.name }
538557
func (x area) vxName() string {
539-
if x.combined46 != nil {
540-
if x.ipV6 {
541-
return "IPv6 " + x.name
542-
}
543-
return "IPv4 " + x.name
544-
}
545-
return x.name
558+
return vxName(x.name, x.ipV6, x.combined46 != nil)
546559
}
547560
func (x area) isCombined46() bool { return x.combined46 != nil }
548561

go/testdata/bridged.t

+1-1
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ router:bridge = {
191191
}
192192
network:n1/right = { ip = 10.1.1.0/24; }
193193
=ERROR=
194-
Error: Must define interface:n1 at router:bridge for corresponding bridge interfaces
194+
Error: Must define interface:bridge.n1 for corresponding bridge interfaces
195195
=END=
196196
197197
############################################################

go/testdata/ipv46-combined.t

+76
Original file line numberDiff line numberDiff line change
@@ -901,3 +901,79 @@ network:n1 = { ip = 10.1.1.0/24; ip6 = 2001:db8:1:1::/64; }
901901
Error: Duplicate any:n1-v4 and any:n1-v6 in any:[network:n1-v6]
902902
Error: Duplicate any:n1-v4 and any:n1-v6 in any:[network:n1-v4]
903903
=END=
904+
905+
############################################################
906+
=TITLE=Bridged network
907+
=INPUT=
908+
network:n1/left = {
909+
ip = 10.1.1.0/24; ip6 = 2001:db8:1:1::/64;
910+
}
911+
router:bridge = {
912+
model = ASA;
913+
managed;
914+
interface:n1 = { ip = 10.1.1.9; ip6 = 2001:db8:1:1::9; hardware = device; }
915+
interface:n1/left = { hardware = left; }
916+
interface:n1/right = { hardware = right; }
917+
}
918+
network:n1/right = {
919+
ip = 10.1.1.0/24; ip6 = 2001:db8:1:1::/64;
920+
}
921+
=WARNING=NONE
922+
923+
############################################################
924+
=TITLE=Bridge with missing IPv6 address
925+
=INPUT=
926+
network:n1/left = {
927+
ip = 10.1.1.0/24; ip6 = 2001:db8:1:1::/64;
928+
}
929+
router:bridge = {
930+
model = ASA;
931+
managed;
932+
interface:n1 = { ip = 10.1.1.9; hardware = device; }
933+
interface:n1/left = { hardware = left; }
934+
interface:n1/right = { hardware = right; }
935+
}
936+
network:n1/right = {
937+
ip = 10.1.1.0/24; ip6 = 2001:db8:1:1::/64;
938+
}
939+
=ERROR=
940+
Error: Must define IPv6 interface:bridge.n1 for corresponding bridge interfaces
941+
=END=
942+
943+
############################################################
944+
=TITLE=IPv6 part of unnumbered network has more than two interfaces
945+
=INPUT=
946+
network:u = { unnumbered; unnumbered6; }
947+
router:r1 = { interface:u = { unnumbered6; } }
948+
router:r2 = { interface:u = { unnumbered; unnumbered6; } }
949+
router:r3 = { interface:u = { unnumbered; unnumbered6; } }
950+
=ERROR=
951+
Error: Unnumbered IPv6 network:u is connected to more than two interfaces:
952+
- interface:r1.u
953+
- interface:r2.u
954+
- interface:r3.u
955+
=END=
956+
957+
############################################################
958+
=TITLE=Duplicate IPv6 address of hosts
959+
=INPUT=
960+
network:n1 = { ip = 10.1.1.0/24; ip6 = 2001:db8:1:1::/64;
961+
host:h1 = { ip = 10.1.1.1; ip6 = 2001:db8:1:1::1; }
962+
host:h2 = { ip = 10.1.1.2; ip6 = 2001:db8:1:1::1; }
963+
}
964+
=ERROR=
965+
Error: Duplicate IP address for host:h1 and IPv6 host:h2
966+
=END=
967+
968+
############################################################
969+
=TITLE=Duplicate IPv4 address of networks
970+
=INPUT=
971+
network:n1 = { ip = 10.1.1.0/24; ip6 = 2001:db8:1:1::/64; }
972+
network:n2 = { ip = 10.1.1.0/24; ip6 = 2001:db8:1:2::/64; }
973+
router:r1 = {
974+
interface:n1;
975+
interface:n2;
976+
}
977+
=ERROR=
978+
Error: IPv4 network:n1 and IPv4 network:n2 have identical address in any:[network:n1]
979+
=END=

go/testdata/ipv6/bridged_ipv6.t

+1-1
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ router:bridge = {
202202
}
203203
network:n1/right = { ip = ::a01:100/120; }
204204
=ERROR=
205-
Error: Must define interface:n1 at router:bridge for corresponding bridge interfaces
205+
Error: Must define interface:bridge.n1 for corresponding bridge interfaces
206206
=END=
207207
208208
############################################################

0 commit comments

Comments
 (0)