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 yamale
sudo pip3 install pyyaml sudo pip3 install pyyaml
sudo pip3 install pyinstaller sudo pip3 install pyinstaller
sudo pip3 install netaddr
sudo pip3 install ipaddress
## Ensure all unittests pass. ## Ensure all unittests pass.
./tests.py -d -t unittest/yaml/*.yaml ./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.vxlan_tunnel as vxlan_tunnel
import config.lcp as lcp import config.lcp as lcp
import config.address as address import config.address as address
import config.mac as mac
def get_qinx_parent_by_name(yaml, ifname): def get_qinx_parent_by_name(yaml, ifname):
""" Returns the sub-interface which matches a QinAD or QinQ outer tag, or None,None """ 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: if not 'state' in iface:
iface['state'] = 'up' 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_mtu = get_mtu(yaml, ifname)
iface_lcp = get_lcp(yaml, ifname) iface_lcp = get_lcp(yaml, ifname)
iface_address = has_address(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