Blob Blame History Raw
#!/usr/bin/env python3
# SPDX-License-Identifier: LGPL-2.1+
# ~~~
#   Description: Tests for Dynamic Host Configuration v6 Server (dhcp6s)
#
#   Author: Susant Sahani <susant@redhat.com>
#   Copyright (c) 2018 Red Hat, Inc.
# ~~~

import errno
import os
import sys
import time
import unittest
import subprocess
import signal
import shutil
import psutil
import socket
from pyroute2 import IPRoute

RADVD_TCP_DUMP_FILE='/tmp/radvd-tcp-dump-ipv6-ra.pcap'

RADVD_CONFIG_FILE ='/var/run/wide-dhcpv6-ci/radvd-ci.conf'
RADVD_PID_FILE='/var/run/wide-dhcpv6-ci/radvd.pid'
RADVD_LOG_FILE='/var/run/wide-dhcpv6-ci/radvd.log'

DHCP6S_CONFIG_FILE='/var/run/wide-dhcpv6-ci/dhcp6s-ci.conf'
DHCP6S_PID_FILE='/var/run/wide-dhcpv6-ci/dhcp6s.pid'

DHCP6C_CONFIG_FILE='/var/run/wide-dhcpv6-ci/dhcp6c.conf'
DHCP6C_PID_FILE='/var/run/wide-dhcpv6-ci/dhcp6c.pid'
DHCP6C_DUID_FILE='/var/db/dhcp6c_duid'

DHCLIENT_PID_FILE='/var/run/wide-dhcpv6-ci/test-dhclient.pid'

def setUpModule():
    """Initialize the environment, and perform sanity checks on it."""

    if shutil.which('radvd') is None:
        raise OSError(errno.ENOENT, 'radvd not found')

    if shutil.which('dhcp6s') is None:
        raise OSError(errno.ENOENT, 'dhcp6s not found')

    if shutil.which('dhclient') is None:
        raise OSError(errno.ENOENT, 'dhclient not found')

def tearDownModule():
    pass

class GenericUtilities():
    """Provide a set of utility functions start stop daemons. write config files etc """

    def StartRadvd(self):
        """Start radvd"""

        subprocess.check_output(['radvd', '-d5', '-C', RADVD_CONFIG_FILE, '-p', RADVD_PID_FILE, '-l', RADVD_LOG_FILE])

    def StartDhcp6s(self):
        """Start dhcp6s"""

        subprocess.check_output(['dhcp6s', '-c', DHCP6S_CONFIG_FILE, 'veth-peer', '-dD', '-P', DHCP6S_PID_FILE])

    def StartDhclient(self, conf_file):
        """Start dhclient"""

        path=os.path.join('/var/run/wide-dhcpv6-ci', conf_file)
        subprocess.check_output(['dhclient', '-i', 'veth-test', '-6','-pf', DHCLIENT_PID_FILE, 'veth-peer', '-cf', path])

    def StopDaemon(self, pid_file):

        with open(pid_file, 'r') as f:
                 pid = f.read().rstrip(' \t\r\n\0')
                 os.kill(int(pid), signal.SIGKILL)

        os.remove(pid_file)

    def SetupVethInterface(self):
        """Setup veth interface"""

        ip = IPRoute()

        ip.link('add', ifname='veth-test', peer='veth-peer', kind='veth')
        idx_veth_test= ip.link_lookup(ifname='veth-test')[0]
        idx_veth_peer = ip.link_lookup(ifname='veth-peer')[0]

        ip.link('set', index=idx_veth_test, address='02:01:02:03:04:08')
        ip.link('set', index=idx_veth_peer, address='02:01:02:03:04:09')
        ip.link('set', index=idx_veth_test, state='up')
        ip.link('set', index=idx_veth_peer, state='up')

        ip.close()
        time.sleep(4)

    def TearDownVethInterface(self):

        ip = IPRoute()
        ip.link('del', index=ip.link_lookup(ifname='veth-test')[0])
        ip.close()

class Dhcp6sTests(unittest.TestCase, GenericUtilities):

    def setUp(self):
        """ setup veth and write radvd and dhcpv6configs """
        self.SetupVethInterface()

    def tearDown(self):
        self.StopDaemon(DHCLIENT_PID_FILE)
        self.StopDaemon(DHCP6S_PID_FILE)
        self.StopDaemon(RADVD_PID_FILE)

        self.TearDownVethInterface()

    def test_dhcp6s_assigns_static_address_using_duid1(self):
        """ DHCP6S assigns same (static) addresses to same hosts using known DUID values"""

        self.StartRadvd()
        self.StartDhcp6s()

        self.StartDhclient('dhclient-ci-duid2.conf')

        time.sleep(10)
        output=subprocess.check_output(['ip','address', 'show', 'veth-test']).rstrip().decode('utf-8')

        # Address prefix
        self.assertRegex(output, "2001:888:db8:1::c")

    def test_dhcp6s_assigns_static_address_using_duid2(self):
        """ DHCP6S assigns same (static) addresses to same hosts using known DUID values"""

        self.StartRadvd()
        self.StartDhcp6s()

        self.StartDhclient('dhclient-ci-duid1.conf')

        time.sleep(15)
        output=subprocess.check_output(['ip','address', 'show', 'veth-test']).rstrip().decode('utf-8')

        # Address prefix
        self.assertRegex(output, "2001:888:db8:1::b")

if __name__ == '__main__':
    unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout,
                                                     verbosity=3))