Skip to content

Commit ace97b9

Browse files
committed
Fix: Check validity of all IP addresses assigned to node interfaces
This fix extends the 'host bits' check to all IP addresses assigned to node interfaces and verifies that the interface IP addresses are not multicast addresses. Closes #1779, #1778
1 parent 58c076f commit ace97b9

File tree

1 file changed

+56
-23
lines changed

1 file changed

+56
-23
lines changed

netsim/augment/links.py

+56-23
Original file line numberDiff line numberDiff line change
@@ -518,29 +518,44 @@ def get_nth_ip_from_prefix(pfx: netaddr.IPNetwork, n_id: int) -> str:
518518
check_interface_host_bits: Check whether the IP addresses on an interface have host bits. The check is disabled
519519
for special prefixes (IPv4: /31, /32, IPv6: /127, /128)
520520
"""
521-
def check_interface_host_bits(intf: Box, node: Box) -> bool:
521+
def check_interface_host_bits(intf: Box, node: Box, link: typing.Optional[Box] = None) -> bool:
522522
OK = True
523523

524+
if 'ifname' in intf:
525+
intf_name = f'{intf.ifname}({intf.name})' if 'name' in intf else intf.ifname
526+
elif link is not None:
527+
intf_name = link._linkname
528+
else:
529+
intf_name = ''
530+
524531
for af in log.AF_LIST:
525532
if af not in intf: # Skip unusued AFs
526533
continue
527534
if not isinstance(intf[af],str): # Skip unnumbered interfaces
528535
continue
529536
pfx = netaddr.IPNetwork(intf[af])
537+
if pfx[0].is_multicast():
538+
log.error(
539+
f'Interfaces cannot have multicast {af} addresses ({intf[af]} on {node.name}/{intf_name})',
540+
log.IncorrectValue,
541+
'links')
542+
OK = False
543+
continue
544+
530545
if pfx.last <= pfx.first + 1: # Are we dealing with special prefix (loopback or /31)
531546
continue # ... then it's OK not to have host bits
532547

533548
if str(pfx) == str(pfx.cidr): # Does the IP address have host bits -- is it different from its CIDR subnet?
534549
log.error(
535-
f'Address {intf[af]} on interface {intf.ifname}/node {node.name} does not contain host bits',
550+
f'Address {intf[af]} on node {node.name}/{intf_name} does not contain host bits',
536551
log.IncorrectValue,
537552
'links')
538553
OK = False
539554
continue
540555

541556
if str(pfx[-1]) == str(pfx.ip) and af == 'ipv4':
542557
log.error(
543-
f'Address {intf[af]} on interface {intf.ifname}/node {node.name} is a subnet broadcast address',
558+
f'Address {intf[af]} on node {node.name}/{intf_name} is a subnet broadcast address',
544559
log.IncorrectValue,
545560
'links')
546561
OK = False
@@ -571,17 +586,7 @@ def set_interface_address(intf: Box, af: str, pfx: netaddr.IPNetwork, node_id: i
571586
'links')
572587
return False
573588

574-
if str(intf_pfx) != str(intf_pfx.cidr): # Does the IP address have host bits -- is it different from its CIDR subnet?
575-
return True # That's it -- the user knows what she's doing
576-
577-
if intf_pfx.last <= intf_pfx.first + 1: # Are we dealing with special prefix (loopback or /31)
578-
return True # ... then it's OK not to have host bits
579-
580-
log.error(
581-
f'Address {intf[af]} for node {intf.node} does not contain host bits',
582-
log.IncorrectValue,
583-
'links')
584-
return False
589+
return True # Further checks done in check_interface_host_bits
585590

586591
# No static interface address, or static address specified as relative node_id
587592
try:
@@ -601,7 +606,8 @@ def set_interface_address(intf: Box, af: str, pfx: netaddr.IPNetwork, node_id: i
601606
602607
If the interface address is set, validate that it's a valid address (can't be int)
603608
"""
604-
def IPAM_unnumbered(link: Box, af: str, pfx: typing.Optional[bool], ndict: Box) -> None:
609+
def IPAM_unnumbered(link: Box, af: str, pfx: typing.Optional[bool], ndict: Box) -> bool:
610+
OK = True
605611
for intf in link.interfaces:
606612
if not af in intf: # No static address
607613
if isinstance(pfx,bool): # Set it to link bool value if it exists
@@ -615,29 +621,43 @@ def IPAM_unnumbered(link: Box, af: str, pfx: typing.Optional[bool], ndict: Box)
615621
f'Node {intf.node} is using host index {intf[af]} for {af} on an unnumbered link',
616622
log.IncorrectValue,
617623
'links')
624+
OK = False
625+
626+
return OK
618627

619-
def IPAM_sequential(link: Box, af: str, pfx: netaddr.IPNetwork, ndict: Box) -> None:
628+
def IPAM_sequential(link: Box, af: str, pfx: netaddr.IPNetwork, ndict: Box) -> bool:
620629
start = 1 if pfx.last != pfx.first + 1 else 0
621630
gwid = get_gateway_id(link)
631+
OK = True
622632
for count,intf in enumerate(link.interfaces):
623633
if count + start == gwid: # Would the next address overlap with gateway ID
624634
start = start + 1 # ... no big deal, just move the starting point ;)
625-
set_interface_address(intf,af,pfx,count+start)
635+
OK = set_interface_address(intf,af,pfx,count+start) and OK
636+
637+
return OK
626638

627-
def IPAM_p2p(link: Box, af: str, pfx: netaddr.IPNetwork, ndict: Box) -> None:
639+
def IPAM_p2p(link: Box, af: str, pfx: netaddr.IPNetwork, ndict: Box) -> bool:
628640
start = 1 if pfx.last != pfx.first + 1 else 0
641+
OK = True
629642
for count,intf in enumerate(sorted(link.interfaces, key=lambda intf: intf.node)):
630-
set_interface_address(intf,af,pfx,count+start)
643+
OK = set_interface_address(intf,af,pfx,count+start) and OK
631644

632-
def IPAM_id_based(link: Box, af: str, pfx: netaddr.IPNetwork, ndict: Box) -> None:
645+
return OK
646+
647+
def IPAM_id_based(link: Box, af: str, pfx: netaddr.IPNetwork, ndict: Box) -> bool:
648+
OK = True
633649
for intf in link.interfaces:
634-
set_interface_address(intf,af,pfx,ndict[intf.node].id)
650+
OK = set_interface_address(intf,af,pfx,ndict[intf.node].id) and OK
651+
652+
return OK
635653

636-
def IPAM_loopback(link: Box, af: str, pfx: netaddr.IPNetwork, ndict: Box) -> None:
654+
def IPAM_loopback(link: Box, af: str, pfx: netaddr.IPNetwork, ndict: Box) -> bool:
637655
for intf in link.interfaces:
638656
pfx.prefixlen = 128 if ':' in str(pfx) else 32
639657
intf[af] = str(pfx)
640658

659+
return True
660+
641661
IPAM_dispatch: typing.Final[dict] = {
642662
'unnumbered': IPAM_unnumbered,
643663
'p2p': IPAM_p2p,
@@ -669,6 +689,7 @@ def assign_interface_addresses(link: Box, addr_pools: Box, ndict: Box, defaults:
669689
link.pop('unnumbered')
670690
return
671691

692+
error = False
672693
for af in ('ipv4','ipv6'):
673694
if not af in pfx_list: # Skip address families not used on the link
674695
continue
@@ -686,6 +707,7 @@ def assign_interface_addresses(link: Box, addr_pools: Box, ndict: Box, defaults:
686707
strings.extra_data_printout(f'{link}'),
687708
log.IncorrectValue,
688709
'links')
710+
error = True
689711
continue
690712

691713
if 'allocation' in pfx_list:
@@ -707,14 +729,25 @@ def assign_interface_addresses(link: Box, addr_pools: Box, ndict: Box, defaults:
707729
more_hints=hints,
708730
category=log.IncorrectValue,
709731
module='links')
732+
error = True
710733
continue
711734

712735
if not allocation_policy in IPAM_dispatch:
713736
log.error(
714737
f'Invalid IP address allocation policy specified in prefix {pfx_list} found on {link._linkname}',
715738
log.IncorrectValue,
716739
'links')
717-
IPAM_dispatch[allocation_policy](link,af,pfx_net,ndict) # execute IPAM policy to get AF addresses on interfaces
740+
error = True
741+
742+
# execute IPAM policy to get AF addresses on interfaces
743+
if not IPAM_dispatch[allocation_policy](link,af,pfx_net,ndict):
744+
error = True
745+
746+
if error:
747+
return
748+
749+
for intf in link.interfaces:
750+
check_interface_host_bits(intf,ndict[intf.node],link)
718751

719752
"""
720753
cleanup 'af: False' entries from interfaces

0 commit comments

Comments
 (0)