Susant Sahani dd405d7
#!/usr/bin/env python3
Susant Sahani dd405d7
# SPDX-License-Identifier: LGPL-2.1+
Susant Sahani dd405d7
# ~~~
Susant Sahani dd405d7
#   Description: Tests for Dynamic Host Configuration v6 Server (dhcp6s)
Susant Sahani dd405d7
#
Susant Sahani dd405d7
#   Author: Susant Sahani <susant@redhat.com>
Susant Sahani dd405d7
#   Copyright (c) 2018 Red Hat, Inc.
Susant Sahani dd405d7
# ~~~
Susant Sahani dd405d7
Susant Sahani dd405d7
import errno
Susant Sahani dd405d7
import os
Susant Sahani dd405d7
import sys
Susant Sahani dd405d7
import time
Susant Sahani dd405d7
import unittest
Susant Sahani dd405d7
import subprocess
Susant Sahani dd405d7
import signal
Susant Sahani dd405d7
import shutil
Susant Sahani dd405d7
import psutil
Susant Sahani dd405d7
import socket
Susant Sahani dd405d7
from pyroute2 import IPRoute
Susant Sahani dd405d7
Susant Sahani dd405d7
RADVD_TCP_DUMP_FILE='/tmp/radvd-tcp-dump-ipv6-ra.pcap'
Susant Sahani dd405d7
Susant Sahani dd405d7
RADVD_CONFIG_FILE ='/var/run/wide-dhcpv6-ci/radvd-ci.conf'
Susant Sahani dd405d7
RADVD_PID_FILE='/var/run/wide-dhcpv6-ci/radvd.pid'
Susant Sahani dd405d7
RADVD_LOG_FILE='/var/run/wide-dhcpv6-ci/radvd.log'
Susant Sahani dd405d7
Susant Sahani dd405d7
DHCP6S_CONFIG_FILE='/var/run/wide-dhcpv6-ci/dhcp6s-ci.conf'
Susant Sahani dd405d7
DHCP6S_PID_FILE='/var/run/wide-dhcpv6-ci/dhcp6s.pid'
Susant Sahani dd405d7
Susant Sahani dd405d7
DHCP6C_CONFIG_FILE='/var/run/wide-dhcpv6-ci/dhcp6c.conf'
Susant Sahani dd405d7
DHCP6C_PID_FILE='/var/run/wide-dhcpv6-ci/dhcp6c.pid'
Susant Sahani dd405d7
DHCP6C_DUID_FILE='/var/db/dhcp6c_duid'
Susant Sahani dd405d7
Susant Sahani dd405d7
DHCLIENT_PID_FILE='/var/run/wide-dhcpv6-ci/test-dhclient.pid'
Susant Sahani dd405d7
Susant Sahani dd405d7
def setUpModule():
Susant Sahani dd405d7
    """Initialize the environment, and perform sanity checks on it."""
Susant Sahani dd405d7
Susant Sahani dd405d7
    if shutil.which('radvd') is None:
Susant Sahani dd405d7
        raise OSError(errno.ENOENT, 'radvd not found')
Susant Sahani dd405d7
Susant Sahani dd405d7
    if shutil.which('dhcp6s') is None:
Susant Sahani dd405d7
        raise OSError(errno.ENOENT, 'dhcp6s not found')
Susant Sahani dd405d7
Susant Sahani dd405d7
    if shutil.which('dhclient') is None:
Susant Sahani dd405d7
        raise OSError(errno.ENOENT, 'dhclient not found')
Susant Sahani dd405d7
Susant Sahani dd405d7
def tearDownModule():
Susant Sahani dd405d7
    pass
Susant Sahani dd405d7
Susant Sahani dd405d7
class GenericUtilities():
Susant Sahani dd405d7
    """Provide a set of utility functions start stop daemons. write config files etc """
Susant Sahani dd405d7
Susant Sahani dd405d7
    def StartRadvd(self):
Susant Sahani dd405d7
        """Start radvd"""
Susant Sahani dd405d7
Susant Sahani dd405d7
        subprocess.check_output(['radvd', '-d5', '-C', RADVD_CONFIG_FILE, '-p', RADVD_PID_FILE, '-l', RADVD_LOG_FILE])
Susant Sahani dd405d7
Susant Sahani dd405d7
    def StartDhcp6s(self):
Susant Sahani dd405d7
        """Start dhcp6s"""
Susant Sahani dd405d7
Susant Sahani dd405d7
        subprocess.check_output(['dhcp6s', '-c', DHCP6S_CONFIG_FILE, 'veth-peer', '-dD', '-P', DHCP6S_PID_FILE])
Susant Sahani dd405d7
Susant Sahani dd405d7
    def StartDhclient(self, conf_file):
Susant Sahani dd405d7
        """Start dhclient"""
Susant Sahani dd405d7
Susant Sahani dd405d7
        path=os.path.join('/var/run/wide-dhcpv6-ci', conf_file)
Susant Sahani dd405d7
        subprocess.check_output(['dhclient', '-i', 'veth-test', '-6','-pf', DHCLIENT_PID_FILE, 'veth-peer', '-cf', path])
Susant Sahani dd405d7
Susant Sahani dd405d7
    def StopDaemon(self, pid_file):
Susant Sahani dd405d7
Susant Sahani dd405d7
        with open(pid_file, 'r') as f:
Susant Sahani dd405d7
                 pid = f.read().rstrip(' \t\r\n\0')
Susant Sahani dd405d7
                 os.kill(int(pid), signal.SIGKILL)
Susant Sahani dd405d7
Susant Sahani dd405d7
        os.remove(pid_file)
Susant Sahani dd405d7
Susant Sahani dd405d7
    def SetupVethInterface(self):
Susant Sahani dd405d7
        """Setup veth interface"""
Susant Sahani dd405d7
Susant Sahani dd405d7
        ip = IPRoute()
Susant Sahani dd405d7
Susant Sahani dd405d7
        ip.link('add', ifname='veth-test', peer='veth-peer', kind='veth')
Susant Sahani dd405d7
        idx_veth_test= ip.link_lookup(ifname='veth-test')[0]
Susant Sahani dd405d7
        idx_veth_peer = ip.link_lookup(ifname='veth-peer')[0]
Susant Sahani dd405d7
Susant Sahani dd405d7
        ip.link('set', index=idx_veth_test, address='02:01:02:03:04:08')
Susant Sahani dd405d7
        ip.link('set', index=idx_veth_peer, address='02:01:02:03:04:09')
Susant Sahani dd405d7
        ip.link('set', index=idx_veth_test, state='up')
Susant Sahani dd405d7
        ip.link('set', index=idx_veth_peer, state='up')
Susant Sahani dd405d7
Susant Sahani dd405d7
        ip.close()
Susant Sahani dd405d7
        time.sleep(4)
Susant Sahani dd405d7
Susant Sahani dd405d7
    def TearDownVethInterface(self):
Susant Sahani dd405d7
Susant Sahani dd405d7
        ip = IPRoute()
Susant Sahani dd405d7
        ip.link('del', index=ip.link_lookup(ifname='veth-test')[0])
Susant Sahani dd405d7
        ip.close()
Susant Sahani dd405d7
Susant Sahani dd405d7
class Dhcp6sTests(unittest.TestCase, GenericUtilities):
Susant Sahani dd405d7
Susant Sahani dd405d7
    def setUp(self):
Susant Sahani dd405d7
        """ setup veth and write radvd and dhcpv6configs """
Susant Sahani dd405d7
        self.SetupVethInterface()
Susant Sahani dd405d7
Susant Sahani dd405d7
    def tearDown(self):
Susant Sahani dd405d7
        self.StopDaemon(DHCLIENT_PID_FILE)
Susant Sahani dd405d7
        self.StopDaemon(DHCP6S_PID_FILE)
Susant Sahani dd405d7
        self.StopDaemon(RADVD_PID_FILE)
Susant Sahani dd405d7
Susant Sahani dd405d7
        self.TearDownVethInterface()
Susant Sahani dd405d7
Susant Sahani dd405d7
    def test_dhcp6s_assigns_static_address_using_duid1(self):
Susant Sahani dd405d7
        """ DHCP6S assigns same (static) addresses to same hosts using known DUID values"""
Susant Sahani dd405d7
Susant Sahani dd405d7
        self.StartRadvd()
Susant Sahani dd405d7
        self.StartDhcp6s()
Susant Sahani dd405d7
Susant Sahani dd405d7
        self.StartDhclient('dhclient-ci-duid2.conf')
Susant Sahani dd405d7
Susant Sahani dd405d7
        time.sleep(10)
Susant Sahani dd405d7
        output=subprocess.check_output(['ip','address', 'show', 'veth-test']).rstrip().decode('utf-8')
Susant Sahani dd405d7
Susant Sahani dd405d7
        # Address prefix
Susant Sahani dd405d7
        self.assertRegex(output, "2001:888:db8:1::c")
Susant Sahani dd405d7
Susant Sahani dd405d7
    def test_dhcp6s_assigns_static_address_using_duid2(self):
Susant Sahani dd405d7
        """ DHCP6S assigns same (static) addresses to same hosts using known DUID values"""
Susant Sahani dd405d7
Susant Sahani dd405d7
        self.StartRadvd()
Susant Sahani dd405d7
        self.StartDhcp6s()
Susant Sahani dd405d7
Susant Sahani dd405d7
        self.StartDhclient('dhclient-ci-duid1.conf')
Susant Sahani dd405d7
Susant Sahani dd405d7
        time.sleep(15)
Susant Sahani dd405d7
        output=subprocess.check_output(['ip','address', 'show', 'veth-test']).rstrip().decode('utf-8')
Susant Sahani dd405d7
Susant Sahani dd405d7
        # Address prefix
Susant Sahani dd405d7
        self.assertRegex(output, "2001:888:db8:1::b")
Susant Sahani dd405d7
Susant Sahani dd405d7
if __name__ == '__main__':
Susant Sahani dd405d7
    unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout,
Susant Sahani dd405d7
                                                     verbosity=3))