From eb0c2efe324da5ff2287000a28b7270b8782f217 Mon Sep 17 00:00:00 2001
From: Christian Poessinger <christian@poessinger.com>
Date: Fri, 31 Jul 2020 15:27:06 +0200
Subject: ipv6: link-local: test address assignment on interfaces

---
 scripts/cli/base_interfaces_test.py     | 42 +++++++++++++++++++++++++++++----
 scripts/cli/test_interfaces_bonding.py  | 37 ++++++++++++++---------------
 scripts/cli/test_interfaces_bridge.py   | 26 +++++++++++++-------
 scripts/cli/test_interfaces_ethernet.py |  1 +
 4 files changed, 73 insertions(+), 33 deletions(-)

diff --git a/scripts/cli/base_interfaces_test.py b/scripts/cli/base_interfaces_test.py
index 8fae5970e..14ec7e137 100644
--- a/scripts/cli/base_interfaces_test.py
+++ b/scripts/cli/base_interfaces_test.py
@@ -15,11 +15,12 @@
 import os
 import unittest
 
-from vyos.configsession import ConfigSession
 from netifaces import ifaddresses, AF_INET, AF_INET6
-from vyos.validate import is_intf_addr_assigned, is_ipv6_link_local
+
+from vyos.configsession import ConfigSession
 from vyos.ifconfig import Interface
 from vyos.util import read_file
+from vyos.validate import is_intf_addr_assigned, is_ipv6_link_local
 
 class BasicInterfaceTest:
     class BaseTest(unittest.TestCase):
@@ -27,6 +28,7 @@ class BasicInterfaceTest:
         _test_mtu = False
         _test_vlan = False
         _test_qinq = False
+        _test_ipv6 = False
         _base_path = []
 
         _options = {}
@@ -47,12 +49,14 @@ class BasicInterfaceTest:
         def tearDown(self):
             # we should not remove ethernet from the overall CLI
             if 'ethernet' in self._base_path:
-                for intf in self._interfaces:
+                for interface in self._interfaces:
                     # when using a dedicated interface to test via TEST_ETH environment
                     # variable only this one will be cleared in the end - usable to test
                     # ethernet interfaces via SSH
-                    self.session.delete(self._base_path + [intf])
-                    self.session.set(self._base_path + [intf])
+                    self.session.delete(self._base_path + [interface])
+                    self.session.set(self._base_path + [interface, 'duplex', 'auto'])
+                    self.session.set(self._base_path + [interface, 'speed', 'auto'])
+                    self.session.set(self._base_path + [interface, 'smp-affinity', 'auto'])
             else:
                 self.session.delete(self._base_path)
 
@@ -117,6 +121,34 @@ class BasicInterfaceTest:
 
                         self.assertTrue(is_intf_addr_assigned(intf, addr['addr']))
 
+        def test_ipv6_link_local(self):
+            """ Common function for IPv6 link-local address assignemnts """
+            if not self._test_ipv6:
+                return None
+
+            for interface in self._interfaces:
+                base = self._base_path + [interface]
+                for option in self._options.get(interface, []):
+                    self.session.set(base + option.split())
+
+            # after commit we must have an IPv6 link-local address
+            self.session.commit()
+
+            for interface in self._interfaces:
+                for addr in ifaddresses(interface)[AF_INET6]:
+                    self.assertTrue(is_ipv6_link_local(addr['addr']))
+
+            # disable IPv6 link-local address assignment
+            for interface in self._interfaces:
+                base = self._base_path + [interface]
+                self.session.set(base + ['ipv6', 'address', 'no-default-link-local'])
+
+            # after commit we must have no IPv6 link-local address
+            self.session.commit()
+
+            for interface in self._interfaces:
+                self.assertTrue(AF_INET6 not in ifaddresses(interface))
+
         def _mtu_test(self, intf):
             """ helper function to verify MTU size """
             with open('/sys/class/net/{}/mtu'.format(intf), 'r') as f:
diff --git a/scripts/cli/test_interfaces_bonding.py b/scripts/cli/test_interfaces_bonding.py
index b4251fa15..e3d3b25ee 100755
--- a/scripts/cli/test_interfaces_bonding.py
+++ b/scripts/cli/test_interfaces_bonding.py
@@ -25,38 +25,37 @@ from vyos.util import read_file
 
 class BondingInterfaceTest(BasicInterfaceTest.BaseTest):
     def setUp(self):
-         super().setUp()
+        super().setUp()
 
-         self._base_path = ['interfaces', 'bonding']
-         self._interfaces = ['bond0']
-         self._test_mtu = True
-         self._test_vlan = True
-         self._test_qinq = True
+        self._base_path = ['interfaces', 'bonding']
+        self._interfaces = ['bond0']
+        self._test_mtu = True
+        self._test_vlan = True
+        self._test_qinq = True
+        self._test_ipv6 = True
 
-    def test_add_member(self):
-        members = []
+        self._members = []
         # we need to filter out VLAN interfaces identified by a dot (.)
         # in their name - just in case!
         if 'TEST_ETH' in os.environ:
-            members = os.environ['TEST_ETH'].split()
+            self._members = os.environ['TEST_ETH'].split()
         else:
             for tmp in Section.interfaces("ethernet"):
                 if not '.' in tmp:
-                    members.append(tmp)
+                    self._members.append(tmp)
+
+        self._options['bond0'] = []
+        for member in self._members:
+            self._options['bond0'].append(f'member interface {member}')
 
-        for interface in self._interfaces:
-            base = self._base_path + [interface]
-            for member in members:
-                # We can not enslave an interface when there is an address
-                # assigned - take care here - or find them dynamically if a user
-                # runs vyos-smoketest on his production device?
-                self.session.set(base + ['member', 'interface', member])
 
-        self.session.commit()
+    def test_add_address_single(self):
+        """ derived method to check if member interfaces are enslaved properly """
+        super().test_add_address_single()
 
         for interface in self._interfaces:
             slaves = read_file(f'/sys/class/net/{interface}/bonding/slaves').split()
-            self.assertListEqual(slaves, members)
+            self.assertListEqual(slaves, self._members)
 
 if __name__ == '__main__':
     unittest.main()
diff --git a/scripts/cli/test_interfaces_bridge.py b/scripts/cli/test_interfaces_bridge.py
index 4402cad68..bc0bb69c6 100755
--- a/scripts/cli/test_interfaces_bridge.py
+++ b/scripts/cli/test_interfaces_bridge.py
@@ -24,27 +24,35 @@ class BridgeInterfaceTest(BasicInterfaceTest.BaseTest):
     def setUp(self):
         super().setUp()
 
+        self._test_ipv6 = True
+
         self._base_path = ['interfaces', 'bridge']
         self._interfaces = ['br0']
 
-    def test_add_remove_member(self):
-        members = []
+        self._members = []
         # we need to filter out VLAN interfaces identified by a dot (.)
         # in their name - just in case!
         if 'TEST_ETH' in os.environ:
-            members = os.environ['TEST_ETH'].split()
+            self._members = os.environ['TEST_ETH'].split()
         else:
             for tmp in Section.interfaces("ethernet"):
-                if not '.' in tmp: members.append(tmp)
+                if not '.' in tmp:
+                    self._members.append(tmp)
 
-        for intf in self._interfaces:
-            base = self._base_path + [intf]
+        self._options['br0'] = []
+        for member in self._members:
+            self._options['br0'].append(f'member interface {member}')
+
+    def test_add_remove_member(self):
+        for interface in self._interfaces:
+            base = self._base_path + [interface]
             self.session.set(base + ['stp'])
+            self.session.set(base + ['address', '192.0.2.1/24'])
 
             cost = 1000
             priority = 10
             # assign members to bridge interface
-            for member in members:
+            for member in self._members:
                 base_member = base + ['member', 'interface', member]
                 self.session.set(base_member + ['cost', str(cost)])
                 self.session.set(base_member + ['priority', str(priority)])
@@ -53,8 +61,8 @@ class BridgeInterfaceTest(BasicInterfaceTest.BaseTest):
 
         self.session.commit()
 
-        for intf in self._interfaces:
-            self.session.delete(self._base_path + [intf, 'member'])
+        for interface in self._interfaces:
+            self.session.delete(self._base_path + [interface, 'member'])
 
         self.session.commit()
 
diff --git a/scripts/cli/test_interfaces_ethernet.py b/scripts/cli/test_interfaces_ethernet.py
index ccc4238e2..c31a606da 100755
--- a/scripts/cli/test_interfaces_ethernet.py
+++ b/scripts/cli/test_interfaces_ethernet.py
@@ -29,6 +29,7 @@ class EthernetInterfaceTest(BasicInterfaceTest.BaseTest):
         self._test_mtu = True
         self._test_vlan = True
         self._test_qinq = True
+        self._test_ipv6 = True
         self._interfaces = []
 
         # we need to filter out VLAN interfaces identified by a dot (.)
-- 
cgit v1.2.3