From e6510cb89ea9b9d4340ad1123dfa8660e04790fd Mon Sep 17 00:00:00 2001 From: "Martine S. Lenders" Date: Tue, 10 Mar 2020 13:23:03 +0100 Subject: [PATCH 1/4] dhcpv6-pd_ia: initial import of a DHCPv6 server bootstrapper --- dist/tools/dhcpv6-pd_ia/README.md | 25 +++ dist/tools/dhcpv6-pd_ia/base.py | 201 ++++++++++++++++++++++++ dist/tools/dhcpv6-pd_ia/dhcpv6-pd_ia.py | 72 +++++++++ dist/tools/dhcpv6-pd_ia/kea.py | 116 ++++++++++++++ dist/tools/dhcpv6-pd_ia/pkg/__init__.py | 50 ++++++ dist/tools/dhcpv6-pd_ia/pkg/apt.py | 25 +++ dist/tools/dhcpv6-pd_ia/pkg/base.py | 63 ++++++++ dist/tools/dhcpv6-pd_ia/pkg/pacman.py | 25 +++ dist/tools/dhcpv6-pd_ia/util.py | 13 ++ 9 files changed, 590 insertions(+) create mode 100644 dist/tools/dhcpv6-pd_ia/README.md create mode 100644 dist/tools/dhcpv6-pd_ia/base.py create mode 100755 dist/tools/dhcpv6-pd_ia/dhcpv6-pd_ia.py create mode 100644 dist/tools/dhcpv6-pd_ia/kea.py create mode 100644 dist/tools/dhcpv6-pd_ia/pkg/__init__.py create mode 100644 dist/tools/dhcpv6-pd_ia/pkg/apt.py create mode 100644 dist/tools/dhcpv6-pd_ia/pkg/base.py create mode 100644 dist/tools/dhcpv6-pd_ia/pkg/pacman.py create mode 100644 dist/tools/dhcpv6-pd_ia/util.py diff --git a/dist/tools/dhcpv6-pd_ia/README.md b/dist/tools/dhcpv6-pd_ia/README.md new file mode 100644 index 0000000000..e17bd58254 --- /dev/null +++ b/dist/tools/dhcpv6-pd_ia/README.md @@ -0,0 +1,25 @@ +# DHCPv6 PD_IA server + +This provides tooling to bootstrap a [DHCPv6] server with [prefix delegation] +support ([Kea]) to provide a prefix for the [`gnrc_border_router` example]'s +or similar applications' subnets. + +# Usage +Just run the script `dhcpv6-pd_ia.py` with sudo, e.g. + +```sh +sudo ./dhcpv6-pd_ia.py tap0 2001:db8::/32 +``` + +For more information on the arguments, have a look at the usage information of +the script + +```sh +./dhcpv6-pd_ia.py -h +``` + + +[DHCPv6]: https://tools.ietf.org/html/rfc8415 +[prefix delegation]: https://en.wikipedia.org/wiki/Prefix_delegation +[Kea]: http://kea.isc.org +[`gnrc_border_router` example]: ../../../examples/gnrc_border_router diff --git a/dist/tools/dhcpv6-pd_ia/base.py b/dist/tools/dhcpv6-pd_ia/base.py new file mode 100644 index 0000000000..96a2bb97c8 --- /dev/null +++ b/dist/tools/dhcpv6-pd_ia/base.py @@ -0,0 +1,201 @@ +# -*- coding: utf-8 -*- +# vim:fenc=utf-8 +# +# Copyright (C) 2020 Freie Universität Berlin +# +# This file is subject to the terms and conditions of the GNU Lesser +# General Public License v2.1. See the file LICENSE in the top level +# directory for more details. + + +import atexit +import os +import shutil +import subprocess +import time +import threading +import signal +import sys + +import pkg + + +__author__ = "Martine S. Lenders" +__copyright__ = "Copyright (C) 2020 Freie Universität Berlin" +__credits__ = ["Martine S. Lenders"] +__license__ = "LGPLv2.1" +__maintainer__ = "Martine S. Lenders" +__email__ = "m.lenders@fu-berlin.de" + + +# see https://refactoring.guru/design-patterns/singleton/python/example +class _SingletonMeta(type): + """ + This is a thread-safe implementation of Singleton. + """ + + _instance = None + + _lock = threading.Lock() + """ + We now have a lock object that will be used to synchronize threads + during first access to the Singleton. + """ + + def __call__(cls, *args, **kwargs): + # Now, imagine that the program has just been launched. Since + # there's no Singleton instance yet, multiple threads can + # simultaneously pass the previous conditional and reach this point + # almost at the same time. The first of them will acquire lock and + # will proceed further, while the rest will wait here. + with cls._lock: + # The first thread to acquire the lock, reaches this + # conditional, goes inside and creates the Singleton instance. + # Once it leaves the lock block, a thread that might have been + # waiting for the lock release may then enter this section. But + # since the Singleton field is already initialized, the thread + # won't create a new object. + if not cls._instance: + cls._instance = super().__call__(*args, **kwargs) + return cls._instance + + +class DHCPv6Server(metaclass=_SingletonMeta): + """ + Inspired by Daemon class + https://web.archive.org/web/20160305151936/http://www.jejik.com/articles/2007/02/a_simple_unix_linux_daemon_in_python/ + """ + PORT = 547 + command = None + package = None + installer = None + + def __init__(self, daemonized=False, pidfile=None, stdin=None, stdout=None, + stderr=None): + if self.command is None or self.package is None: + raise NotImplementedError("Please inherit from {} and set the " + "the static attributes `command` and " + "`packet`".format(type(self)), file=sys) + assert (not daemonized) or (pidfile is not None) + self.daemonized = daemonized + self.pidfile = pidfile + self.stdin = stdin + self.stdout = stdout + self.stderr = stderr + + @classmethod + def is_installed(cls): + return shutil.which(cls.command[0]) is not None + + def install(self): + self.installer = pkg.PackageManagerFactory.get_installer() + self.installer.install(self.package) + + def daemonize(self): + """ + do the UNIX double-fork magic, see Stevens' "Advanced + Programming in the UNIX Environment" for details (ISBN 0201563177) + http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16 + """ + try: + pid = os.fork() + if pid > 0: + # exit first parent + sys.exit(0) + except OSError as e: + sys.stderr.write("fork #1 failed: %d (%s)\n" % (e.errno, e.strerror)) + sys.exit(1) + + # decouple from parent environment + os.chdir("/") + os.setsid() + os.umask(0) + + # do second fork + try: + pid = os.fork() + if pid > 0: + # exit from second parent + sys.exit(0) + except OSError as e: + sys.stderr.write("fork #2 failed: %d (%s)\n" % (e.errno, e.strerror)) + sys.exit(1) + + # redirect standard file descriptors + if self.stdin: + si = open(self.stdin, 'r') + os.dup2(si.fileno(), sys.stdin.fileno()) + if self.stdout: + sys.stdout.flush() + so = open(self.stdout, 'a+') + os.dup2(so.fileno(), sys.stdout.fileno()) + if self.stderr: + sys.stderr.flush() + se = open(self.stderr, 'a+', 0) + os.dup2(se.fileno(), sys.stderr.fileno()) + + atexit.register(self.delpid) + # write pidfile + with open(self.pidfile, "w+") as f: + f.write("{}\n".format(os.getpid())) + + def delpid(self): + os.remove(self.pidfile) + + def stop(self): + """ + Stop the daemon + """ + # Get the pid from the pidfile + try: + pf = open(self.pidfile, 'r') + pid = int(pf.read().strip()) + pf.close() + except IOError: + pid = None + if not pid: + message = "pidfile %s does not exist. Daemon not running?\n" + sys.stderr.write(message % self.pidfile) + return # not an error in a restart + + # Try killing the daemon process + try: + while 1: + os.kill(pid, signal.SIGTERM) + time.sleep(0.1) + except OSError as err: + err = str(err) + if err.find("No such process") > 0: + if os.path.exists(self.pidfile): + os.remove(self.pidfile) + else: + print(str(err), file=sys.stderr) + sys.exit(1) + + def pre_run(self): + # may be overridden by implementation to do things before running + # the daemon or script + pass + + def run(self): + if not self.is_installed(): + self.install() + if self.daemonized: + """ + Start the daemon + """ + # Check for a pidfile to see if the daemon already runs + try: + pf = open(self.pidfile, 'r') + pid = int(pf.read().strip()) + pf.close() + except (IOError, ValueError): + pid = None + if pid: + message = "pidfile %s already exist. Daemon already running?\n" + sys.stderr.write(message % self.pidfile) + sys.exit(1) + # Start the daemon + self.daemonize() + self.pre_run() + subprocess.run(self.command) diff --git a/dist/tools/dhcpv6-pd_ia/dhcpv6-pd_ia.py b/dist/tools/dhcpv6-pd_ia/dhcpv6-pd_ia.py new file mode 100755 index 0000000000..f463cf7a4c --- /dev/null +++ b/dist/tools/dhcpv6-pd_ia/dhcpv6-pd_ia.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# vim:fenc=utf-8 +# +# Copyright (C) 2020 Freie Universität Berlin +# +# This file is subject to the terms and conditions of the GNU Lesser +# General Public License v2.1. See the file LICENSE in the top level +# directory for more details. + +import argparse +import os + +import kea +import util + +__author__ = "Martine S. Lenders" +__copyright__ = "Copyright (C) 2020 Freie Universität Berlin" +__credits__ = ["Martine S. Lenders"] +__license__ = "LGPLv2.1" +__version__ = "0.0.1" +__maintainer__ = "Martine S. Lenders" +__email__ = "m.lenders@fu-berlin.de" +__status__ = "Experimental" + + +DEFAULT_NEXT_HOP = "fe80::2" +DEFAULT_DELEGATED_LEN = 64 + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("-d", "--daemonized", action="store_true", + help="Run server in background") + parser.add_argument("-p", "--pidfile", nargs="?", + help="PID file for the server. Required with -d.") + parser.add_argument( + "-n", "--next-hop", default=DEFAULT_NEXT_HOP, nargs="?", + help="Next hop address for application (default: fe80::2)" + ) + parser.add_argument( + "-g", "--delegated-len", default=DEFAULT_DELEGATED_LEN, nargs="?", + type=int, + help="The prefix length delegated by the DHCPv6 server. " + "Must be greater or equal to the prefix length of the subnet. " + "This may differ from the prefix length provided in subnet more " + "to understand as a template from which to generate the " + "delegated prefixes from. " + "(default: 64)" + ) + parser.add_argument( + "interface", help="Interface to bind DHCPv6 server to" + ) + parser.add_argument( + "subnet", type=util.split_prefix, + help="Subnet to delegate (must have format /)" + ) + args = parser.parse_args() + if "SUDO_USER" not in os.environ: + raise PermissionError("Must be run with sudo") + if args.delegated_len < args.subnet[1]: + raise ValueError("delegated_len {} is lesser than prefix length {}" + .format(args.delegated_len, args.subnet[1])) + config = kea.KeaConfig(args.interface, args.subnet[0], args.subnet[1], + args.delegated_len) + server = kea.KeaServer(config, args.next_hop, daemonized=args.daemonized, + pidfile=args.pidfile) + server.run() + + +if __name__ == "__main__": + main() diff --git a/dist/tools/dhcpv6-pd_ia/kea.py b/dist/tools/dhcpv6-pd_ia/kea.py new file mode 100644 index 0000000000..bd4bd97e0f --- /dev/null +++ b/dist/tools/dhcpv6-pd_ia/kea.py @@ -0,0 +1,116 @@ +# -*- coding: utf-8 -*- +# vim:fenc=utf-8 +# +# Copyright (C) 2020 Freie Universität Berlin +# +# This file is subject to the terms and conditions of the GNU Lesser +# General Public License v2.1. See the file LICENSE in the top level +# directory for more details. + +import json +import os +import tempfile +import time + +import base + + +__author__ = "Martine S. Lenders" +__copyright__ = "Copyright (C) 2020 Freie Universität Berlin" +__credits__ = ["Martine S. Lenders"] +__license__ = "LGPLv2.1" +__maintainer__ = "Martine S. Lenders" +__email__ = "m.lenders@fu-berlin.de" + + +class KeaConfig(object): + def __init__(self, interface, prefix, prefix_len, delegated_len=None, + valid_lifetime=40000, preferred_lifetime=30000, + renew_timer=10000, rebind_timer=20000): + if not prefix.endswith("::"): + raise ValueError("prefix must end with '::'") + if int(prefix_len) < 1 or int(prefix_len) > 128: + raise ValueError("prefix_len must be between 1 and 128") + if int(valid_lifetime) <= 0: + raise ValueError("valid_lifetime must be greater than 0") + if int(preferred_lifetime) <= 0: + raise ValueError("preferred_lifetime must be greater than 0") + if int(renew_timer) <= 0: + raise ValueError("renew_timer must be greater than 0") + if int(rebind_timer) <= 0: + raise ValueError("rebind_timer must be greater than 0") + if delegated_len is None: + delegated_len = prefix_len + self._config_dict = { + "Dhcp6": { + "interfaces-config": { + "interfaces": [interface] + }, + "lease-database": { + "type": "memfile" + }, + "valid-lifetime": int(valid_lifetime), + "preferred-lifetime": int(preferred_lifetime), + "renew-timer": int(renew_timer), + "rebind-timer": int(rebind_timer), + "subnet6": [{ + "interface": interface, + "subnet": "{}/{}".format(prefix, prefix_len), + "pd-pools": [{ + "prefix": prefix, + "prefix-len": prefix_len, + "delegated-len": delegated_len + }], + }], + }, + } + self.config_file = None + + def __del__(self): + if self.config_file is not None: + self.config_file.close() + + def _dump_json(self): + json.dump(self._config_dict, self.config_file) + self.config_file.flush() + + def __str__(self): + if self.config_file is None: + self.config_file = tempfile.NamedTemporaryFile(mode="w") + self._dump_json() + return self.config_file.name + + @property + def interface(self): + return self._config_dict["Dhcp6"]["interfaces-config"]["interfaces"][0] + + +class KeaServer(base.DHCPv6Server): + command = ["kea-dhcp6", "-c"] + package = { + "generic": {"name": "kea-dhcp6", "url": "https://kea.isc.org/"}, + "Debian": {"name": "kea-dhcp6-server"}, + "Arch": {"name": "kea"}, + } + + def __init__(self, config, next_hop="fe80::2", *args, **kwargs): + super().__init__(*args, **kwargs) + self.next_hop = next_hop + self.config = config + + def pre_run(self): + # create config file in daemon so it is not automatically deleted + self.command.append(str(self.config)) + if self.daemonized: + # need to wait for interface to connect before we can run server + time.sleep(2) + + def run(self): + if not self.is_installed(): + self.install() + if self.installer.os in ["Arch"] and \ + not os.path.exists("/var/run/kea/"): + # workaround: Arch does not create that directory on first + # install + os.makedirs("/var/run/kea/") + super().run() diff --git a/dist/tools/dhcpv6-pd_ia/pkg/__init__.py b/dist/tools/dhcpv6-pd_ia/pkg/__init__.py new file mode 100644 index 0000000000..cfb69d8726 --- /dev/null +++ b/dist/tools/dhcpv6-pd_ia/pkg/__init__.py @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- +# vim:fenc=utf-8 +# +# Copyright (C) 2020 Freie Universität Berlin +# +# This file is subject to the terms and conditions of the GNU Lesser +# General Public License v2.1. See the file LICENSE in the top level +# directory for more details. + + +import platform +import re +import os + +from .apt import Apt +from .pacman import PacMan +from .base import AskToInstall + +__author__ = "Martine S. Lenders" +__copyright__ = "Copyright (C) 2020 Freie Universität Berlin" +__credits__ = ["Martine S. Lenders"] +__license__ = "LGPLv2.1" +__maintainer__ = "Martine S. Lenders" +__email__ = "m.lenders@fu-berlin.de" + + +class PackageManagerFactory(object): + @staticmethod + def _get_linux_distro(): + if hasattr(platform, "linux_distribution"): + return platform.linux_distribution()[0] + elif os.path.exists("/etc/os-release"): + with open("/etc/os-release") as f: + for line in f: + m = re.match(r"^NAME=\"(.+)\"$", line) + if m is not None: + return m.group(1) + return None + + @classmethod + def get_installer(cls): + system = platform.system() + if system == "Linux": + system = cls._get_linux_distro() + if system in ["Debian", "Ubuntu"]: + return Apt("Debian") + if system in ["Arch Linux"]: + return PacMan("Arch") + else: + return AskToInstall() diff --git a/dist/tools/dhcpv6-pd_ia/pkg/apt.py b/dist/tools/dhcpv6-pd_ia/pkg/apt.py new file mode 100644 index 0000000000..1f8dcc52f7 --- /dev/null +++ b/dist/tools/dhcpv6-pd_ia/pkg/apt.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# vim:fenc=utf-8 +# +# Copyright (C) 2020 Freie Universität Berlin +# +# This file is subject to the terms and conditions of the GNU Lesser +# General Public License v2.1. See the file LICENSE in the top level +# directory for more details. + +import subprocess + +from .base import Installer + +__author__ = "Martine S. Lenders" +__copyright__ = "Copyright (C) 2020 Freie Universität Berlin" +__credits__ = ["Martine S. Lenders"] +__license__ = "LGPLv2.1" +__maintainer__ = "Martine S. Lenders" +__email__ = "m.lenders@fu-berlin.de" + + +class Apt(Installer): + def _install(self, package): + subprocess.run(["apt-get", "-y", "install", + package[self.os]["name"]]) diff --git a/dist/tools/dhcpv6-pd_ia/pkg/base.py b/dist/tools/dhcpv6-pd_ia/pkg/base.py new file mode 100644 index 0000000000..bd85ec1435 --- /dev/null +++ b/dist/tools/dhcpv6-pd_ia/pkg/base.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- +# vim:fenc=utf-8 +# +# Copyright (C) 2020 Freie Universität Berlin +# +# This file is subject to the terms and conditions of the GNU Lesser +# General Public License v2.1. See the file LICENSE in the top level +# directory for more details. + +import abc +import sys + + +__author__ = "Martine S. Lenders" +__copyright__ = "Copyright (C) 2020 Freie Universität Berlin" +__credits__ = ["Martine S. Lenders"] +__license__ = "LGPLv2.1" +__maintainer__ = "Martine S. Lenders" +__email__ = "m.lenders@fu-berlin.de" + + +class Installer(abc.ABC): + def __init__(self, os): + self.os = os + + @abc.abstractmethod + def _install(self, package): + """ + Executes the install command + """ + pass + + def install(self, package): + """ + Executes the install command, but asks the user before-hand if it is + okay to do so. + """ + if self._ask(package): + self._install(package) + + @staticmethod + def _ask(package): + valid = {"yes": True, "y": True, "ye": True, + "no": False, "n": False} + while True: + sys.stdout.write("Install package {}? [Y/n] " + .format(package["generic"]["name"])) + sys.stdout.flush() + choice = input().lower() + if choice == '': + return True + elif choice in valid: + return valid[choice] + else: + raise ValueError( + "Please respond with 'yes' or 'no' (or 'y' or 'n').", + ) + + +class AskToInstall(Installer): + def install(self, package): + print("Please install {name} ({url})".format(**package["generic"]), + file=sys.stderr) diff --git a/dist/tools/dhcpv6-pd_ia/pkg/pacman.py b/dist/tools/dhcpv6-pd_ia/pkg/pacman.py new file mode 100644 index 0000000000..1d21580d30 --- /dev/null +++ b/dist/tools/dhcpv6-pd_ia/pkg/pacman.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# vim:fenc=utf-8 +# +# Copyright (C) 2020 Freie Universität Berlin +# +# This file is subject to the terms and conditions of the GNU Lesser +# General Public License v2.1. See the file LICENSE in the top level +# directory for more details. + +import subprocess + +from .base import Installer + +__author__ = "Martine S. Lenders" +__copyright__ = "Copyright (C) 2020 Freie Universität Berlin" +__credits__ = ["Martine S. Lenders"] +__license__ = "LGPLv2.1" +__maintainer__ = "Martine S. Lenders" +__email__ = "m.lenders@fu-berlin.de" + + +class PacMan(Installer): + def _install(self, package): + subprocess.run(["pacman", "--noconfirm", "-S", + package[self.os]["name"]]) diff --git a/dist/tools/dhcpv6-pd_ia/util.py b/dist/tools/dhcpv6-pd_ia/util.py new file mode 100644 index 0000000000..f639db8e3e --- /dev/null +++ b/dist/tools/dhcpv6-pd_ia/util.py @@ -0,0 +1,13 @@ +# -*- coding: utf-8 -*- +# vim:fenc=utf-8 +# +# Copyright (C) 2020 Freie Universität Berlin +# +# This file is subject to the terms and conditions of the GNU Lesser +# General Public License v2.1. See the file LICENSE in the top level +# directory for more details. + + +def split_prefix(route): + comp = route.split("/") + return comp[0], int(comp[1]) From 0c51285b9b52c15fbc3628b4eff7b1eebb087bb9 Mon Sep 17 00:00:00 2001 From: "Martine S. Lenders" Date: Tue, 10 Mar 2020 15:44:49 +0100 Subject: [PATCH 2/4] dist/tools: provide DHCPv6 server support for network setup scripts --- dist/tools/dhcpv6-pd_ia/base.py | 6 ++++-- dist/tools/ethos/setup_network.sh | 29 +++++++++++++++++++++++-- dist/tools/ethos/start_network.sh | 33 ++++++++++++++++++++++++----- dist/tools/sliptty/start_network.sh | 27 ++++++++++++++++++----- 4 files changed, 81 insertions(+), 14 deletions(-) diff --git a/dist/tools/dhcpv6-pd_ia/base.py b/dist/tools/dhcpv6-pd_ia/base.py index 96a2bb97c8..b68f2d6438 100644 --- a/dist/tools/dhcpv6-pd_ia/base.py +++ b/dist/tools/dhcpv6-pd_ia/base.py @@ -63,7 +63,8 @@ class _SingletonMeta(type): class DHCPv6Server(metaclass=_SingletonMeta): """ Inspired by Daemon class - https://web.archive.org/web/20160305151936/http://www.jejik.com/articles/2007/02/a_simple_unix_linux_daemon_in_python/ + https://web.archive.org/web/20160305151936/\ + http://www.jejik.com/articles/2007/02/a_simple_unix_linux_daemon_in_python/ """ PORT = 547 command = None @@ -118,7 +119,8 @@ class DHCPv6Server(metaclass=_SingletonMeta): # exit from second parent sys.exit(0) except OSError as e: - sys.stderr.write("fork #2 failed: %d (%s)\n" % (e.errno, e.strerror)) + sys.stderr.write("fork #2 failed: %d (%s)\n" % + (e.errno, e.strerror)) sys.exit(1) # redirect standard file descriptors diff --git a/dist/tools/ethos/setup_network.sh b/dist/tools/ethos/setup_network.sh index baee92b2d5..3db37236c1 100755 --- a/dist/tools/ethos/setup_network.sh +++ b/dist/tools/ethos/setup_network.sh @@ -18,20 +18,41 @@ cleanup() { echo "Cleaning up..." remove_tap ip a d fd00:dead:beef::1/128 dev lo + if [ -n "${UHCPD_PID}" ]; then + kill ${UHCPD_PID} + fi + if [ -n "${DHCPD_PIDFILE}" ]; then + kill "$(cat ${DHCPD_PIDFILE})" + rm "${DHCPD_PIDFILE}" + fi trap "" INT QUIT TERM EXIT } start_uhcpd() { ${UHCPD} ${TAP} ${PREFIX} + UHCPD_PID=$! } +start_dhcpd() { + DHCPD_PIDFILE=$(mkfile) + ${DHCPD} -d -p ${DHCPD_PIDFILE} ${TAP} ${PREFIX} 2> /dev/null +} + +if [ "$1" = "-d" ] || [ "$1" = "--use-dhcp" ]; then + USE_DHCPV6=1 + shift 1 +else + USE_DHCPV6=0 +fi + TAP=$1 PREFIX=$2 _USER=$3 : ${UHCPD:="$(readlink -f $(dirname $0)"/../uhcpd/bin")/uhcpd"} +: ${DHCPD:="$(readlink -f $(dirname $0)"/../dhcpdv6-pd_ia")/dhcpv6-pd_ia.py"} [ -z "${TAP}" -o -z "${PREFIX}" ] && { - echo "usage: $0 []" + echo "usage: $0 [-d|--use-dhcp] []" exit 1 } @@ -46,4 +67,8 @@ fi trap "cleanup" INT QUIT TERM EXIT -create_tap && start_uhcpd +create_tap && if [ ${USE_DHCPV6} -eq 1 ]; then + start_dhcpd +else + start_uhcpd +fi diff --git a/dist/tools/ethos/start_network.sh b/dist/tools/ethos/start_network.sh index 7b7378994e..3a06c126f0 100755 --- a/dist/tools/ethos/start_network.sh +++ b/dist/tools/ethos/start_network.sh @@ -20,9 +20,13 @@ cleanup() { echo "Cleaning up..." remove_tap ip a d fd00:dead:beef::1/128 dev lo - if [ ${ETHOS_ONLY} -ne 1 ]; then + if [ -n "${UHCPD_PID}" ]; then kill ${UHCPD_PID} fi + if [ -n "${DHCPD_PIDFILE}" ]; then + kill "$(cat ${DHCPD_PIDFILE})" + rm "${DHCPD_PIDFILE}" + fi trap "" INT QUIT TERM EXIT } @@ -31,6 +35,18 @@ start_uhcpd() { UHCPD_PID=$! } +start_dhcpd() { + DHCPD_PIDFILE=$(mktemp) + ${DHCPD} -d -p ${DHCPD_PIDFILE} ${TAP} ${PREFIX} 2> /dev/null +} + +if [ "$1" = "-d" ] || [ "$1" = "--use-dhcpv6" ]; then + USE_DHCPV6=1 + shift 1 +else + USE_DHCPV6=0 +fi + if [ "$1" = "-e" ] || [ "$1" = "--ethos-only" ]; then ETHOS_ONLY=1 shift 1 @@ -44,7 +60,8 @@ PREFIX=$3 BAUDRATE=115200 [ -z "${PORT}" -o -z "${TAP}" -o -z "${PREFIX}" ] && { - echo "usage: $0 [-e|--ethos-only] " \ + echo "usage: $0 [-d|--use-dhcp] [-e|--ethos-only] " \ + " " \ "[baudrate]" exit 1 } @@ -58,9 +75,15 @@ trap "cleanup" INT QUIT TERM EXIT create_tap && \ if [ ${ETHOS_ONLY} -ne 1 ]; then - UHCPD="$(readlink -f "${ETHOS_DIR}/../uhcpd/bin")/uhcpd" - start_uhcpd - START_ETHOS=$? + if [ ${USE_DHCPV6} -eq 1 ]; then + DHCPD="$(readlink -f "${ETHOS_DIR}/../dhcpv6-pd_ia/")/dhcpv6-pd_ia.py" + start_dhcpd + START_ETHOS=$? + else + UHCPD="$(readlink -f "${ETHOS_DIR}/../uhcpd/bin")/uhcpd" + start_uhcpd + START_ETHOS=$? + fi else START_ETHOS=0 fi diff --git a/dist/tools/sliptty/start_network.sh b/dist/tools/sliptty/start_network.sh index fad956f5d7..1cb4455ac5 100755 --- a/dist/tools/sliptty/start_network.sh +++ b/dist/tools/sliptty/start_network.sh @@ -2,6 +2,7 @@ SLIPTTY_DIR="$(cd "$(dirname "$0")" && pwd -P)" UHCPD="$(cd "${SLIPTTY_DIR}/../uhcpd/bin" && pwd -P)/uhcpd" +DHCPD="$(cd "${SLIPTTY_DIR}/../dhcpv6-pd_ia/" && pwd -P)/dhcpv6-pd_ia.py" TUN=sl0 TUN_GLB="fdea:dbee:f::1/64" UHCPD_PID= @@ -92,6 +93,10 @@ cleanup() { if [ -n "${UHCPD_PID}" ]; then kill ${UHCPD_PID} fi + if [ -n "${DHCPD_PIDFILE}" ]; then + kill "$(cat ${DHCPD_PIDFILE})" + rm "${DHCPD_PIDFILE}" + fi trap "" INT QUIT TERM EXIT } @@ -100,17 +105,24 @@ start_uhcpd() { UHCPD_PID=$! } +start_dhcpd() { + DHCPD_PIDFILE=$(mktemp) + ${DHCPD} -d -p ${DHCPD_PIDFILE} ${TAP} ${PREFIX} 2> /dev/null +} + usage() { - echo "usage: $1 [-I ] [-e] [-g /] serial [baudrate]" - echo "usage: $1 [-I ] [-e] [-g /] tcp:host [port]" + echo "usage: $1 [-I ] [-d] [-e] [-g /] serial [baudrate]" + echo "usage: $1 [-I ] [-d] [-e] [-g /] tcp:host [port]" } trap "cleanup" INT QUIT TERM EXIT SLIP_ONLY=0 +USE_DHCPV6=1 -while getopts ehI: opt; do +while getopts dehI: opt; do case ${opt} in + d) USE_DHCPV6=1; shift 1;; e) SLIP_ONLY=1; shift 1;; I) TUN=${OPTARG}; shift 2;; g) TUN_GLB=${OPTARG}; shift 2;; @@ -128,8 +140,13 @@ fi create_tun && \ if [ ${SLIP_ONLY} -ne 1 ]; then - start_uhcpd - START_SLIP=$? + if [ ${USE_DHCPV6} -eq 1 ]; then + start_dhcpd + START_SLIP=$? + else + start_uhcpd + START_SLIP=$? + fi else START_SLIP=0 fi From 74b34c2a03d6288557ff0e3ba106df6e597740f4 Mon Sep 17 00:00:00 2001 From: "Martine S. Lenders" Date: Tue, 10 Mar 2020 16:17:06 +0100 Subject: [PATCH 3/4] examples: bootstrap host-side DHCPv6 server with gnrc_border_router --- examples/gnrc_border_router/Makefile | 2 +- examples/gnrc_border_router/Makefile.ethos.conf | 4 ++-- examples/gnrc_border_router/Makefile.slip.conf | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/gnrc_border_router/Makefile b/examples/gnrc_border_router/Makefile index 00348ed2fe..acf337533f 100644 --- a/examples/gnrc_border_router/Makefile +++ b/examples/gnrc_border_router/Makefile @@ -52,7 +52,7 @@ QUIET ?= 1 # Ethos/native TAP interface and UHCP prefix can be configured from make command TAP ?= tap0 -IPV6_PREFIX ?= 2001:db8::/64 +IPV6_PREFIX ?= 2001:db8::/32 # MODULE DEPENDENT CONFIGURATION IMPORT # ===================================== diff --git a/examples/gnrc_border_router/Makefile.ethos.conf b/examples/gnrc_border_router/Makefile.ethos.conf index 138ee5077b..4d8c504738 100644 --- a/examples/gnrc_border_router/Makefile.ethos.conf +++ b/examples/gnrc_border_router/Makefile.ethos.conf @@ -3,10 +3,10 @@ CFLAGS += -DETHOS_BAUDRATE=$(ETHOS_BAUDRATE) STATIC_ROUTES ?= 1 ifeq (1,$(USE_DHCPV6)) - ETHOS_ONLY=--ethos-only + FLAGS_EXTRAS=--use-dhcpv6 endif # Configure terminal parameters TERMDEPS += host-tools TERMPROG ?= sudo sh $(RIOTTOOLS)/ethos/start_network.sh -TERMFLAGS ?= $(ETHOS_ONLY) $(PORT) $(TAP) $(IPV6_PREFIX) +TERMFLAGS ?= $(FLAGS_EXTRAS) $(PORT) $(TAP) $(IPV6_PREFIX) diff --git a/examples/gnrc_border_router/Makefile.slip.conf b/examples/gnrc_border_router/Makefile.slip.conf index 7721ff6fb9..a6d420d3c9 100644 --- a/examples/gnrc_border_router/Makefile.slip.conf +++ b/examples/gnrc_border_router/Makefile.slip.conf @@ -5,10 +5,10 @@ CFLAGS += -DSLIPDEV_PARAM_BAUDRATE=$(SLIP_BAUDRATE) STATIC_ROUTES ?= 1 ifeq (1,$(USE_DHCPV6)) - SLIP_ONLY=-e + FLAGS_EXTRAS=-d endif # Configure terminal parameters TERMDEPS += sliptty TERMPROG ?= sudo sh $(RIOTTOOLS)/sliptty/start_network.sh -TERMFLAGS ?= $(SLIP_ONLY) $(IPV6_PREFIX) $(PORT) $(SLIP_BAUDRATE) +TERMFLAGS ?= $(FLAGS_EXTRAS) $(IPV6_PREFIX) $(PORT) $(SLIP_BAUDRATE) From 58a2d13b01c56c0f049e18dabe0c3a6a7eb11cad Mon Sep 17 00:00:00 2001 From: "Martine S. Lenders" Date: Tue, 10 Mar 2020 16:49:20 +0100 Subject: [PATCH 4/4] CODEOWNERS: update for dhcpv6-pd_ia --- CODEOWNERS | 1 + 1 file changed, 1 insertion(+) diff --git a/CODEOWNERS b/CODEOWNERS index 27c1908b70..a49182d219 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -61,6 +61,7 @@ /doc/ @aabadie @jia200x /dist/tools/sliptty/ @miri64 +/dist/tools/dhcpv6-pd_ia/ @miri64 /drivers/ad7746/ @leandrolanzieri /drivers/at24mac/ @benpicco