diff --git a/clab/config.go b/clab/config.go index 91b9042a7..88b291b0d 100644 --- a/clab/config.go +++ b/clab/config.go @@ -608,7 +608,7 @@ func (c *CLab) NewEndpoint(e string) *Endpoint { // split the string to get node name and endpoint name split := strings.Split(e, ":") if len(split) != 2 { - log.Fatalf("endpoint %s has wrong syntax", e) + log.Fatalf("endpoint %s has wrong syntax", e) // skipcq: GO-S0904 } nName := split[0] // node name epName := split[1] // endpoint name @@ -641,7 +641,7 @@ func (c *CLab) NewEndpoint(e string) *Endpoint { // stop the deployment if the matching node element was not found // "host" node name is an exception, it may exist without a matching node if endpoint.Node == nil { - log.Fatalf("Not all nodes are specified in the 'topology.nodes' section or the names don't match in the 'links.endpoints' section: %s", nName) + log.Fatalf("Not all nodes are specified in the 'topology.nodes' section or the names don't match in the 'links.endpoints' section: %s", nName) // skipcq: GO-S0904 } // initialize the endpoint name based on the split function @@ -660,6 +660,9 @@ func (c *CLab) CheckTopologyDefinition(ctx context.Context) error { if err := c.verifyLinks(); err != nil { return err } + if err := c.verifyRootNetnsInterfaceUniqueness(); err != nil { + return err + } if err := c.VerifyContainersUniqueness(ctx); err != nil { return err } @@ -790,6 +793,26 @@ func (c *CLab) verifyHostIfaces() error { return nil } +// verifyRootNetnsInterfaceUniqueness ensures that interafaces that appear in the root ns (bridge, ovs-bridge and host) +// are uniquely defined in the topology file +func (c *CLab) verifyRootNetnsInterfaceUniqueness() error { + rootNsIfaces := map[string]struct{}{} + for _, l := range c.Links { + endpoints := [2]*Endpoint{l.A, l.B} + for _, e := range endpoints { + if e.Node.Kind == "bridge" || e.Node.Kind == "ovs-bridge" || e.Node.Kind == "host" { + if _, ok := rootNsIfaces[e.EndpointName]; ok { + return fmt.Errorf(`interface %s defined for node %s has already been used in other bridges, ovs-bridges or host interfaces. + Make sure that nodes of these kinds use unique interface names`, e.EndpointName, e.Node.ShortName) + } else { + rootNsIfaces[e.EndpointName] = struct{}{} + } + } + } + } + return nil +} + // verifyVirtSupport checks if virtualization supported by vcpu if vrnetlab nodes are used func (c *CLab) verifyVirtSupport() error { virtNeeded := false diff --git a/clab/config_test.go b/clab/config_test.go index 7de853920..064c1a891 100644 --- a/clab/config_test.go +++ b/clab/config_test.go @@ -380,3 +380,22 @@ func TestLablesInit(t *testing.T) { }) } } + +func TestVerifyRootNetnsInterfaceUniqueness(t *testing.T) { + + opts := []ClabOption{ + WithTopoFile("test_data/topo7-dup-rootnetns.yml"), + } + c := NewContainerLab(opts...) + + if err := c.ParseTopology(); err != nil { + t.Fatal(err) + } + + err := c.verifyRootNetnsInterfaceUniqueness() + if err == nil { + t.Fatalf("expected duplicate rootns links error") + } + t.Logf("error: %v", err) + +} diff --git a/clab/test_data/topo7-dup-rootnetns.yml b/clab/test_data/topo7-dup-rootnetns.yml new file mode 100644 index 000000000..150bb2b99 --- /dev/null +++ b/clab/test_data/topo7-dup-rootnetns.yml @@ -0,0 +1,11 @@ +name: topo7 + +topology: + nodes: + br1: + kind: bridge + br2: + kind: bridge + + links: + - endpoints: ["br1:eth1", "br2:eth1"]