From a7545ac5afe4b8629c3ea7e0df30cc52767a544d Mon Sep 17 00:00:00 2001 From: Pim van Pelt Date: Sun, 10 Apr 2022 09:54:51 +0000 Subject: [PATCH] Add ability to manipulate MACs Special care is taken for bondethernet, where the MAC changes when the first member is added to it. BondEthernet requires its MAC to be set in the bondethernets section, disallowing the MAC of individual members to be set. Also write a dumper for MACs of all types. Update integration test cases to stress the MAC changes on loops, bonds, and phys. --- example.yaml | 3 +++ intest/hippo-empty.yaml | 4 ++++ intest/hippo12.yaml | 1 + intest/hippo13.yaml | 1 + intest/hippo3.yaml | 1 + intest/hippo4.yaml | 1 + intest/hippo7.yaml | 1 + intest/hippo8.yaml | 1 + intest/hippo9.yaml | 2 ++ intest/intest.sh | 4 ++++ vpp/reconciler.py | 45 ++++++++++++++++++++++++++++++++++++++--- vpp/vppapi.py | 15 +++++++++----- 12 files changed, 71 insertions(+), 8 deletions(-) diff --git a/example.yaml b/example.yaml index cfa6137..99d595f 100644 --- a/example.yaml +++ b/example.yaml @@ -1,6 +1,7 @@ bondethernets: BondEthernet0: interfaces: [ GigabitEthernet3/0/0, GigabitEthernet3/0/1 ] + mac: 02:b0:b0:01:02:03 mode: xor load-balance: l2 @@ -14,6 +15,7 @@ interfaces: HundredGigabitEthernet12/0/0: lcp: "ice12-0-0" + mac: f2:01:00:12:00:00 mtu: 9000 addresses: [ 192.0.2.17/30, 2001:db8:3::1/64 ] sub-interfaces: @@ -74,6 +76,7 @@ loopbacks: loop1: lcp: "bvi1" mtu: 2000 + mac: 02:de:ad:01:be:ef addresses: [ 10.0.1.1/24, 2001:db8:1::1/64 ] bridgedomains: diff --git a/intest/hippo-empty.yaml b/intest/hippo-empty.yaml index 35e1811..68c59bc 100644 --- a/intest/hippo-empty.yaml +++ b/intest/hippo-empty.yaml @@ -1,17 +1,21 @@ interfaces: GigabitEthernet3/0/0: mtu: 1500 + mac: 00:25:90:0c:05:00 state: down description: Not Used GigabitEthernet3/0/1: mtu: 1500 + mac: 00:25:90:0c:05:01 state: down description: Not Used HundredGigabitEthernet12/0/0: mtu: 1500 + mac: b4:96:91:b3:b1:10 state: down description: Not Used HundredGigabitEthernet12/0/1: mtu: 1500 + mac: b4:96:91:b3:b1:11 state: down description: Not Used diff --git a/intest/hippo12.yaml b/intest/hippo12.yaml index 6cf930f..d198bc3 100644 --- a/intest/hippo12.yaml +++ b/intest/hippo12.yaml @@ -70,6 +70,7 @@ interfaces: loopbacks: loop0: lcp: "lo0" + mac: de:ad:00:be:ef:00 addresses: [ 10.0.0.1/32, 2001:db8::1/128 ] loop1: mtu: 2000 diff --git a/intest/hippo13.yaml b/intest/hippo13.yaml index 28a9d0b..1ec0cc8 100644 --- a/intest/hippo13.yaml +++ b/intest/hippo13.yaml @@ -1,5 +1,6 @@ bondethernets: BondEthernet0: + mac: 02:b0:b0:00:00:01 interfaces: [ GigabitEthernet3/0/0, GigabitEthernet3/0/1 ] interfaces: diff --git a/intest/hippo3.yaml b/intest/hippo3.yaml index cc7911c..ddcce4b 100644 --- a/intest/hippo3.yaml +++ b/intest/hippo3.yaml @@ -1,5 +1,6 @@ bondethernets: BondEthernet1: + mac: 02:b0:b0:00:00:02 interfaces: [ GigabitEthernet3/0/0, GigabitEthernet3/0/1 ] mode: round-robin diff --git a/intest/hippo4.yaml b/intest/hippo4.yaml index eaca1e4..9a0b2f9 100644 --- a/intest/hippo4.yaml +++ b/intest/hippo4.yaml @@ -1,5 +1,6 @@ interfaces: GigabitEthernet3/0/0: + mac: 12:00:ba:03:00:00 mtu: 9216 sub-interfaces: 100: diff --git a/intest/hippo7.yaml b/intest/hippo7.yaml index c4c0991..db363e7 100644 --- a/intest/hippo7.yaml +++ b/intest/hippo7.yaml @@ -1,5 +1,6 @@ interfaces: GigabitEthernet3/0/0: + mac: 02:ff:ba:03:00:00 mtu: 9216 sub-interfaces: 100: diff --git a/intest/hippo8.yaml b/intest/hippo8.yaml index ed56410..92bce2d 100644 --- a/intest/hippo8.yaml +++ b/intest/hippo8.yaml @@ -45,6 +45,7 @@ interfaces: loopbacks: loop11: mtu: 3000 + mac: de:ad:00:be:ef:11 lcp: "bvi11" addresses: [ 2001:db8:1::1/64, 192.0.2.1/30 ] diff --git a/intest/hippo9.yaml b/intest/hippo9.yaml index 1a83d97..0200740 100644 --- a/intest/hippo9.yaml +++ b/intest/hippo9.yaml @@ -1,5 +1,6 @@ bondethernets: BondEthernet0: + mac: 02:b0:b0:00:00:00 interfaces: [ GigabitEthernet3/0/0, GigabitEthernet3/0/1 ] interfaces: @@ -11,6 +12,7 @@ interfaces: description: "LAG #2" HundredGigabitEthernet12/0/0: + mac: 02:ff:ba:12:00:00 lcp: "ice0" HundredGigabitEthernet12/0/1: diff --git a/intest/intest.sh b/intest/intest.sh index 2aff749..eca54b2 100755 --- a/intest/intest.sh +++ b/intest/intest.sh @@ -25,5 +25,9 @@ echo " - Checking that from $j to $j is empty" ../vppcfg plan -s ../schema.yaml -c $j -o /tmp/vppcfg-exec_${j}_${j}_null + [ -s /tmp/vppcfg-exec_${j}_${j}_null ] && { + echo " - ERROR Transition is not empty" + cat /tmp/vppcfg-exec_${j}_${j}_null + } done done diff --git a/vpp/reconciler.py b/vpp/reconciler.py index d0bef87..9e43781 100644 --- a/vpp/reconciler.py +++ b/vpp/reconciler.py @@ -613,6 +613,9 @@ class Reconciler(): continue instance = int(ifname[4:]) cli="create loopback interface instance %d" % (instance) + ifname, iface = loopback.get_by_name(self.cfg, ifname) + if 'mac' in iface: + cli += " mac %s" % iface['mac'] self.cli['create'].append(cli); return True @@ -627,6 +630,8 @@ class Reconciler(): lb = bondethernet.get_lb(self.cfg, ifname) if lb: cli += " load-balance %s" % lb + if 'mac' in iface: + cli += " hw-addr %s" % iface['mac'] self.cli['create'].append(cli); return True @@ -727,6 +732,9 @@ class Reconciler(): def sync(self): ret = True + if not self.sync_loopbacks(): + self.logger.warning("Could not sync Loopbacks in VPP") + ret = False if not self.sync_bondethernets(): self.logger.warning("Could not sync bondethernets in VPP") ret = False @@ -742,18 +750,46 @@ class Reconciler(): if not self.sync_addresses(): self.logger.warning("Could not sync interface addresses in VPP") ret = False + if not self.sync_phys(): + self.logger.warning("Could not sync PHYs in VPP") + ret = False if not self.sync_admin_state(): self.logger.warning("Could not sync interface adminstate in VPP") ret = False return ret + def sync_loopbacks(self): + for ifname in loopback.get_loopbacks(self.cfg): + if not ifname in self.vpp.cache['interface_names']: + ## New loopback + continue + vpp_iface = self.vpp.cache['interface_names'][ifname] + config_ifname, config_iface = loopback.get_by_name(self.cfg, ifname) + if 'mac' in config_iface and config_iface['mac'] != str(vpp_iface.l2_address): + cli="set interface mac address %s %s" % (config_ifname, config_iface['mac']) + self.cli['sync'].append(cli) + return True + + def sync_phys(self): + for ifname in interface.get_phys(self.cfg): + if not ifname in self.vpp.cache['interface_names']: + ## New interface + continue + vpp_iface = self.vpp.cache['interface_names'][ifname] + config_ifname, config_iface = interface.get_by_name(self.cfg, ifname) + if 'mac' in config_iface and config_iface['mac'] != str(vpp_iface.l2_address): + cli="set interface mac address %s %s" % (config_ifname, config_iface['mac']) + self.cli['sync'].append(cli) + return True + def sync_bondethernets(self): for ifname in bondethernet.get_bondethernets(self.cfg): if ifname in self.vpp.cache['interface_names']: - vpp_bond_sw_if_index = self.vpp.cache['interface_names'][ifname].sw_if_index - vpp_members = [self.vpp.cache['interfaces'][x].interface_name for x in self.vpp.cache['bondethernet_members'][vpp_bond_sw_if_index]] + vpp_iface = self.vpp.cache['interface_names'][ifname] + vpp_members = [self.vpp.cache['interfaces'][x].interface_name for x in self.vpp.cache['bondethernet_members'][vpp_iface.sw_if_index]] else: ## New BondEthernet + vpp_iface = None vpp_members = [] config_bond_ifname, config_bond_iface = bondethernet.get_by_name(self.cfg, ifname) @@ -769,7 +805,10 @@ class Reconciler(): bondmac = member_iface.l2_address cli="bond add %s %s" % (config_bond_ifname, member_iface.interface_name) self.cli['sync'].append(cli); - if bondmac and 'lcp' in config_iface: + if vpp_iface and 'mac' in config_iface and str(vpp_iface.l2_address) != config_iface['mac']: + cli="set interface mac address %s %s" % (config_ifname, config_iface['mac']) + self.cli['sync'].append(cli); + elif bondmac and 'lcp' in config_iface: ## TODO(pim) - Ensure LCP has the same MAC as the BondEthernet ## VPP, when creating a BondEthernet, will give it an ephemeral MAC. Then, when the ## first member is enslaved, the MAC address changes to that of the first member. diff --git a/vpp/vppapi.py b/vpp/vppapi.py index 5802b3f..c828a79 100644 --- a/vpp/vppapi.py +++ b/vpp/vppapi.py @@ -272,17 +272,19 @@ class VPPApiDumper(VPPApi): def cache_to_config(self): config = {"loopbacks": {}, "bondethernets": {}, "interfaces": {}, "bridgedomains": {}, "vxlan_tunnels": {} } - for idx, iface in self.cache['bondethernets'].items(): + for idx, bond_iface in self.cache['bondethernets'].items(): bond = {"description": ""} - if iface.sw_if_index in self.cache['bondethernet_members']: - members = [self.cache['interfaces'][x].interface_name for x in self.cache['bondethernet_members'][iface.sw_if_index]] + if bond_iface.sw_if_index in self.cache['bondethernet_members']: + members = [self.cache['interfaces'][x].interface_name for x in self.cache['bondethernet_members'][bond_iface.sw_if_index]] if len(members) > 0: bond['interfaces'] = members - mode = bondethernet.int_to_mode(iface.mode) + mode = bondethernet.int_to_mode(bond_iface.mode) bond['mode'] = mode if mode in ['xor', 'lacp']: - bond['load-balance'] = bondethernet.int_to_lb(iface.lb) + bond['load-balance'] = bondethernet.int_to_lb(bond_iface.lb) + iface = self.cache['interfaces'][bond_iface.sw_if_index] + bond['mac'] = str(iface.l2_address) config['bondethernets'][iface.interface_name] = bond for numtags in [ 0, 1, 2 ]: @@ -296,6 +298,7 @@ class VPPApiDumper(VPPApi): continue loop = {"description": ""} loop['mtu'] = iface.mtu[0] + loop['mac'] = str(iface.l2_address) if iface.sw_if_index in self.cache['lcps']: loop['lcp'] = self.cache['lcps'][iface.sw_if_index].host_if_name if iface.sw_if_index in self.cache['interface_addresses']: @@ -315,6 +318,8 @@ class VPPApiDumper(VPPApi): if not self.cache['interfaces'][idx].flags & 1: # IF_STATUS_API_FLAG_ADMIN_UP i['state'] = 'down' + if iface.interface_dev_type == 'dpdk': + i['mac'] = str(iface.l2_address) i['mtu'] = iface.mtu[0] if iface.sub_number_of_tags == 0: config['interfaces'][iface.interface_name] = i