Statistics
| Branch: | Tag: | Revision:

root / nfdhcpd @ dfe6cc3b

History | View | Annotate | Download (29.7 kB)

1 1f3139f3 Apollon Oikonomopoulos
#!/usr/bin/env python
2 1f3139f3 Apollon Oikonomopoulos
#
3 1f3139f3 Apollon Oikonomopoulos
4 1f3139f3 Apollon Oikonomopoulos
# nfdcpd: A promiscuous, NFQUEUE-based DHCP server for virtual machine hosting
5 1f3139f3 Apollon Oikonomopoulos
# Copyright (c) 2010 GRNET SA
6 1f3139f3 Apollon Oikonomopoulos
#
7 1f3139f3 Apollon Oikonomopoulos
#    This program is free software; you can redistribute it and/or modify
8 1f3139f3 Apollon Oikonomopoulos
#    it under the terms of the GNU General Public License as published by
9 1f3139f3 Apollon Oikonomopoulos
#    the Free Software Foundation; either version 2 of the License, or
10 1f3139f3 Apollon Oikonomopoulos
#    (at your option) any later version.
11 1f3139f3 Apollon Oikonomopoulos
#
12 1f3139f3 Apollon Oikonomopoulos
#    This program is distributed in the hope that it will be useful,
13 1f3139f3 Apollon Oikonomopoulos
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
14 1f3139f3 Apollon Oikonomopoulos
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 1f3139f3 Apollon Oikonomopoulos
#    GNU General Public License for more details.
16 1f3139f3 Apollon Oikonomopoulos
#
17 1f3139f3 Apollon Oikonomopoulos
#    You should have received a copy of the GNU General Public License along
18 1f3139f3 Apollon Oikonomopoulos
#    with this program; if not, write to the Free Software Foundation, Inc.,
19 1f3139f3 Apollon Oikonomopoulos
#    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 1f3139f3 Apollon Oikonomopoulos
#
21 1f3139f3 Apollon Oikonomopoulos
22 1f3139f3 Apollon Oikonomopoulos
import os
23 1f3139f3 Apollon Oikonomopoulos
import re
24 6765a36f Apollon Oikonomopoulos
import sys
25 1f3139f3 Apollon Oikonomopoulos
import glob
26 fa04d422 Apollon Oikonomopoulos
import time
27 1f3139f3 Apollon Oikonomopoulos
import logging
28 1f3139f3 Apollon Oikonomopoulos
import logging.handlers
29 83027c6b Apollon Oikonomopoulos
import threading
30 feca7bb9 Apollon Oikonomopoulos
import traceback
31 1f3139f3 Apollon Oikonomopoulos
import subprocess
32 1f3139f3 Apollon Oikonomopoulos
33 1f3139f3 Apollon Oikonomopoulos
import daemon
34 df3e8fac Vangelis Koukis
import daemon.pidlockfile
35 1f3139f3 Apollon Oikonomopoulos
import nfqueue
36 1f3139f3 Apollon Oikonomopoulos
import pyinotify
37 1f3139f3 Apollon Oikonomopoulos
38 1f3139f3 Apollon Oikonomopoulos
import IPy
39 810a20fa Apollon Oikonomopoulos
import socket
40 1f3139f3 Apollon Oikonomopoulos
from select import select
41 699cc6e3 Apollon Oikonomopoulos
from socket import AF_INET, AF_INET6
42 1f3139f3 Apollon Oikonomopoulos
43 68da8f20 Apollon Oikonomopoulos
from scapy.data import ETH_P_ALL
44 68da8f20 Apollon Oikonomopoulos
from scapy.packet import BasePacket
45 1f3139f3 Apollon Oikonomopoulos
from scapy.layers.l2 import Ether
46 1f3139f3 Apollon Oikonomopoulos
from scapy.layers.inet import IP, UDP
47 6765a36f Apollon Oikonomopoulos
from scapy.layers.inet6 import IPv6, ICMPv6ND_RA, ICMPv6ND_NA, \
48 6765a36f Apollon Oikonomopoulos
                               ICMPv6NDOptDstLLAddr, \
49 6765a36f Apollon Oikonomopoulos
                               ICMPv6NDOptPrefixInfo, \
50 6765a36f Apollon Oikonomopoulos
                               ICMPv6NDOptRDNSS
51 1f3139f3 Apollon Oikonomopoulos
from scapy.layers.dhcp import BOOTP, DHCP
52 1f3139f3 Apollon Oikonomopoulos
53 ea915b1a Apollon Oikonomopoulos
DEFAULT_CONFIG = "/etc/nfdhcpd/nfdhcpd.conf"
54 1f3139f3 Apollon Oikonomopoulos
DEFAULT_PATH = "/var/run/ganeti-dhcpd"
55 1f3139f3 Apollon Oikonomopoulos
DEFAULT_USER = "nobody"
56 ea915b1a Apollon Oikonomopoulos
DEFAULT_LEASE_LIFETIME = 604800 # 1 week
57 ea915b1a Apollon Oikonomopoulos
DEFAULT_LEASE_RENEWAL = 600  # 10 min
58 ea915b1a Apollon Oikonomopoulos
DEFAULT_RA_PERIOD = 300 # seconds
59 ea915b1a Apollon Oikonomopoulos
DHCP_DUMMY_SERVER_IP = "1.2.3.4"
60 1f3139f3 Apollon Oikonomopoulos
61 ea915b1a Apollon Oikonomopoulos
LOG_FILENAME = "nfdhcpd.log"
62 1f3139f3 Apollon Oikonomopoulos
63 1f3139f3 Apollon Oikonomopoulos
SYSFS_NET = "/sys/class/net"
64 1f3139f3 Apollon Oikonomopoulos
65 1f3139f3 Apollon Oikonomopoulos
LOG_FORMAT = "%(asctime)-15s %(levelname)-6s %(message)s"
66 1f3139f3 Apollon Oikonomopoulos
67 651e531d Apollon Oikonomopoulos
# Configuration file specification (see configobj documentation)
68 651e531d Apollon Oikonomopoulos
CONFIG_SPEC = """
69 651e531d Apollon Oikonomopoulos
[general]
70 651e531d Apollon Oikonomopoulos
pidfile = string()
71 651e531d Apollon Oikonomopoulos
datapath = string()
72 651e531d Apollon Oikonomopoulos
logdir = string()
73 651e531d Apollon Oikonomopoulos
user = string()
74 651e531d Apollon Oikonomopoulos
75 651e531d Apollon Oikonomopoulos
[dhcp]
76 651e531d Apollon Oikonomopoulos
enable_dhcp = boolean(default=True)
77 651e531d Apollon Oikonomopoulos
lease_lifetime = integer(min=0, max=4294967295)
78 651e531d Apollon Oikonomopoulos
lease_renewal = integer(min=0, max=4294967295)
79 651e531d Apollon Oikonomopoulos
server_ip = ip_addr()
80 651e531d Apollon Oikonomopoulos
dhcp_queue = integer(min=0, max=65535)
81 651e531d Apollon Oikonomopoulos
nameservers = ip_addr_list(family=4)
82 26ba9dba Costas Drogos
domain = string(default=None)
83 651e531d Apollon Oikonomopoulos
84 651e531d Apollon Oikonomopoulos
[ipv6]
85 651e531d Apollon Oikonomopoulos
enable_ipv6 = boolean(default=True)
86 651e531d Apollon Oikonomopoulos
ra_period = integer(min=1, max=4294967295)
87 651e531d Apollon Oikonomopoulos
rs_queue = integer(min=0, max=65535)
88 651e531d Apollon Oikonomopoulos
ns_queue = integer(min=0, max=65535)
89 651e531d Apollon Oikonomopoulos
nameservers = ip_addr_list(family=6)
90 651e531d Apollon Oikonomopoulos
"""
91 651e531d Apollon Oikonomopoulos
92 651e531d Apollon Oikonomopoulos
93 1f3139f3 Apollon Oikonomopoulos
DHCPDISCOVER = 1
94 1f3139f3 Apollon Oikonomopoulos
DHCPOFFER = 2
95 1f3139f3 Apollon Oikonomopoulos
DHCPREQUEST = 3
96 1f3139f3 Apollon Oikonomopoulos
DHCPDECLINE = 4
97 1f3139f3 Apollon Oikonomopoulos
DHCPACK = 5
98 1f3139f3 Apollon Oikonomopoulos
DHCPNAK = 6
99 1f3139f3 Apollon Oikonomopoulos
DHCPRELEASE = 7
100 1f3139f3 Apollon Oikonomopoulos
DHCPINFORM = 8
101 1f3139f3 Apollon Oikonomopoulos
102 1f3139f3 Apollon Oikonomopoulos
DHCP_TYPES = {
103 1f3139f3 Apollon Oikonomopoulos
    DHCPDISCOVER: "DHCPDISCOVER",
104 1f3139f3 Apollon Oikonomopoulos
    DHCPOFFER: "DHCPOFFER",
105 1f3139f3 Apollon Oikonomopoulos
    DHCPREQUEST: "DHCPREQUEST",
106 1f3139f3 Apollon Oikonomopoulos
    DHCPDECLINE: "DHCPDECLINE",
107 1f3139f3 Apollon Oikonomopoulos
    DHCPACK: "DHCPACK",
108 1f3139f3 Apollon Oikonomopoulos
    DHCPNAK: "DHCPNAK",
109 1f3139f3 Apollon Oikonomopoulos
    DHCPRELEASE: "DHCPRELEASE",
110 1f3139f3 Apollon Oikonomopoulos
    DHCPINFORM: "DHCPINFORM",
111 1f3139f3 Apollon Oikonomopoulos
}
112 1f3139f3 Apollon Oikonomopoulos
113 1f3139f3 Apollon Oikonomopoulos
DHCP_REQRESP = {
114 1f3139f3 Apollon Oikonomopoulos
    DHCPDISCOVER: DHCPOFFER,
115 1f3139f3 Apollon Oikonomopoulos
    DHCPREQUEST: DHCPACK,
116 1f3139f3 Apollon Oikonomopoulos
    DHCPINFORM: DHCPACK,
117 1f3139f3 Apollon Oikonomopoulos
    }
118 1f3139f3 Apollon Oikonomopoulos
119 519ec23c Apollon Oikonomopoulos
120 6765a36f Apollon Oikonomopoulos
def parse_routing_table(table="main", family=4):
121 6765a36f Apollon Oikonomopoulos
    """ Parse the given routing table to get connected route, gateway and
122 6765a36f Apollon Oikonomopoulos
    default device.
123 6765a36f Apollon Oikonomopoulos
124 6765a36f Apollon Oikonomopoulos
    """
125 6765a36f Apollon Oikonomopoulos
    ipro = subprocess.Popen(["ip", "-%d" % family, "ro", "ls",
126 6765a36f Apollon Oikonomopoulos
                             "table", table], stdout=subprocess.PIPE)
127 6765a36f Apollon Oikonomopoulos
    routes = ipro.stdout.readlines()
128 6765a36f Apollon Oikonomopoulos
129 6765a36f Apollon Oikonomopoulos
    def_gw = None
130 6765a36f Apollon Oikonomopoulos
    def_dev = None
131 6765a36f Apollon Oikonomopoulos
    def_net = None
132 6765a36f Apollon Oikonomopoulos
133 6765a36f Apollon Oikonomopoulos
    for route in routes:
134 6765a36f Apollon Oikonomopoulos
        match = re.match(r'^default.*via ([^\s]+).*dev ([^\s]+)', route)
135 6765a36f Apollon Oikonomopoulos
        if match:
136 6765a36f Apollon Oikonomopoulos
            def_gw, def_dev = match.groups()
137 6765a36f Apollon Oikonomopoulos
            break
138 6765a36f Apollon Oikonomopoulos
139 6765a36f Apollon Oikonomopoulos
    for route in routes:
140 6765a36f Apollon Oikonomopoulos
        # Find the least-specific connected route
141 6765a36f Apollon Oikonomopoulos
        m = re.match("^([^\\s]+) dev %s" % def_dev, route)
142 6765a36f Apollon Oikonomopoulos
        if not m:
143 6765a36f Apollon Oikonomopoulos
            continue
144 61739de0 Apollon Oikonomopoulos
145 61739de0 Apollon Oikonomopoulos
        if family == 6 and m.group(1).startswith("fe80:"):
146 61739de0 Apollon Oikonomopoulos
            # Skip link-local declarations in "main" table
147 61739de0 Apollon Oikonomopoulos
            continue
148 61739de0 Apollon Oikonomopoulos
149 c6341392 Apollon Oikonomopoulos
        def_net = m.group(1)
150 6765a36f Apollon Oikonomopoulos
151 6765a36f Apollon Oikonomopoulos
        try:
152 6765a36f Apollon Oikonomopoulos
            def_net = IPy.IP(def_net)
153 6765a36f Apollon Oikonomopoulos
        except ValueError, e:
154 6765a36f Apollon Oikonomopoulos
            logging.warn("Unable to parse default route entry %s: %s",
155 6765a36f Apollon Oikonomopoulos
                         def_net, str(e))
156 6765a36f Apollon Oikonomopoulos
157 6765a36f Apollon Oikonomopoulos
    return Subnet(net=def_net, gw=def_gw, dev=def_dev)
158 6765a36f Apollon Oikonomopoulos
159 6765a36f Apollon Oikonomopoulos
160 6765a36f Apollon Oikonomopoulos
def parse_binding_file(path):
161 6765a36f Apollon Oikonomopoulos
    """ Read a client configuration from a tap file
162 6765a36f Apollon Oikonomopoulos
163 6765a36f Apollon Oikonomopoulos
    """
164 6765a36f Apollon Oikonomopoulos
    try:
165 6765a36f Apollon Oikonomopoulos
        iffile = open(path, 'r')
166 6765a36f Apollon Oikonomopoulos
    except EnvironmentError, e:
167 6765a36f Apollon Oikonomopoulos
        logging.warn("Unable to open binding file %s: %s", path, str(e))
168 a2eba3d0 Apollon Oikonomopoulos
        return None
169 6765a36f Apollon Oikonomopoulos
170 6765a36f Apollon Oikonomopoulos
    mac = None
171 6765a36f Apollon Oikonomopoulos
    ips = None
172 6765a36f Apollon Oikonomopoulos
    link = None
173 6765a36f Apollon Oikonomopoulos
    hostname = None
174 6765a36f Apollon Oikonomopoulos
175 6765a36f Apollon Oikonomopoulos
    for line in iffile:
176 6765a36f Apollon Oikonomopoulos
        if line.startswith("IP="):
177 6765a36f Apollon Oikonomopoulos
            ip = line.strip().split("=")[1]
178 6765a36f Apollon Oikonomopoulos
            ips = ip.split()
179 6765a36f Apollon Oikonomopoulos
        elif line.startswith("MAC="):
180 6765a36f Apollon Oikonomopoulos
            mac = line.strip().split("=")[1]
181 6765a36f Apollon Oikonomopoulos
        elif line.startswith("LINK="):
182 6765a36f Apollon Oikonomopoulos
            link = line.strip().split("=")[1]
183 6765a36f Apollon Oikonomopoulos
        elif line.startswith("HOSTNAME="):
184 6765a36f Apollon Oikonomopoulos
            hostname = line.strip().split("=")[1]
185 6765a36f Apollon Oikonomopoulos
186 6765a36f Apollon Oikonomopoulos
    return Client(mac=mac, ips=ips, link=link, hostname=hostname)
187 6765a36f Apollon Oikonomopoulos
188 6765a36f Apollon Oikonomopoulos
189 699cc6e3 Apollon Oikonomopoulos
class ClientFileHandler(pyinotify.ProcessEvent):
190 699cc6e3 Apollon Oikonomopoulos
    def __init__(self, server):
191 1f3139f3 Apollon Oikonomopoulos
        pyinotify.ProcessEvent.__init__(self)
192 699cc6e3 Apollon Oikonomopoulos
        self.server = server
193 1f3139f3 Apollon Oikonomopoulos
194 c01dec04 Apollon Oikonomopoulos
    def process_IN_DELETE(self, event): # pylint: disable=C0103
195 6765a36f Apollon Oikonomopoulos
        """ Delete file handler
196 6765a36f Apollon Oikonomopoulos
197 6765a36f Apollon Oikonomopoulos
        Currently this removes an interface from the watch list
198 6765a36f Apollon Oikonomopoulos
199 6765a36f Apollon Oikonomopoulos
        """
200 699cc6e3 Apollon Oikonomopoulos
        self.server.remove_iface(event.name)
201 1f3139f3 Apollon Oikonomopoulos
202 c01dec04 Apollon Oikonomopoulos
    def process_IN_CLOSE_WRITE(self, event): # pylint: disable=C0103
203 6765a36f Apollon Oikonomopoulos
        """ Add file handler
204 6765a36f Apollon Oikonomopoulos
205 6765a36f Apollon Oikonomopoulos
        Currently this adds an interface to the watch list
206 6765a36f Apollon Oikonomopoulos
207 6765a36f Apollon Oikonomopoulos
        """
208 699cc6e3 Apollon Oikonomopoulos
        self.server.add_iface(os.path.join(event.path, event.name))
209 1f3139f3 Apollon Oikonomopoulos
210 699cc6e3 Apollon Oikonomopoulos
211 699cc6e3 Apollon Oikonomopoulos
class Client(object):
212 1f3139f3 Apollon Oikonomopoulos
    def __init__(self, mac=None, ips=None, link=None, hostname=None):
213 1f3139f3 Apollon Oikonomopoulos
        self.mac = mac
214 1f3139f3 Apollon Oikonomopoulos
        self.ips = ips
215 1f3139f3 Apollon Oikonomopoulos
        self.hostname = hostname
216 1f3139f3 Apollon Oikonomopoulos
        self.link = link
217 1f3139f3 Apollon Oikonomopoulos
        self.iface = None
218 699cc6e3 Apollon Oikonomopoulos
219 1f3139f3 Apollon Oikonomopoulos
    @property
220 1f3139f3 Apollon Oikonomopoulos
    def ip(self):
221 1f3139f3 Apollon Oikonomopoulos
        return self.ips[0]
222 1f3139f3 Apollon Oikonomopoulos
223 1f3139f3 Apollon Oikonomopoulos
    def is_valid(self):
224 1f3139f3 Apollon Oikonomopoulos
        return self.mac is not None and self.ips is not None\
225 1f3139f3 Apollon Oikonomopoulos
               and self.hostname is not None
226 1f3139f3 Apollon Oikonomopoulos
227 1f3139f3 Apollon Oikonomopoulos
228 1f3139f3 Apollon Oikonomopoulos
class Subnet(object):
229 1f3139f3 Apollon Oikonomopoulos
    def __init__(self, net=None, gw=None, dev=None):
230 1f3139f3 Apollon Oikonomopoulos
        if isinstance(net, str):
231 1f3139f3 Apollon Oikonomopoulos
            self.net = IPy.IP(net)
232 1f3139f3 Apollon Oikonomopoulos
        else:
233 1f3139f3 Apollon Oikonomopoulos
            self.net = net
234 1f3139f3 Apollon Oikonomopoulos
        self.gw = gw
235 1f3139f3 Apollon Oikonomopoulos
        self.dev = dev
236 1f3139f3 Apollon Oikonomopoulos
237 1f3139f3 Apollon Oikonomopoulos
    @property
238 1f3139f3 Apollon Oikonomopoulos
    def netmask(self):
239 6765a36f Apollon Oikonomopoulos
        """ Return the netmask in textual representation
240 6765a36f Apollon Oikonomopoulos
241 6765a36f Apollon Oikonomopoulos
        """
242 1f3139f3 Apollon Oikonomopoulos
        return str(self.net.netmask())
243 1f3139f3 Apollon Oikonomopoulos
244 1f3139f3 Apollon Oikonomopoulos
    @property
245 1f3139f3 Apollon Oikonomopoulos
    def broadcast(self):
246 6765a36f Apollon Oikonomopoulos
        """ Return the broadcast address in textual representation
247 6765a36f Apollon Oikonomopoulos
248 6765a36f Apollon Oikonomopoulos
        """
249 1f3139f3 Apollon Oikonomopoulos
        return str(self.net.broadcast())
250 1f3139f3 Apollon Oikonomopoulos
251 247ad61d Apollon Oikonomopoulos
    @property
252 247ad61d Apollon Oikonomopoulos
    def prefix(self):
253 6765a36f Apollon Oikonomopoulos
        """ Return the network as an IPy.IP
254 6765a36f Apollon Oikonomopoulos
255 6765a36f Apollon Oikonomopoulos
        """
256 247ad61d Apollon Oikonomopoulos
        return self.net.net()
257 247ad61d Apollon Oikonomopoulos
258 247ad61d Apollon Oikonomopoulos
    @property
259 247ad61d Apollon Oikonomopoulos
    def prefixlen(self):
260 6765a36f Apollon Oikonomopoulos
        """ Return the prefix length as an integer
261 6765a36f Apollon Oikonomopoulos
262 6765a36f Apollon Oikonomopoulos
        """
263 247ad61d Apollon Oikonomopoulos
        return self.net.prefixlen()
264 247ad61d Apollon Oikonomopoulos
265 247ad61d Apollon Oikonomopoulos
    @staticmethod
266 247ad61d Apollon Oikonomopoulos
    def _make_eui64(net, mac):
267 247ad61d Apollon Oikonomopoulos
        """ Compute an EUI-64 address from an EUI-48 (MAC) address
268 247ad61d Apollon Oikonomopoulos
269 247ad61d Apollon Oikonomopoulos
        """
270 247ad61d Apollon Oikonomopoulos
        comp = mac.split(":")
271 247ad61d Apollon Oikonomopoulos
        prefix = IPy.IP(net).net().strFullsize().split(":")[:4]
272 247ad61d Apollon Oikonomopoulos
        eui64 = comp[:3] + ["ff", "fe"] + comp[3:]
273 247ad61d Apollon Oikonomopoulos
        eui64[0] = "%02x" % (int(eui64[0], 16) ^ 0x02)
274 247ad61d Apollon Oikonomopoulos
        for l in range(0, len(eui64), 2):
275 247ad61d Apollon Oikonomopoulos
            prefix += ["".join(eui64[l:l+2])]
276 247ad61d Apollon Oikonomopoulos
        return IPy.IP(":".join(prefix))
277 247ad61d Apollon Oikonomopoulos
278 247ad61d Apollon Oikonomopoulos
    def make_eui64(self, mac):
279 6765a36f Apollon Oikonomopoulos
        """ Compute an EUI-64 address from an EUI-48 (MAC) address in this
280 6765a36f Apollon Oikonomopoulos
        subnet.
281 6765a36f Apollon Oikonomopoulos
282 6765a36f Apollon Oikonomopoulos
        """
283 247ad61d Apollon Oikonomopoulos
        return self._make_eui64(self.net, mac)
284 247ad61d Apollon Oikonomopoulos
285 247ad61d Apollon Oikonomopoulos
    def make_ll64(self, mac):
286 6765a36f Apollon Oikonomopoulos
        """ Compute an IPv6 Link-local address from an EUI-48 (MAC) address
287 6765a36f Apollon Oikonomopoulos
288 6765a36f Apollon Oikonomopoulos
        """
289 247ad61d Apollon Oikonomopoulos
        return self._make_eui64("fe80::", mac)
290 247ad61d Apollon Oikonomopoulos
291 1f3139f3 Apollon Oikonomopoulos
292 6765a36f Apollon Oikonomopoulos
class VMNetProxy(object): # pylint: disable=R0902
293 6765a36f Apollon Oikonomopoulos
    def __init__(self, data_path, dhcp_queue_num=None, # pylint: disable=R0913
294 ea915b1a Apollon Oikonomopoulos
                 rs_queue_num=None, ns_queue_num=None,
295 ea915b1a Apollon Oikonomopoulos
                 dhcp_lease_lifetime=DEFAULT_LEASE_LIFETIME,
296 ea915b1a Apollon Oikonomopoulos
                 dhcp_lease_renewal=DEFAULT_LEASE_RENEWAL,
297 26ba9dba Costas Drogos
                 dhcp_domain='',
298 6765a36f Apollon Oikonomopoulos
                 dhcp_server_ip=DHCP_DUMMY_SERVER_IP, dhcp_nameservers=None,
299 6765a36f Apollon Oikonomopoulos
                 ra_period=DEFAULT_RA_PERIOD, ipv6_nameservers=None):
300 ea915b1a Apollon Oikonomopoulos
301 1f3139f3 Apollon Oikonomopoulos
        self.data_path = data_path
302 ea915b1a Apollon Oikonomopoulos
        self.lease_lifetime = dhcp_lease_lifetime
303 ea915b1a Apollon Oikonomopoulos
        self.lease_renewal = dhcp_lease_renewal
304 26ba9dba Costas Drogos
        self.dhcp_domain = dhcp_domain
305 ea915b1a Apollon Oikonomopoulos
        self.dhcp_server_ip = dhcp_server_ip
306 ea915b1a Apollon Oikonomopoulos
        self.ra_period = ra_period
307 6765a36f Apollon Oikonomopoulos
        if dhcp_nameservers is None:
308 6765a36f Apollon Oikonomopoulos
            self.dhcp_nameserver = []
309 6765a36f Apollon Oikonomopoulos
        else:
310 6765a36f Apollon Oikonomopoulos
            self.dhcp_nameservers = dhcp_nameservers
311 6765a36f Apollon Oikonomopoulos
312 6765a36f Apollon Oikonomopoulos
        if ipv6_nameservers is None:
313 6765a36f Apollon Oikonomopoulos
            self.ipv6_nameservers = []
314 6765a36f Apollon Oikonomopoulos
        else:
315 6765a36f Apollon Oikonomopoulos
            self.ipv6_nameservers = ipv6_nameservers
316 6765a36f Apollon Oikonomopoulos
317 31d21144 Apollon Oikonomopoulos
        self.ipv6_enabled = False
318 ea915b1a Apollon Oikonomopoulos
319 1f3139f3 Apollon Oikonomopoulos
        self.clients = {}
320 1f3139f3 Apollon Oikonomopoulos
        self.subnets = {}
321 1f3139f3 Apollon Oikonomopoulos
        self.ifaces = {}
322 247ad61d Apollon Oikonomopoulos
        self.v6nets = {}
323 699cc6e3 Apollon Oikonomopoulos
        self.nfq = {}
324 68da8f20 Apollon Oikonomopoulos
        self.l2socket = socket.socket(socket.AF_PACKET,
325 68da8f20 Apollon Oikonomopoulos
                                      socket.SOCK_RAW, ETH_P_ALL)
326 68da8f20 Apollon Oikonomopoulos
        self.l2socket.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 0)
327 699cc6e3 Apollon Oikonomopoulos
328 1f3139f3 Apollon Oikonomopoulos
        # Inotify setup
329 1f3139f3 Apollon Oikonomopoulos
        self.wm = pyinotify.WatchManager()
330 1f3139f3 Apollon Oikonomopoulos
        mask = pyinotify.EventsCodes.ALL_FLAGS["IN_DELETE"]
331 1f3139f3 Apollon Oikonomopoulos
        mask |= pyinotify.EventsCodes.ALL_FLAGS["IN_CLOSE_WRITE"]
332 6765a36f Apollon Oikonomopoulos
        inotify_handler = ClientFileHandler(self)
333 6765a36f Apollon Oikonomopoulos
        self.notifier = pyinotify.Notifier(self.wm, inotify_handler)
334 1f3139f3 Apollon Oikonomopoulos
        self.wm.add_watch(self.data_path, mask, rec=True)
335 1f3139f3 Apollon Oikonomopoulos
336 699cc6e3 Apollon Oikonomopoulos
        # NFQUEUE setup
337 699cc6e3 Apollon Oikonomopoulos
        if dhcp_queue_num is not None:
338 699cc6e3 Apollon Oikonomopoulos
            self._setup_nfqueue(dhcp_queue_num, AF_INET, self.dhcp_response)
339 699cc6e3 Apollon Oikonomopoulos
340 699cc6e3 Apollon Oikonomopoulos
        if rs_queue_num is not None:
341 699cc6e3 Apollon Oikonomopoulos
            self._setup_nfqueue(rs_queue_num, AF_INET6, self.rs_response)
342 31d21144 Apollon Oikonomopoulos
            self.ipv6_enabled = True
343 699cc6e3 Apollon Oikonomopoulos
344 699cc6e3 Apollon Oikonomopoulos
        if ns_queue_num is not None:
345 699cc6e3 Apollon Oikonomopoulos
            self._setup_nfqueue(ns_queue_num, AF_INET6, self.ns_response)
346 31d21144 Apollon Oikonomopoulos
            self.ipv6_enabled = True
347 699cc6e3 Apollon Oikonomopoulos
348 36e1175b Apollon Oikonomopoulos
    def _cleanup(self):
349 36e1175b Apollon Oikonomopoulos
        """ Free all resources for a graceful exit
350 36e1175b Apollon Oikonomopoulos
351 36e1175b Apollon Oikonomopoulos
        """
352 36e1175b Apollon Oikonomopoulos
        logging.info("Cleaning up")
353 36e1175b Apollon Oikonomopoulos
354 36e1175b Apollon Oikonomopoulos
        logging.debug("Closing netfilter queues")
355 36e1175b Apollon Oikonomopoulos
        for q in self.nfq.values():
356 36e1175b Apollon Oikonomopoulos
            q.close()
357 36e1175b Apollon Oikonomopoulos
358 36e1175b Apollon Oikonomopoulos
        logging.debug("Closing socket")
359 36e1175b Apollon Oikonomopoulos
        self.l2socket.close()
360 36e1175b Apollon Oikonomopoulos
361 36e1175b Apollon Oikonomopoulos
        logging.debug("Stopping inotify watches")
362 36e1175b Apollon Oikonomopoulos
        self.notifier.stop()
363 36e1175b Apollon Oikonomopoulos
364 36e1175b Apollon Oikonomopoulos
        logging.info("Cleanup finished")
365 36e1175b Apollon Oikonomopoulos
366 699cc6e3 Apollon Oikonomopoulos
    def _setup_nfqueue(self, queue_num, family, callback):
367 6765a36f Apollon Oikonomopoulos
        logging.debug("Setting up NFQUEUE for queue %d, AF %s",
368 6765a36f Apollon Oikonomopoulos
                      queue_num, family)
369 699cc6e3 Apollon Oikonomopoulos
        q = nfqueue.queue()
370 699cc6e3 Apollon Oikonomopoulos
        q.set_callback(callback)
371 699cc6e3 Apollon Oikonomopoulos
        q.fast_open(queue_num, family)
372 699cc6e3 Apollon Oikonomopoulos
        q.set_queue_maxlen(5000)
373 1f3139f3 Apollon Oikonomopoulos
        # This is mandatory for the queue to operate
374 699cc6e3 Apollon Oikonomopoulos
        q.set_mode(nfqueue.NFQNL_COPY_PACKET)
375 699cc6e3 Apollon Oikonomopoulos
        self.nfq[q.get_fd()] = q
376 1f3139f3 Apollon Oikonomopoulos
377 68da8f20 Apollon Oikonomopoulos
    def sendp(self, data, iface):
378 68da8f20 Apollon Oikonomopoulos
        """ Send a raw packet using a layer-2 socket
379 68da8f20 Apollon Oikonomopoulos
380 68da8f20 Apollon Oikonomopoulos
        """
381 68da8f20 Apollon Oikonomopoulos
        if isinstance(data, BasePacket):
382 68da8f20 Apollon Oikonomopoulos
            data = str(data)
383 68da8f20 Apollon Oikonomopoulos
384 68da8f20 Apollon Oikonomopoulos
        self.l2socket.bind((iface, ETH_P_ALL))
385 68da8f20 Apollon Oikonomopoulos
        count = self.l2socket.send(data)
386 68da8f20 Apollon Oikonomopoulos
        ldata = len(data)
387 68da8f20 Apollon Oikonomopoulos
        if count != ldata:
388 68da8f20 Apollon Oikonomopoulos
            logging.warn("Truncated send on %s (%d/%d bytes sent)",
389 68da8f20 Apollon Oikonomopoulos
                         iface, count, ldata)
390 68da8f20 Apollon Oikonomopoulos
391 1f3139f3 Apollon Oikonomopoulos
    def build_config(self):
392 1f3139f3 Apollon Oikonomopoulos
        self.clients.clear()
393 1f3139f3 Apollon Oikonomopoulos
        self.subnets.clear()
394 1f3139f3 Apollon Oikonomopoulos
395 6765a36f Apollon Oikonomopoulos
        for path in glob.glob(os.path.join(self.data_path, "*")):
396 6765a36f Apollon Oikonomopoulos
            self.add_iface(path)
397 1f3139f3 Apollon Oikonomopoulos
398 1f3139f3 Apollon Oikonomopoulos
    def get_ifindex(self, iface):
399 1f3139f3 Apollon Oikonomopoulos
        """ Get the interface index from sysfs
400 1f3139f3 Apollon Oikonomopoulos
401 1f3139f3 Apollon Oikonomopoulos
        """
402 6765a36f Apollon Oikonomopoulos
        path = os.path.abspath(os.path.join(SYSFS_NET, iface, "ifindex"))
403 6765a36f Apollon Oikonomopoulos
        if not path.startswith(SYSFS_NET):
404 1f3139f3 Apollon Oikonomopoulos
            return None
405 1f3139f3 Apollon Oikonomopoulos
406 1f3139f3 Apollon Oikonomopoulos
        ifindex = None
407 1f3139f3 Apollon Oikonomopoulos
408 1f3139f3 Apollon Oikonomopoulos
        try:
409 6765a36f Apollon Oikonomopoulos
            f = open(path, 'r')
410 810a20fa Apollon Oikonomopoulos
        except EnvironmentError:
411 6765a36f Apollon Oikonomopoulos
            logging.debug("%s is probably down, removing", iface)
412 6ca53b5c Apollon Oikonomopoulos
            self.remove_iface(iface)
413 1f3139f3 Apollon Oikonomopoulos
414 810a20fa Apollon Oikonomopoulos
            return ifindex
415 810a20fa Apollon Oikonomopoulos
416 810a20fa Apollon Oikonomopoulos
        try:
417 810a20fa Apollon Oikonomopoulos
            ifindex = f.readline().strip()
418 810a20fa Apollon Oikonomopoulos
            try:
419 810a20fa Apollon Oikonomopoulos
                ifindex = int(ifindex)
420 810a20fa Apollon Oikonomopoulos
            except ValueError, e:
421 810a20fa Apollon Oikonomopoulos
                logging.warn("Failed to get ifindex for %s, cannot parse sysfs"
422 6765a36f Apollon Oikonomopoulos
                             " output '%s'", iface, ifindex)
423 810a20fa Apollon Oikonomopoulos
        except EnvironmentError, e:
424 6765a36f Apollon Oikonomopoulos
            logging.warn("Error reading %s's ifindex from sysfs: %s",
425 6765a36f Apollon Oikonomopoulos
                         iface, str(e))
426 810a20fa Apollon Oikonomopoulos
            self.remove_iface(iface)
427 810a20fa Apollon Oikonomopoulos
        finally:
428 810a20fa Apollon Oikonomopoulos
            f.close()
429 810a20fa Apollon Oikonomopoulos
430 1f3139f3 Apollon Oikonomopoulos
        return ifindex
431 699cc6e3 Apollon Oikonomopoulos
432 699cc6e3 Apollon Oikonomopoulos
433 1f3139f3 Apollon Oikonomopoulos
    def get_iface_hw_addr(self, iface):
434 1f3139f3 Apollon Oikonomopoulos
        """ Get the interface hardware address from sysfs
435 1f3139f3 Apollon Oikonomopoulos
436 1f3139f3 Apollon Oikonomopoulos
        """
437 6765a36f Apollon Oikonomopoulos
        path = os.path.abspath(os.path.join(SYSFS_NET, iface, "address"))
438 6765a36f Apollon Oikonomopoulos
        if not path.startswith(SYSFS_NET):
439 1f3139f3 Apollon Oikonomopoulos
            return None
440 1f3139f3 Apollon Oikonomopoulos
441 1f3139f3 Apollon Oikonomopoulos
        addr = None
442 1f3139f3 Apollon Oikonomopoulos
        try:
443 6765a36f Apollon Oikonomopoulos
            f = open(path, 'r')
444 810a20fa Apollon Oikonomopoulos
        except EnvironmentError:
445 6765a36f Apollon Oikonomopoulos
            logging.debug("%s is probably down, removing", iface)
446 810a20fa Apollon Oikonomopoulos
            self.remove_iface(iface)
447 810a20fa Apollon Oikonomopoulos
            return addr
448 810a20fa Apollon Oikonomopoulos
449 810a20fa Apollon Oikonomopoulos
        try:
450 1f3139f3 Apollon Oikonomopoulos
            addr = f.readline().strip()
451 810a20fa Apollon Oikonomopoulos
        except EnvironmentError, e:
452 6765a36f Apollon Oikonomopoulos
            logging.warn("Failed to read hw address for %s from sysfs: %s",
453 6765a36f Apollon Oikonomopoulos
                         iface, str(e))
454 810a20fa Apollon Oikonomopoulos
        finally:
455 1f3139f3 Apollon Oikonomopoulos
            f.close()
456 6ca53b5c Apollon Oikonomopoulos
457 1f3139f3 Apollon Oikonomopoulos
        return addr
458 1f3139f3 Apollon Oikonomopoulos
459 1f3139f3 Apollon Oikonomopoulos
    def add_iface(self, path):
460 1f3139f3 Apollon Oikonomopoulos
        """ Add an interface to monitor
461 1f3139f3 Apollon Oikonomopoulos
462 1f3139f3 Apollon Oikonomopoulos
        """
463 1f3139f3 Apollon Oikonomopoulos
        iface = os.path.basename(path)
464 1f3139f3 Apollon Oikonomopoulos
465 6765a36f Apollon Oikonomopoulos
        logging.debug("Updating configuration for %s", iface)
466 6765a36f Apollon Oikonomopoulos
        binding = parse_binding_file(path)
467 a2eba3d0 Apollon Oikonomopoulos
        if binding is None:
468 a2eba3d0 Apollon Oikonomopoulos
            return
469 1f3139f3 Apollon Oikonomopoulos
        ifindex = self.get_ifindex(iface)
470 1f3139f3 Apollon Oikonomopoulos
471 1f3139f3 Apollon Oikonomopoulos
        if ifindex is None:
472 6765a36f Apollon Oikonomopoulos
            logging.warn("Stale configuration for %s found", iface)
473 1f3139f3 Apollon Oikonomopoulos
        else:
474 1f3139f3 Apollon Oikonomopoulos
            if binding.is_valid():
475 1f3139f3 Apollon Oikonomopoulos
                binding.iface = iface
476 1f3139f3 Apollon Oikonomopoulos
                self.clients[binding.mac] = binding
477 6765a36f Apollon Oikonomopoulos
                self.subnets[binding.link] = parse_routing_table(binding.link)
478 6765a36f Apollon Oikonomopoulos
                logging.debug("Added client %s on %s", binding.hostname, iface)
479 1f3139f3 Apollon Oikonomopoulos
                self.ifaces[ifindex] = iface
480 6765a36f Apollon Oikonomopoulos
                self.v6nets[iface] = parse_routing_table(binding.link, 6)
481 1f3139f3 Apollon Oikonomopoulos
482 1f3139f3 Apollon Oikonomopoulos
    def remove_iface(self, iface):
483 1f3139f3 Apollon Oikonomopoulos
        """ Cleanup clients on a removed interface
484 1f3139f3 Apollon Oikonomopoulos
485 1f3139f3 Apollon Oikonomopoulos
        """
486 247ad61d Apollon Oikonomopoulos
        if iface in self.v6nets:
487 6ca53b5c Apollon Oikonomopoulos
            del self.v6nets[iface]
488 247ad61d Apollon Oikonomopoulos
489 1f3139f3 Apollon Oikonomopoulos
        for mac in self.clients.keys():
490 1f3139f3 Apollon Oikonomopoulos
            if self.clients[mac].iface == iface:
491 1f3139f3 Apollon Oikonomopoulos
                del self.clients[mac]
492 1f3139f3 Apollon Oikonomopoulos
493 1f3139f3 Apollon Oikonomopoulos
        for ifindex in self.ifaces.keys():
494 1f3139f3 Apollon Oikonomopoulos
            if self.ifaces[ifindex] == iface:
495 1f3139f3 Apollon Oikonomopoulos
                del self.ifaces[ifindex]
496 1f3139f3 Apollon Oikonomopoulos
497 6765a36f Apollon Oikonomopoulos
        logging.debug("Removed interface %s", iface)
498 1f3139f3 Apollon Oikonomopoulos
499 6765a36f Apollon Oikonomopoulos
    def dhcp_response(self, i, payload): # pylint: disable=W0613,R0914
500 1f3139f3 Apollon Oikonomopoulos
        """ Generate a reply to a BOOTP/DHCP request
501 1f3139f3 Apollon Oikonomopoulos
502 1f3139f3 Apollon Oikonomopoulos
        """
503 4c042e71 Apollon Oikonomopoulos
        indev = payload.get_indev()
504 4c042e71 Apollon Oikonomopoulos
        try:
505 4c042e71 Apollon Oikonomopoulos
            # Get the actual interface from the ifindex
506 4c042e71 Apollon Oikonomopoulos
            iface = self.ifaces[indev]
507 4c042e71 Apollon Oikonomopoulos
        except KeyError:
508 4c042e71 Apollon Oikonomopoulos
            # We don't know anything about this interface, so accept the packet
509 4c042e71 Apollon Oikonomopoulos
            # and return
510 4c042e71 Apollon Oikonomopoulos
            logging.debug("Ignoring DHCP request on unknown iface %d", indev)
511 4c042e71 Apollon Oikonomopoulos
            # We don't know what to do with this packet, so let the kernel
512 4c042e71 Apollon Oikonomopoulos
            # handle it
513 4c042e71 Apollon Oikonomopoulos
            payload.set_verdict(nfqueue.NF_ACCEPT)
514 4c042e71 Apollon Oikonomopoulos
            return
515 4c042e71 Apollon Oikonomopoulos
516 1f3139f3 Apollon Oikonomopoulos
        # Decode the response - NFQUEUE relays IP packets
517 1f3139f3 Apollon Oikonomopoulos
        pkt = IP(payload.get_data())
518 1f3139f3 Apollon Oikonomopoulos
519 1f3139f3 Apollon Oikonomopoulos
        # Signal the kernel that it shouldn't further process the packet
520 1f3139f3 Apollon Oikonomopoulos
        payload.set_verdict(nfqueue.NF_DROP)
521 699cc6e3 Apollon Oikonomopoulos
522 1f3139f3 Apollon Oikonomopoulos
        # Get the client MAC address
523 1f3139f3 Apollon Oikonomopoulos
        resp = pkt.getlayer(BOOTP).copy()
524 1f3139f3 Apollon Oikonomopoulos
        hlen = resp.hlen
525 1f3139f3 Apollon Oikonomopoulos
        mac = resp.chaddr[:hlen].encode("hex")
526 1f3139f3 Apollon Oikonomopoulos
        mac, _ = re.subn(r'([0-9a-fA-F]{2})', r'\1:', mac, hlen-1)
527 1f3139f3 Apollon Oikonomopoulos
528 1f3139f3 Apollon Oikonomopoulos
        # Server responses are always BOOTREPLYs
529 1f3139f3 Apollon Oikonomopoulos
        resp.op = "BOOTREPLY"
530 1f3139f3 Apollon Oikonomopoulos
        del resp.payload
531 1f3139f3 Apollon Oikonomopoulos
532 1f3139f3 Apollon Oikonomopoulos
        try:
533 1f3139f3 Apollon Oikonomopoulos
            binding = self.clients[mac]
534 1f3139f3 Apollon Oikonomopoulos
        except KeyError:
535 6765a36f Apollon Oikonomopoulos
            logging.warn("Invalid client %s on %s", mac, iface)
536 1f3139f3 Apollon Oikonomopoulos
            return
537 1f3139f3 Apollon Oikonomopoulos
538 1f3139f3 Apollon Oikonomopoulos
        if iface != binding.iface:
539 1f3139f3 Apollon Oikonomopoulos
            logging.warn("Received spoofed DHCP request for %s from interface"
540 6765a36f Apollon Oikonomopoulos
                         " %s instead of %s", mac, iface, binding.iface)
541 1f3139f3 Apollon Oikonomopoulos
            return
542 1f3139f3 Apollon Oikonomopoulos
543 1f3139f3 Apollon Oikonomopoulos
        resp = Ether(dst=mac, src=self.get_iface_hw_addr(iface))/\
544 699cc6e3 Apollon Oikonomopoulos
               IP(src=DHCP_DUMMY_SERVER_IP, dst=binding.ip)/\
545 1f3139f3 Apollon Oikonomopoulos
               UDP(sport=pkt.dport, dport=pkt.sport)/resp
546 1f3139f3 Apollon Oikonomopoulos
        subnet = self.subnets[binding.link]
547 1f3139f3 Apollon Oikonomopoulos
548 1f3139f3 Apollon Oikonomopoulos
        if not DHCP in pkt:
549 1f3139f3 Apollon Oikonomopoulos
            logging.warn("Invalid request from %s on %s, no DHCP"
550 6765a36f Apollon Oikonomopoulos
                         " payload found", binding.mac, iface)
551 1f3139f3 Apollon Oikonomopoulos
            return
552 1f3139f3 Apollon Oikonomopoulos
553 1f3139f3 Apollon Oikonomopoulos
        dhcp_options = []
554 1f3139f3 Apollon Oikonomopoulos
        requested_addr = binding.ip
555 1f3139f3 Apollon Oikonomopoulos
        for opt in pkt[DHCP].options:
556 1f3139f3 Apollon Oikonomopoulos
            if type(opt) is tuple and opt[0] == "message-type":
557 1f3139f3 Apollon Oikonomopoulos
                req_type = opt[1]
558 1f3139f3 Apollon Oikonomopoulos
            if type(opt) is tuple and opt[0] == "requested_addr":
559 1f3139f3 Apollon Oikonomopoulos
                requested_addr = opt[1]
560 1f3139f3 Apollon Oikonomopoulos
561 6765a36f Apollon Oikonomopoulos
        logging.info("%s from %s on %s", DHCP_TYPES.get(req_type, "UNKNOWN"),
562 6765a36f Apollon Oikonomopoulos
                     binding.mac, iface)
563 1f3139f3 Apollon Oikonomopoulos
564 26ba9dba Costas Drogos
        if self.dhcp_domain:
565 26ba9dba Costas Drogos
            domainname = self.dhcp_domain
566 26ba9dba Costas Drogos
        else:
567 26ba9dba Costas Drogos
            domainname = binding.hostname.split('.', 1)[-1]
568 26ba9dba Costas Drogos
569 1f3139f3 Apollon Oikonomopoulos
        if req_type == DHCPREQUEST and requested_addr != binding.ip:
570 1f3139f3 Apollon Oikonomopoulos
            resp_type = DHCPNAK
571 1f3139f3 Apollon Oikonomopoulos
            logging.info("Sending DHCPNAK to %s on %s: requested %s"
572 6765a36f Apollon Oikonomopoulos
                         " instead of %s", binding.mac, iface, requested_addr,
573 6765a36f Apollon Oikonomopoulos
                         binding.ip)
574 1f3139f3 Apollon Oikonomopoulos
575 1f3139f3 Apollon Oikonomopoulos
        elif req_type in (DHCPDISCOVER, DHCPREQUEST):
576 1f3139f3 Apollon Oikonomopoulos
            resp_type = DHCP_REQRESP[req_type]
577 1f3139f3 Apollon Oikonomopoulos
            resp.yiaddr = self.clients[mac].ip
578 1f3139f3 Apollon Oikonomopoulos
            dhcp_options += [
579 1f3139f3 Apollon Oikonomopoulos
                 ("hostname", binding.hostname),
580 26ba9dba Costas Drogos
                 ("domain", domainname),
581 1f3139f3 Apollon Oikonomopoulos
                 ("router", subnet.gw),
582 1f3139f3 Apollon Oikonomopoulos
                 ("broadcast_address", str(subnet.broadcast)),
583 1f3139f3 Apollon Oikonomopoulos
                 ("subnet_mask", str(subnet.netmask)),
584 ea915b1a Apollon Oikonomopoulos
                 ("renewal_time", self.lease_renewal),
585 ea915b1a Apollon Oikonomopoulos
                 ("lease_time", self.lease_lifetime),
586 1f3139f3 Apollon Oikonomopoulos
            ]
587 0be961fb Apollon Oikonomopoulos
            dhcp_options += [("name_server", x) for x in self.dhcp_nameservers]
588 1f3139f3 Apollon Oikonomopoulos
589 1f3139f3 Apollon Oikonomopoulos
        elif req_type == DHCPINFORM:
590 1f3139f3 Apollon Oikonomopoulos
            resp_type = DHCP_REQRESP[req_type]
591 1f3139f3 Apollon Oikonomopoulos
            dhcp_options += [
592 1f3139f3 Apollon Oikonomopoulos
                 ("hostname", binding.hostname),
593 26ba9dba Costas Drogos
                 ("domain", domainname),
594 1f3139f3 Apollon Oikonomopoulos
            ]
595 0be961fb Apollon Oikonomopoulos
            dhcp_options += [("name_server", x) for x in self.dhcp_nameservers]
596 1f3139f3 Apollon Oikonomopoulos
597 1f3139f3 Apollon Oikonomopoulos
        elif req_type == DHCPRELEASE:
598 1f3139f3 Apollon Oikonomopoulos
            # Log and ignore
599 6765a36f Apollon Oikonomopoulos
            logging.info("DHCPRELEASE from %s on %s", binding.mac, iface)
600 1f3139f3 Apollon Oikonomopoulos
            return
601 1f3139f3 Apollon Oikonomopoulos
602 1f3139f3 Apollon Oikonomopoulos
        # Finally, always add the server identifier and end options
603 1f3139f3 Apollon Oikonomopoulos
        dhcp_options += [
604 1f3139f3 Apollon Oikonomopoulos
            ("message-type", resp_type),
605 699cc6e3 Apollon Oikonomopoulos
            ("server_id", DHCP_DUMMY_SERVER_IP),
606 1f3139f3 Apollon Oikonomopoulos
            "end"
607 1f3139f3 Apollon Oikonomopoulos
        ]
608 1f3139f3 Apollon Oikonomopoulos
        resp /= DHCP(options=dhcp_options)
609 1f3139f3 Apollon Oikonomopoulos
610 6765a36f Apollon Oikonomopoulos
        logging.info("%s to %s (%s) on %s", DHCP_TYPES[resp_type], mac,
611 6765a36f Apollon Oikonomopoulos
                     binding.ip, iface)
612 68da8f20 Apollon Oikonomopoulos
        self.sendp(resp, iface)
613 1f3139f3 Apollon Oikonomopoulos
614 6765a36f Apollon Oikonomopoulos
    def rs_response(self, i, payload): # pylint: disable=W0613
615 247ad61d Apollon Oikonomopoulos
        """ Generate a reply to a BOOTP/DHCP request
616 247ad61d Apollon Oikonomopoulos
617 247ad61d Apollon Oikonomopoulos
        """
618 4c042e71 Apollon Oikonomopoulos
        indev = payload.get_indev()
619 4c042e71 Apollon Oikonomopoulos
        try:
620 4c042e71 Apollon Oikonomopoulos
            # Get the actual interface from the ifindex
621 4c042e71 Apollon Oikonomopoulos
            iface = self.ifaces[indev]
622 4c042e71 Apollon Oikonomopoulos
        except KeyError:
623 4c042e71 Apollon Oikonomopoulos
            logging.debug("Ignoring router solicitation on"
624 4c042e71 Apollon Oikonomopoulos
                          " unknown interface %d", indev)
625 4c042e71 Apollon Oikonomopoulos
            # We don't know what to do with this packet, so let the kernel
626 4c042e71 Apollon Oikonomopoulos
            # handle it
627 4c042e71 Apollon Oikonomopoulos
            payload.set_verdict(nfqueue.NF_ACCEPT)
628 4c042e71 Apollon Oikonomopoulos
            return
629 4c042e71 Apollon Oikonomopoulos
630 247ad61d Apollon Oikonomopoulos
        ifmac = self.get_iface_hw_addr(iface)
631 247ad61d Apollon Oikonomopoulos
        subnet = self.v6nets[iface]
632 247ad61d Apollon Oikonomopoulos
        ifll = subnet.make_ll64(ifmac)
633 247ad61d Apollon Oikonomopoulos
634 247ad61d Apollon Oikonomopoulos
        # Signal the kernel that it shouldn't further process the packet
635 247ad61d Apollon Oikonomopoulos
        payload.set_verdict(nfqueue.NF_DROP)
636 247ad61d Apollon Oikonomopoulos
637 247ad61d Apollon Oikonomopoulos
        resp = Ether(src=self.get_iface_hw_addr(iface))/\
638 247ad61d Apollon Oikonomopoulos
               IPv6(src=str(ifll))/ICMPv6ND_RA(routerlifetime=14400)/\
639 247ad61d Apollon Oikonomopoulos
               ICMPv6NDOptPrefixInfo(prefix=str(subnet.prefix),
640 247ad61d Apollon Oikonomopoulos
                                     prefixlen=subnet.prefixlen)
641 247ad61d Apollon Oikonomopoulos
642 30dd1f9e Apollon Oikonomopoulos
        if self.ipv6_nameservers:
643 30dd1f9e Apollon Oikonomopoulos
            resp /= ICMPv6NDOptRDNSS(dns=self.ipv6_nameservers,
644 30dd1f9e Apollon Oikonomopoulos
                                     lifetime=self.ra_period * 3)
645 30dd1f9e Apollon Oikonomopoulos
646 6765a36f Apollon Oikonomopoulos
        logging.info("RA on %s for %s", iface, subnet.net)
647 68da8f20 Apollon Oikonomopoulos
        self.sendp(resp, iface)
648 1f3139f3 Apollon Oikonomopoulos
649 6765a36f Apollon Oikonomopoulos
    def ns_response(self, i, payload): # pylint: disable=W0613
650 a5c8c1fe Apollon Oikonomopoulos
        """ Generate a reply to an ICMPv6 neighbor solicitation
651 a5c8c1fe Apollon Oikonomopoulos
652 a5c8c1fe Apollon Oikonomopoulos
        """
653 4c042e71 Apollon Oikonomopoulos
        indev = payload.get_indev()
654 4c042e71 Apollon Oikonomopoulos
        try:
655 4c042e71 Apollon Oikonomopoulos
            # Get the actual interface from the ifindex
656 4c042e71 Apollon Oikonomopoulos
            iface = self.ifaces[indev]
657 4c042e71 Apollon Oikonomopoulos
        except KeyError:
658 4c042e71 Apollon Oikonomopoulos
            logging.debug("Ignoring neighbour solicitation on"
659 4c042e71 Apollon Oikonomopoulos
                          " unknown interface %d", indev)
660 4c042e71 Apollon Oikonomopoulos
            # We don't know what to do with this packet, so let the kernel
661 4c042e71 Apollon Oikonomopoulos
            # handle it
662 4c042e71 Apollon Oikonomopoulos
            payload.set_verdict(nfqueue.NF_ACCEPT)
663 4c042e71 Apollon Oikonomopoulos
            return
664 4c042e71 Apollon Oikonomopoulos
665 a5c8c1fe Apollon Oikonomopoulos
        ifmac = self.get_iface_hw_addr(iface)
666 a5c8c1fe Apollon Oikonomopoulos
        subnet = self.v6nets[iface]
667 a5c8c1fe Apollon Oikonomopoulos
        ifll = subnet.make_ll64(ifmac)
668 a5c8c1fe Apollon Oikonomopoulos
669 a5c8c1fe Apollon Oikonomopoulos
        ns = IPv6(payload.get_data())
670 a5c8c1fe Apollon Oikonomopoulos
671 a5c8c1fe Apollon Oikonomopoulos
        if not (subnet.net.overlaps(ns.tgt) or str(ns.tgt) == str(ifll)):
672 6765a36f Apollon Oikonomopoulos
            logging.debug("Received NS for a non-routable IP (%s)", ns.tgt)
673 a5c8c1fe Apollon Oikonomopoulos
            payload.set_verdict(nfqueue.NF_ACCEPT)
674 a5c8c1fe Apollon Oikonomopoulos
            return 1
675 a5c8c1fe Apollon Oikonomopoulos
676 a5c8c1fe Apollon Oikonomopoulos
        payload.set_verdict(nfqueue.NF_DROP)
677 a5c8c1fe Apollon Oikonomopoulos
678 948d4918 Apollon Oikonomopoulos
        try:
679 948d4918 Apollon Oikonomopoulos
            client_lladdr = ns.lladdr
680 948d4918 Apollon Oikonomopoulos
        except AttributeError:
681 948d4918 Apollon Oikonomopoulos
            return 1
682 948d4918 Apollon Oikonomopoulos
683 948d4918 Apollon Oikonomopoulos
        resp = Ether(src=ifmac, dst=client_lladdr)/\
684 a5c8c1fe Apollon Oikonomopoulos
               IPv6(src=str(ifll), dst=ns.src)/\
685 a5c8c1fe Apollon Oikonomopoulos
               ICMPv6ND_NA(R=1, O=0, S=1, tgt=ns.tgt)/\
686 a5c8c1fe Apollon Oikonomopoulos
               ICMPv6NDOptDstLLAddr(lladdr=ifmac)
687 a5c8c1fe Apollon Oikonomopoulos
688 6765a36f Apollon Oikonomopoulos
        logging.info("NA on %s for %s", iface, ns.tgt)
689 68da8f20 Apollon Oikonomopoulos
        self.sendp(resp, iface)
690 a5c8c1fe Apollon Oikonomopoulos
        return 1
691 a5c8c1fe Apollon Oikonomopoulos
692 fa04d422 Apollon Oikonomopoulos
    def send_periodic_ra(self):
693 83027c6b Apollon Oikonomopoulos
        # Use a separate thread as this may take a _long_ time with
694 83027c6b Apollon Oikonomopoulos
        # many interfaces and we want to be responsive in the mean time
695 83027c6b Apollon Oikonomopoulos
        threading.Thread(target=self._send_periodic_ra).start()
696 83027c6b Apollon Oikonomopoulos
697 83027c6b Apollon Oikonomopoulos
    def _send_periodic_ra(self):
698 fa04d422 Apollon Oikonomopoulos
        logging.debug("Sending out periodic RAs")
699 6ca53b5c Apollon Oikonomopoulos
        start = time.time()
700 6ca53b5c Apollon Oikonomopoulos
        i = 0
701 fa04d422 Apollon Oikonomopoulos
        for client in self.clients.values():
702 fa04d422 Apollon Oikonomopoulos
            iface = client.iface
703 fa04d422 Apollon Oikonomopoulos
            ifmac = self.get_iface_hw_addr(iface)
704 6ca53b5c Apollon Oikonomopoulos
            if not ifmac:
705 6ca53b5c Apollon Oikonomopoulos
                continue
706 6ca53b5c Apollon Oikonomopoulos
707 6ca53b5c Apollon Oikonomopoulos
            subnet = self.v6nets[iface]
708 b0b3ad51 Apollon Oikonomopoulos
            if subnet.net is None:
709 b0b3ad51 Apollon Oikonomopoulos
                logging.debug("Skipping periodic RA on interface %s,"
710 b0b3ad51 Apollon Oikonomopoulos
                              " as it is not IPv6-connected", iface)
711 b0b3ad51 Apollon Oikonomopoulos
                continue
712 b0b3ad51 Apollon Oikonomopoulos
713 fa04d422 Apollon Oikonomopoulos
            ifll = subnet.make_ll64(ifmac)
714 fa04d422 Apollon Oikonomopoulos
            resp = Ether(src=ifmac)/\
715 fa04d422 Apollon Oikonomopoulos
                   IPv6(src=str(ifll))/ICMPv6ND_RA(routerlifetime=14400)/\
716 fa04d422 Apollon Oikonomopoulos
                   ICMPv6NDOptPrefixInfo(prefix=str(subnet.prefix),
717 fa04d422 Apollon Oikonomopoulos
                                         prefixlen=subnet.prefixlen)
718 30dd1f9e Apollon Oikonomopoulos
            if self.ipv6_nameservers:
719 30dd1f9e Apollon Oikonomopoulos
                resp /= ICMPv6NDOptRDNSS(dns=self.ipv6_nameservers,
720 30dd1f9e Apollon Oikonomopoulos
                                         lifetime=self.ra_period * 3)
721 fa04d422 Apollon Oikonomopoulos
            try:
722 68da8f20 Apollon Oikonomopoulos
                self.sendp(resp, iface)
723 810a20fa Apollon Oikonomopoulos
            except socket.error, e:
724 6765a36f Apollon Oikonomopoulos
                logging.warn("Periodic RA on %s failed: %s", iface, str(e))
725 810a20fa Apollon Oikonomopoulos
            except Exception, e:
726 6765a36f Apollon Oikonomopoulos
                logging.warn("Unkown error during periodic RA on %s: %s",
727 6765a36f Apollon Oikonomopoulos
                             iface, str(e))
728 6ca53b5c Apollon Oikonomopoulos
            i += 1
729 6765a36f Apollon Oikonomopoulos
        logging.debug("Sent %d RAs in %.2f seconds", i, time.time() - start)
730 fa04d422 Apollon Oikonomopoulos
731 1f3139f3 Apollon Oikonomopoulos
    def serve(self):
732 36e1175b Apollon Oikonomopoulos
        """ Safely perform the main loop, freeing all resources upon exit
733 36e1175b Apollon Oikonomopoulos
734 36e1175b Apollon Oikonomopoulos
        """
735 36e1175b Apollon Oikonomopoulos
        try:
736 36e1175b Apollon Oikonomopoulos
            self._serve()
737 36e1175b Apollon Oikonomopoulos
        finally:
738 36e1175b Apollon Oikonomopoulos
            self._cleanup()
739 36e1175b Apollon Oikonomopoulos
740 36e1175b Apollon Oikonomopoulos
    def _serve(self):
741 1f3139f3 Apollon Oikonomopoulos
        """ Loop forever, serving DHCP requests
742 1f3139f3 Apollon Oikonomopoulos
743 1f3139f3 Apollon Oikonomopoulos
        """
744 1f3139f3 Apollon Oikonomopoulos
        self.build_config()
745 1f3139f3 Apollon Oikonomopoulos
746 26ba9dba Costas Drogos
        # Yes, we are accessing _fd directly, but it's the only way to have a
747 6765a36f Apollon Oikonomopoulos
        # single select() loop ;-)
748 6765a36f Apollon Oikonomopoulos
        iwfd = self.notifier._fd # pylint: disable=W0212
749 1f3139f3 Apollon Oikonomopoulos
750 f4b0c05f Apollon Oikonomopoulos
        start = time.time()
751 31d21144 Apollon Oikonomopoulos
        if self.ipv6_enabled:
752 31d21144 Apollon Oikonomopoulos
            timeout = self.ra_period
753 31d21144 Apollon Oikonomopoulos
            self.send_periodic_ra()
754 31d21144 Apollon Oikonomopoulos
        else:
755 31d21144 Apollon Oikonomopoulos
            timeout = None
756 fa04d422 Apollon Oikonomopoulos
757 1f3139f3 Apollon Oikonomopoulos
        while True:
758 fa04d422 Apollon Oikonomopoulos
            rlist, _, xlist = select(self.nfq.keys() + [iwfd], [], [], timeout)
759 bf84c4a5 Apollon Oikonomopoulos
            if xlist:
760 6765a36f Apollon Oikonomopoulos
                logging.warn("Warning: Exception on %s",
761 bf84c4a5 Apollon Oikonomopoulos
                             ", ".join([ str(fd) for fd in xlist]))
762 bf84c4a5 Apollon Oikonomopoulos
763 41a0f754 Apollon Oikonomopoulos
            if rlist:
764 f4b0c05f Apollon Oikonomopoulos
                if iwfd in rlist:
765 41a0f754 Apollon Oikonomopoulos
                # First check if there are any inotify (= configuration change)
766 41a0f754 Apollon Oikonomopoulos
                # events
767 f4b0c05f Apollon Oikonomopoulos
                    self.notifier.read_events()
768 f4b0c05f Apollon Oikonomopoulos
                    self.notifier.process_events()
769 f4b0c05f Apollon Oikonomopoulos
                    rlist.remove(iwfd)
770 1f3139f3 Apollon Oikonomopoulos
771 f4b0c05f Apollon Oikonomopoulos
                for fd in rlist:
772 bf84c4a5 Apollon Oikonomopoulos
                    try:
773 bf84c4a5 Apollon Oikonomopoulos
                        self.nfq[fd].process_pending()
774 6765a36f Apollon Oikonomopoulos
                    except RuntimeError, e:
775 6765a36f Apollon Oikonomopoulos
                        logging.warn("Error processing fd %d: %s", fd, str(e))
776 810a20fa Apollon Oikonomopoulos
                    except Exception, e:
777 6765a36f Apollon Oikonomopoulos
                        logging.warn("Unknown error processing fd %d: %s",
778 6765a36f Apollon Oikonomopoulos
                                     fd, str(e))
779 1f3139f3 Apollon Oikonomopoulos
780 31d21144 Apollon Oikonomopoulos
            if self.ipv6_enabled:
781 31d21144 Apollon Oikonomopoulos
                # Calculate the new timeout
782 ea915b1a Apollon Oikonomopoulos
                timeout = self.ra_period - (time.time() - start)
783 fa04d422 Apollon Oikonomopoulos
784 31d21144 Apollon Oikonomopoulos
                if timeout <= 0:
785 31d21144 Apollon Oikonomopoulos
                    start = time.time()
786 31d21144 Apollon Oikonomopoulos
                    self.send_periodic_ra()
787 31d21144 Apollon Oikonomopoulos
                    timeout = self.ra_period - (time.time() - start)
788 1f3139f3 Apollon Oikonomopoulos
789 f4b0c05f Apollon Oikonomopoulos
790 1f3139f3 Apollon Oikonomopoulos
if __name__ == "__main__":
791 6765a36f Apollon Oikonomopoulos
    import capng
792 1f3139f3 Apollon Oikonomopoulos
    import optparse
793 651e531d Apollon Oikonomopoulos
    from cStringIO import StringIO
794 1f3139f3 Apollon Oikonomopoulos
    from pwd import getpwnam, getpwuid
795 810a20fa Apollon Oikonomopoulos
    from configobj import ConfigObj, ConfigObjError, flatten_errors
796 651e531d Apollon Oikonomopoulos
797 651e531d Apollon Oikonomopoulos
    import validate
798 651e531d Apollon Oikonomopoulos
799 651e531d Apollon Oikonomopoulos
    validator = validate.Validator()
800 651e531d Apollon Oikonomopoulos
801 651e531d Apollon Oikonomopoulos
    def is_ip_list(value, family=4):
802 651e531d Apollon Oikonomopoulos
        try:
803 651e531d Apollon Oikonomopoulos
            family = int(family)
804 651e531d Apollon Oikonomopoulos
        except ValueError:
805 c63ad0e2 Apollon Oikonomopoulos
            raise validate.VdtParamError(family)
806 651e531d Apollon Oikonomopoulos
        if isinstance(value, (str, unicode)):
807 651e531d Apollon Oikonomopoulos
            value = [value]
808 651e531d Apollon Oikonomopoulos
        if not isinstance(value, list):
809 651e531d Apollon Oikonomopoulos
            raise validate.VdtTypeError(value)
810 651e531d Apollon Oikonomopoulos
811 651e531d Apollon Oikonomopoulos
        for entry in value:
812 651e531d Apollon Oikonomopoulos
            try:
813 651e531d Apollon Oikonomopoulos
                ip = IPy.IP(entry)
814 651e531d Apollon Oikonomopoulos
            except ValueError:
815 651e531d Apollon Oikonomopoulos
                raise validate.VdtValueError(entry)
816 651e531d Apollon Oikonomopoulos
817 651e531d Apollon Oikonomopoulos
            if ip.version() != family:
818 651e531d Apollon Oikonomopoulos
                raise validate.VdtValueError(entry)
819 651e531d Apollon Oikonomopoulos
        return value
820 651e531d Apollon Oikonomopoulos
821 651e531d Apollon Oikonomopoulos
    validator.functions["ip_addr_list"] = is_ip_list
822 651e531d Apollon Oikonomopoulos
    config_spec = StringIO(CONFIG_SPEC)
823 651e531d Apollon Oikonomopoulos
824 1f3139f3 Apollon Oikonomopoulos
825 1f3139f3 Apollon Oikonomopoulos
    parser = optparse.OptionParser()
826 ea915b1a Apollon Oikonomopoulos
    parser.add_option("-c", "--config", dest="config_file",
827 ea915b1a Apollon Oikonomopoulos
                      help="The location of the data files", metavar="FILE",
828 ea915b1a Apollon Oikonomopoulos
                      default=DEFAULT_CONFIG)
829 1f3139f3 Apollon Oikonomopoulos
    parser.add_option("-d", "--debug", action="store_true", dest="debug",
830 1f3139f3 Apollon Oikonomopoulos
                      help="Turn on debugging messages")
831 0679a724 Apollon Oikonomopoulos
    parser.add_option("-f", "--foreground", action="store_false",
832 0679a724 Apollon Oikonomopoulos
                      dest="daemonize", default=True,
833 0679a724 Apollon Oikonomopoulos
                      help="Do not daemonize, stay in the foreground")
834 1f3139f3 Apollon Oikonomopoulos
835 1f3139f3 Apollon Oikonomopoulos
836 1f3139f3 Apollon Oikonomopoulos
    opts, args = parser.parse_args()
837 1f3139f3 Apollon Oikonomopoulos
838 ea915b1a Apollon Oikonomopoulos
    try:
839 651e531d Apollon Oikonomopoulos
        config = ConfigObj(opts.config_file, configspec=config_spec)
840 6765a36f Apollon Oikonomopoulos
    except ConfigObjError, err:
841 810a20fa Apollon Oikonomopoulos
        sys.stderr.write("Failed to parse config file %s: %s" %
842 6765a36f Apollon Oikonomopoulos
                         (opts.config_file, str(err)))
843 ea915b1a Apollon Oikonomopoulos
        sys.exit(1)
844 ea915b1a Apollon Oikonomopoulos
845 651e531d Apollon Oikonomopoulos
    results = config.validate(validator)
846 651e531d Apollon Oikonomopoulos
    if results != True:
847 651e531d Apollon Oikonomopoulos
        logging.fatal("Configuration file validation failed! See errors below:")
848 6765a36f Apollon Oikonomopoulos
        for (section_list, key, unused) in flatten_errors(config, results):
849 651e531d Apollon Oikonomopoulos
            if key is not None:
850 6765a36f Apollon Oikonomopoulos
                logging.fatal(" '%s' in section '%s' failed validation",
851 6765a36f Apollon Oikonomopoulos
                              key, ", ".join(section_list))
852 651e531d Apollon Oikonomopoulos
            else:
853 6765a36f Apollon Oikonomopoulos
                logging.fatal(" Section '%s' is missing",
854 0679a724 Apollon Oikonomopoulos
                              ", ".join(section_list))
855 651e531d Apollon Oikonomopoulos
        sys.exit(1)
856 ea915b1a Apollon Oikonomopoulos
857 1f3139f3 Apollon Oikonomopoulos
    logger = logging.getLogger()
858 1f3139f3 Apollon Oikonomopoulos
    if opts.debug:
859 1f3139f3 Apollon Oikonomopoulos
        logger.setLevel(logging.DEBUG)
860 1f3139f3 Apollon Oikonomopoulos
    else:
861 1f3139f3 Apollon Oikonomopoulos
        logger.setLevel(logging.INFO)
862 1f3139f3 Apollon Oikonomopoulos
863 df3e8fac Vangelis Koukis
    if opts.daemonize:
864 df3e8fac Vangelis Koukis
        logfile = os.path.join(config["general"]["logdir"], LOG_FILENAME)
865 df3e8fac Vangelis Koukis
        handler = logging.handlers.RotatingFileHandler(logfile,
866 df3e8fac Vangelis Koukis
                                                       maxBytes=2097152)
867 df3e8fac Vangelis Koukis
    else:
868 df3e8fac Vangelis Koukis
        handler = logging.StreamHandler()
869 df3e8fac Vangelis Koukis
870 df3e8fac Vangelis Koukis
    handler.setFormatter(logging.Formatter(LOG_FORMAT))
871 df3e8fac Vangelis Koukis
    logger.addHandler(handler)
872 df3e8fac Vangelis Koukis
873 df3e8fac Vangelis Koukis
    if opts.daemonize:
874 dfe6cc3b Costas Drogos
        pidfile = daemon.pidlockfile.TimeoutPIDLockFile(
875 df3e8fac Vangelis Koukis
            config["general"]["pidfile"], 10)
876 df3e8fac Vangelis Koukis
877 df3e8fac Vangelis Koukis
        d = daemon.DaemonContext(pidfile=pidfile,
878 df3e8fac Vangelis Koukis
                                 stdout=handler.stream,
879 df3e8fac Vangelis Koukis
                                 stderr=handler.stream,
880 df3e8fac Vangelis Koukis
                                 files_preserve=[handler.stream])
881 df3e8fac Vangelis Koukis
        d.umask = 0022
882 df3e8fac Vangelis Koukis
        d.open()
883 df3e8fac Vangelis Koukis
884 1f3139f3 Apollon Oikonomopoulos
    logging.info("Starting up")
885 ea915b1a Apollon Oikonomopoulos
886 ea915b1a Apollon Oikonomopoulos
    proxy_opts = {}
887 ea915b1a Apollon Oikonomopoulos
    if config["dhcp"].as_bool("enable_dhcp"):
888 ea915b1a Apollon Oikonomopoulos
        proxy_opts.update({
889 ea915b1a Apollon Oikonomopoulos
            "dhcp_queue_num": config["dhcp"].as_int("dhcp_queue"),
890 ea915b1a Apollon Oikonomopoulos
            "dhcp_lease_lifetime": config["dhcp"].as_int("lease_lifetime"),
891 ea915b1a Apollon Oikonomopoulos
            "dhcp_lease_renewal": config["dhcp"].as_int("lease_renewal"),
892 ea915b1a Apollon Oikonomopoulos
            "dhcp_server_ip": config["dhcp"]["server_ip"],
893 ea915b1a Apollon Oikonomopoulos
            "dhcp_nameservers": config["dhcp"]["nameservers"],
894 26ba9dba Costas Drogos
            "dhcp_domain": config["dhcp"]["domain"],
895 ea915b1a Apollon Oikonomopoulos
        })
896 ea915b1a Apollon Oikonomopoulos
897 ea915b1a Apollon Oikonomopoulos
    if config["ipv6"].as_bool("enable_ipv6"):
898 ea915b1a Apollon Oikonomopoulos
        proxy_opts.update({
899 ea915b1a Apollon Oikonomopoulos
            "rs_queue_num": config["ipv6"].as_int("rs_queue"),
900 ea915b1a Apollon Oikonomopoulos
            "ns_queue_num": config["ipv6"].as_int("ns_queue"),
901 ea915b1a Apollon Oikonomopoulos
            "ra_period": config["ipv6"].as_int("ra_period"),
902 ea915b1a Apollon Oikonomopoulos
            "ipv6_nameservers": config["ipv6"]["nameservers"],
903 ea915b1a Apollon Oikonomopoulos
        })
904 ea915b1a Apollon Oikonomopoulos
905 6765a36f Apollon Oikonomopoulos
    # pylint: disable=W0142
906 ea915b1a Apollon Oikonomopoulos
    proxy = VMNetProxy(data_path=config["general"]["datapath"], **proxy_opts)
907 1f3139f3 Apollon Oikonomopoulos
908 1f3139f3 Apollon Oikonomopoulos
    # Drop all capabilities except CAP_NET_RAW and change uid
909 1f3139f3 Apollon Oikonomopoulos
    try:
910 ea915b1a Apollon Oikonomopoulos
        uid = getpwuid(config["general"].as_int("user"))
911 1f3139f3 Apollon Oikonomopoulos
    except ValueError:
912 ea915b1a Apollon Oikonomopoulos
        uid = getpwnam(config["general"]["user"])
913 1f3139f3 Apollon Oikonomopoulos
914 f2c7bb76 Apollon Oikonomopoulos
    logging.debug("Setting capabilities and changing uid")
915 6765a36f Apollon Oikonomopoulos
    logging.debug("User: %s, uid: %d, gid: %d",
916 6765a36f Apollon Oikonomopoulos
                  config["general"]["user"], uid.pw_uid, uid.pw_gid)
917 18142d8d Apollon Oikonomopoulos
918 18142d8d Apollon Oikonomopoulos
    # Keep only the capabilities we need
919 18142d8d Apollon Oikonomopoulos
    # CAP_NET_ADMIN: we need to send nfqueue packet verdicts to a netlinkgroup
920 6765a36f Apollon Oikonomopoulos
    capng.capng_clear(capng.CAPNG_SELECT_BOTH)
921 6765a36f Apollon Oikonomopoulos
    capng.capng_update(capng.CAPNG_ADD,
922 6765a36f Apollon Oikonomopoulos
                       capng.CAPNG_EFFECTIVE|capng.CAPNG_PERMITTED,
923 18142d8d Apollon Oikonomopoulos
                       capng.CAP_NET_ADMIN)
924 6765a36f Apollon Oikonomopoulos
    capng.capng_change_id(uid.pw_uid, uid.pw_gid,
925 6765a36f Apollon Oikonomopoulos
                          capng.CAPNG_DROP_SUPP_GRP|capng.CAPNG_CLEAR_BOUNDING)
926 f2c7bb76 Apollon Oikonomopoulos
927 1f3139f3 Apollon Oikonomopoulos
    logging.info("Ready to serve requests")
928 feca7bb9 Apollon Oikonomopoulos
    try:
929 feca7bb9 Apollon Oikonomopoulos
        proxy.serve()
930 feca7bb9 Apollon Oikonomopoulos
    except Exception:
931 feca7bb9 Apollon Oikonomopoulos
        if opts.daemonize:
932 feca7bb9 Apollon Oikonomopoulos
            exc = "".join(traceback.format_exception(*sys.exc_info()))
933 feca7bb9 Apollon Oikonomopoulos
            logging.critical(exc)
934 feca7bb9 Apollon Oikonomopoulos
        raise
935 1f3139f3 Apollon Oikonomopoulos
936 1f3139f3 Apollon Oikonomopoulos
937 1f3139f3 Apollon Oikonomopoulos
# vim: set ts=4 sts=4 sw=4 et :