Merge pull request #475 from coreemu/cleanup/interface-addresses

Cleanup/interface addresses
This commit is contained in:
bharnden 2020-06-19 11:15:26 -07:00 committed by GitHub
commit 3638b05cd1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 328 additions and 363 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.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.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,10 @@ 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 ip4 = iface.get_ip4()
for x in iface.addrlist:
address, prefix = x.split("/")
if netaddr.valid_ipv4(address):
ip4 = address
if ip4: if ip4:
addresses.append(ip4) address = str(ip4.ip)
addresses.append(address)
break break
return addresses return addresses

View file

@ -142,18 +142,18 @@ class InterfaceData:
ip6: str = None ip6: str = None
ip6_mask: int = None ip6_mask: int = None
def get_addresses(self) -> List[str]: def get_ips(self) -> List[str]:
""" """
Returns a list of ip4 and ip6 addresses when present. Returns a list of ip4 and ip6 addresses when present.
:return: list of addresses :return: list of ip addresses
""" """
addresses = [] ips = []
if self.ip4 and self.ip4_mask: if self.ip4 and self.ip4_mask:
addresses.append(f"{self.ip4}/{self.ip4_mask}") ips.append(f"{self.ip4}/{self.ip4_mask}")
if self.ip6 and self.ip6_mask: if self.ip6 and self.ip6_mask:
addresses.append(f"{self.ip6}/{self.ip6_mask}") ips.append(f"{self.ip6}/{self.ip6_mask}")
return addresses return ips
@dataclass @dataclass

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.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

@ -7,7 +7,7 @@ import os
import shutil import shutil
import threading import threading
from threading import RLock from threading import RLock
from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple, Type, Union from typing import TYPE_CHECKING, Dict, List, Optional, Tuple, Type, Union
import netaddr import netaddr
@ -138,6 +138,13 @@ class NodeBase(abc.ABC):
return self.position.get() return self.position.get()
def get_iface(self, iface_id: int) -> CoreInterface: def get_iface(self, iface_id: int) -> CoreInterface:
"""
Retrieve interface based on id.
:param iface_id: id of interface to retrieve
:return: interface
:raises CoreError: when interface does not exist
"""
if iface_id not in self.ifaces: if iface_id not in self.ifaces:
raise CoreError(f"node({self.name}) does not have interface({iface_id})") raise CoreError(f"node({self.name}) does not have interface({iface_id})")
return self.ifaces[iface_id] return self.ifaces[iface_id]
@ -436,7 +443,6 @@ class CoreNode(CoreNodeBase):
""" """
apitype: NodeTypes = NodeTypes.DEFAULT apitype: NodeTypes = NodeTypes.DEFAULT
valid_address_types: Set[str] = {"inet", "inet6", "inet6link"}
def __init__( def __init__(
self, self,
@ -750,40 +756,39 @@ class CoreNode(CoreNodeBase):
if self.up: if self.up:
self.node_net_client.device_mac(iface.name, mac) self.node_net_client.device_mac(iface.name, mac)
def addaddr(self, iface_id: int, addr: str) -> None: def add_ip(self, iface_id: int, ip: str) -> None:
""" """
Add interface address. Add an ip address to an interface in the format "10.0.0.1/24".
:param iface_id: id of interface to add address to :param iface_id: id of interface to add address to
:param addr: address to add to interface :param ip: address to add to interface
:return: nothing
"""
addr = utils.validate_ip(addr)
iface = self.get_iface(iface_id)
iface.addaddr(addr)
if self.up:
# ipv4 check
broadcast = None
if netaddr.valid_ipv4(addr):
broadcast = "+"
self.node_net_client.create_address(iface.name, addr, broadcast)
def deladdr(self, iface_id: int, addr: str) -> None:
"""
Delete address from an interface.
:param iface_id: id of interface to delete address from
:param addr: address to delete from interface
:return: nothing :return: nothing
:raises CoreError: when ip address provided is invalid
:raises CoreCommandError: when a non-zero exit status occurs :raises CoreCommandError: when a non-zero exit status occurs
""" """
iface = self.get_iface(iface_id) iface = self.get_iface(iface_id)
try: iface.add_ip(ip)
iface.deladdr(addr)
except ValueError:
logging.exception("trying to delete unknown address: %s", addr)
if self.up: if self.up:
self.node_net_client.delete_address(iface.name, addr) # ipv4 check
broadcast = None
if netaddr.valid_ipv4(ip):
broadcast = "+"
self.node_net_client.create_address(iface.name, ip, broadcast)
def remove_ip(self, iface_id: int, ip: str) -> None:
"""
Remove an ip address from an interface in the format "10.0.0.1/24".
:param iface_id: id of interface to delete address from
:param ip: ip address to remove from interface
:return: nothing
:raises CoreError: when ip address provided is invalid
:raises CoreCommandError: when a non-zero exit status occurs
"""
iface = self.get_iface(iface_id)
iface.remove_ip(ip)
if self.up:
self.node_net_client.delete_address(iface.name, ip)
def ifup(self, iface_id: int) -> None: def ifup(self, iface_id: int) -> None:
""" """
@ -806,7 +811,7 @@ class CoreNode(CoreNodeBase):
:param iface_data: interface data for new interface :param iface_data: interface data for new interface
:return: interface index :return: interface index
""" """
addresses = iface_data.get_addresses() ips = iface_data.get_ips()
with self.lock: with self.lock:
# TODO: emane specific code # TODO: emane specific code
if net.is_emane is True: if net.is_emane is True:
@ -818,15 +823,15 @@ class CoreNode(CoreNodeBase):
self.attachnet(iface_id, net) self.attachnet(iface_id, net)
iface = self.get_iface(iface_id) iface = self.get_iface(iface_id)
iface.set_mac(iface_data.mac) iface.set_mac(iface_data.mac)
for address in addresses: for ip in ips:
iface.addaddr(address) iface.add_ip(ip)
else: else:
iface_id = self.newveth(iface_data.id, iface_data.name) iface_id = self.newveth(iface_data.id, iface_data.name)
self.attachnet(iface_id, net) self.attachnet(iface_id, net)
if iface_data.mac: if iface_data.mac:
self.set_mac(iface_id, iface_data.mac) self.set_mac(iface_id, iface_data.mac)
for address in addresses: for ip in ips:
self.addaddr(iface_id, address) self.add_ip(iface_id, ip)
self.ifup(iface_id) self.ifup(iface_id)
iface = self.get_iface(iface_id) iface = self.get_iface(iface_id)
return iface return iface
@ -1053,18 +1058,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 +1076,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,24 +134,65 @@ 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 add_ip(self, ip: str) -> None:
""" """
Add address. Add ip address in the format "10.0.0.1/24".
:param addr: address to add :param ip: ip address to add
:return: nothing :return: nothing
:raises CoreError: when ip address provided is invalid
""" """
addr = utils.validate_ip(addr) try:
self.addrlist.append(addr) ip = netaddr.IPNetwork(ip)
address = str(ip.ip)
if netaddr.valid_ipv4(address):
self.ip4s.append(ip)
else:
self.ip6s.append(ip)
except netaddr.AddrFormatError:
raise CoreError(f"adding invalid address {ip}")
def deladdr(self, addr: str) -> None: def remove_ip(self, ip: str) -> None:
""" """
Delete address. Remove ip address in the format "10.0.0.1/24".
:param addr: address to delete :param ip: ip address to delete
:return: nothing :return: nothing
:raises CoreError: when ip address provided is invalid
""" """
self.addrlist.remove(addr) try:
ip = netaddr.IPNetwork(ip)
address = str(ip.ip)
if netaddr.valid_ipv4(address):
self.ip4s.remove(ip)
else:
self.ip6s.remove(ip)
except (netaddr.AddrFormatError, ValueError):
raise CoreError(f"deleting invalid address {ip}")
def get_ip4(self) -> Optional[netaddr.IPNetwork]:
"""
Looks for the first ip4 address.
:return: ip4 address, None otherwise
"""
return next(iter(self.ip4s), None)
def get_ip6(self) -> Optional[netaddr.IPNetwork]:
"""
Looks for the first ip6 address.
:return: ip6 address, None otherwise
"""
return next(iter(self.ip6s), None)
def ips(self) -> List[netaddr.IPNetwork]:
"""
Retrieve a list of all ip4 and ip6 addresses combined.
:return: ip4 and ip6 addresses
"""
return self.ip4s + self.ip6s
def set_mac(self, mac: str) -> None: def set_mac(self, mac: str) -> None:
""" """
@ -487,13 +531,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.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

@ -575,18 +575,17 @@ class CoreNetwork(CoreNetworkBase):
return iface return iface
return None return None
def addrconfig(self, addrlist: List[str]) -> None: def add_ips(self, ips: List[str]) -> None:
""" """
Set addresses on the bridge. Add ip addresses on the bridge in the format "10.0.0.1/24".
:param addrlist: address list :param ips: ip address to add
:return: nothing :return: nothing
""" """
if not self.up: if not self.up:
return return
for ip in ips:
for addr in addrlist: self.net_client.create_address(self.brname, ip)
self.net_client.create_address(self.brname, str(addr))
class GreTapBridge(CoreNetwork): class GreTapBridge(CoreNetwork):
@ -663,22 +662,22 @@ class GreTapBridge(CoreNetwork):
self.gretap = None self.gretap = None
super().shutdown() super().shutdown()
def addrconfig(self, addrlist: List[str]) -> None: def add_ips(self, ips: List[str]) -> None:
""" """
Set the remote tunnel endpoint. This is a one-time method for Set the remote tunnel endpoint. This is a one-time method for
creating the GreTap device, which requires the remoteip at startup. creating the GreTap device, which requires the remoteip at startup.
The 1st address in the provided list is remoteip, 2nd optionally The 1st address in the provided list is remoteip, 2nd optionally
specifies localip. specifies localip.
:param addrlist: address list :param ips: address list
:return: nothing :return: nothing
""" """
if self.gretap: if self.gretap:
raise ValueError(f"gretap already exists for {self.name}") raise ValueError(f"gretap already exists for {self.name}")
remoteip = addrlist[0].split("/")[0] remoteip = ips[0].split("/")[0]
localip = None localip = None
if len(addrlist) > 1: if len(ips) > 1:
localip = addrlist[1].split("/")[0] localip = ips[1].split("/")[0]
self.gretap = GreTap( self.gretap = GreTap(
session=self.session, session=self.session,
remoteip=remoteip, remoteip=remoteip,
@ -698,9 +697,9 @@ class GreTapBridge(CoreNetwork):
:return: nothing :return: nothing
""" """
self.grekey = key self.grekey = key
addresses = iface_data.get_addresses() ips = iface_data.get_ips()
if addresses: if ips:
self.addrconfig(addresses) self.add_ips(ips)
class CtrlNet(CoreNetwork): class CtrlNet(CoreNetwork):
@ -881,28 +880,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

@ -75,43 +75,43 @@ class PhysicalNode(CoreNodeBase):
:raises CoreCommandError: when a non-zero exit status occurs :raises CoreCommandError: when a non-zero exit status occurs
""" """
mac = utils.validate_mac(mac) mac = utils.validate_mac(mac)
iface = self.ifaces[iface_id] iface = self.get_iface(iface_id)
iface.set_mac(mac) iface.set_mac(mac)
if self.up: if self.up:
self.net_client.device_mac(iface.name, mac) self.net_client.device_mac(iface.name, mac)
def addaddr(self, iface_id: int, addr: str) -> None: def add_ip(self, iface_id: int, ip: str) -> None:
""" """
Add an address to an interface. Add an ip address to an interface in the format "10.0.0.1/24".
:param iface_id: index of interface to add address to :param iface_id: id of interface to add address to
:param addr: address to add :param ip: address to add to interface
:return: nothing :return: nothing
:raises CoreError: when ip address provided is invalid
:raises CoreCommandError: when a non-zero exit status occurs
""" """
addr = utils.validate_ip(addr)
iface = self.get_iface(iface_id) iface = self.get_iface(iface_id)
iface.add_ip(ip)
if self.up: if self.up:
self.net_client.create_address(iface.name, addr) self.net_client.create_address(iface.name, ip)
iface.addaddr(addr)
def deladdr(self, iface_id: int, addr: str) -> None: def remove_ip(self, iface_id: int, ip: str) -> None:
""" """
Delete an address from an interface. Remove an ip address from an interface in the format "10.0.0.1/24".
:param iface_id: index of interface to delete :param iface_id: id of interface to delete address from
:param addr: address to delete :param ip: ip address to remove from interface
:return: nothing :return: nothing
:raises CoreError: when ip address provided is invalid
:raises CoreCommandError: when a non-zero exit status occurs
""" """
iface = self.ifaces[iface_id] iface = self.get_iface(iface_id)
try: iface.remove_ip(ip)
iface.deladdr(addr)
except ValueError:
logging.exception("trying to delete unknown address: %s", addr)
if self.up: if self.up:
self.net_client.delete_address(iface.name, addr) self.net_client.delete_address(iface.name, ip)
def adopt_iface( def adopt_iface(
self, iface: CoreInterface, iface_id: int, mac: str, addrlist: List[str] self, iface: CoreInterface, iface_id: int, mac: str, ips: List[str]
) -> None: ) -> None:
""" """
When a link message is received linking this node to another part of When a link message is received linking this node to another part of
@ -128,8 +128,8 @@ class PhysicalNode(CoreNodeBase):
iface.localname = iface.name iface.localname = iface.name
if mac: if mac:
self.set_mac(iface_id, mac) self.set_mac(iface_id, mac)
for addr in addrlist: for ip in ips:
self.addaddr(iface_id, addr) self.add_ip(iface_id, ip)
if self.up: if self.up:
self.net_client.device_up(iface.localname) self.net_client.device_up(iface.localname)
@ -156,7 +156,7 @@ class PhysicalNode(CoreNodeBase):
self, net: CoreNetworkBase, iface_data: InterfaceData self, net: CoreNetworkBase, iface_data: InterfaceData
) -> CoreInterface: ) -> CoreInterface:
logging.info("creating interface") logging.info("creating interface")
addresses = iface_data.get_addresses() ips = iface_data.get_ips()
iface_id = iface_data.id iface_id = iface_data.id
if iface_id is None: if iface_id is None:
iface_id = self.next_iface_id() iface_id = self.next_iface_id()
@ -167,12 +167,12 @@ class PhysicalNode(CoreNodeBase):
# this is reached when this node is linked to a network node # this is reached when this node is linked to a network node
# tunnel to net not built yet, so build it now and adopt it # tunnel to net not built yet, so build it now and adopt it
_, remote_tap = self.session.distributed.create_gre_tunnel(net, self.server) _, remote_tap = self.session.distributed.create_gre_tunnel(net, self.server)
self.adopt_iface(remote_tap, iface_id, iface_data.mac, addresses) self.adopt_iface(remote_tap, iface_id, iface_data.mac, ips)
return remote_tap return remote_tap
else: else:
# this is reached when configuring services (self.up=False) # this is reached when configuring services (self.up=False)
iface = GreTap(node=self, name=name, session=self.session, start=False) iface = GreTap(node=self, name=name, session=self.session, start=False)
self.adopt_iface(iface, iface_id, iface_data.mac, addresses) self.adopt_iface(iface, iface_id, iface_data.mac, ips)
return iface return iface
def privatedir(self, path: str) -> None: def privatedir(self, path: str) -> None:
@ -316,8 +316,8 @@ class Rj45Node(CoreNodeBase):
self.iface_id = iface_id self.iface_id = iface_id
if net is not None: if net is not None:
self.iface.attachnet(net) self.iface.attachnet(net)
for addr in iface_data.get_addresses(): for ip in iface_data.get_ips():
self.addaddr(addr) self.add_ip(ip)
return self.iface return self.iface
def delete_iface(self, iface_id: int) -> None: def delete_iface(self, iface_id: int) -> None:
@ -348,30 +348,31 @@ class Rj45Node(CoreNodeBase):
raise CoreError(f"node({self.name}) does not have interface({iface.name})") raise CoreError(f"node({self.name}) does not have interface({iface.name})")
return self.iface_id return self.iface_id
def addaddr(self, addr: str) -> None: def add_ip(self, ip: str) -> None:
""" """
Add address to to network interface. Add an ip address to an interface in the format "10.0.0.1/24".
:param addr: address to add :param ip: address to add to interface
:return: nothing :return: nothing
:raises CoreCommandError: when there is a command exception :raises CoreError: when ip address provided is invalid
:raises CoreCommandError: when a non-zero exit status occurs
""" """
addr = utils.validate_ip(addr) self.iface.add_ip(ip)
if self.up: if self.up:
self.net_client.create_address(self.name, addr) self.net_client.create_address(self.name, ip)
self.iface.addaddr(addr)
def deladdr(self, addr: str) -> None: def remove_ip(self, ip: str) -> None:
""" """
Delete address from network interface. Remove an ip address from an interface in the format "10.0.0.1/24".
:param addr: address to delete :param ip: ip address to remove from interface
:return: nothing :return: nothing
:raises CoreCommandError: when there is a command exception :raises CoreError: when ip address provided is invalid
:raises CoreCommandError: when a non-zero exit status occurs
""" """
self.iface.remove_ip(ip)
if self.up: if self.up:
self.net_client.delete_address(self.name, addr) self.net_client.delete_address(self.name, ip)
self.iface.deladdr(addr)
def savestate(self) -> None: def savestate(self) -> None:
""" """

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.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.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.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.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.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,7 @@ 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.ips()])
return "<html><body>%s</body></html>" % body return "<html><body>%s</body></html>" % body
@ -625,7 +623,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.ips()))
if len(prefixes) < 1: if len(prefixes) < 1:
continue continue
cfg += ( cfg += (
@ -658,14 +656,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.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.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

@ -75,31 +75,31 @@ class TestNodes:
with pytest.raises(CoreError): with pytest.raises(CoreError):
node.set_mac(iface.node_id, mac) node.set_mac(iface.node_id, mac)
def test_node_addaddr(self, session: Session): def test_node_add_ip(self, session: Session):
# given # given
node = session.add_node(CoreNode) node = session.add_node(CoreNode)
switch = session.add_node(SwitchNode) switch = session.add_node(SwitchNode)
iface_data = InterfaceData() iface_data = InterfaceData()
iface = node.new_iface(switch, iface_data) iface = node.new_iface(switch, iface_data)
addr = "192.168.0.1/24" ip = "192.168.0.1/24"
# when # when
node.addaddr(iface.node_id, addr) node.add_ip(iface.node_id, ip)
# then # then
assert iface.addrlist[0] == addr assert str(iface.get_ip4()) == ip
def test_node_addaddr_exception(self, session): def test_node_add_ip_exception(self, session):
# given # given
node = session.add_node(CoreNode) node = session.add_node(CoreNode)
switch = session.add_node(SwitchNode) switch = session.add_node(SwitchNode)
iface_data = InterfaceData() iface_data = InterfaceData()
iface = node.new_iface(switch, iface_data) iface = node.new_iface(switch, iface_data)
addr = "256.168.0.1/24" ip = "256.168.0.1/24"
# when # when
with pytest.raises(CoreError): with pytest.raises(CoreError):
node.addaddr(iface.node_id, addr) node.add_ip(iface.node_id, ip)
@pytest.mark.parametrize("net_type", NET_TYPES) @pytest.mark.parametrize("net_type", NET_TYPES)
def test_net(self, session, net_type): def test_net(self, session, net_type):