diff --git a/examples/scion/S02-scion-bgp-mixed/scion-bgp-mixed.py b/examples/scion/S02-scion-bgp-mixed/scion-bgp-mixed.py index 7e47a1aec..a1fe99ae7 100755 --- a/examples/scion/S02-scion-bgp-mixed/scion-bgp-mixed.py +++ b/examples/scion/S02-scion-bgp-mixed/scion-bgp-mixed.py @@ -34,6 +34,8 @@ as150.createNetwork('net1') as150.createNetwork('net2') as150.createNetwork('net3') +as150.setBeaconingIntervals('5s', '5s', '5s') +as150.setBeaconPolicy('core_registration', {'Filter': {'AllowIsdLoop': False}}) as150.createControlService('cs1').joinNetwork('net0') as150.createControlService('cs2').joinNetwork('net2') as150_br0 = as150.createRouter('br0') diff --git a/examples/scion/S05-scion-internet/.gitignore b/examples/scion/S05-scion-internet/.gitignore new file mode 100644 index 000000000..8da335fe7 --- /dev/null +++ b/examples/scion/S05-scion-internet/.gitignore @@ -0,0 +1,2 @@ +bgp/ +scion/ diff --git a/examples/scion/S05-scion-internet/README.md b/examples/scion/S05-scion-internet/README.md new file mode 100644 index 000000000..53b7bb692 --- /dev/null +++ b/examples/scion/S05-scion-internet/README.md @@ -0,0 +1,62 @@ +SCION Internet Topology +======================= +Two topology examples showing a possible transition from a BGP-based to a +SCION-based Internet. + +BGP Topology +------------ +A "flattened" Internet topology with Tier-1 ISPs providing transit and three +IXes providing shortcuts between the other ASes including access networks and a +large content provider. + +![BGP Internet Topology](./images/bgp_topology.png) + +Lines with arrowheads indicate paid transit connections, dashed lines are used +for settlement-free peering. The three IXes are not ASes themselves, the are +transparent on the network layer. All IX participants are connected to a route +server in each IX enabling full multilateral peering. + +### Running the Topology + +```bash +./bgp-internet.py +cd bgp +docker-compose build +docker-compose up -d +``` + +SCION Topology +-------------- +A relatively large pure SCION Internet topology created by promoting IXPs in the +BGP topology to ASes and exposing the internal structure of BGP ASes by +partitioning them into many SCION ASes. The topology is roughly divided +in two large Internet regions that are represented by ISD 1 and ISD 2. +Additional ISDs exist for large multi-region ASes that can be expected to run +their own ISD. All ASes contain a single internal network and a single control +service and border router. The following graph gives an overview of the AS-level +topology: + +![SCION Internet Topology](./images/scion_topology.png) + +SCION core ASes have double outlines, the other ASes are non-core ASes. Thick +lines represent core links, links with arrowheads are transit links. There are +no peering links. The topology consists of Tier-1 ASes, IXPs, a large content +provider and access networks of different sizes. + +### Running the Topology + +```bash +./scion-internet.py +cd scion +docker-compose build +docker compose up -d # use docker compose v2 (no hyphen) +``` + +### Implementation Notes + +- Everything is in ISD 1 until AS multi-homing is implemented. +- IX networks are used for internal links within IXPs and large ASes that + consists of many SCION ASes to limit the number of Docker networks that have + to be created. +- Cross-connections are mainly used for core and transit links between the large + ASes. diff --git a/examples/scion/S05-scion-internet/bgp-internet.py b/examples/scion/S05-scion-internet/bgp-internet.py new file mode 100755 index 000000000..9f582634f --- /dev/null +++ b/examples/scion/S05-scion-internet/bgp-internet.py @@ -0,0 +1,176 @@ +#!/usr/bin/env python3 + +from ipaddress import IPv4Network +from seedemu.compiler import Docker +from seedemu.core import Emulator +from seedemu.layers import ScionBase, ScionRouting, ScionIsd, Scion, Ebgp +from seedemu.layers import PeerRelationship as PeerRel +from seedemu.layers.Scion import LinkType as ScLinkType + + +def create_as(isd, asn, is_core=False, issuer=None): + as_ = base.createAutonomousSystem(asn) + scion_isd.addIsdAs(isd, asn, is_core) + if not is_core: + scion_isd.setCertIssuer((isd, asn), issuer) + as_.createNetwork('net0') + as_.createControlService('cs1').joinNetwork('net0') + br = as_.createRouter('br0') + br.joinNetwork('net0') + return as_, br + + +class CrossConnectNetAssigner: + def __init__(self): + self.subnet_iter = IPv4Network("10.3.0.0/16").subnets(new_prefix=29) + self.xc_nets = {} + + def next_addr(self, net): + if net not in self.xc_nets: + hosts = next(self.subnet_iter).hosts() + next(hosts) # Skip first IP (reserved for Docker) + self.xc_nets[net] = hosts + return "{}/29".format(next(self.xc_nets[net])) + +xc_nets = CrossConnectNetAssigner() + + +# Initialize +emu = Emulator() +base = ScionBase() +routing = ScionRouting() +scion_isd = ScionIsd() +scion = Scion() +ebgp = Ebgp() + +# SCION ISDs +base.createIsolationDomain(1) + +# IXes +base.createInternetExchange(10) # Large IXP (east) +base.createInternetExchange(11) # Large IXP (west) +base.createInternetExchange(12) # Small IXP + +# ASes +_, br = create_as(1, 50, is_core=True) # Tier-1 ISP +br.crossConnect(60, 'br0', xc_nets.next_addr('50-60')) +br.crossConnect(70, 'br0', xc_nets.next_addr('50-70')) +br.crossConnect(150, 'br0', xc_nets.next_addr('50-150')) +br.crossConnect(151, 'br0', xc_nets.next_addr('50-151')) +br.crossConnect(180, 'br0', xc_nets.next_addr('50-180')) +br.crossConnect(200, 'br0', xc_nets.next_addr('50-200')) +_, br = create_as(1, 60, is_core=True) # Tier-1 ISP +br.crossConnect(50, 'br0', xc_nets.next_addr('50-60')) +br.crossConnect(70, 'br0', xc_nets.next_addr('60-70')) +br.crossConnect(152, 'br0', xc_nets.next_addr('60-152')) +br.crossConnect(180, 'br0', xc_nets.next_addr('60-180')) +_, br = create_as(1, 70, is_core=True) # Tier-1 ISP +br.crossConnect(50, 'br0', xc_nets.next_addr('50-70')) +br.crossConnect(60, 'br0', xc_nets.next_addr('60-70')) +br.crossConnect(160, 'br0', xc_nets.next_addr('70-160')) +br.crossConnect(161, 'br0', xc_nets.next_addr('70-161')) +br.crossConnect(162, 'br0', xc_nets.next_addr('70-162')) +br.crossConnect(163, 'br0', xc_nets.next_addr('70-163')) +br.crossConnect(170, 'br0', xc_nets.next_addr('70-170')) +br.crossConnect(200, 'br0', xc_nets.next_addr('70-200')) +_, br = create_as(1, 170, issuer=70) # Access Network +br.joinNetwork('ix11').joinNetwork('ix12') +br.crossConnect(70, 'br0', xc_nets.next_addr('70-170')) +_, br = create_as(1, 180, issuer=50) # Large Access Network +br.joinNetwork('ix10') +br.crossConnect(50, 'br0', xc_nets.next_addr('50-180')) +br.crossConnect(60, 'br0', xc_nets.next_addr('60-180')) +_, br = create_as(1, 200, issuer=50) # Content Provider +br.joinNetwork('ix10').joinNetwork('ix11').joinNetwork('ix12') +br.crossConnect(50, 'br0', xc_nets.next_addr('50-200')) +br.crossConnect(70, 'br0', xc_nets.next_addr('70-200')) +br.crossConnect(150, 'br0', xc_nets.next_addr('150-200')) +br.crossConnect(163, 'br0', xc_nets.next_addr('163-200')) +_, br = create_as(1, 150, issuer=50) +br.joinNetwork('ix10') +br.crossConnect(50, 'br0', xc_nets.next_addr('50-150')) +br.crossConnect(200, 'br0', xc_nets.next_addr('150-200')) +_, br = create_as(1, 151, issuer=50) +br.joinNetwork('ix10') +br.crossConnect(50, 'br0', xc_nets.next_addr('50-151')) +_, br = create_as(1, 152, issuer=60) +br.joinNetwork('ix10') +br.crossConnect(60, 'br0', xc_nets.next_addr('60-152')) +_, br = create_as(1, 160, issuer=70) +br.joinNetwork('ix11').joinNetwork('ix12') +br.crossConnect(70, 'br0', xc_nets.next_addr('70-160')) +_, br = create_as(1, 161, issuer=70) +br.joinNetwork('ix12') +br.crossConnect(70, 'br0', xc_nets.next_addr('70-161')) +_, br = create_as(1, 162, issuer=70) +br.joinNetwork('ix12') +br.crossConnect(70, 'br0', xc_nets.next_addr('70-162')) +_, br = create_as(1, 163, issuer=70) +br.joinNetwork('ix11').joinNetwork('ix12') +br.crossConnect(70, 'br0', xc_nets.next_addr('70-163')) +br.crossConnect(200, 'br0', xc_nets.next_addr('163-200')) + +# BGP +ebgp.addCrossConnectPeering(70, 50, PeerRel.Peer) +ebgp.addCrossConnectPeering(50, 60, PeerRel.Peer) +ebgp.addCrossConnectPeering(60, 70, PeerRel.Peer) +ebgp.addCrossConnectPeering(70, 170, PeerRel.Provider) +ebgp.addCrossConnectPeering(70, 160, PeerRel.Provider) +ebgp.addCrossConnectPeering(70, 161, PeerRel.Provider) +ebgp.addCrossConnectPeering(70, 162, PeerRel.Provider) +ebgp.addCrossConnectPeering(70, 163, PeerRel.Provider) +ebgp.addCrossConnectPeering(70, 200, PeerRel.Provider) +ebgp.addCrossConnectPeering(50, 200, PeerRel.Provider) +ebgp.addCrossConnectPeering(50, 150, PeerRel.Provider) +ebgp.addCrossConnectPeering(50, 151, PeerRel.Provider) +ebgp.addCrossConnectPeering(50, 180, PeerRel.Provider) +ebgp.addCrossConnectPeering(60, 180, PeerRel.Provider) +ebgp.addCrossConnectPeering(60, 152, PeerRel.Provider) +ebgp.addCrossConnectPeering(200, 163, PeerRel.Peer) +ebgp.addCrossConnectPeering(200, 150, PeerRel.Peer) +peers_ix12 = [160, 161, 162, 163, 170, 200] +for peer in peers_ix12: + ebgp.addRsPeer(12, peer) +peers_ix11 = [160, 163, 170, 200] +for peer in peers_ix11: + ebgp.addRsPeer(11, peer) +peers_ix10 = [150, 151, 152, 180, 200] +for peer in peers_ix10: + ebgp.addRsPeer(10, peer) + +# SCION links +scion.addXcLink((1, 70), (1, 50), ScLinkType.Core) +scion.addXcLink((1, 50), (1, 60), ScLinkType.Core) +scion.addXcLink((1, 60), (1, 70), ScLinkType.Core) +scion.addXcLink((1, 70), (1, 170), ScLinkType.Transit) +scion.addXcLink((1, 70), (1, 160), ScLinkType.Transit) +scion.addXcLink((1, 70), (1, 161), ScLinkType.Transit) +scion.addXcLink((1, 70), (1, 162), ScLinkType.Transit) +scion.addXcLink((1, 70), (1, 163), ScLinkType.Transit) +scion.addXcLink((1, 70), (1, 200), ScLinkType.Transit) +scion.addXcLink((1, 50), (1, 200), ScLinkType.Transit) +scion.addXcLink((1, 50), (1, 150), ScLinkType.Transit) +scion.addXcLink((1, 50), (1, 151), ScLinkType.Transit) +scion.addXcLink((1, 50), (1, 180), ScLinkType.Transit) +scion.addXcLink((1, 60), (1, 180), ScLinkType.Transit) +scion.addXcLink((1, 60), (1, 152), ScLinkType.Transit) +scion.addXcLink((1, 200), (1, 163), ScLinkType.Peer) +scion.addXcLink((1, 200), (1, 150), ScLinkType.Peer) +for a, b in [(a, b) for a in peers_ix12 for b in peers_ix12 if a < b]: + scion.addIxLink(12, (1, a), (1, b), ScLinkType.Peer) +for a, b in [(a, b) for a in peers_ix11 for b in peers_ix11 if a < b]: + scion.addIxLink(11, (1, a), (1, b), ScLinkType.Peer) +for a, b in [(a, b) for a in peers_ix10 for b in peers_ix10 if a < b]: + scion.addIxLink(10, (1, a), (1, b), ScLinkType.Peer) + +# Rendering +emu.addLayer(base) +emu.addLayer(routing) +emu.addLayer(scion_isd) +emu.addLayer(scion) +emu.addLayer(ebgp) + +emu.render() + +# Compilation +emu.compile(Docker(), './bgp') diff --git a/examples/scion/S05-scion-internet/images/bgp_topology.png b/examples/scion/S05-scion-internet/images/bgp_topology.png new file mode 100644 index 000000000..0b2ba9758 Binary files /dev/null and b/examples/scion/S05-scion-internet/images/bgp_topology.png differ diff --git a/examples/scion/S05-scion-internet/images/scion_topology.png b/examples/scion/S05-scion-internet/images/scion_topology.png new file mode 100644 index 000000000..a561632df Binary files /dev/null and b/examples/scion/S05-scion-internet/images/scion_topology.png differ diff --git a/examples/scion/S05-scion-internet/scion-internet.py b/examples/scion/S05-scion-internet/scion-internet.py new file mode 100755 index 000000000..60f58a681 --- /dev/null +++ b/examples/scion/S05-scion-internet/scion-internet.py @@ -0,0 +1,299 @@ +#!/usr/bin/env python3 + +from ipaddress import IPv4Network +from seedemu.compiler import Docker +from seedemu.core import Emulator +from seedemu.layers import ScionBase, ScionRouting, ScionIsd, Scion +from seedemu.layers.Scion import LinkType as ScLinkType + + +def create_as(isd, asn, is_core=False, issuer=None): + as_ = base.createAutonomousSystem(asn) + scion_isd.addIsdAs(isd, asn, is_core) + if not is_core: + scion_isd.setCertIssuer((isd, asn), issuer) + as_.createNetwork('net0') + as_.createControlService('cs1').joinNetwork('net0') + as_.setBeaconingIntervals('30s', '30s', '30s') + if is_core: + policy = { + 'Filter': { + 'MaxHopsLength': 4, + 'AllowIsdLoop': False + } + } + as_.setBeaconPolicy('propagation', policy).setBeaconPolicy('core_registration', policy) + br = as_.createRouter('br0') + br.joinNetwork('net0') + return as_, br + + +class CrossConnectNetAssigner: + def __init__(self): + self.subnet_iter = IPv4Network("10.3.0.0/16").subnets(new_prefix=29) + self.xc_nets = {} + + def next_addr(self, net): + if net not in self.xc_nets: + hosts = next(self.subnet_iter).hosts() + next(hosts) # Skip first IP (reserved for Docker) + self.xc_nets[net] = hosts + return "{}/29".format(next(self.xc_nets[net])) + +xc_nets = CrossConnectNetAssigner() + + +# Initialize +emu = Emulator() +base = ScionBase() +routing = ScionRouting() +scion_isd = ScionIsd() +scion = Scion() + +# SCION ISDs +base.createIsolationDomain(1) + +# Internet Exchanges +# We use "Internet Exchanges" as internal networks of the subdivided ASes in +# order to reduce the number of networks Docker has to create. +base.createInternetExchange(5) # Tier-1 ISP +base.createInternetExchange(7) # Tier-1 ISP +base.createInternetExchange(10) # Large IXP +base.createInternetExchange(11) # Large IXP +base.createInternetExchange(12) # Small IXP +base.createInternetExchange(17) # Small access network +base.createInternetExchange(18) # Large access network +base.createInternetExchange(20) # Large content provider + +# Tier-1 ISP as50-53 +br = create_as(1, 50, is_core=True)[1].joinNetwork('ix5') +br.crossConnect(60, 'br0', xc_nets.next_addr('50-60')) +br.crossConnect(70, 'br0', xc_nets.next_addr('50-70')) +br.crossConnect(201, 'br0', xc_nets.next_addr('50-201')) +br = create_as(1, 51, issuer=50)[1].joinNetwork('ix5') +br.crossConnect(150, 'br0', xc_nets.next_addr('51-150')) +br.crossConnect(151, 'br0', xc_nets.next_addr('51-151')) +br.crossConnect(181, 'br0', xc_nets.next_addr('51-181')) +create_as(1, 52, issuer=50)[1].joinNetwork('ix5') +br = create_as(1, 53, issuer=50)[1].joinNetwork('ix5') +br.crossConnect(152, 'br0', xc_nets.next_addr('53-152')) +scion.addIxLink(5, (1, 50), (1, 51), ScLinkType.Transit) +scion.addIxLink(5, (1, 50), (1, 52), ScLinkType.Transit) +scion.addIxLink(5, (1, 50), (1, 53), ScLinkType.Transit) +scion.addIxLink(5, (1, 52), (1, 51), ScLinkType.Transit, count=2) +scion.addIxLink(5, (1, 52), (1, 53), ScLinkType.Transit, count=2) + +# Tier-1 ISP as60 +_, br = create_as(1, 60, is_core=True) +br.crossConnect(50, 'br0', xc_nets.next_addr('50-60')) +br.crossConnect(70, 'br0', xc_nets.next_addr('60-70')) +br.crossConnect(103, 'br0', xc_nets.next_addr('60-103')) +br.crossConnect(152, 'br0', xc_nets.next_addr('60-152')) +br.crossConnect(180, 'br0', xc_nets.next_addr('60-180')) + +# Tier-1 ISP as70-73 +br = create_as(1, 70, is_core=True)[1].joinNetwork('ix7') +br.crossConnect(50, 'br0', xc_nets.next_addr('50-70')) +br.crossConnect(60, 'br0', xc_nets.next_addr('60-70')) +br.crossConnect(110, 'br0', xc_nets.next_addr('70-110')) +br.crossConnect(170, 'br0', xc_nets.next_addr('70-170')) +br.crossConnect(200, 'br0', xc_nets.next_addr('70-200')) +br = create_as(1, 71, issuer=70)[1].joinNetwork('ix7') +br.crossConnect(120, 'br0', xc_nets.next_addr('71-120')) +br.crossConnect(160, 'br0', xc_nets.next_addr('71-160')) +br.crossConnect(161, 'br0', xc_nets.next_addr('71-161')) +create_as(1, 72, issuer=70)[1].joinNetwork('ix7') +br = create_as(1, 73, issuer=70)[1].joinNetwork('ix7') +br.crossConnect(162, 'br0', xc_nets.next_addr('73-162')) +br.crossConnect(163, 'br0', xc_nets.next_addr('73-163')) +scion.addIxLink(7, (1, 70), (1, 71), ScLinkType.Transit) +scion.addIxLink(7, (1, 70), (1, 72), ScLinkType.Transit) +scion.addIxLink(7, (1, 70), (1, 73), ScLinkType.Transit) +scion.addIxLink(7, (1, 72), (1, 71), ScLinkType.Transit, count=2) +scion.addIxLink(7, (1, 72), (1, 73), ScLinkType.Transit, count=2) + +# Large IXP as100-113 +br = create_as(1, 100, is_core=True)[1].joinNetwork('ix10') +br.crossConnect(111, 'br0', xc_nets.next_addr('100-111')) +create_as(1, 101, is_core=True)[1].joinNetwork('ix10') +create_as(1, 102, is_core=True)[1].joinNetwork('ix10') +br = create_as(1, 103, is_core=True)[1].joinNetwork('ix10') +br.crossConnect(60, 'br0', xc_nets.next_addr('60-103')) +create_as(1, 104, issuer=100)[1].joinNetwork('ix10') +create_as(1, 105, issuer=100)[1].joinNetwork('ix10') +create_as(1, 106, issuer=100)[1].joinNetwork('ix10') +create_as(1, 107, issuer=100)[1].joinNetwork('ix10') +create_as(1, 108, issuer=100)[1].joinNetwork('ix10') +create_as(1, 109, issuer=100)[1].joinNetwork('ix10') +scion.addIxLink(10, (1, 100), (1, 101), ScLinkType.Core) +scion.addIxLink(10, (1, 101), (1, 102), ScLinkType.Core) +scion.addIxLink(10, (1, 102), (1, 103), ScLinkType.Core) +scion.addIxLink(10, (1, 100), (1, 102), ScLinkType.Core) +scion.addIxLink(10, (1, 101), (1, 103), ScLinkType.Core) +scion.addIxLink(10, (1, 100), (1, 104), ScLinkType.Transit, count=2) +scion.addIxLink(10, (1, 100), (1, 105), ScLinkType.Transit, count=2) +scion.addIxLink(10, (1, 100), (1, 106), ScLinkType.Transit, count=2) +scion.addIxLink(10, (1, 100), (1, 107), ScLinkType.Transit, count=2) +scion.addIxLink(10, (1, 101), (1, 104), ScLinkType.Transit, count=2) +scion.addIxLink(10, (1, 101), (1, 105), ScLinkType.Transit, count=2) +scion.addIxLink(10, (1, 101), (1, 106), ScLinkType.Transit, count=2) +scion.addIxLink(10, (1, 101), (1, 107), ScLinkType.Transit, count=2) +scion.addIxLink(10, (1, 102), (1, 104), ScLinkType.Transit, count=2) +scion.addIxLink(10, (1, 102), (1, 105), ScLinkType.Transit, count=2) +scion.addIxLink(10, (1, 102), (1, 106), ScLinkType.Transit, count=2) +scion.addIxLink(10, (1, 102), (1, 107), ScLinkType.Transit, count=2) +scion.addIxLink(10, (1, 103), (1, 104), ScLinkType.Transit, count=2) +scion.addIxLink(10, (1, 103), (1, 105), ScLinkType.Transit, count=2) +scion.addIxLink(10, (1, 103), (1, 106), ScLinkType.Transit, count=2) +scion.addIxLink(10, (1, 103), (1, 107), ScLinkType.Transit, count=2) +scion.addIxLink(10, (1, 105), (1, 108), ScLinkType.Transit, count=2) +scion.addIxLink(10, (1, 106), (1, 109), ScLinkType.Transit, count=2) +br = create_as(1, 110, is_core=True)[1].joinNetwork('ix11') +br.crossConnect(70, 'br0', xc_nets.next_addr('70-110')) +br = create_as(1, 111, is_core=True)[1].joinNetwork('ix11') +br.crossConnect(100, 'br0', xc_nets.next_addr('100-111')) +create_as(1, 112, issuer=110)[1].joinNetwork('ix11') +create_as(1, 113, issuer=110)[1].joinNetwork('ix11') +scion.addIxLink(11, (1, 110), (1, 111), ScLinkType.Core) +scion.addIxLink(11, (1, 110), (1, 112), ScLinkType.Transit, count=2) +scion.addIxLink(11, (1, 110), (1, 113), ScLinkType.Transit, count=2) +scion.addIxLink(11, (1, 111), (1, 112), ScLinkType.Transit, count=2) +scion.addIxLink(11, (1, 111), (1, 113), ScLinkType.Transit, count=2) + +# Small IXP as120-122 +br = create_as(1, 120, is_core=True)[1].joinNetwork('ix12') +br.crossConnect(71, 'br0', xc_nets.next_addr('71-120')) +create_as(1, 121, issuer=120)[1].joinNetwork('ix12') +create_as(1, 122, issuer=120)[1].joinNetwork('ix12') +scion.addIxLink(12, (1, 120), (1, 121), ScLinkType.Transit, count=2) +scion.addIxLink(12, (1, 120), (1, 122), ScLinkType.Transit, count=2) + +# Large content provider as200-205 +br = create_as(1, 200, is_core=True)[1].joinNetwork('ix20') +br.crossConnect(70, 'br0', xc_nets.next_addr('70-200')) +br = create_as(1, 201, is_core=True)[1].joinNetwork('ix20') +br.crossConnect(50, 'br0', xc_nets.next_addr('50-201')) +create_as(1, 202, issuer=200)[1].joinNetwork('ix20').joinNetwork('ix11').joinNetwork('ix12') +create_as(1, 203, issuer=200)[1].joinNetwork('ix20').joinNetwork('ix10') +br = create_as(1, 204, issuer=200)[1].joinNetwork('ix20') +br.crossConnect(163, 'br0', xc_nets.next_addr('163-204')) +br = create_as(1, 205, issuer=200)[1].joinNetwork('ix20') +br.crossConnect(150, 'br0', xc_nets.next_addr('150-205')) +scion.addIxLink(20, (1, 200), (1, 201), ScLinkType.Core, count=2) +scion.addIxLink(20, (1, 200), (1, 202), ScLinkType.Transit) +scion.addIxLink(20, (1, 202), (1, 204), ScLinkType.Transit) +scion.addIxLink(20, (1, 201), (1, 203), ScLinkType.Transit) +scion.addIxLink(20, (1, 203), (1, 205), ScLinkType.Transit) +scion.addIxLink(11, (1, 113), (1, 202), ScLinkType.Transit, count=2) +scion.addIxLink(10, (1, 104), (1, 203), ScLinkType.Transit, count=2) +scion.addIxLink(12, (1, 122), (1, 202), ScLinkType.Transit) + +# Small access network as170-171 +br = create_as(1, 170, is_core=True)[1].joinNetwork('ix17') +br.crossConnect(70, 'br0', xc_nets.next_addr('70-170')) +create_as(1, 171, issuer=170)[1].joinNetwork('ix17').joinNetwork('ix11').joinNetwork('ix12') +scion.addIxLink(17, (1, 170), (1, 171), ScLinkType.Transit) +scion.addIxLink(11, (1, 112), (1, 171), ScLinkType.Transit, count=2) +scion.addIxLink(12, (1, 121), (1, 171), ScLinkType.Transit) + +# Core links +scion.addXcLink((1, 50), (1, 60), ScLinkType.Core) +scion.addXcLink((1, 50), (1, 70), ScLinkType.Core, count=2) +scion.addXcLink((1, 60), (1, 70), ScLinkType.Core) +scion.addXcLink((1, 50), (1, 201), ScLinkType.Core) +scion.addXcLink((1, 60), (1, 103), ScLinkType.Core) +scion.addXcLink((1, 70), (1, 200), ScLinkType.Core) +scion.addXcLink((1, 70), (1, 110), ScLinkType.Core) +scion.addXcLink((1, 70), (1, 170), ScLinkType.Core) +scion.addXcLink((1, 100), (1, 111), ScLinkType.Core) + +# Large access network as180-191 +br = create_as(1, 180, is_core=True)[1].joinNetwork('ix18') +br.crossConnect(60, 'br0', xc_nets.next_addr('60-180')) +br = create_as(1, 181, issuer=180)[1].joinNetwork('ix18').joinNetwork('ix10') +br.crossConnect(51, 'br0', xc_nets.next_addr('51-181')) +br = create_as(1, 182, issuer=180)[1].joinNetwork('ix18').joinNetwork('ix10') +br = create_as(1, 183, issuer=180)[1].joinNetwork('ix18').joinNetwork('ix10') +for asn in range(184, 192): + create_as(1, asn, issuer=180)[1].joinNetwork('ix18') +scion.addIxLink(18, (1, 180), (1, 181), ScLinkType.Transit) +scion.addIxLink(18, (1, 180), (1, 182), ScLinkType.Transit) +scion.addIxLink(18, (1, 180), (1, 183), ScLinkType.Transit) +scion.addIxLink(18, (1, 181), (1, 184), ScLinkType.Transit) +scion.addIxLink(18, (1, 181), (1, 185), ScLinkType.Transit) +scion.addIxLink(18, (1, 182), (1, 184), ScLinkType.Transit) +scion.addIxLink(18, (1, 182), (1, 185), ScLinkType.Transit) +scion.addIxLink(18, (1, 182), (1, 186), ScLinkType.Transit) +scion.addIxLink(18, (1, 183), (1, 185), ScLinkType.Transit) +scion.addIxLink(18, (1, 183), (1, 186), ScLinkType.Transit) +scion.addIxLink(18, (1, 184), (1, 187), ScLinkType.Transit) +scion.addIxLink(18, (1, 184), (1, 188), ScLinkType.Transit) +scion.addIxLink(18, (1, 184), (1, 189), ScLinkType.Transit) +scion.addIxLink(18, (1, 185), (1, 187), ScLinkType.Transit) +scion.addIxLink(18, (1, 185), (1, 188), ScLinkType.Transit) +scion.addIxLink(18, (1, 185), (1, 190), ScLinkType.Transit) +scion.addIxLink(18, (1, 185), (1, 191), ScLinkType.Transit) +scion.addIxLink(18, (1, 186), (1, 189), ScLinkType.Transit) +scion.addIxLink(18, (1, 186), (1, 190), ScLinkType.Transit) +scion.addIxLink(18, (1, 186), (1, 191), ScLinkType.Transit) +scion.addXcLink((1, 60), (1, 180), ScLinkType.Core) +scion.addXcLink((1, 51), (1, 181), ScLinkType.Transit) +scion.addIxLink(10, (1, 108), (1, 181), ScLinkType.Transit) +scion.addIxLink(10, (1, 109), (1, 182), ScLinkType.Transit) +scion.addIxLink(10, (1, 107), (1, 183), ScLinkType.Transit) + +# Leaf ASes +br = create_as(1, 150, issuer=50)[1].joinNetwork('ix10') +br.crossConnect(51, 'br0', xc_nets.next_addr('51-150')) +br.crossConnect(205, 'br0', xc_nets.next_addr('150-205')) +scion.addXcLink((1, 51), (1, 150), ScLinkType.Transit) +scion.addIxLink(10, (1, 104), (1, 150), ScLinkType.Transit) +scion.addXcLink((1, 205), (1, 150), ScLinkType.Transit) + +br = create_as(1, 151, issuer=50)[1].joinNetwork('ix10') +br.crossConnect(51, 'br0', xc_nets.next_addr('51-151')) +scion.addXcLink((1, 51), (1, 151), ScLinkType.Transit) +scion.addIxLink(10, (1, 104), (1, 151), ScLinkType.Transit) +scion.addIxLink(10, (1, 105), (1, 151), ScLinkType.Transit) + +br = create_as(1, 152, issuer=50)[1].joinNetwork('ix10') +br.crossConnect(53, 'br0', xc_nets.next_addr('53-152')) +br.crossConnect(60, 'br0', xc_nets.next_addr('60-152')) +scion.addXcLink((1, 53), (1, 152), ScLinkType.Transit) +scion.addXcLink((1, 60), (1, 152), ScLinkType.Transit) +scion.addIxLink(10, (1, 107), (1, 152), ScLinkType.Transit) + +br = create_as(1, 160, issuer=70)[1].joinNetwork('ix11').joinNetwork('ix12') +br.crossConnect(71, 'br0', xc_nets.next_addr('71-160')) +scion.addXcLink((1, 71), (1, 160), ScLinkType.Transit) +scion.addIxLink(11, (1, 112), (1, 160), ScLinkType.Transit) +scion.addIxLink(12, (1, 121), (1, 160), ScLinkType.Transit) + +br = create_as(1, 161, issuer=70)[1].joinNetwork('ix12') +br.crossConnect(71, 'br0', xc_nets.next_addr('71-161')) +scion.addXcLink((1, 71), (1, 161), ScLinkType.Transit) +scion.addIxLink(12, (1, 121), (1, 161), ScLinkType.Transit) + +br = create_as(1, 162, issuer=70)[1].joinNetwork('ix12') +br.crossConnect(73, 'br0', xc_nets.next_addr('73-162')) +scion.addXcLink((1, 73), (1, 162), ScLinkType.Transit) +scion.addIxLink(12, (1, 122), (1, 162), ScLinkType.Transit) + +br = create_as(1, 163, issuer=70)[1].joinNetwork('ix11').joinNetwork('ix12') +br.crossConnect(73, 'br0', xc_nets.next_addr('73-163')) +br.crossConnect(204, 'br0', xc_nets.next_addr('163-204')) +scion.addXcLink((1, 73), (1, 163), ScLinkType.Transit) +scion.addXcLink((1, 204), (1, 163), ScLinkType.Transit) +scion.addIxLink(11, (1, 113), (1, 163), ScLinkType.Transit) +scion.addIxLink(12, (1, 122), (1, 163), ScLinkType.Transit) + +# Rendering +emu.addLayer(base) +emu.addLayer(routing) +emu.addLayer(scion_isd) +emu.addLayer(scion) + +emu.render() + +# Compilation +emu.compile(Docker(), './scion') diff --git a/seedemu/core/ScionAutonomousSystem.py b/seedemu/core/ScionAutonomousSystem.py index d4438df06..55b659362 100644 --- a/seedemu/core/ScionAutonomousSystem.py +++ b/seedemu/core/ScionAutonomousSystem.py @@ -32,6 +32,9 @@ class ScionAutonomousSystem(AutonomousSystem): __attributes: Dict[int, Set] # Set of AS attributes per ISD __mtu: Optional[int] # Minimum MTU in the AS's internal networks __control_services: Dict[str, Node] + # Origination, propagation, and registration intervals + __beaconing_intervals: Tuple[Optional[str], Optional[str], Optional[str]] + __beaconing_policy: Dict[str, Dict] __next_ifid: int # Next IFID assigned to a link def __init__(self, asn: int, subnetTemplate: str = "10.{}.0.0/16"): @@ -43,6 +46,8 @@ def __init__(self, asn: int, subnetTemplate: str = "10.{}.0.0/16"): self.__keys = None self.__attributes = defaultdict(set) self.__mtu = None + self.__beaconing_intervals = (None, None, None) + self.__beaconing_policy = {} self.__next_ifid = 1 def registerNodes(self, emulator: Emulator): @@ -109,6 +114,53 @@ def getAsAttributes(self, isd: int) -> List[str]: """ return list(self.__attributes[isd]) + def setBeaconingIntervals(self, + origination: Optional[str] = None, + propagation: Optional[str] = None, + registration: Optional[str] = None + ) -> ScionAutonomousSystem: + """! + @brief Set the beaconing intervals. Intervals are specified as string of a positive decimal + integer followed by one of the units y, w, d, h, m, s, us, or ns. Setting an interval to + None uses the beacon server's default setting. + + @returns self + """ + self.__beaconing_intervals = (origination, propagation, registration) + return self + + def getBeaconingIntervals(self) -> Tuple[Optional[str], Optional[str], Optional[str]]: + """! + @brief Get the beaconing intervals. + + @returns Tuple of origination, propagation, and registration interval. + """ + return self.__beaconing_intervals + + def setBeaconPolicy(self, type: str, policy: Optional[Dict]) -> ScionAutonomousSystem: + """! + @brief Set the beaconing policy of the AS. + + @param type One of "propagation", "core_registration", "up_registration", "down_registration". + @param policy Policy. Setting a policy to None clears it. + @returns self + """ + types = ["propagation", "core_registration", "up_registration", "down_registration"] + assert type in types, "Unknown policy type" + self.__beaconing_policy[type] = policy + return self + + def getBeaconingPolicy(self, type: set) -> Optional[Dict]: + """! + @brief Get the beaconing policy of the AS. + + @param type One of "propagation", "core_registration", "up_registration", "down_registration". + @returns Policy or None if no policy of the requested type is set. + """ + types = ["propagation", "core_registration", "up_registration", "down_registration"] + assert type in types, "Unknown policy type" + return self.__beaconing_policy.get(type) + def getTopology(self, isd: int) -> Dict: """! @brief Create the AS topology definition. @@ -190,7 +242,7 @@ def _doCreateGraphs(self, emulator: Emulator): net = iface.getNet() netname = 'Network: {}'.format(net.getName()) l2graph.addEdge(rtrname, netname) - + def print(self, indent: int) -> str: """! @copydoc AutonomousSystem.print() diff --git a/seedemu/layers/ScionRouting.py b/seedemu/layers/ScionRouting.py index c9addcffe..debf83618 100644 --- a/seedemu/layers/ScionRouting.py +++ b/seedemu/layers/ScionRouting.py @@ -3,6 +3,8 @@ import os.path from typing import Dict +import yaml + from seedemu.core import Emulator, Node, ScionAutonomousSystem, ScionRouter from seedemu.layers import Routing, ScionBase, ScionIsd @@ -18,24 +20,28 @@ level = "debug" """ -_Templates["trust"] = """\ +_Templates["trust_db"] = """\ [trust_db] connection = "/cache/{name}.trust.db" + """ -_Templates["path"] = """\ +_Templates["path_db"] = """\ [path_db] connection = "/cache/{name}.path.db" + """ -_Templates["beacon"] = """\ +_Templates["beacon_db"] = """\ [beacon_db] connection = "/cache/{name}.beacon.db" + """ _Templates["dispatcher"] = """\ [dispatcher] id = "dispatcher" + """ _CommandTemplates: Dict[str, str] = {} @@ -142,7 +148,7 @@ def render(self, emulator: Emulator): self.__provision_router_config(rnode) elif type == 'csnode': csnode: Node = obj - self._provision_cs_config(csnode) + self._provision_cs_config(csnode, as_) @staticmethod def __provision_base_config(node: Node): @@ -152,8 +158,8 @@ def __provision_base_config(node: Node): node.setFile("/etc/scion/sciond.toml", _Templates["general"].format(name="sd1") + - _Templates["trust"].format(name="sd1") + - _Templates["path"].format(name="sd1")) + _Templates["trust_db"].format(name="sd1") + + _Templates["path_db"].format(name="sd1")) node.setFile("/etc/scion/dispatcher.toml", _Templates["dispatcher"]) @@ -166,12 +172,30 @@ def __provision_router_config(router: ScionRouter): _Templates["general"].format(name=name)) @staticmethod - def _provision_cs_config(node: Node): + def _provision_cs_config(node: Node, as_: ScionAutonomousSystem): """Set control service configuration.""" + # Start building the beaconing section + beaconing = ["[beaconing]"] + interval_keys = ["origination_interval", "propagation_interval", "registration_interval"] + for key, value in zip(interval_keys, as_.getBeaconingIntervals()): + if value is not None: + beaconing.append(f'{key} = "{value}"') + + # Create policy files + beaconing.append("\n[beaconing.policies]") + for type in ["propagation", "core_registration", "up_registration", "down_registration"]: + policy = as_.getBeaconingPolicy(type) + if policy is not None: + file_name = f"/etc/scion/{type}_policy.yaml" + node.setFile(file_name, yaml.dump(policy, indent=2)) + beaconing.append(f'{type} = "{file_name}"') + + # Concatenate configuration sections name = node.getName() node.setFile(os.path.join("/etc/scion/", name + ".toml"), _Templates["general"].format(name=name) + - _Templates["trust"].format(name=name) + - _Templates["beacon"].format(name=name) + - _Templates["path"].format(name=name)) + _Templates["trust_db"].format(name=name) + + _Templates["beacon_db"].format(name=name) + + _Templates["path_db"].format(name=name) + + "\n".join(beaconing)) diff --git a/test/scion/scion_bgp_mixed/emulator-code/test-emulator.py b/test/scion/scion_bgp_mixed/emulator-code/test-emulator.py index 7e47a1aec..a1fe99ae7 100755 --- a/test/scion/scion_bgp_mixed/emulator-code/test-emulator.py +++ b/test/scion/scion_bgp_mixed/emulator-code/test-emulator.py @@ -34,6 +34,8 @@ as150.createNetwork('net1') as150.createNetwork('net2') as150.createNetwork('net3') +as150.setBeaconingIntervals('5s', '5s', '5s') +as150.setBeaconPolicy('core_registration', {'Filter': {'AllowIsdLoop': False}}) as150.createControlService('cs1').joinNetwork('net0') as150.createControlService('cs2').joinNetwork('net2') as150_br0 = as150.createRouter('br0')