daemon: refactored CoreInterface.addrlist storing strings into CoreInterface.ip4s and ip6s, stored as netaddr.IPNetwork objects

This commit is contained in:
Blake Harnden 2020-06-19 08:50:36 -07:00
parent ca2b1c9e4c
commit d88f3a2535
20 changed files with 209 additions and 262 deletions

View file

@ -3,7 +3,6 @@ import time
from typing import Any, Dict, List, Tuple, Type, Union from typing import Any, Dict, List, Tuple, Type, Union
import grpc import grpc
import netaddr
from grpc import ServicerContext from grpc import ServicerContext
from core import utils from core import utils
@ -447,18 +446,16 @@ def iface_to_proto(iface: CoreInterface) -> core_pb2.Interface:
net_id = iface.net.id net_id = iface.net.id
ip4 = None ip4 = None
ip4_mask = None ip4_mask = None
ip4_net = iface.get_ip4()
if ip4_net:
ip4 = str(ip4_net.ip)
ip4_mask = ip4_net.prefixlen
ip6 = None ip6 = None
ip6_mask = None ip6_mask = None
for addr in iface.addrlist: ip6_net = iface.get_ip6()
network = netaddr.IPNetwork(addr) if ip6_net:
mask = network.prefixlen ip6 = str(ip6_net.ip)
ip = str(network.ip) ip6_mask = ip6_net.prefixlen
if netaddr.valid_ipv4(ip) and not ip4:
ip4 = ip
ip4_mask = mask
elif netaddr.valid_ipv6(ip) and not ip6:
ip6 = ip
ip6_mask = mask
return core_pb2.Interface( return core_pb2.Interface(
id=iface.node_id, id=iface.node_id,
net_id=net_id, net_id=net_id,

View file

@ -1,8 +1,6 @@
import abc import abc
from typing import Any, Dict, List from typing import Any, Dict, List
import netaddr
from core import constants from core import constants
from core.config import Configuration from core.config import Configuration
from core.configservice.base import ConfigService, ConfigServiceMode from core.configservice.base import ConfigService, ConfigServiceMode
@ -49,10 +47,9 @@ def get_router_id(node: CoreNodeBase) -> str:
Helper to return the first IPv4 address of a node as its router ID. Helper to return the first IPv4 address of a node as its router ID.
""" """
for iface in node.get_ifaces(control=False): for iface in node.get_ifaces(control=False):
for a in iface.addrlist: ip4 = iface.get_ip4()
a = a.split("/")[0] if ip4:
if netaddr.valid_ipv4(a): return str(ip4.ip)
return a
return "0.0.0.0" return "0.0.0.0"
@ -102,12 +99,10 @@ class FRRZebra(ConfigService):
for iface in self.node.get_ifaces(): for iface in self.node.get_ifaces():
ip4s = [] ip4s = []
ip6s = [] ip6s = []
for x in iface.addrlist: for ip4 in iface.ip4s:
addr = x.split("/")[0] ip4s.append(str(ip4.ip))
if netaddr.valid_ipv4(addr): for ip6 in iface.ip6s:
ip4s.append(x) ip6s.append(str(ip6.ip))
else:
ip6s.append(x)
is_control = getattr(iface, "control", False) is_control = getattr(iface, "control", False)
ifaces.append((iface, ip4s, ip6s, is_control)) ifaces.append((iface, ip4s, ip6s, is_control))
@ -163,10 +158,8 @@ class FRROspfv2(FrrService, ConfigService):
router_id = get_router_id(self.node) router_id = get_router_id(self.node)
addresses = [] addresses = []
for iface in self.node.get_ifaces(control=False): for iface in self.node.get_ifaces(control=False):
for a in iface.addrlist: for ip4 in iface.ip4s:
addr = a.split("/")[0] addresses.append(str(ip4.ip))
if netaddr.valid_ipv4(addr):
addresses.append(a)
data = dict(router_id=router_id, addresses=addresses) data = dict(router_id=router_id, addresses=addresses)
text = """ text = """
router ospf router ospf

View file

@ -1,7 +1,5 @@
from typing import Any, Dict, List from typing import Any, Dict, List
import netaddr
from core import utils from core import utils
from core.config import Configuration from core.config import Configuration
from core.configservice.base import ConfigService, ConfigServiceMode from core.configservice.base import ConfigService, ConfigServiceMode
@ -75,13 +73,10 @@ class NrlSmf(ConfigService):
ip4_prefix = None ip4_prefix = None
for iface in self.node.get_ifaces(control=False): for iface in self.node.get_ifaces(control=False):
ifnames.append(iface.name) ifnames.append(iface.name)
if ip4_prefix: ip4 = iface.get_ip4()
continue if ip4:
for a in iface.addrlist: ip4_prefix = f"{ip4.ip}/{24}"
a = a.split("/")[0] break
if netaddr.valid_ipv4(a):
ip4_prefix = f"{a}/{24}"
break
return dict( return dict(
has_arouted=has_arouted, has_arouted=has_arouted,
has_nhdp=has_nhdp, has_nhdp=has_nhdp,
@ -191,11 +186,8 @@ class Arouted(ConfigService):
def data(self) -> Dict[str, Any]: def data(self) -> Dict[str, Any]:
ip4_prefix = None ip4_prefix = None
for iface in self.node.get_ifaces(control=False): for iface in self.node.get_ifaces(control=False):
if ip4_prefix: ip4 = iface.get_ip4()
continue if ip4:
for a in iface.addrlist: ip4_prefix = f"{ip4.ip}/{24}"
a = a.split("/")[0] break
if netaddr.valid_ipv4(a):
ip4_prefix = f"{a}/{24}"
break
return dict(ip4_prefix=ip4_prefix) return dict(ip4_prefix=ip4_prefix)

View file

@ -2,8 +2,6 @@ import abc
import logging import logging
from typing import Any, Dict, List from typing import Any, Dict, List
import netaddr
from core import constants from core import constants
from core.config import Configuration from core.config import Configuration
from core.configservice.base import ConfigService, ConfigServiceMode from core.configservice.base import ConfigService, ConfigServiceMode
@ -50,10 +48,9 @@ def get_router_id(node: CoreNodeBase) -> str:
Helper to return the first IPv4 address of a node as its router ID. Helper to return the first IPv4 address of a node as its router ID.
""" """
for iface in node.get_ifaces(control=False): for iface in node.get_ifaces(control=False):
for a in iface.addrlist: ip4 = iface.get_ip4()
a = a.split("/")[0] if ip4:
if netaddr.valid_ipv4(a): return str(ip4.ip)
return a
return "0.0.0.0" return "0.0.0.0"
@ -103,12 +100,10 @@ class Zebra(ConfigService):
for iface in self.node.get_ifaces(): for iface in self.node.get_ifaces():
ip4s = [] ip4s = []
ip6s = [] ip6s = []
for x in iface.addrlist: for ip4 in iface.ip4s:
addr = x.split("/")[0] ip4s.append(str(ip4.ip))
if netaddr.valid_ipv4(addr): for ip6 in iface.ip6s:
ip4s.append(x) ip6s.append(str(ip6.ip))
else:
ip6s.append(x)
is_control = getattr(iface, "control", False) is_control = getattr(iface, "control", False)
ifaces.append((iface, ip4s, ip6s, is_control)) ifaces.append((iface, ip4s, ip6s, is_control))
@ -170,10 +165,8 @@ class Ospfv2(QuaggaService, ConfigService):
router_id = get_router_id(self.node) router_id = get_router_id(self.node)
addresses = [] addresses = []
for iface in self.node.get_ifaces(control=False): for iface in self.node.get_ifaces(control=False):
for a in iface.addrlist: for ip4 in iface.ip4s:
addr = a.split("/")[0] addresses.append(str(ip4.ip))
if netaddr.valid_ipv4(addr):
addresses.append(a)
data = dict(router_id=router_id, addresses=addresses) data = dict(router_id=router_id, addresses=addresses)
text = """ text = """
router ospf router ospf

View file

@ -1,7 +1,5 @@
from typing import Any, Dict, List from typing import Any, Dict, List
import netaddr
from core.config import Configuration from core.config import Configuration
from core.configservice.base import ConfigService, ConfigServiceMode from core.configservice.base import ConfigService, ConfigServiceMode
from core.emulator.enumerations import ConfigDataTypes from core.emulator.enumerations import ConfigDataTypes
@ -79,10 +77,10 @@ class VpnServer(ConfigService):
def data(self) -> Dict[str, Any]: def data(self) -> Dict[str, Any]:
address = None address = None
for iface in self.node.get_ifaces(control=False): for iface in self.node.get_ifaces(control=False):
for x in iface.addrlist: ip4 = iface.get_ip4()
addr = x.split("/")[0] if ip4:
if netaddr.valid_ipv4(addr): address = str(ip4.ip)
address = addr break
return dict(address=address) return dict(address=address)

View file

@ -29,8 +29,8 @@ class DefaultRouteService(ConfigService):
ifaces = self.node.get_ifaces() ifaces = self.node.get_ifaces()
if ifaces: if ifaces:
iface = ifaces[0] iface = ifaces[0]
for x in iface.addrlist: for ip in iface.all_ips():
net = netaddr.IPNetwork(x).cidr net = ip.cidr
if net.size > 1: if net.size > 1:
router = net[1] router = net[1]
routes.append(str(router)) routes.append(str(router))
@ -76,15 +76,14 @@ class StaticRouteService(ConfigService):
def data(self) -> Dict[str, Any]: def data(self) -> Dict[str, Any]:
routes = [] routes = []
for iface in self.node.get_ifaces(control=False): for iface in self.node.get_ifaces(control=False):
for x in iface.addrlist: for ip in iface.all_ips():
addr = x.split("/")[0] address = str(ip.ip)
if netaddr.valid_ipv6(addr): if netaddr.valid_ipv6(address):
dst = "3ffe:4::/64" dst = "3ffe:4::/64"
else: else:
dst = "10.9.8.0/24" dst = "10.9.8.0/24"
net = netaddr.IPNetwork(x) if ip[-2] != ip[1]:
if net[-2] != net[1]: routes.append((dst, ip[1]))
routes.append((dst, net[1]))
return dict(routes=routes) return dict(routes=routes)
@ -149,15 +148,12 @@ class DhcpService(ConfigService):
def data(self) -> Dict[str, Any]: def data(self) -> Dict[str, Any]:
subnets = [] subnets = []
for iface in self.node.get_ifaces(control=False): for iface in self.node.get_ifaces(control=False):
for x in iface.addrlist: for ip4 in iface.ip4s:
addr = x.split("/")[0] # divide the address space in half
if netaddr.valid_ipv4(addr): index = (ip4.size - 2) / 2
net = netaddr.IPNetwork(x) rangelow = ip4[index]
# divide the address space in half rangehigh = ip4[-2]
index = (net.size - 2) / 2 subnets.append((ip4.ip, ip4.netmask, rangelow, rangehigh, str(ip4.ip)))
rangelow = net[index]
rangehigh = net[-2]
subnets.append((net.ip, net.netmask, rangelow, rangehigh, addr))
return dict(subnets=subnets) return dict(subnets=subnets)
@ -238,10 +234,8 @@ class RadvdService(ConfigService):
ifaces = [] ifaces = []
for iface in self.node.get_ifaces(control=False): for iface in self.node.get_ifaces(control=False):
prefixes = [] prefixes = []
for x in iface.addrlist: for ip6 in iface.ip6s:
addr = x.split("/")[0] prefixes.append(str(ip6))
if netaddr.valid_ipv6(addr):
prefixes.append(x)
if not prefixes: if not prefixes:
continue continue
ifaces.append((iface.name, prefixes)) ifaces.append((iface.name, prefixes))

View file

@ -4,7 +4,6 @@ import threading
import time import time
from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple
import netaddr
from lxml import etree from lxml import etree
from core.emulator.data import LinkData from core.emulator.data import LinkData
@ -214,13 +213,12 @@ class EmaneLinkMonitor:
for node in nodes: for node in nodes:
for iface in node.get_ifaces(): for iface in node.get_ifaces():
if isinstance(iface.net, CtrlNet): if isinstance(iface.net, CtrlNet):
ip4 = None address = None
for x in iface.addrlist: ip4 = iface.get_ip4()
address, prefix = x.split("/")
if netaddr.valid_ipv4(address):
ip4 = address
if ip4: if ip4:
addresses.append(ip4) address = str(ip4.ip)
if address:
addresses.append(address)
break break
return addresses return addresses

View file

@ -1548,9 +1548,8 @@ class Session:
entries = [] entries = []
for iface in control_net.get_ifaces(): for iface in control_net.get_ifaces():
name = iface.node.name name = iface.node.name
for address in iface.addrlist: for ip in iface.all_ips():
address = address.split("/")[0] entries.append(f"{ip.ip} {name}")
entries.append(f"{address} {name}")
logging.info("Adding %d /etc/hosts file entries.", len(entries)) logging.info("Adding %d /etc/hosts file entries.", len(entries))
utils.file_munge("/etc/hosts", header, "\n".join(entries) + "\n") utils.file_munge("/etc/hosts", header, "\n".join(entries) + "\n")

View file

@ -1053,18 +1053,17 @@ class CoreNetworkBase(NodeBase):
if uni: if uni:
unidirectional = 1 unidirectional = 1
iface2 = InterfaceData( iface2_data = InterfaceData(
id=linked_node.get_iface_id(iface), name=iface.name, mac=iface.mac id=linked_node.get_iface_id(iface), name=iface.name, mac=iface.mac
) )
for address in iface.addrlist: ip4 = iface.get_ip4()
ip, _sep, mask = address.partition("/") if ip4:
mask = int(mask) iface2_data.ip4 = str(ip4.ip)
if netaddr.valid_ipv4(ip): iface2_data.ip4_mask = ip4.prefixlen
iface2.ip4 = ip ip6 = iface.get_ip6()
iface2.ip4_mask = mask if ip6:
else: iface2_data.ip6 = str(ip6.ip)
iface2.ip6 = ip iface2_data.ip6_mask = ip6.prefixlen
iface2.ip6_mask = mask
options_data = iface.get_link_options(unidirectional) options_data = iface.get_link_options(unidirectional)
link_data = LinkData( link_data = LinkData(
@ -1072,7 +1071,7 @@ class CoreNetworkBase(NodeBase):
type=self.linktype, type=self.linktype,
node1_id=self.id, node1_id=self.id,
node2_id=linked_node.id, node2_id=linked_node.id,
iface2=iface2, iface2=iface2_data,
options=options_data, options=options_data,
) )
all_links.append(link_data) all_links.append(link_data)

View file

@ -6,6 +6,8 @@ import logging
import time import time
from typing import TYPE_CHECKING, Callable, Dict, List, Optional, Tuple from typing import TYPE_CHECKING, Callable, Dict, List, Optional, Tuple
import netaddr
from core import utils from core import utils
from core.emulator.data import LinkOptions from core.emulator.data import LinkOptions
from core.emulator.enumerations import TransportType from core.emulator.enumerations import TransportType
@ -52,7 +54,8 @@ class CoreInterface:
self.net: Optional[CoreNetworkBase] = None self.net: Optional[CoreNetworkBase] = None
self.othernet: Optional[CoreNetworkBase] = None self.othernet: Optional[CoreNetworkBase] = None
self._params: Dict[str, float] = {} self._params: Dict[str, float] = {}
self.addrlist: List[str] = [] self.ip4s: List[netaddr.IPNetwork] = []
self.ip6s: List[netaddr.IPNetwork] = []
self.mac: Optional[str] = None self.mac: Optional[str] = None
# placeholder position hook # placeholder position hook
self.poshook: Callable[[CoreInterface], None] = lambda x: None self.poshook: Callable[[CoreInterface], None] = lambda x: None
@ -131,15 +134,22 @@ class CoreInterface:
if self.net is not None: if self.net is not None:
self.net.detach(self) self.net.detach(self)
def addaddr(self, addr: str) -> None: def addaddr(self, address: str) -> None:
""" """
Add address. Add ip address in the format "10.0.0.1/24".
:param addr: address to add :param address: address to add
:return: nothing :return: nothing
""" """
addr = utils.validate_ip(addr) try:
self.addrlist.append(addr) ip = netaddr.IPNetwork(address)
value = str(ip.ip)
if netaddr.valid_ipv4(value):
self.ip4s.append(ip)
else:
self.ip6s.append(ip)
except netaddr.AddrFormatError:
raise CoreError(f"adding invalid address {address}")
def deladdr(self, addr: str) -> None: def deladdr(self, addr: str) -> None:
""" """
@ -148,7 +158,23 @@ class CoreInterface:
:param addr: address to delete :param addr: address to delete
:return: nothing :return: nothing
""" """
self.addrlist.remove(addr) if netaddr.valid_ipv4(addr):
ip4 = netaddr.IPNetwork(addr)
self.ip4s.remove(ip4)
elif netaddr.valid_ipv6(addr):
ip6 = netaddr.IPNetwork(addr)
self.ip6s.remove(ip6)
else:
raise CoreError(f"deleting invalid address {addr}")
def get_ip4(self) -> Optional[netaddr.IPNetwork]:
return next(iter(self.ip4s), None)
def get_ip6(self) -> Optional[netaddr.IPNetwork]:
return next(iter(self.ip6s), None)
def all_ips(self) -> List[netaddr.IPNetwork]:
return self.ip4s + self.ip6s
def set_mac(self, mac: str) -> None: def set_mac(self, mac: str) -> None:
""" """
@ -487,13 +513,13 @@ class TunTap(CoreInterface):
def setaddrs(self) -> None: def setaddrs(self) -> None:
""" """
Set interface addresses based on self.addrlist. Set interface addresses.
:return: nothing :return: nothing
""" """
self.waitfordevicenode() self.waitfordevicenode()
for addr in self.addrlist: for ip in self.all_ips():
self.node.node_net_client.create_address(self.name, str(addr)) self.node.node_net_client.create_address(self.name, str(ip))
class GreTap(CoreInterface): class GreTap(CoreInterface):

View file

@ -881,28 +881,26 @@ class PtpNet(CoreNetwork):
iface1_data = InterfaceData( iface1_data = InterfaceData(
id=iface1.node.get_iface_id(iface1), name=iface1.name, mac=iface1.mac id=iface1.node.get_iface_id(iface1), name=iface1.name, mac=iface1.mac
) )
for address in iface1.addrlist: ip4 = iface1.get_ip4()
ip, _sep, mask = address.partition("/") if ip4:
mask = int(mask) iface1_data.ip4 = str(ip4.ip)
if netaddr.valid_ipv4(ip): iface1_data.ip4_mask = ip4.prefixlen
iface1.ip4 = ip ip6 = iface1.get_ip6()
iface1.ip4_mask = mask if ip6:
else: iface1_data.ip6 = str(ip6.ip)
iface1.ip6 = ip iface1_data.ip6_mask = ip6.prefixlen
iface1.ip6_mask = mask
iface2_data = InterfaceData( iface2_data = InterfaceData(
id=iface2.node.get_iface_id(iface2), name=iface2.name, mac=iface2.mac id=iface2.node.get_iface_id(iface2), name=iface2.name, mac=iface2.mac
) )
for address in iface2.addrlist: ip4 = iface2.get_ip4()
ip, _sep, mask = address.partition("/") if ip4:
mask = int(mask) iface2_data.ip4 = str(ip4.ip)
if netaddr.valid_ipv4(ip): iface2_data.ip4_mask = ip4.prefixlen
iface2.ip4 = ip ip6 = iface2.get_ip6()
iface2.ip4_mask = mask if ip6:
else: iface2_data.ip6 = str(ip6.ip)
iface2.ip6 = ip iface2_data.ip6_mask = ip6.prefixlen
iface2.ip6_mask = mask
options_data = iface1.get_link_options(unidirectional) options_data = iface1.get_link_options(unidirectional)
link_data = LinkData( link_data = LinkData(

View file

@ -3,8 +3,6 @@ bird.py: defines routing services provided by the BIRD Internet Routing Daemon.
""" """
from typing import Optional, Tuple from typing import Optional, Tuple
import netaddr
from core.nodes.base import CoreNode from core.nodes.base import CoreNode
from core.services.coreservices import CoreService from core.services.coreservices import CoreService
@ -39,10 +37,9 @@ class Bird(CoreService):
Helper to return the first IPv4 address of a node as its router ID. Helper to return the first IPv4 address of a node as its router ID.
""" """
for iface in node.get_ifaces(control=False): for iface in node.get_ifaces(control=False):
for a in iface.addrlist: ip4 = iface.get_ip4()
a = a.split("/")[0] if ip4:
if netaddr.valid_ipv4(a): return str(ip4.ip)
return a
return "0.0.0.0" return "0.0.0.0"
@classmethod @classmethod

View file

@ -67,7 +67,7 @@ class FRRZebra(CoreService):
# include control interfaces in addressing but not routing daemons # include control interfaces in addressing but not routing daemons
if hasattr(iface, "control") and iface.control is True: if hasattr(iface, "control") and iface.control is True:
cfg += " " cfg += " "
cfg += "\n ".join(map(cls.addrstr, iface.addrlist)) cfg += "\n ".join(map(cls.addrstr, iface.all_ips()))
cfg += "\n" cfg += "\n"
continue continue
cfgv4 = "" cfgv4 = ""
@ -87,19 +87,13 @@ class FRRZebra(CoreService):
cfgv4 += iface_config cfgv4 += iface_config
if want_ipv4: if want_ipv4:
ipv4list = filter(
lambda x: netaddr.valid_ipv4(x.split("/")[0]), iface.addrlist
)
cfg += " " cfg += " "
cfg += "\n ".join(map(cls.addrstr, ipv4list)) cfg += "\n ".join(map(cls.addrstr, iface.ip4s))
cfg += "\n" cfg += "\n"
cfg += cfgv4 cfg += cfgv4
if want_ipv6: if want_ipv6:
ipv6list = filter(
lambda x: netaddr.valid_ipv6(x.split("/")[0]), iface.addrlist
)
cfg += " " cfg += " "
cfg += "\n ".join(map(cls.addrstr, ipv6list)) cfg += "\n ".join(map(cls.addrstr, iface.ip6s))
cfg += "\n" cfg += "\n"
cfg += cfgv6 cfg += cfgv6
cfg += "!\n" cfg += "!\n"
@ -111,17 +105,17 @@ class FRRZebra(CoreService):
return cfg return cfg
@staticmethod @staticmethod
def addrstr(x: str) -> str: def addrstr(ip: netaddr.IPNetwork) -> str:
""" """
helper for mapping IP addresses to zebra config statements helper for mapping IP addresses to zebra config statements
""" """
addr = x.split("/")[0] address = str(ip.ip)
if netaddr.valid_ipv4(addr): if netaddr.valid_ipv4(address):
return "ip address %s" % x return "ip address %s" % ip
elif netaddr.valid_ipv6(addr): elif netaddr.valid_ipv6(address):
return "ipv6 address %s" % x return "ipv6 address %s" % ip
else: else:
raise ValueError("invalid address: %s", x) raise ValueError("invalid address: %s", ip)
@classmethod @classmethod
def generate_frr_boot(cls, node: CoreNode) -> str: def generate_frr_boot(cls, node: CoreNode) -> str:
@ -333,10 +327,9 @@ class FrrService(CoreService):
Helper to return the first IPv4 address of a node as its router ID. Helper to return the first IPv4 address of a node as its router ID.
""" """
for iface in node.get_ifaces(control=False): for iface in node.get_ifaces(control=False):
for a in iface.addrlist: ip4 = iface.get_ip4()
a = a.split("/")[0] if ip4:
if netaddr.valid_ipv4(a): return str(ip4.ip)
return a
return "0.0.0.0" return "0.0.0.0"
@staticmethod @staticmethod
@ -413,11 +406,8 @@ class FRROspfv2(FrrService):
cfg += " router-id %s\n" % rtrid cfg += " router-id %s\n" % rtrid
# network 10.0.0.0/24 area 0 # network 10.0.0.0/24 area 0
for iface in node.get_ifaces(control=False): for iface in node.get_ifaces(control=False):
for a in iface.addrlist: for ip4 in iface.ip4s:
addr = a.split("/")[0] cfg += f" network {ip4} area 0\n"
if not netaddr.valid_ipv4(addr):
continue
cfg += " network %s area 0\n" % a
cfg += "!\n" cfg += "!\n"
return cfg return cfg

View file

@ -4,8 +4,6 @@ nrl.py: defines services provided by NRL protolib tools hosted here:
""" """
from typing import Optional, Tuple from typing import Optional, Tuple
import netaddr
from core import utils from core import utils
from core.nodes.base import CoreNode from core.nodes.base import CoreNode
from core.services.coreservices import CoreService from core.services.coreservices import CoreService
@ -32,10 +30,9 @@ class NrlService(CoreService):
interface's prefix length, so e.g. '/32' can turn into '/24'. interface's prefix length, so e.g. '/32' can turn into '/24'.
""" """
for iface in node.get_ifaces(control=False): for iface in node.get_ifaces(control=False):
for a in iface.addrlist: ip4 = iface.get_ip4()
a = a.split("/")[0] if ip4:
if netaddr.valid_ipv4(a): return f"{ip4.ip}/{prefixlen}"
return f"{a}/{prefixlen}"
return "0.0.0.0/%s" % prefixlen return "0.0.0.0/%s" % prefixlen

View file

@ -64,7 +64,7 @@ class Zebra(CoreService):
# include control interfaces in addressing but not routing daemons # include control interfaces in addressing but not routing daemons
if getattr(iface, "control", False): if getattr(iface, "control", False):
cfg += " " cfg += " "
cfg += "\n ".join(map(cls.addrstr, iface.addrlist)) cfg += "\n ".join(map(cls.addrstr, iface.all_ips()))
cfg += "\n" cfg += "\n"
continue continue
cfgv4 = "" cfgv4 = ""
@ -86,19 +86,13 @@ class Zebra(CoreService):
cfgv4 += iface_config cfgv4 += iface_config
if want_ipv4: if want_ipv4:
ipv4list = filter(
lambda x: netaddr.valid_ipv4(x.split("/")[0]), iface.addrlist
)
cfg += " " cfg += " "
cfg += "\n ".join(map(cls.addrstr, ipv4list)) cfg += "\n ".join(map(cls.addrstr, iface.ip4s))
cfg += "\n" cfg += "\n"
cfg += cfgv4 cfg += cfgv4
if want_ipv6: if want_ipv6:
ipv6list = filter(
lambda x: netaddr.valid_ipv6(x.split("/")[0]), iface.addrlist
)
cfg += " " cfg += " "
cfg += "\n ".join(map(cls.addrstr, ipv6list)) cfg += "\n ".join(map(cls.addrstr, iface.ip6s))
cfg += "\n" cfg += "\n"
cfg += cfgv6 cfg += cfgv6
cfg += "!\n" cfg += "!\n"
@ -112,17 +106,17 @@ class Zebra(CoreService):
return cfg return cfg
@staticmethod @staticmethod
def addrstr(x: str) -> str: def addrstr(ip: netaddr.IPNetwork) -> str:
""" """
helper for mapping IP addresses to zebra config statements helper for mapping IP addresses to zebra config statements
""" """
addr = x.split("/")[0] address = str(ip.ip)
if netaddr.valid_ipv4(addr): if netaddr.valid_ipv4(address):
return "ip address %s" % x return "ip address %s" % ip
elif netaddr.valid_ipv6(addr): elif netaddr.valid_ipv6(address):
return "ipv6 address %s" % x return "ipv6 address %s" % ip
else: else:
raise ValueError("invalid address: %s", x) raise ValueError("invalid address: %s", ip)
@classmethod @classmethod
def generate_quagga_boot(cls, node: CoreNode) -> str: def generate_quagga_boot(cls, node: CoreNode) -> str:
@ -255,10 +249,9 @@ class QuaggaService(CoreService):
Helper to return the first IPv4 address of a node as its router ID. Helper to return the first IPv4 address of a node as its router ID.
""" """
for iface in node.get_ifaces(control=False): for iface in node.get_ifaces(control=False):
for a in iface.addrlist: ip4 = iface.get_ip4()
a = a.split("/")[0] if ip4:
if netaddr.valid_ipv4(a): return str(ip4.ip)
return a
return f"0.0.0.{node.id:d}" return f"0.0.0.{node.id:d}"
@staticmethod @staticmethod
@ -335,10 +328,8 @@ class Ospfv2(QuaggaService):
cfg += " router-id %s\n" % rtrid cfg += " router-id %s\n" % rtrid
# network 10.0.0.0/24 area 0 # network 10.0.0.0/24 area 0
for iface in node.get_ifaces(control=False): for iface in node.get_ifaces(control=False):
for a in iface.addrlist: for ip4 in iface.ip4s:
addr = a.split("/")[0] cfg += f" network {ip4} area 0\n"
if netaddr.valid_ipv4(addr):
cfg += " network %s area 0\n" % a
cfg += "!\n" cfg += "!\n"
return cfg return cfg

View file

@ -5,8 +5,6 @@ sdn.py defines services to start Open vSwitch and the Ryu SDN Controller.
import re import re
from typing import Tuple from typing import Tuple
import netaddr
from core.nodes.base import CoreNode from core.nodes.base import CoreNode
from core.services.coreservices import CoreService from core.services.coreservices import CoreService
@ -65,18 +63,14 @@ class OvsService(SdnService):
# remove ip address of eths because quagga/zebra will assign same IPs to rtr interfaces # remove ip address of eths because quagga/zebra will assign same IPs to rtr interfaces
# or assign them manually to rtr interfaces if zebra is not running # or assign them manually to rtr interfaces if zebra is not running
for addr in iface.addrlist: for ip4 in iface.ip4s:
addr = addr.split("/")[0] cfg += "ip addr del %s dev %s\n" % (ip4.ip, iface.name)
if netaddr.valid_ipv4(addr): if has_zebra == 0:
cfg += "ip addr del %s dev %s\n" % (addr, iface.name) cfg += "ip addr add %s dev rtr%s\n" % (ip4.ip, ifnum)
if has_zebra == 0: for ip6 in iface.ip6s:
cfg += "ip addr add %s dev rtr%s\n" % (addr, ifnum) cfg += "ip -6 addr del %s dev %s\n" % (ip6.ip, iface.name)
elif netaddr.valid_ipv6(addr): if has_zebra == 0:
cfg += "ip -6 addr del %s dev %s\n" % (addr, iface.name) cfg += "ip -6 addr add %s dev rtr%s\n" % (ip6.ip, ifnum)
if has_zebra == 0:
cfg += "ip -6 addr add %s dev rtr%s\n" % (addr, ifnum)
else:
raise ValueError("invalid address: %s" % addr)
# add interfaces to bridge # add interfaces to bridge
# Make port numbers explicit so they're easier to follow in reading the script # Make port numbers explicit so they're easier to follow in reading the script

View file

@ -74,8 +74,8 @@ class DefaultRouteService(UtilService):
ifaces = node.get_ifaces() ifaces = node.get_ifaces()
if ifaces: if ifaces:
iface = ifaces[0] iface = ifaces[0]
for x in iface.addrlist: for ip in iface.all_ips():
net = netaddr.IPNetwork(x).cidr net = ip.cidr
if net.size > 1: if net.size > 1:
router = net[1] router = net[1]
routes.append(str(router)) routes.append(str(router))
@ -118,23 +118,22 @@ class StaticRouteService(UtilService):
cfg += "# NOTE: this service must be customized to be of any use\n" cfg += "# NOTE: this service must be customized to be of any use\n"
cfg += "# Below are samples that you can uncomment and edit.\n#\n" cfg += "# Below are samples that you can uncomment and edit.\n#\n"
for iface in node.get_ifaces(control=False): for iface in node.get_ifaces(control=False):
cfg += "\n".join(map(cls.routestr, iface.addrlist)) cfg += "\n".join(map(cls.routestr, iface.all_ips()))
cfg += "\n" cfg += "\n"
return cfg return cfg
@staticmethod @staticmethod
def routestr(x: str) -> str: def routestr(ip: netaddr.IPNetwork) -> str:
addr = x.split("/")[0] address = str(ip.ip)
if netaddr.valid_ipv6(addr): if netaddr.valid_ipv6(address):
dst = "3ffe:4::/64" dst = "3ffe:4::/64"
else: else:
dst = "10.9.8.0/24" dst = "10.9.8.0/24"
net = netaddr.IPNetwork(x) if ip[-2] == ip[1]:
if net[-2] == net[1]:
return "" return ""
else: else:
rtcmd = "#/sbin/ip route add %s via" % dst rtcmd = "#/sbin/ip route add %s via" % dst
return "%s %s" % (rtcmd, net[1]) return "%s %s" % (rtcmd, ip[1])
class SshService(UtilService): class SshService(UtilService):
@ -242,25 +241,24 @@ max-lease-time 7200;
ddns-update-style none; ddns-update-style none;
""" """
for iface in node.get_ifaces(control=False): for iface in node.get_ifaces(control=False):
cfg += "\n".join(map(cls.subnetentry, iface.addrlist)) cfg += "\n".join(map(cls.subnetentry, iface.all_ips()))
cfg += "\n" cfg += "\n"
return cfg return cfg
@staticmethod @staticmethod
def subnetentry(x: str) -> str: def subnetentry(ip: netaddr.IPNetwork) -> str:
""" """
Generate a subnet declaration block given an IPv4 prefix string Generate a subnet declaration block given an IPv4 prefix string
for inclusion in the dhcpd3 config file. for inclusion in the dhcpd3 config file.
""" """
addr = x.split("/")[0] address = str(ip.ip)
if netaddr.valid_ipv6(addr): if netaddr.valid_ipv6(address):
return "" return ""
else: else:
net = netaddr.IPNetwork(x)
# divide the address space in half # divide the address space in half
index = (net.size - 2) / 2 index = (ip.size - 2) / 2
rangelow = net[index] rangelow = ip[index]
rangehigh = net[-2] rangehigh = ip[-2]
return """ return """
subnet %s netmask %s { subnet %s netmask %s {
pool { pool {
@ -270,11 +268,11 @@ subnet %s netmask %s {
} }
} }
""" % ( """ % (
net.ip, ip.ip,
net.netmask, ip.netmask,
rangelow, rangelow,
rangehigh, rangehigh,
addr, address,
) )
@ -557,7 +555,10 @@ export LANG
% node.name % node.name
) )
for iface in node.get_ifaces(control=False): for iface in node.get_ifaces(control=False):
body += "<li>%s - %s</li>\n" % (iface.name, iface.addrlist) body += "<li>%s - %s</li>\n" % (
iface.name,
[str(x) for x in iface.all_ips()],
)
return "<html><body>%s</body></html>" % body return "<html><body>%s</body></html>" % body
@ -625,7 +626,7 @@ class RadvdService(UtilService):
""" """
cfg = "# auto-generated by RADVD service (utility.py)\n" cfg = "# auto-generated by RADVD service (utility.py)\n"
for iface in node.get_ifaces(control=False): for iface in node.get_ifaces(control=False):
prefixes = list(map(cls.subnetentry, iface.addrlist)) prefixes = list(map(cls.subnetentry, iface.all_ips()))
if len(prefixes) < 1: if len(prefixes) < 1:
continue continue
cfg += ( cfg += (
@ -658,14 +659,14 @@ interface %s
return cfg return cfg
@staticmethod @staticmethod
def subnetentry(x: str) -> str: def subnetentry(ip: netaddr.IPNetwork) -> str:
""" """
Generate a subnet declaration block given an IPv6 prefix string Generate a subnet declaration block given an IPv6 prefix string
for inclusion in the RADVD config file. for inclusion in the RADVD config file.
""" """
addr = x.split("/")[0] address = str(ip.ip)
if netaddr.valid_ipv6(addr): if netaddr.valid_ipv6(address):
return x return str(ip)
else: else:
return "" return ""

View file

@ -40,7 +40,7 @@ class XorpRtrmgr(CoreService):
for iface in node.get_ifaces(): for iface in node.get_ifaces():
cfg += " interface %s {\n" % iface.name cfg += " interface %s {\n" % iface.name
cfg += "\tvif %s {\n" % iface.name cfg += "\tvif %s {\n" % iface.name
cfg += "".join(map(cls.addrstr, iface.addrlist)) cfg += "".join(map(cls.addrstr, iface.all_ips()))
cfg += cls.lladdrstr(iface) cfg += cls.lladdrstr(iface)
cfg += "\t}\n" cfg += "\t}\n"
cfg += " }\n" cfg += " }\n"
@ -55,13 +55,12 @@ class XorpRtrmgr(CoreService):
return cfg return cfg
@staticmethod @staticmethod
def addrstr(x: str) -> str: def addrstr(ip: netaddr.IPNetwork) -> str:
""" """
helper for mapping IP addresses to XORP config statements helper for mapping IP addresses to XORP config statements
""" """
addr, plen = x.split("/") cfg = "\t address %s {\n" % ip.ip
cfg = "\t address %s {\n" % addr cfg += "\t\tprefix-length: %s\n" % ip.prefixlen
cfg += "\t\tprefix-length: %s\n" % plen
cfg += "\t }\n" cfg += "\t }\n"
return cfg return cfg
@ -145,10 +144,9 @@ class XorpService(CoreService):
Helper to return the first IPv4 address of a node as its router ID. Helper to return the first IPv4 address of a node as its router ID.
""" """
for iface in node.get_ifaces(control=False): for iface in node.get_ifaces(control=False):
for a in iface.addrlist: ip4 = iface.get_ip4()
a = a.split("/")[0] if ip4:
if netaddr.valid_ipv4(a): return str(ip4.ip)
return a
return "0.0.0.0" return "0.0.0.0"
@classmethod @classmethod
@ -180,11 +178,8 @@ class XorpOspfv2(XorpService):
for iface in node.get_ifaces(control=False): for iface in node.get_ifaces(control=False):
cfg += "\t interface %s {\n" % iface.name cfg += "\t interface %s {\n" % iface.name
cfg += "\t\tvif %s {\n" % iface.name cfg += "\t\tvif %s {\n" % iface.name
for a in iface.addrlist: for ip4 in iface.ip4s:
addr = a.split("/")[0] cfg += "\t\t address %s {\n" % ip4.ip
if not netaddr.valid_ipv4(addr):
continue
cfg += "\t\t address %s {\n" % addr
cfg += "\t\t }\n" cfg += "\t\t }\n"
cfg += "\t\t}\n" cfg += "\t\t}\n"
cfg += "\t }\n" cfg += "\t }\n"
@ -269,11 +264,8 @@ class XorpRip(XorpService):
for iface in node.get_ifaces(control=False): for iface in node.get_ifaces(control=False):
cfg += "\tinterface %s {\n" % iface.name cfg += "\tinterface %s {\n" % iface.name
cfg += "\t vif %s {\n" % iface.name cfg += "\t vif %s {\n" % iface.name
for a in iface.addrlist: for ip4 in iface.ip4s:
addr = a.split("/")[0] cfg += "\t\taddress %s {\n" % ip4.ip
if not netaddr.valid_ipv4(addr):
continue
cfg += "\t\taddress %s {\n" % addr
cfg += "\t\t disable: false\n" cfg += "\t\t disable: false\n"
cfg += "\t\t}\n" cfg += "\t\t}\n"
cfg += "\t }\n" cfg += "\t }\n"
@ -435,11 +427,8 @@ class XorpOlsr(XorpService):
for iface in node.get_ifaces(control=False): for iface in node.get_ifaces(control=False):
cfg += "\tinterface %s {\n" % iface.name cfg += "\tinterface %s {\n" % iface.name
cfg += "\t vif %s {\n" % iface.name cfg += "\t vif %s {\n" % iface.name
for a in iface.addrlist: for ip4 in iface.ip4s:
addr = a.split("/")[0] cfg += "\t\taddress %s {\n" % ip4.ip
if not netaddr.valid_ipv4(addr):
continue
cfg += "\t\taddress %s {\n" % addr
cfg += "\t\t}\n" cfg += "\t\t}\n"
cfg += "\t }\n" cfg += "\t }\n"
cfg += "\t}\n" cfg += "\t}\n"

View file

@ -164,6 +164,7 @@ class CoreXmlDeployment:
if emane_element is not None: if emane_element is not None:
parent_element = emane_element parent_element = emane_element
for address in iface.addrlist: for ip in iface.all_ips():
address = str(ip.ip)
address_type = get_address_type(address) address_type = get_address_type(address)
add_address(parent_element, address_type, address, iface.name) add_address(parent_element, address_type, address, iface.name)

View file

@ -87,7 +87,7 @@ class TestNodes:
node.addaddr(iface.node_id, addr) node.addaddr(iface.node_id, addr)
# then # then
assert iface.addrlist[0] == addr assert str(iface.get_ip4()) == addr
def test_node_addaddr_exception(self, session): def test_node_addaddr_exception(self, session):
# given # given