1# Copyright 2015 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""
6Encapsulate functionality of the dhcpd Daemon. Support writing out a
7configuration file as well as starting and stopping the service.
8"""
9
10import os
11import signal
12
13from autotest_lib.client.common_lib import error
14from autotest_lib.client.common_lib import utils
15
16# Filenames used for execution.
17DHCPV6_SERVER_EXECUTABLE = '/usr/local/sbin/dhcpd'
18DHCPV6_SERVER_CONFIG_FILE = '/tmp/dhcpv6_test.conf'
19DHCPV6_SERVER_PID_FILE = '/tmp/dhcpv6_test.pid'
20
21DHCPV6_SERVER_ADDRESS = '2001:db8:0:1::1'
22DHCPV6_SERVER_SUBNET_PREFIX = '2001:db8:0:1::'
23DHCPV6_SERVER_SUBNET_PREFIX_LENGTH = 64
24DHCPV6_ADDRESS_RANGE_LOW = 0x100
25DHCPV6_ADDRESS_RANGE_HIGH = 0x1ff
26DHCPV6_PREFIX_DELEGATION_INDEX_LOW = 0x1
27DHCPV6_PREFIX_DELEGATION_INDEX_HIGH = 0xf
28DHCPV6_PREFIX_DELEGATION_RANGE_FORMAT = '2001:db8:0:%x00::'
29DHCPV6_PREFIX_DELEGATION_RANGE_LOW = (DHCPV6_PREFIX_DELEGATION_RANGE_FORMAT %
30                                      (DHCPV6_PREFIX_DELEGATION_INDEX_LOW))
31DHCPV6_PREFIX_DELEGATION_RANGE_HIGH = (DHCPV6_PREFIX_DELEGATION_RANGE_FORMAT %
32                                       (DHCPV6_PREFIX_DELEGATION_INDEX_HIGH))
33DHCPV6_PREFIX_DELEGATION_PREFIX_LENGTH = 56
34DHCPV6_DEFAULT_LEASE_TIME = 600
35DHCPV6_MAX_LEASE_TIME = 7200
36DHCPV6_NAME_SERVERS = 'fec0:0:0:1::1'
37DHCPV6_DOMAIN_SEARCH = 'domain.example'
38
39CONFIG_DEFAULT_LEASE_TIME = 'default_lease_time'
40CONFIG_MAX_LEASE_TIME = 'max_lease_time'
41CONFIG_SUBNET = 'subnet'
42CONFIG_RANGE = 'range'
43CONFIG_NAME_SERVERS = 'name_servers'
44CONFIG_DOMAIN_SEARCH = 'domain_search'
45CONFIG_PREFIX_RANGE = 'prefix_range'
46
47class Dhcpv6TestServer(object):
48    """
49    This is an embodiment of the DHCPv6 server (dhcpd) process.  It converts an
50    config dict into parameters for the dhcpd configuration file and
51    manages startup and cleanup of the process.
52    """
53
54    def __init__(self, interface = None):
55        if not os.path.exists(DHCPV6_SERVER_EXECUTABLE):
56            raise error.TestNAError('Could not find executable %s; '
57                                    'this is likely an old version of '
58                                    'ChromiumOS' %
59                                    DHCPV6_SERVER_EXECUTABLE)
60        self._interface = interface
61        # "2001:db8:0:1::/64"
62        subnet = '%s/%d' % (DHCPV6_SERVER_SUBNET_PREFIX,
63                            DHCPV6_SERVER_SUBNET_PREFIX_LENGTH)
64        # "2001:db8:0:1::100 2001:db8:1::1ff"
65        range = '%s%x %s%x' % (DHCPV6_SERVER_SUBNET_PREFIX,
66                               DHCPV6_ADDRESS_RANGE_LOW,
67                               DHCPV6_SERVER_SUBNET_PREFIX,
68                               DHCPV6_ADDRESS_RANGE_HIGH)
69        # "2001:db8:0:100:: 2001:db8:1:f00:: /56"
70        prefix_range = '%s %s /%d' % (DHCPV6_PREFIX_DELEGATION_RANGE_LOW,
71                                      DHCPV6_PREFIX_DELEGATION_RANGE_HIGH,
72                                      DHCPV6_PREFIX_DELEGATION_PREFIX_LENGTH)
73        self._config = {
74            CONFIG_DEFAULT_LEASE_TIME: DHCPV6_DEFAULT_LEASE_TIME,
75            CONFIG_MAX_LEASE_TIME: DHCPV6_MAX_LEASE_TIME,
76            CONFIG_SUBNET: subnet,
77            CONFIG_RANGE: range,
78            CONFIG_NAME_SERVERS: DHCPV6_NAME_SERVERS,
79            CONFIG_DOMAIN_SEARCH: DHCPV6_DOMAIN_SEARCH,
80            CONFIG_PREFIX_RANGE: prefix_range
81        }
82
83
84    def _write_config_file(self):
85        """
86        Write out a configuration file for DHCPv6 server to use.
87        """
88        config = '\n'.join([
89                     'default-lease-time %(default_lease_time)d;',
90                     'max-lease-time %(max_lease_time)d;',
91                     'subnet6 %(subnet)s {',
92                     '  range6 %(range)s;',
93                     '  option dhcp6.name-servers %(name_servers)s;',
94                     '  option dhcp6.domain-search \"%(domain_search)s\";',
95                     '  prefix6 %(prefix_range)s;',
96                     '}'
97                     '']) % self._config
98        with open(DHCPV6_SERVER_CONFIG_FILE, 'w') as f:
99            f.write(config)
100
101
102    def _cleanup(self):
103        """
104        Cleanup temporary files.  If PID file exists, also kill the
105        associated process.
106        """
107        if os.path.exists(DHCPV6_SERVER_PID_FILE):
108            pid = int(file(DHCPV6_SERVER_PID_FILE).read())
109            os.remove(DHCPV6_SERVER_PID_FILE)
110            try:
111                os.kill(pid, signal.SIGTERM)
112            except OSError:
113                pass
114        if os.path.exists(DHCPV6_SERVER_CONFIG_FILE):
115            os.remove(DHCPV6_SERVER_CONFIG_FILE)
116
117
118    def start(self):
119        """
120        Start the DHCPv6 server.  The server will daemonize itself and
121        run in the background.
122        """
123        self._cleanup()
124        self._write_config_file()
125        utils.system('%s -6 -pf %s -cf %s %s' %
126                     (DHCPV6_SERVER_EXECUTABLE,
127                      DHCPV6_SERVER_PID_FILE,
128                      DHCPV6_SERVER_CONFIG_FILE,
129                      self._interface))
130
131
132    def stop(self):
133        """
134        Halt the DHCPv6 server.
135        """
136        self._cleanup()
137