• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#   Copyright 2016 - The Android Open Source Project
2#
3#   Licensed under the Apache License, Version 2.0 (the "License");
4#   you may not use this file except in compliance with the License.
5#   You may obtain a copy of the License at
6#
7#       http://www.apache.org/licenses/LICENSE-2.0
8#
9#   Unless required by applicable law or agreed to in writing, software
10#   distributed under the License is distributed on an "AS IS" BASIS,
11#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12#   See the License for the specific language governing permissions and
13#   limitations under the License.
14
15import copy
16
17_ROUTER_DNS = '8.8.8.8, 4.4.4.4'
18
19
20class Subnet(object):
21    """Configs for a subnet  on the dhcp server.
22
23    Attributes:
24        network: ipaddress.IPv4Network, the network that this subnet is in.
25        start: ipaddress.IPv4Address, the start ip address.
26        end: ipaddress.IPv4Address, the end ip address.
27        router: The router to give to all hosts in this subnet.
28        lease_time: The lease time of all hosts in this subnet.
29        additional_parameters: A dictionary corresponding to DHCP parameters.
30        additional_options: A dictionary corresponding to DHCP options.
31    """
32
33    def __init__(self,
34                 subnet,
35                 start=None,
36                 end=None,
37                 router=None,
38                 lease_time=None,
39                 additional_parameters={},
40                 additional_options={}):
41        """
42        Args:
43            subnet: ipaddress.IPv4Network, The address space of the subnetwork
44                    served by the DHCP server.
45            start: ipaddress.IPv4Address, The start of the address range to
46                   give hosts in this subnet. If not given, the second ip in
47                   the network is used, under the assumption that the first
48                   address is the router.
49            end: ipaddress.IPv4Address, The end of the address range to give
50                 hosts. If not given then the address prior to the broadcast
51                 address (i.e. the second to last ip in the network) is used.
52            router: ipaddress.IPv4Address, The router hosts should use in this
53                    subnet. If not given the first ip in the network is used.
54            lease_time: int, The amount of lease time in seconds
55                        hosts in this subnet have.
56            additional_parameters: A dictionary corresponding to DHCP parameters.
57            additional_options: A dictionary corresponding to DHCP options.
58        """
59        self.network = subnet
60
61        if start:
62            self.start = start
63        else:
64            self.start = self.network[2]
65
66        if not self.start in self.network:
67            raise ValueError('The start range is not in the subnet.')
68        if self.start.is_reserved:
69            raise ValueError('The start of the range cannot be reserved.')
70
71        if end:
72            self.end = end
73        else:
74            self.end = self.network[-2]
75
76        if not self.end in self.network:
77            raise ValueError('The end range is not in the subnet.')
78        if self.end.is_reserved:
79            raise ValueError('The end of the range cannot be reserved.')
80        if self.end < self.start:
81            raise ValueError(
82                'The end must be an address larger than the start.')
83
84        if router:
85            if router >= self.start and router <= self.end:
86                raise ValueError('Router must not be in pool range.')
87            if not router in self.network:
88                raise ValueError('Router must be in the given subnet.')
89
90            self.router = router
91        else:
92            # TODO: Use some more clever logic so that we don't have to search
93            # every host potentially.
94            # This is especially important if we support IPv6 networks in this
95            # configuration. The improved logic that we can use is:
96            #    a) erroring out if start and end encompass the whole network, and
97            #    b) picking any address before self.start or after self.end.
98            self.router = None
99            for host in self.network.hosts():
100                if host < self.start or host > self.end:
101                    self.router = host
102                    break
103
104            if not self.router:
105                raise ValueError('No useable host found.')
106
107        self.lease_time = lease_time
108        self.additional_parameters = additional_parameters
109        self.additional_options = additional_options
110        if 'domain-name-servers' not in self.additional_options:
111            self.additional_options['domain-name-servers'] = _ROUTER_DNS
112
113
114class StaticMapping(object):
115    """Represents a static dhcp host.
116
117    Attributes:
118        identifier: How id of the host (usually the mac addres
119                    e.g. 00:11:22:33:44:55).
120        address: ipaddress.IPv4Address, The ipv4 address to give the host.
121        lease_time: How long to give a lease to this host.
122    """
123
124    def __init__(self, identifier, address, lease_time=None):
125        self.identifier = identifier
126        self.ipv4_address = address
127        self.lease_time = lease_time
128
129
130class DhcpConfig(object):
131    """The configs for a dhcp server.
132
133    Attributes:
134        subnets: A list of all subnets for the dhcp server to create.
135        static_mappings: A list of static host addresses.
136        default_lease_time: The default time for a lease.
137        max_lease_time: The max time to allow a lease.
138    """
139
140    def __init__(self,
141                 subnets=None,
142                 static_mappings=None,
143                 default_lease_time=600,
144                 max_lease_time=7200):
145        self.subnets = copy.deepcopy(subnets) if subnets else []
146        self.static_mappings = (copy.deepcopy(static_mappings)
147                                if static_mappings else [])
148        self.default_lease_time = default_lease_time
149        self.max_lease_time = max_lease_time
150
151    def render_config_file(self):
152        """Renders the config parameters into a format compatible with
153        the ISC DHCP server (dhcpd).
154        """
155        lines = []
156
157        if self.default_lease_time:
158            lines.append('default-lease-time %d;' % self.default_lease_time)
159        if self.max_lease_time:
160            lines.append('max-lease-time %s;' % self.max_lease_time)
161
162        for subnet in self.subnets:
163            address = subnet.network.network_address
164            mask = subnet.network.netmask
165            router = subnet.router
166            start = subnet.start
167            end = subnet.end
168            lease_time = subnet.lease_time
169            additional_parameters = subnet.additional_parameters
170            additional_options = subnet.additional_options
171
172            lines.append('subnet %s netmask %s {' % (address, mask))
173            lines.append('\tpool {')
174            lines.append('\t\toption subnet-mask %s;' % mask)
175            lines.append('\t\toption routers %s;' % router)
176            lines.append('\t\trange %s %s;' % (start, end))
177            if lease_time:
178                lines.append('\t\tdefault-lease-time %d;' % lease_time)
179                lines.append('\t\tmax-lease-time %d;' % lease_time)
180            for param, value in additional_parameters.items():
181                lines.append('\t\t%s %s;' % (param, value))
182            for option, value in additional_options.items():
183                lines.append('\t\toption %s %s;' % (option, value))
184            lines.append('\t}')
185            lines.append('}')
186
187        for mapping in self.static_mappings:
188            identifier = mapping.identifier
189            fixed_address = mapping.ipv4_address
190            host_fake_name = 'host%s' % identifier.replace(':', '')
191            lease_time = mapping.lease_time
192
193            lines.append('host %s {' % host_fake_name)
194            lines.append('\thardware ethernet %s;' % identifier)
195            lines.append('\tfixed-address %s;' % fixed_address)
196            if lease_time:
197                lines.append('\tdefault-lease-time %d;' % lease_time)
198                lines.append('\tmax-lease-time %d;' % lease_time)
199            lines.append('}')
200
201        config_str = '\n'.join(lines)
202
203        return config_str
204