Repository: scionproto/scion
Pull request #3840 (2020-07-28 12:04:47)

topogen: allocate ipv4 subnets for docker
If we just specify an IPv6 subnet in a docker network, docker-compose chooses
an IPv4 subnet which might overlap with another IPv4 network that we allocated.
This will make the topology fail able to start. This commit prevents this and
should fix a few CI errors.

Also remove the `--in-docker` flag and supporting code, because it is no longer needed.
Also add extra_hosts entry for jaeger tracing.

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/scionproto/scion/3840)
<!-- Reviewable:end -->



Patch:
diff --git a/acceptance/common.sh b/acceptance/common.sh
index 64f894a9c..f63ee36ef 100644
--- a/acceptance/common.sh
+++ b/acceptance/common.sh
@@ -51,13 +51,6 @@ fail() {
     exit 1
 }
 
-#######################################
-# Returns whether this script is running in docker
-#######################################
-is_running_in_docker() {
-    cut -d: -f 3 /proc/1/cgroup | grep -q '^/docker/'
-}
-
 #######################################
 # Return the ip of the container
 # Arguments:
diff --git a/go/integration/BUILD.bazel b/go/integration/BUILD.bazel
index e2739c485..23860e773 100644
--- a/go/integration/BUILD.bazel
+++ b/go/integration/BUILD.bazel
@@ -15,5 +15,6 @@ go_library(
         "//go/lib/snet:go_default_library",
         "//go/lib/sock/reliable:go_default_library",
         "@com_github_opentracing_opentracing_go//:go_default_library",
+        "@com_github_uber_jaeger_client_go//:go_default_library",
     ],
 )
diff --git a/go/integration/common.go b/go/integration/common.go
index 7b343a1db..70d746e7d 100644
--- a/go/integration/common.go
+++ b/go/integration/common.go
@@ -19,10 +19,12 @@ import (
 	"context"
 	"flag"
 	"fmt"
+	"net"
 	"os"
 	"time"
 
 	"github.com/opentracing/opentracing-go"
+	"github.com/uber/jaeger-client-go"
 
 	"github.com/scionproto/scion/go/lib/addr"
 	"github.com/scionproto/scion/go/lib/env"
@@ -67,9 +69,18 @@ func addFlags() {
 
 // InitTracer initializes the global tracer and returns a closer function.
 func InitTracer(name string) (func(), error) {
+	agent := fmt.Sprintf("jaeger:%d", jaeger.DefaultUDPSpanServerPort)
+	c, err := net.DialTimeout("udp", agent, 100*time.Millisecond)
+	if err != nil {
+		log.Debug("Jaeger tracer not found, using default", "err", err)
+		agent = ""
+	} else if c != nil {
+		c.Close()
+	}
 	cfg := &env.Tracing{
 		Enabled: true,
 		Debug:   true,
+		Agent:   agent,
 	}
 	cfg.InitDefaults()
 	tr, closer, err := cfg.NewTracer(name)
diff --git a/integration/common.sh b/integration/common.sh
index 9c8e76e6b..2d55d9c6e 100755
--- a/integration/common.sh
+++ b/integration/common.sh
@@ -39,10 +39,6 @@ is_docker_be() {
     [ -f gen/scion-dc.yml ]
 }
 
-is_running_in_docker() {
-    cut -d: -f 3 /proc/1/cgroup | grep -q '^/docker/'
-}
-
 usage() {
     echo "Usage: $0: [-b brs]"
     exit 1
diff --git a/python/topology/common.py b/python/topology/common.py
index 0b830c2b9..670577122 100644
--- a/python/topology/common.py
+++ b/python/topology/common.py
@@ -13,15 +13,15 @@
 # limitations under the License.
 
 # Stdlib
-import ipaddress
+from ipaddress import ip_address, ip_network
 import os
 import subprocess
-import sys
 from urllib.parse import urlsplit
+from typing import Mapping
 
 # SCION
 from python.lib.scion_addr import ISD_AS
-from python.topology.net import AddressProxy
+from python.topology.net import AddressProxy, NetworkDescription
 
 COMMON_DIR = 'endhost'
 
@@ -109,21 +109,23 @@ def split_host_port(addr: str) -> (str, int):
 
 
 def join_host_port(host: str, port: int) -> str:
-    ip = ipaddress.ip_address(host)
+    ip = ip_address(host)
     if ip.version == 4:
         return '{}:{}'.format(host, port)
     return '[{}]:{}'.format(host, port)
 
 
-def sciond_ip(docker, topo_id, networks):
-    for i, net in enumerate(networks):
-        for prog, ip_net in networks[net].items():
+def sciond_ip(docker, topo_id, networks: Mapping[ip_network, NetworkDescription]):
+    for net_desc in networks.values():
+        for prog, ip_net in net_desc.ip_net.items():
             if prog == 'sd%s' % topo_id.file_fmt():
                 return ip_net.ip
     return None
 
 
-def prom_addr_dispatcher(docker, topo_id, networks, port, name):
+def prom_addr_dispatcher(docker, topo_id,
+                         networks: Mapping[ip_network, NetworkDescription],
+                         port, name):
     if not docker:
         return "[127.0.0.1]:%s" % port
     target_name = ''
@@ -133,9 +135,9 @@ def prom_addr_dispatcher(docker, topo_id, networks, port, name):
         target_name = 'sig%s' % topo_id.file_fmt()
     else:
         target_name = 'disp%s' % topo_id.file_fmt()
-    for _, net in enumerate(networks):
-        if target_name in networks[net]:
-            return '[%s]:%s' % (networks[net][target_name].ip, port)
+    for net_desc in networks.values():
+        if target_name in net_desc.ip_net:
+            return '[%s]:%s' % (net_desc.ip_net[target_name].ip, port)
     return None
 
 
@@ -159,14 +161,8 @@ def docker_image(args, image):
     return image
 
 
-def docker_host(in_docker, docker, addr=None):
-    if in_docker:
-        # If in-docker we need to know the DOCKER0 IP
-        addr = os.getenv('DOCKER0', None)
-        if not addr:
-            print('DOCKER0 env variable required! Exiting!')
-            sys.exit(1)
-    elif docker or not addr:
+def docker_host(docker, addr=None):
+    if docker or not addr:
         # Using docker topology or there is no default addr,
         # we directly get the DOCKER0 IP
         addr = docker_ip()
diff --git a/python/topology/config.py b/python/topology/config.py
index 61e2a9a47..3bfcae72b 100755
--- a/python/topology/config.py
+++ b/python/topology/config.py
@@ -23,6 +23,8 @@ import logging
 import os
 import sys
 from io import StringIO
+from ipaddress import ip_network
+from typing import Mapping
 
 # SCION
 from python.lib.defines import (
@@ -41,6 +43,7 @@ from python.topology.docker import DockerGenArgs, DockerGenerator
 from python.topology.go import GoGenArgs, GoGenerator
 from python.topology.jaeger import JaegerGenArgs, JaegerGenerator
 from python.topology.net import (
+    NetworkDescription,
     SubnetGenerator,
     DEFAULT_NETWORK,
 )
@@ -81,8 +84,8 @@ class ConfigGenerator(object):
         Configure default network.
         """
         defaults = self.topo_config.get("defaults", {})
-        self.subnet_gen4 = SubnetGenerator(DEFAULT_NETWORK, self.args.docker, self.args.in_docker)
-        self.subnet_gen6 = SubnetGenerator(DEFAULT6_NETWORK, self.args.docker, self.args.in_docker)
+        self.subnet_gen4 = SubnetGenerator(DEFAULT_NETWORK, self.args.docker)
+        self.subnet_gen6 = SubnetGenerator(DEFAULT6_NETWORK, self.args.docker)
         self.default_mtu = defaults.get("mtu", DEFAULT_MTU)
 
     def generate_all(self):
@@ -90,7 +93,8 @@ class ConfigGenerator(object):
         Generate all needed files.
         """
         self._ensure_uniq_ases()
-        topo_dicts, self.networks = self._generate_topology()
+        topo_dicts, self.all_networks = self._generate_topology()
+        self.networks = remove_v4_nets(self.all_networks)
         self._generate_with_topo(topo_dicts)
         self._write_networks_conf(self.networks, NETWORKS_FILE)
         self._write_sciond_conf(self.networks, SCIOND_ADDRESSES_FILE)
@@ -160,7 +164,7 @@ class ConfigGenerator(object):
         docker_gen.generate()
 
     def _docker_args(self, topo_dicts):
-        return DockerGenArgs(self.args, topo_dicts, self.networks)
+        return DockerGenArgs(self.args, topo_dicts, self.all_networks)
 
     def _generate_prom_conf(self, topo_dicts):
         args = self._prometheus_args(topo_dicts)
@@ -179,23 +183,35 @@ class ConfigGenerator(object):
             for path, value in ca_files[int(isd)].items():
                 write_file(os.path.join(base, path), value.decode())
 
-    def _write_networks_conf(self, networks, out_file):
+    def _write_networks_conf(self,
+                             networks: Mapping[ip_network, NetworkDescription],
+                             out_file: str):
         config = configparser.ConfigParser(interpolation=None)
-        for i, net in enumerate(networks):
+        for net, net_desc in networks.items():
             sub_conf = {}
-            for prog, ip_net in networks[net].items():
+            for prog, ip_net in net_desc.ip_net.items():
                 sub_conf[prog] = ip_net.ip
             config[net] = sub_conf
         text = StringIO()
         config.write(text)
         write_file(os.path.join(self.args.output_dir, out_file), text.getvalue())
 
-    def _write_sciond_conf(self, networks, out_file):
+    def _write_sciond_conf(self, networks: Mapping[ip_network, NetworkDescription], out_file: str):
         d = dict()
-        for i, net in enumerate(networks):
-            for prog, ip_net in networks[net].items():
+        for net_desc in networks.values():
+            for prog, ip_net in net_desc.ip_net.items():
                 if prog.startswith("sd"):
                     ia = prog[2:].replace("_", ":")
                     d[ia] = str(ip_net.ip)
         with open(os.path.join(self.args.output_dir, out_file), mode="w") as f:
             json.dump(d, f, sort_keys=True, indent=4)
+
+
+def remove_v4_nets(nets: Mapping[ip_network, NetworkDescription]
+                   ) -> Mapping[ip_network, NetworkDescription]:
+    res = {}
+    for net, net_desc in nets.items():
+        if net_desc.name.endswith('_v4'):
+            continue
+        res[net] = net_desc
+    return res
diff --git a/python/topology/docker.py b/python/topology/docker.py
index 00d4e4dcf..2eb9627e8 100644
--- a/python/topology/docker.py
+++ b/python/topology/docker.py
@@ -15,6 +15,8 @@
 # Stdlib
 import copy
 import os
+from ipaddress import ip_network
+from typing import Mapping
 # External packages
 import yaml
 # SCION
@@ -24,18 +26,20 @@ from python.lib.util import (
 )
 from python.topology.common import (
     ArgsTopoDicts,
+    docker_host,
     docker_image,
     DOCKER_USR_VOL,
     sciond_svc_name
 )
 from python.topology.docker_utils import DockerUtilsGenArgs, DockerUtilsGenerator
+from python.topology.net import NetworkDescription
 from python.topology.sig import SIGGenArgs, SIGGenerator
 
 DOCKER_CONF = 'scion-dc.yml'
 
 
 class DockerGenArgs(ArgsTopoDicts):
-    def __init__(self, args, topo_dicts, networks):
+    def __init__(self, args, topo_dicts, networks: Mapping[ip_network, NetworkDescription]):
         """
         :param object args: Contains the passed command line arguments as named attributes.
         :param dict topo_dicts: The generated topo dicts from TopoGenerator.
@@ -57,7 +61,7 @@ class DockerGenerator(object):
         self.bridges = {}
         self.output_base = os.environ.get('SCION_OUTPUT_BASE', os.getcwd())
         self.user_spec = os.environ.get('SCION_USERSPEC', '$LOGNAME')
-        self.prefix = 'scion_docker_' if self.args.in_docker else 'scion_'
+        self.prefix = 'scion_'
 
     def generate(self):
         self._create_networks()
@@ -89,19 +93,32 @@ class DockerGenerator(object):
         self.dc_conf = sig_gen.generate()
 
     def _create_networks(self):
-        for network in self.args.networks:
-            for elem in self.args.networks[network]:
+        # first find v4 allocations, those networks don't need to be generated.
+        v4nets = {}
+        ignore_nets = []
+        for network, net_desc in self.args.networks.items():
+            if network.version == 6:
+                continue
+            if net_desc.name.endswith('_v4'):
+                v4nets[net_desc.name[:-3]] = network
+                ignore_nets.append(network)
+
+        for network, net_desc in self.args.networks.items():
+            if network in ignore_nets:
+                continue
+            for elem in net_desc.ip_net:
                 if elem not in self.elem_networks:
                     self.elem_networks[elem] = []
                 ipv = 'ipv4'
-                if self.args.networks[network][elem].ip.version == 6:
+                ip = net_desc.ip_net[elem].ip
+                if ip.version == 6:
                     ipv = 'ipv6'
                 self.elem_networks[elem].append({
                     'net': str(network),
-                    ipv: self.args.networks[network][elem].ip
+                    ipv: ip
                 })
             # Create docker networks
-            prefix = 'scnd_' if self.args.in_docker else 'scn_'
+            prefix = 'scn_'
             net_name = "%s%03d" % (prefix, len(self.bridges))
             self.bridges[str(network)] = net_name
             self.dc_conf['networks'][net_name] = {
@@ -113,6 +130,9 @@ class DockerGenerator(object):
                     'com.docker.network.bridge.name': net_name
                 }
             }
+            if net_desc.name in v4nets:
+                v4_net = v4nets[net_desc.name]
+                self.dc_conf['networks'][net_name]['ipam']['config'].append({'subnet': str(v4_net)})
             if network.version == 6:
                 self.dc_conf['networks'][net_name]['enable_ipv6'] = True
 
@@ -178,6 +198,7 @@ class DockerGenerator(object):
     def _dispatcher_conf(self, topo_id, topo, base):
         image = 'dispatcher'
         base_entry = {
+            'extra_hosts': ['jaeger:%s' % docker_host(self.args.docker)],
             'image': docker_image(self.args, image),
             'environment': {
                 'SU_EXEC_USERSPEC': self.user_spec,
@@ -216,6 +237,7 @@ class DockerGenerator(object):
         ip = str(net[ipv])
         disp_id = 'cs%s-1' % topo_id.file_fmt()
         entry = {
+            'extra_hosts': ['jaeger:%s' % docker_host(self.args.docker)],
             'image': docker_image(self.args, 'sciond'),
             'container_name': '%ssd%s' % (self.prefix, topo_id.file_fmt()),
             'depends_on': [
diff --git a/python/topology/docker_utils.py b/python/topology/docker_utils.py
index ec4f7a1a3..0018a237a 100644
--- a/python/topology/docker_utils.py
+++ b/python/topology/docker_utils.py
@@ -17,7 +17,12 @@
 import os
 # SCION
 from python.lib.util import write_file
-from python.topology.common import ArgsBase, docker_image, remote_nets
+from python.topology.common import (
+    ArgsBase,
+    docker_host,
+    docker_image,
+    remote_nets,
+)
 
 
 class DockerUtilsGenArgs(ArgsBase):
@@ -55,6 +60,7 @@ class DockerUtilsGenerator(object):
     def _utils_conf(self):
         entry_chown = {
             'image': 'busybox',
+            'network_mode': 'none',
             'volumes': [
                 '/etc/passwd:/etc/passwd:ro',
                 '/etc/group:/etc/group:ro'
@@ -63,6 +69,7 @@ class DockerUtilsGenerator(object):
         }
         entry_clean = {
             'image': 'busybox',
+            'network_mode': 'none',
             'volumes': [],
             'command': 'sh -c "find /mnt/volumes -type s -print0 | xargs -r0 rm -v"'
         }
@@ -73,17 +80,17 @@ class DockerUtilsGenerator(object):
         self.dc_conf['services']['utils_cleaner'] = entry_clean
 
     def _test_conf(self, topo_id):
-        docker = 'docker_' if self.args.in_docker else ''
         cntr_base = '/share'
         name = 'tester_%s' % topo_id.file_fmt()
         entry = {
+            'extra_hosts': ['jaeger:%s' % docker_host(self.args.docker)],
             'image': docker_image(self.args, 'tester'),
-            'container_name': 'tester_%s%s' % (docker, topo_id.file_fmt()),
+            'container_name': 'tester_%s' % topo_id.file_fmt(),
             'privileged': True,
             'entrypoint': 'sh tester.sh',
             'environment': {},
             'volumes': [
-                'vol_scion_%sdisp_cs%s-1:/run/shm/dispatcher:rw' % (docker, topo_id.file_fmt()),
+                'vol_scion_disp_cs%s-1:/run/shm/dispatcher:rw' % topo_id.file_fmt(),
                 self.output_base + '/logs:' + cntr_base + '/logs:rw',
                 self.output_base + '/gen:' + cntr_base + '/gen:rw',
                 self.output_base + '/gen-certs:' + cntr_base + '/gen-certs:rw'
diff --git a/python/topology/generator.py b/python/topology/generator.py
index a4333ea40..a5c36e4e5 100755
--- a/python/topology/generator.py
+++ b/python/topology/generator.py
@@ -45,8 +45,6 @@ def add_arguments(parser):
                         available timeout')
     parser.add_argument('--random-ifids', action='store_true',
                         help='Generate random IFIDs')
-    parser.add_argument('--in-docker', action='store_true',
-                        help='Set if running in a docker container')
     parser.add_argument('--docker-registry', help='Specify docker registry to pull images from')
     parser.add_argument('--image-tag', help='Docker image tag')
     parser.add_argument('--sig', action='store_true',
diff --git a/python/topology/go.py b/python/topology/go.py
index 177ba358e..89921e930 100755
--- a/python/topology/go.py
+++ b/python/topology/go.py
@@ -20,6 +20,8 @@
 import os
 import toml
 import yaml
+from ipaddress import ip_network
+from typing import Mapping
 
 # SCION
 from python.lib.util import write_file
@@ -41,7 +43,7 @@ from python.topology.common import (
     CO_CONFIG_NAME,
 )
 
-from python.topology.net import socket_address_str
+from python.topology.net import socket_address_str, NetworkDescription
 
 from python.topology.prometheus import (
     CS_PROM_PORT,
@@ -57,7 +59,7 @@ CO_QUIC_PORT = 30357
 
 
 class GoGenArgs(ArgsTopoDicts):
-    def __init__(self, args, topo_dicts, networks):
+    def __init__(self, args, topo_dicts, networks: Mapping[ip_network, NetworkDescription]):
         super().__init__(args, topo_dicts)
         self.networks = networks
 
@@ -298,7 +300,7 @@ class GoGenerator(object):
         }
 
     def _tracing_entry(self):
-        docker_ip = docker_host(self.args.in_docker, self.args.docker)
+        docker_ip = docker_host(self.args.docker)
         entry = {
             'enabled': True,
             'debug': True,
diff --git a/python/topology/jaeger.py b/python/topology/jaeger.py
index 369e25e77..e401dd62f 100644
--- a/python/topology/jaeger.py
+++ b/python/topology/jaeger.py
@@ -43,7 +43,7 @@ class JaegerGenerator(object):
                    yaml.dump(dc_conf, default_flow_style=False))
 
     def _generate_dc(self):
-        name = 'jaeger-docker' if self.args.in_docker else 'jaeger'
+        name = 'jaeger'
         entry = {
             'version': '2',
             'services': {
diff --git a/python/topology/net.py b/python/topology/net.py
index 503aeb90e..26bb6869a 100755
--- a/python/topology/net.py
+++ b/python/topology/net.py
@@ -22,6 +22,7 @@ import math
 import sys
 from collections import defaultdict
 from ipaddress import ip_interface, ip_network
+from typing import Mapping
 
 # External packages
 import yaml
@@ -32,17 +33,62 @@ from python.lib.defines import DEFAULT6_NETWORK_ADDR
 DEFAULT_NETWORK = "127.0.0.0/8"
 DEFAULT_PRIV_NETWORK = "192.168.0.0/16"
 DEFAULT_SCN_DC_NETWORK = "172.20.0.0/20"
-DEFAULT_SCN_IN_D_NETWORK = "172.20.16.0/20"
+
+
+class NetworkDescription(object):
+    def __init__(self, name: str, ip_net: Mapping[str, ip_interface]):
+        self.name = name
+        self.ip_net = ip_net
+
+
+class AddressProxy(yaml.YAMLObject):
+    yaml_tag = ""
+
+    def __init__(self):
+        self._intf = None
+        self.ip = None
+
+    def set_intf(self, intf):
+        self._intf = intf
+        self.ip = self._intf.ip
+
+    def __str__(self):
+        return str(self._intf)
+
+    @classmethod
+    def to_yaml(cls, dumper, inst):
+        return dumper.represent_scalar('tag:yaml.org,2002:str', str(inst.ip))
+
+
+class AddressGenerator(object):
+    def __init__(self, docker):
+        self._addrs = defaultdict(lambda: AddressProxy())
+        self.docker = docker
+
+    def register(self, id_: str) -> AddressProxy:
+        return self._addrs[id_]
+
+    def alloc_addrs(self, subnet) -> Mapping[str, ip_interface]:
+        hosts = subnet.hosts()
+        interfaces = {}
+        # With the docker backend, docker itself claims the first ip of every network
+        if self.docker:
+            next(hosts)
+        for elem, proxy in sorted(self._addrs.items()):
+            intf = ip_interface("%s/%s" % (next(hosts), subnet.prefixlen))
+            interfaces[elem] = intf
+            proxy.set_intf(intf)
+        return interfaces
+
+    def __len__(self):
+        return len(self._addrs)
 
 
 class SubnetGenerator(object):
-    def __init__(self, network, docker, in_docker):
+    def __init__(self, network: str, docker: bool):
         self.docker = docker
         if self.docker and network == DEFAULT_NETWORK:
-            if in_docker:
-                network = DEFAULT_SCN_IN_D_NETWORK
-            else:
-                network = DEFAULT_SCN_DC_NETWORK
+            network = DEFAULT_SCN_DC_NETWORK
         if "/" not in network:
             logging.critical("No prefix length specified for network '%s'",
                              network)
@@ -70,10 +116,10 @@ class SubnetGenerator(object):
 
         self._allocations[self._net.prefixlen].append(self._net)
 
-    def register(self, location):
+    def register(self, location: str) -> AddressGenerator:
         return self._subnets[location]
 
-    def alloc_subnets(self):
+    def alloc_subnets(self) -> Mapping[ip_network, NetworkDescription]:
         max_prefix = self._net.max_prefixlen
         networks = {}
         for topo, subnet in sorted(self._subnets.items(), key=lambda x: str(x)):
@@ -101,7 +147,7 @@ class SubnetGenerator(object):
                 new_net = _workaround_ip_network_hosts_py35(new_net)
                 logging.debug("Allocating %s from %s for subnet size %d" %
                               (new_net, alloc, len(subnet)))
-                networks[new_net] = subnet.alloc_addrs(new_net)
+                networks[new_net] = NetworkDescription(topo, subnet.alloc_addrs(new_net))
                 # Repopulate the allocations list with the left-over space
                 self._exclude_net(alloc, new_net)
                 break
@@ -115,68 +161,25 @@ class SubnetGenerator(object):
             self._allocations[net.prefixlen].append(net)
 
 
-class AddressGenerator(object):
-    def __init__(self, docker):
-        self._addrs = defaultdict(lambda: AddressProxy())
-        self.docker = docker
-
-    def register(self, id_):
-        return self._addrs[id_]
-
-    def alloc_addrs(self, subnet):
-        hosts = subnet.hosts()
-        interfaces = {}
-        # With the docker backend, docker itself claims the first ip of every network
-        if self.docker:
-            next(hosts)
-        for elem, proxy in sorted(self._addrs.items()):
-            intf = ip_interface("%s/%s" % (next(hosts), subnet.prefixlen))
-            interfaces[elem] = intf
-            proxy.set_intf(intf)
-        return interfaces
-
-    def __len__(self):
-        return len(self._addrs)
-
-
-class AddressProxy(yaml.YAMLObject):
-    yaml_tag = ""
-
-    def __init__(self):
-        self._intf = None
-        self.ip = None
-
-    def set_intf(self, intf):
-        self._intf = intf
-        self.ip = self._intf.ip
-
-    def __str__(self):
-        return str(self._intf)
-
-    @classmethod
-    def to_yaml(cls, dumper, inst):
-        return dumper.represent_scalar('tag:yaml.org,2002:str', str(inst.ip))
-
-
 class PortGenerator(object):
     def __init__(self):
         self.iter = iter(range(31000, 35000))
         self._ports = defaultdict(lambda: next(self.iter))
 
-    def register(self, id_):
+    def register(self, id_: str) -> int:
         p = self._ports[id_]
         # reserve a quic port
         self._ports[id_+"quic"]
         return p
 
 
-def socket_address_str(ip, port):
+def socket_address_str(ip: str, port: int) -> str:
     if ip.version == 4:
         return "%s:%d" % (ip, port)
     return "[%s]:%d" % (ip, port)
 
 
-def _workaround_ip_network_hosts_py35(net):
+def _workaround_ip_network_hosts_py35(net: ip_network) -> ip_network:
     """
     Returns an _identical_ ipaddress.ip_network for which hosts() which will work as it should.
 
diff --git a/python/topology/prometheus.py b/python/topology/prometheus.py
index f4e750f96..e87de67b4 100755
--- a/python/topology/prometheus.py
+++ b/python/topology/prometheus.py
@@ -19,6 +19,8 @@
 # Stdlib
 import os
 from collections import defaultdict
+from ipaddress import ip_network
+from typing import Mapping
 
 # External packages
 import yaml
@@ -32,6 +34,9 @@ from python.topology.common import (
     prom_addr_dispatcher,
     sciond_ip,
 )
+from python.topology.net import (
+    NetworkDescription
+)
 
 CS_PROM_PORT = 30452
 SCIOND_PROM_PORT = 30455
@@ -44,7 +49,7 @@ PROM_DC_FILE = "prom-dc.yml"
 
 
 class PrometheusGenArgs(ArgsTopoDicts):
-    def __init__(self, args, topo_dicts, networks):
+    def __init__(self, args, topo_dicts, networks: Mapping[ip_network, NetworkDescription]):
         super().__init__(args, topo_dicts)
         self.networks = networks
 
@@ -145,12 +150,11 @@ class PrometheusGenerator(object):
         write_file(targets_path, yaml.dump(target_config, default_flow_style=False))
 
     def _write_dc_file(self):
-        name_prefix = 'prometheus'
-        name = '%s_docker' % name_prefix if self.args.in_docker else name_prefix
+        name = 'prometheus'
         prom_dc = {
             'version': DOCKER_COMPOSE_CONFIG_VERSION,
             'services': {
-                name_prefix: {
+                name: {
                     'image': 'prom/prometheus:v2.6.0',
                     'container_name': name,
                     'network_mode': 'host',
diff --git a/python/topology/sig.py b/python/topology/sig.py
index bbfce9e47..5d1e78078 100644
--- a/python/topology/sig.py
+++ b/python/topology/sig.py
@@ -56,7 +56,7 @@ class SIGGenerator(object):
         self.dc_conf = args.dc_conf
         self.user_spec = os.environ.get('SCION_USERSPEC', '$LOGNAME')
         self.output_base = os.environ.get('SCION_OUTPUT_BASE', os.getcwd())
-        self.prefix = 'docker_' if self.args.in_docker else ''
+        self.prefix = ''
 
     def generate(self):
         for topo_id, topo in self.args.topo_dicts.items():
diff --git a/python/topology/topo.py b/python/topology/topo.py
index 4fe1ccf89..09d753c0a 100755
--- a/python/topology/topo.py
+++ b/python/topology/topo.py
@@ -30,7 +30,6 @@ import yaml
 # SCION
 from python.lib.defines import (
     AS_LIST_FILE,
-    DEFAULT_MTU,
     IFIDS_FILE,
     SCION_MIN_MTU,
     SCION_ROUTER_PORT,
@@ -46,7 +45,10 @@ from python.topology.common import (
     srv_iter,
     TopoID
 )
-from python.topology.net import PortGenerator
+from python.topology.net import (
+    PortGenerator,
+    SubnetGenerator
+)
 
 DEFAULT_LINK_BW = 1000
 
@@ -63,7 +65,12 @@ ADDR_TYPE_6 = 'IPv6'
 
 
 class TopoGenArgs(ArgsBase):
-    def __init__(self, args, topo_config, subnet_gen4, subnet_gen6, default_mtu):
+    def __init__(self,
+                 args: ArgsBase,
+                 topo_config,
+                 subnet_gen4: SubnetGenerator,
+                 subnet_gen6: SubnetGenerator,
+                 default_mtu: int):
         """
         :param ArgsBase args: Contains the passed command line arguments.
         :param dict topo_config: The parsed topology config.
@@ -94,14 +101,24 @@ class TopoGenerator(object):
         self.links = defaultdict(list)
         self.ifid_map = {}
 
-    def _reg_addr(self, topo_id, elem_id, addr_type):
-        subnet = self.args.subnet_gen[addr_type].register(topo_id)
+    def _reg_addr(self, topo_id: TopoID, elem_id, addr_type):
+        subnet = self.args.subnet_gen[addr_type].register(str(topo_id))
+        if self.args.docker and addr_type == ADDR_TYPE_6:
+            # for docker also allocate an IPv4 address so that we have ipv4
+            # range allocated for the network.
+            v4subnet = self.args.subnet_gen[ADDR_TYPE_4].register(str(topo_id) + '_v4')
+            v4subnet.register(elem_id + '_v4')
         return subnet.register(elem_id)
 
     def _reg_link_addrs(self, local_br, remote_br, local_ifid, remote_ifid, addr_type):
         link_name = str(sorted((local_br, remote_br)))
         link_name += str(sorted((local_ifid, remote_ifid)))
         subnet = self.args.subnet_gen[addr_type].register(link_name)
+        if self.args.docker and addr_type == ADDR_TYPE_6:
+            # for docker also allocate an IPv4 address so that we have ipv4
+            # range allocated for the network.
+            v4subnet = self.args.subnet_gen[ADDR_TYPE_4].register(link_name + '_v4')
+            v4subnet.register(local_br + '_v4')
         return subnet.register(local_br), subnet.register(remote_br)
 
     def _iterate(self, f):
@@ -323,7 +340,7 @@ class TopoGenerator(object):
             'bandwidth': attrs.get('bw', DEFAULT_LINK_BW),
             'isd_as': str(remote),
             'link_to': LinkType.to_str(remote_type.lower()),
-            'mtu': attrs.get('mtu', DEFAULT_MTU)
+            'mtu': attrs.get('mtu', self.args.default_mtu)
         }
 
     def _gen_sig_entries(self, topo_id, as_conf):
diff --git a/scion.sh b/scion.sh
index 94151499f..be8ceecf1 100755
--- a/scion.sh
+++ b/scion.sh
@@ -30,7 +30,6 @@ cmd_topology() {
     tar --overwrite -xf bazel-bin/scion-topo.tar -C bin
 
     echo "Create topology, configuration, and execution files."
-    is_running_in_docker && set -- "$@" --in-docker
     python/topology/generator.py "$@"
     if is_docker_be; then
         ./tools/quiet ./tools/dc run utils_chowner
@@ -222,10 +221,6 @@ is_supervisor() {
    [ -f gen/dispatcher/supervisord.conf ]
 }
 
-is_running_in_docker() {
-    cut -d: -f 3 /proc/1/cgroup | grep -q '^/docker/'
-}
-
 cmd_test(){
     local ret=0
     case "$1" in
diff --git a/tools/dc b/tools/dc
index 4343d9824..92004e04f 100755
--- a/tools/dc
+++ b/tools/dc
@@ -85,7 +85,6 @@ dc() {
     local project="$1"
     local dc_file="gen/$1-dc.yml"
     shift
-    is_running_in_docker && project="${project}_docker"
     COMPOSE_FILE="$dc_file" docker-compose -p "$project" --no-ansi "$@"
 }
 
@@ -133,10 +132,6 @@ service_running() {
     [ -n "$cntr" ] && [ -n "$(docker ps -q --no-trunc | grep $cntr)" ]
 }
 
-is_running_in_docker() {
-    cut -d: -f 3 /proc/1/cgroup | grep -q '^/docker/'
-}
-
 PROGRAM="${0##*/}"
 COMMAND="$1"
 shift


Test patch:
diff --git a/integration/integration_test.sh b/integration/integration_test.sh
index 1bcbce8d4..1c32dcce4 100755
--- a/integration/integration_test.sh
+++ b/integration/integration_test.sh
@@ -33,13 +33,9 @@ shutdown() {
     fi
 }
 
-if is_running_in_docker; then
-    log "Starting scion (without building)"
-    ./scion.sh run nobuild | grep -v "started" || exit 1
-else
-     log "Starting scion"
-    ./scion.sh run | grep -v "started" || exit 1
-fi
+log "Starting scion"
+./scion.sh run | grep -v "started" || exit 1
+
 log "Scion status:"
 ./scion.sh status || exit 1
 if is_docker_be; then