Add a few MAC checks

Notably mac.is_multicast() which is needed for TAPs / LCPs and
interfaces.

Add a test for interface MAC addresses being non-multicast.
This commit is contained in:
Pim van Pelt
2022-04-09 14:16:27 +00:00
parent 1a00f14b53
commit 92a20d0755
5 changed files with 93 additions and 0 deletions

View File

@ -17,6 +17,8 @@ sudo pip3 install argparse
sudo pip3 install yamale
sudo pip3 install pyyaml
sudo pip3 install pyinstaller
sudo pip3 install netaddr
sudo pip3 install ipaddress
## Ensure all unittests pass.
./tests.py -d -t unittest/yaml/*.yaml

View File

@ -18,6 +18,7 @@ import config.loopback as loopback
import config.vxlan_tunnel as vxlan_tunnel
import config.lcp as lcp
import config.address as address
import config.mac as mac
def get_qinx_parent_by_name(yaml, ifname):
""" Returns the sub-interface which matches a QinAD or QinQ outer tag, or None,None
@ -424,6 +425,10 @@ def validate_interfaces(yaml):
if not 'state' in iface:
iface['state'] = 'up'
if 'mac' in iface and mac.is_multicast(iface['mac']):
msgs.append("interface %s MAC address %s cannot be multicast" % (ifname, iface['mac']))
result = False
iface_mtu = get_mtu(yaml, ifname)
iface_lcp = get_lcp(yaml, ifname)
iface_address = has_address(yaml, ifname)

48
config/mac.py Normal file
View File

@ -0,0 +1,48 @@
#
# Copyright (c) 2022 Pim van Pelt
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import logging
import netaddr
def is_valid(mac):
""" Return True if the string given in `mac` is a valid (6-byte) MAC address,
as defined by netaddr.EUI """
try:
addr = netaddr.EUI(mac)
except:
return False
return True
def is_local(mac):
""" Return True if a MAC address is a valid locally administered one. """
try:
addr = netaddr.EUI(mac)
except:
return False
return bool(addr.words[0] & 0b10)
def is_multicast(mac):
""" Return True if a MAC address is a valid multicast one. """
try:
addr = netaddr.EUI(mac)
except:
return False
return bool(addr.words[0] & 0b01)
def is_unicast(mac):
""" Return True if a MAC address is a valid unicast one. """
try:
addr = netaddr.EUI(mac)
except:
return False
return not bool(addr.words[0] & 0b01)

21
config/test_mac.py Normal file
View File

@ -0,0 +1,21 @@
import unittest
import config.mac as mac
class TestMACMethods(unittest.TestCase):
def test_is_valid(self):
self.assertTrue(mac.is_valid("00:01:02:03:04:05"))
self.assertTrue(mac.is_valid("00-01-02-03-04-05"))
self.assertTrue(mac.is_valid("0001.0203.0405"))
self.assertFalse(mac.is_valid("hoi"))
def test_is_local(self):
self.assertTrue(mac.is_local("02:00:00:00:00:00"))
self.assertFalse(mac.is_local("00:00:00:00:00:00"))
def test_is_multicast(self):
self.assertTrue(mac.is_multicast("01:00:00:00:00:00"))
self.assertFalse(mac.is_multicast("00:00:00:00:00:00"))
def test_is_unicast(self):
self.assertFalse(mac.is_unicast("01:00:00:00:00:00"))
self.assertTrue(mac.is_unicast("00:00:00:00:00:00"))

View File

@ -0,0 +1,17 @@
test:
description: "Interface mac addresses cannot be multicast"
errors:
expected:
- "interface .* MAC address .* cannot be multicast"
count: 1
---
interfaces:
GigabitEthernet3/0/0:
description: "Cool, local MACs are fine"
mac: 02:00:00:00:00:00
GigabitEthernet3/0/1:
description: "Cool, global unicast MACs are fine"
mac: 04:00:00:00:00:00
GigabitEthernet3/0/2:
description: "Not cool, multicast MACs"
mac: 01:00:00:00:00:00