Statistics
| Branch: | Tag: | Revision:

root / nfdhcpd @ 651e531d

History | View | Annotate | Download (23.2 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 1f3139f3 Apollon Oikonomopoulos
import glob
25 fa04d422 Apollon Oikonomopoulos
import time
26 1f3139f3 Apollon Oikonomopoulos
import logging
27 1f3139f3 Apollon Oikonomopoulos
import logging.handlers
28 83027c6b Apollon Oikonomopoulos
import threading
29 1f3139f3 Apollon Oikonomopoulos
import subprocess
30 1f3139f3 Apollon Oikonomopoulos
31 1f3139f3 Apollon Oikonomopoulos
import daemon
32 1f3139f3 Apollon Oikonomopoulos
import nfqueue
33 1f3139f3 Apollon Oikonomopoulos
import pyinotify
34 1f3139f3 Apollon Oikonomopoulos
35 1f3139f3 Apollon Oikonomopoulos
import IPy
36 1f3139f3 Apollon Oikonomopoulos
from select import select
37 699cc6e3 Apollon Oikonomopoulos
from socket import AF_INET, AF_INET6
38 1f3139f3 Apollon Oikonomopoulos
39 1f3139f3 Apollon Oikonomopoulos
from scapy.layers.l2 import Ether
40 1f3139f3 Apollon Oikonomopoulos
from scapy.layers.inet import IP, UDP
41 699cc6e3 Apollon Oikonomopoulos
from scapy.layers.inet6 import *
42 1f3139f3 Apollon Oikonomopoulos
from scapy.layers.dhcp import BOOTP, DHCP
43 1f3139f3 Apollon Oikonomopoulos
from scapy.sendrecv import sendp
44 1f3139f3 Apollon Oikonomopoulos
45 ea915b1a Apollon Oikonomopoulos
DEFAULT_CONFIG = "/etc/nfdhcpd/nfdhcpd.conf"
46 1f3139f3 Apollon Oikonomopoulos
DEFAULT_PATH = "/var/run/ganeti-dhcpd"
47 1f3139f3 Apollon Oikonomopoulos
DEFAULT_USER = "nobody"
48 ea915b1a Apollon Oikonomopoulos
DEFAULT_LEASE_LIFETIME = 604800 # 1 week
49 ea915b1a Apollon Oikonomopoulos
DEFAULT_LEASE_RENEWAL = 600  # 10 min
50 ea915b1a Apollon Oikonomopoulos
DEFAULT_RA_PERIOD = 300 # seconds
51 ea915b1a Apollon Oikonomopoulos
DHCP_DUMMY_SERVER_IP = "1.2.3.4"
52 1f3139f3 Apollon Oikonomopoulos
53 ea915b1a Apollon Oikonomopoulos
LOG_FILENAME = "nfdhcpd.log"
54 1f3139f3 Apollon Oikonomopoulos
55 1f3139f3 Apollon Oikonomopoulos
SYSFS_NET = "/sys/class/net"
56 1f3139f3 Apollon Oikonomopoulos
57 1f3139f3 Apollon Oikonomopoulos
LOG_FORMAT = "%(asctime)-15s %(levelname)-6s %(message)s"
58 1f3139f3 Apollon Oikonomopoulos
59 651e531d Apollon Oikonomopoulos
# Configuration file specification (see configobj documentation)
60 651e531d Apollon Oikonomopoulos
CONFIG_SPEC = """
61 651e531d Apollon Oikonomopoulos
[general]
62 651e531d Apollon Oikonomopoulos
pidfile = string()
63 651e531d Apollon Oikonomopoulos
datapath = string()
64 651e531d Apollon Oikonomopoulos
logdir = string()
65 651e531d Apollon Oikonomopoulos
user = string()
66 651e531d Apollon Oikonomopoulos
67 651e531d Apollon Oikonomopoulos
[dhcp]
68 651e531d Apollon Oikonomopoulos
enable_dhcp = boolean(default=True)
69 651e531d Apollon Oikonomopoulos
lease_lifetime = integer(min=0, max=4294967295)
70 651e531d Apollon Oikonomopoulos
lease_renewal = integer(min=0, max=4294967295)
71 651e531d Apollon Oikonomopoulos
server_ip = ip_addr()
72 651e531d Apollon Oikonomopoulos
dhcp_queue = integer(min=0, max=65535)
73 651e531d Apollon Oikonomopoulos
nameservers = ip_addr_list(family=4)
74 651e531d Apollon Oikonomopoulos
75 651e531d Apollon Oikonomopoulos
[ipv6]
76 651e531d Apollon Oikonomopoulos
enable_ipv6 = boolean(default=True)
77 651e531d Apollon Oikonomopoulos
ra_period = integer(min=1, max=4294967295)
78 651e531d Apollon Oikonomopoulos
rs_queue = integer(min=0, max=65535)
79 651e531d Apollon Oikonomopoulos
ns_queue = integer(min=0, max=65535)
80 651e531d Apollon Oikonomopoulos
nameservers = ip_addr_list(family=6)
81 651e531d Apollon Oikonomopoulos
"""
82 651e531d Apollon Oikonomopoulos
83 651e531d Apollon Oikonomopoulos
84 1f3139f3 Apollon Oikonomopoulos
DHCPDISCOVER = 1
85 1f3139f3 Apollon Oikonomopoulos
DHCPOFFER = 2
86 1f3139f3 Apollon Oikonomopoulos
DHCPREQUEST = 3
87 1f3139f3 Apollon Oikonomopoulos
DHCPDECLINE = 4
88 1f3139f3 Apollon Oikonomopoulos
DHCPACK = 5
89 1f3139f3 Apollon Oikonomopoulos
DHCPNAK = 6
90 1f3139f3 Apollon Oikonomopoulos
DHCPRELEASE = 7
91 1f3139f3 Apollon Oikonomopoulos
DHCPINFORM = 8
92 1f3139f3 Apollon Oikonomopoulos
93 1f3139f3 Apollon Oikonomopoulos
DHCP_TYPES = {
94 1f3139f3 Apollon Oikonomopoulos
    DHCPDISCOVER: "DHCPDISCOVER",
95 1f3139f3 Apollon Oikonomopoulos
    DHCPOFFER: "DHCPOFFER",
96 1f3139f3 Apollon Oikonomopoulos
    DHCPREQUEST: "DHCPREQUEST",
97 1f3139f3 Apollon Oikonomopoulos
    DHCPDECLINE: "DHCPDECLINE",
98 1f3139f3 Apollon Oikonomopoulos
    DHCPACK: "DHCPACK",
99 1f3139f3 Apollon Oikonomopoulos
    DHCPNAK: "DHCPNAK",
100 1f3139f3 Apollon Oikonomopoulos
    DHCPRELEASE: "DHCPRELEASE",
101 1f3139f3 Apollon Oikonomopoulos
    DHCPINFORM: "DHCPINFORM",
102 1f3139f3 Apollon Oikonomopoulos
}
103 1f3139f3 Apollon Oikonomopoulos
104 1f3139f3 Apollon Oikonomopoulos
DHCP_REQRESP = {
105 1f3139f3 Apollon Oikonomopoulos
    DHCPDISCOVER: DHCPOFFER,
106 1f3139f3 Apollon Oikonomopoulos
    DHCPREQUEST: DHCPACK,
107 1f3139f3 Apollon Oikonomopoulos
    DHCPINFORM: DHCPACK,
108 1f3139f3 Apollon Oikonomopoulos
    }
109 1f3139f3 Apollon Oikonomopoulos
110 519ec23c Apollon Oikonomopoulos
111 699cc6e3 Apollon Oikonomopoulos
class ClientFileHandler(pyinotify.ProcessEvent):
112 699cc6e3 Apollon Oikonomopoulos
    def __init__(self, server):
113 1f3139f3 Apollon Oikonomopoulos
        pyinotify.ProcessEvent.__init__(self)
114 699cc6e3 Apollon Oikonomopoulos
        self.server = server
115 1f3139f3 Apollon Oikonomopoulos
116 1f3139f3 Apollon Oikonomopoulos
    def process_IN_DELETE(self, event):
117 699cc6e3 Apollon Oikonomopoulos
        self.server.remove_iface(event.name)
118 1f3139f3 Apollon Oikonomopoulos
119 1f3139f3 Apollon Oikonomopoulos
    def process_IN_CLOSE_WRITE(self, event):
120 699cc6e3 Apollon Oikonomopoulos
        self.server.add_iface(os.path.join(event.path, event.name))
121 1f3139f3 Apollon Oikonomopoulos
122 699cc6e3 Apollon Oikonomopoulos
123 699cc6e3 Apollon Oikonomopoulos
class Client(object):
124 1f3139f3 Apollon Oikonomopoulos
    def __init__(self, mac=None, ips=None, link=None, hostname=None):
125 1f3139f3 Apollon Oikonomopoulos
        self.mac = mac
126 1f3139f3 Apollon Oikonomopoulos
        self.ips = ips
127 1f3139f3 Apollon Oikonomopoulos
        self.hostname = hostname
128 1f3139f3 Apollon Oikonomopoulos
        self.link = link
129 1f3139f3 Apollon Oikonomopoulos
        self.iface = None
130 699cc6e3 Apollon Oikonomopoulos
131 1f3139f3 Apollon Oikonomopoulos
    @property
132 1f3139f3 Apollon Oikonomopoulos
    def ip(self):
133 1f3139f3 Apollon Oikonomopoulos
        return self.ips[0]
134 1f3139f3 Apollon Oikonomopoulos
135 1f3139f3 Apollon Oikonomopoulos
    def is_valid(self):
136 1f3139f3 Apollon Oikonomopoulos
        return self.mac is not None and self.ips is not None\
137 1f3139f3 Apollon Oikonomopoulos
               and self.hostname is not None
138 1f3139f3 Apollon Oikonomopoulos
139 1f3139f3 Apollon Oikonomopoulos
140 1f3139f3 Apollon Oikonomopoulos
class Subnet(object):
141 1f3139f3 Apollon Oikonomopoulos
    def __init__(self, net=None, gw=None, dev=None):
142 1f3139f3 Apollon Oikonomopoulos
        if isinstance(net, str):
143 1f3139f3 Apollon Oikonomopoulos
            self.net = IPy.IP(net)
144 1f3139f3 Apollon Oikonomopoulos
        else:
145 1f3139f3 Apollon Oikonomopoulos
            self.net = net
146 1f3139f3 Apollon Oikonomopoulos
        self.gw = gw
147 1f3139f3 Apollon Oikonomopoulos
        self.dev = dev
148 1f3139f3 Apollon Oikonomopoulos
149 1f3139f3 Apollon Oikonomopoulos
    @property
150 1f3139f3 Apollon Oikonomopoulos
    def netmask(self):
151 1f3139f3 Apollon Oikonomopoulos
        return str(self.net.netmask())
152 1f3139f3 Apollon Oikonomopoulos
153 1f3139f3 Apollon Oikonomopoulos
    @property
154 1f3139f3 Apollon Oikonomopoulos
    def broadcast(self):
155 1f3139f3 Apollon Oikonomopoulos
        return str(self.net.broadcast())
156 1f3139f3 Apollon Oikonomopoulos
157 247ad61d Apollon Oikonomopoulos
    @property
158 247ad61d Apollon Oikonomopoulos
    def prefix(self):
159 247ad61d Apollon Oikonomopoulos
        return self.net.net()
160 247ad61d Apollon Oikonomopoulos
161 247ad61d Apollon Oikonomopoulos
    @property
162 247ad61d Apollon Oikonomopoulos
    def prefixlen(self):
163 247ad61d Apollon Oikonomopoulos
        return self.net.prefixlen()
164 247ad61d Apollon Oikonomopoulos
165 247ad61d Apollon Oikonomopoulos
    @staticmethod
166 247ad61d Apollon Oikonomopoulos
    def _make_eui64(net, mac):
167 247ad61d Apollon Oikonomopoulos
        """ Compute an EUI-64 address from an EUI-48 (MAC) address
168 247ad61d Apollon Oikonomopoulos
169 247ad61d Apollon Oikonomopoulos
        """
170 247ad61d Apollon Oikonomopoulos
        comp = mac.split(":")
171 247ad61d Apollon Oikonomopoulos
        prefix = IPy.IP(net).net().strFullsize().split(":")[:4]
172 247ad61d Apollon Oikonomopoulos
        eui64 = comp[:3] + ["ff", "fe"] + comp[3:]
173 247ad61d Apollon Oikonomopoulos
        eui64[0] = "%02x" % (int(eui64[0], 16) ^ 0x02)
174 247ad61d Apollon Oikonomopoulos
        for l in range(0, len(eui64), 2):
175 247ad61d Apollon Oikonomopoulos
            prefix += ["".join(eui64[l:l+2])]
176 247ad61d Apollon Oikonomopoulos
        return IPy.IP(":".join(prefix))
177 247ad61d Apollon Oikonomopoulos
178 247ad61d Apollon Oikonomopoulos
    def make_eui64(self, mac):
179 247ad61d Apollon Oikonomopoulos
        return self._make_eui64(self.net, mac)
180 247ad61d Apollon Oikonomopoulos
181 247ad61d Apollon Oikonomopoulos
    def make_ll64(self, mac):
182 247ad61d Apollon Oikonomopoulos
        return self._make_eui64("fe80::", mac)
183 247ad61d Apollon Oikonomopoulos
184 1f3139f3 Apollon Oikonomopoulos
185 699cc6e3 Apollon Oikonomopoulos
class VMNetProxy(object):
186 699cc6e3 Apollon Oikonomopoulos
    def __init__(self, data_path, dhcp_queue_num=None,
187 ea915b1a Apollon Oikonomopoulos
                 rs_queue_num=None, ns_queue_num=None,
188 ea915b1a Apollon Oikonomopoulos
                 dhcp_lease_lifetime=DEFAULT_LEASE_LIFETIME,
189 ea915b1a Apollon Oikonomopoulos
                 dhcp_lease_renewal=DEFAULT_LEASE_RENEWAL,
190 ea915b1a Apollon Oikonomopoulos
                 dhcp_server_ip=DHCP_DUMMY_SERVER_IP, dhcp_nameservers = [],
191 ea915b1a Apollon Oikonomopoulos
                 ra_period=DEFAULT_RA_PERIOD, ipv6_nameservers = []):
192 ea915b1a Apollon Oikonomopoulos
193 1f3139f3 Apollon Oikonomopoulos
        self.data_path = data_path
194 ea915b1a Apollon Oikonomopoulos
        self.lease_lifetime = dhcp_lease_lifetime
195 ea915b1a Apollon Oikonomopoulos
        self.lease_renewal = dhcp_lease_renewal
196 ea915b1a Apollon Oikonomopoulos
        self.dhcp_server_ip = dhcp_server_ip
197 ea915b1a Apollon Oikonomopoulos
        self.ra_period = ra_period
198 ea915b1a Apollon Oikonomopoulos
        self.dhcp_nameservers = dhcp_nameservers
199 ea915b1a Apollon Oikonomopoulos
        self.ipv6_nameservers = ipv6_nameservers
200 ea915b1a Apollon Oikonomopoulos
201 1f3139f3 Apollon Oikonomopoulos
        self.clients = {}
202 1f3139f3 Apollon Oikonomopoulos
        self.subnets = {}
203 1f3139f3 Apollon Oikonomopoulos
        self.ifaces = {}
204 247ad61d Apollon Oikonomopoulos
        self.v6nets = {}
205 699cc6e3 Apollon Oikonomopoulos
        self.nfq = {}
206 699cc6e3 Apollon Oikonomopoulos
207 1f3139f3 Apollon Oikonomopoulos
        # Inotify setup
208 1f3139f3 Apollon Oikonomopoulos
        self.wm = pyinotify.WatchManager()
209 1f3139f3 Apollon Oikonomopoulos
        mask = pyinotify.EventsCodes.ALL_FLAGS["IN_DELETE"]
210 1f3139f3 Apollon Oikonomopoulos
        mask |= pyinotify.EventsCodes.ALL_FLAGS["IN_CLOSE_WRITE"]
211 699cc6e3 Apollon Oikonomopoulos
        handler = ClientFileHandler(self)
212 1f3139f3 Apollon Oikonomopoulos
        self.notifier = pyinotify.Notifier(self.wm, handler)
213 1f3139f3 Apollon Oikonomopoulos
        self.wm.add_watch(self.data_path, mask, rec=True)
214 1f3139f3 Apollon Oikonomopoulos
215 699cc6e3 Apollon Oikonomopoulos
        # NFQUEUE setup
216 699cc6e3 Apollon Oikonomopoulos
        if dhcp_queue_num is not None:
217 699cc6e3 Apollon Oikonomopoulos
            self._setup_nfqueue(dhcp_queue_num, AF_INET, self.dhcp_response)
218 699cc6e3 Apollon Oikonomopoulos
219 699cc6e3 Apollon Oikonomopoulos
        if rs_queue_num is not None:
220 699cc6e3 Apollon Oikonomopoulos
            self._setup_nfqueue(rs_queue_num, AF_INET6, self.rs_response)
221 699cc6e3 Apollon Oikonomopoulos
222 699cc6e3 Apollon Oikonomopoulos
        if ns_queue_num is not None:
223 699cc6e3 Apollon Oikonomopoulos
            self._setup_nfqueue(ns_queue_num, AF_INET6, self.ns_response)
224 699cc6e3 Apollon Oikonomopoulos
225 699cc6e3 Apollon Oikonomopoulos
    def _setup_nfqueue(self, queue_num, family, callback):
226 247ad61d Apollon Oikonomopoulos
        logging.debug("Setting up NFQUEUE for queue %d, AF %s" %
227 247ad61d Apollon Oikonomopoulos
                      (queue_num, family))
228 699cc6e3 Apollon Oikonomopoulos
        q = nfqueue.queue()
229 699cc6e3 Apollon Oikonomopoulos
        q.set_callback(callback)
230 699cc6e3 Apollon Oikonomopoulos
        q.fast_open(queue_num, family)
231 699cc6e3 Apollon Oikonomopoulos
        q.set_queue_maxlen(5000)
232 1f3139f3 Apollon Oikonomopoulos
        # This is mandatory for the queue to operate
233 699cc6e3 Apollon Oikonomopoulos
        q.set_mode(nfqueue.NFQNL_COPY_PACKET)
234 699cc6e3 Apollon Oikonomopoulos
        self.nfq[q.get_fd()] = q
235 1f3139f3 Apollon Oikonomopoulos
236 1f3139f3 Apollon Oikonomopoulos
    def build_config(self):
237 1f3139f3 Apollon Oikonomopoulos
        self.clients.clear()
238 1f3139f3 Apollon Oikonomopoulos
        self.subnets.clear()
239 1f3139f3 Apollon Oikonomopoulos
240 1f3139f3 Apollon Oikonomopoulos
        for file in glob.glob(os.path.join(self.data_path, "*")):
241 1f3139f3 Apollon Oikonomopoulos
            self.add_iface(file)
242 1f3139f3 Apollon Oikonomopoulos
243 1f3139f3 Apollon Oikonomopoulos
    def get_ifindex(self, iface):
244 1f3139f3 Apollon Oikonomopoulos
        """ Get the interface index from sysfs
245 1f3139f3 Apollon Oikonomopoulos
246 1f3139f3 Apollon Oikonomopoulos
        """
247 1f3139f3 Apollon Oikonomopoulos
        file = os.path.abspath(os.path.join(SYSFS_NET, iface, "ifindex"))
248 1f3139f3 Apollon Oikonomopoulos
        if not file.startswith(SYSFS_NET):
249 1f3139f3 Apollon Oikonomopoulos
            return None
250 1f3139f3 Apollon Oikonomopoulos
251 1f3139f3 Apollon Oikonomopoulos
        ifindex = None
252 1f3139f3 Apollon Oikonomopoulos
253 1f3139f3 Apollon Oikonomopoulos
        try:
254 1f3139f3 Apollon Oikonomopoulos
            f = open(file, 'r')
255 1f3139f3 Apollon Oikonomopoulos
            ifindex = int(f.readline().strip())
256 1f3139f3 Apollon Oikonomopoulos
            f.close()
257 6ca53b5c Apollon Oikonomopoulos
        except IOError:
258 6ca53b5c Apollon Oikonomopoulos
            logging.debug("%s is down, removing" % iface)
259 6ca53b5c Apollon Oikonomopoulos
            self.remove_iface(iface)
260 1f3139f3 Apollon Oikonomopoulos
261 1f3139f3 Apollon Oikonomopoulos
        return ifindex
262 699cc6e3 Apollon Oikonomopoulos
263 699cc6e3 Apollon Oikonomopoulos
264 1f3139f3 Apollon Oikonomopoulos
    def get_iface_hw_addr(self, iface):
265 1f3139f3 Apollon Oikonomopoulos
        """ Get the interface hardware address from sysfs
266 1f3139f3 Apollon Oikonomopoulos
267 1f3139f3 Apollon Oikonomopoulos
        """
268 1f3139f3 Apollon Oikonomopoulos
        file = os.path.abspath(os.path.join(SYSFS_NET, iface, "address"))
269 1f3139f3 Apollon Oikonomopoulos
        if not file.startswith(SYSFS_NET):
270 1f3139f3 Apollon Oikonomopoulos
            return None
271 1f3139f3 Apollon Oikonomopoulos
272 1f3139f3 Apollon Oikonomopoulos
        addr = None
273 1f3139f3 Apollon Oikonomopoulos
        try:
274 1f3139f3 Apollon Oikonomopoulos
            f = open(file, 'r')
275 1f3139f3 Apollon Oikonomopoulos
            addr = f.readline().strip()
276 1f3139f3 Apollon Oikonomopoulos
            f.close()
277 6ca53b5c Apollon Oikonomopoulos
        except IOError:
278 6ca53b5c Apollon Oikonomopoulos
            logging.debug("%s is down, removing" % iface)
279 6ca53b5c Apollon Oikonomopoulos
            self.remove_iface(iface)
280 6ca53b5c Apollon Oikonomopoulos
281 1f3139f3 Apollon Oikonomopoulos
        return addr
282 1f3139f3 Apollon Oikonomopoulos
283 699cc6e3 Apollon Oikonomopoulos
    def parse_routing_table(self, table="main", family=4):
284 1f3139f3 Apollon Oikonomopoulos
        """ Parse the given routing table to get connected route, gateway and
285 1f3139f3 Apollon Oikonomopoulos
        default device.
286 1f3139f3 Apollon Oikonomopoulos
287 1f3139f3 Apollon Oikonomopoulos
        """
288 699cc6e3 Apollon Oikonomopoulos
        ipro = subprocess.Popen(["ip", "-%d" % family, "ro", "ls",
289 699cc6e3 Apollon Oikonomopoulos
                                 "table", table], stdout=subprocess.PIPE)
290 1f3139f3 Apollon Oikonomopoulos
        routes = ipro.stdout.readlines()
291 699cc6e3 Apollon Oikonomopoulos
292 1f3139f3 Apollon Oikonomopoulos
        def_gw = None
293 1f3139f3 Apollon Oikonomopoulos
        def_dev = None
294 1f3139f3 Apollon Oikonomopoulos
        def_net = None
295 1f3139f3 Apollon Oikonomopoulos
296 1f3139f3 Apollon Oikonomopoulos
        for route in routes:
297 1f3139f3 Apollon Oikonomopoulos
            match = re.match(r'^default.*via ([^\s]+).*dev ([^\s]+)', route)
298 1f3139f3 Apollon Oikonomopoulos
            if match:
299 1f3139f3 Apollon Oikonomopoulos
                def_gw, def_dev = match.groups()
300 1f3139f3 Apollon Oikonomopoulos
                break
301 1f3139f3 Apollon Oikonomopoulos
302 1f3139f3 Apollon Oikonomopoulos
        for route in routes:
303 1f3139f3 Apollon Oikonomopoulos
            # Find the least-specific connected route
304 1f3139f3 Apollon Oikonomopoulos
            try:
305 1f3139f3 Apollon Oikonomopoulos
                def_net = re.match("^([^\\s]+) dev %s" %
306 1f3139f3 Apollon Oikonomopoulos
                                   def_dev, route).groups()[0]
307 1f3139f3 Apollon Oikonomopoulos
                def_net = IPy.IP(def_net)
308 1f3139f3 Apollon Oikonomopoulos
            except:
309 1f3139f3 Apollon Oikonomopoulos
                pass
310 1f3139f3 Apollon Oikonomopoulos
311 1f3139f3 Apollon Oikonomopoulos
        return Subnet(net=def_net, gw=def_gw, dev=def_dev)
312 699cc6e3 Apollon Oikonomopoulos
313 1f3139f3 Apollon Oikonomopoulos
    def parse_binding_file(self, path):
314 1f3139f3 Apollon Oikonomopoulos
        """ Read a client configuration from a tap file
315 1f3139f3 Apollon Oikonomopoulos
316 1f3139f3 Apollon Oikonomopoulos
        """
317 1f3139f3 Apollon Oikonomopoulos
        try:
318 1f3139f3 Apollon Oikonomopoulos
            iffile = open(path, 'r')
319 1f3139f3 Apollon Oikonomopoulos
        except:
320 1f3139f3 Apollon Oikonomopoulos
            return (None, None, None, None)
321 1f3139f3 Apollon Oikonomopoulos
        mac = None
322 1f3139f3 Apollon Oikonomopoulos
        ips = None
323 1f3139f3 Apollon Oikonomopoulos
        link = None
324 1f3139f3 Apollon Oikonomopoulos
        hostname = None
325 1f3139f3 Apollon Oikonomopoulos
326 1f3139f3 Apollon Oikonomopoulos
        for line in iffile:
327 1f3139f3 Apollon Oikonomopoulos
            if line.startswith("IP="):
328 1f3139f3 Apollon Oikonomopoulos
                ip = line.strip().split("=")[1]
329 1f3139f3 Apollon Oikonomopoulos
                ips = ip.split()
330 1f3139f3 Apollon Oikonomopoulos
            elif line.startswith("MAC="):
331 1f3139f3 Apollon Oikonomopoulos
                mac = line.strip().split("=")[1]
332 1f3139f3 Apollon Oikonomopoulos
            elif line.startswith("LINK="):
333 1f3139f3 Apollon Oikonomopoulos
                link = line.strip().split("=")[1]
334 1f3139f3 Apollon Oikonomopoulos
            elif line.startswith("HOSTNAME="):
335 1f3139f3 Apollon Oikonomopoulos
                hostname = line.strip().split("=")[1]
336 1f3139f3 Apollon Oikonomopoulos
337 699cc6e3 Apollon Oikonomopoulos
        return Client(mac=mac, ips=ips, link=link, hostname=hostname)
338 1f3139f3 Apollon Oikonomopoulos
339 1f3139f3 Apollon Oikonomopoulos
    def add_iface(self, path):
340 1f3139f3 Apollon Oikonomopoulos
        """ Add an interface to monitor
341 1f3139f3 Apollon Oikonomopoulos
342 1f3139f3 Apollon Oikonomopoulos
        """
343 1f3139f3 Apollon Oikonomopoulos
        iface = os.path.basename(path)
344 1f3139f3 Apollon Oikonomopoulos
345 1f3139f3 Apollon Oikonomopoulos
        logging.debug("Updating configuration for %s" % iface)
346 1f3139f3 Apollon Oikonomopoulos
        binding = self.parse_binding_file(path)
347 1f3139f3 Apollon Oikonomopoulos
        ifindex = self.get_ifindex(iface)
348 1f3139f3 Apollon Oikonomopoulos
349 1f3139f3 Apollon Oikonomopoulos
        if ifindex is None:
350 1f3139f3 Apollon Oikonomopoulos
            logging.warn("Stale configuration for %s found" % iface)
351 1f3139f3 Apollon Oikonomopoulos
        else:
352 1f3139f3 Apollon Oikonomopoulos
            if binding.is_valid():
353 1f3139f3 Apollon Oikonomopoulos
                binding.iface = iface
354 1f3139f3 Apollon Oikonomopoulos
                self.clients[binding.mac] = binding
355 1f3139f3 Apollon Oikonomopoulos
                self.subnets[binding.link] = self.parse_routing_table(
356 1f3139f3 Apollon Oikonomopoulos
                                                binding.link)
357 1f3139f3 Apollon Oikonomopoulos
                logging.debug("Added client %s on %s" %
358 1f3139f3 Apollon Oikonomopoulos
                              (binding.hostname, iface))
359 1f3139f3 Apollon Oikonomopoulos
                self.ifaces[ifindex] = iface
360 247ad61d Apollon Oikonomopoulos
                self.v6nets[iface] = self.parse_routing_table(binding.link, 6)
361 1f3139f3 Apollon Oikonomopoulos
362 1f3139f3 Apollon Oikonomopoulos
    def remove_iface(self, iface):
363 1f3139f3 Apollon Oikonomopoulos
        """ Cleanup clients on a removed interface
364 1f3139f3 Apollon Oikonomopoulos
365 1f3139f3 Apollon Oikonomopoulos
        """
366 247ad61d Apollon Oikonomopoulos
        if iface in self.v6nets:
367 6ca53b5c Apollon Oikonomopoulos
            del self.v6nets[iface]
368 247ad61d Apollon Oikonomopoulos
369 1f3139f3 Apollon Oikonomopoulos
        for mac in self.clients.keys():
370 1f3139f3 Apollon Oikonomopoulos
            if self.clients[mac].iface == iface:
371 1f3139f3 Apollon Oikonomopoulos
                del self.clients[mac]
372 1f3139f3 Apollon Oikonomopoulos
373 1f3139f3 Apollon Oikonomopoulos
        for ifindex in self.ifaces.keys():
374 1f3139f3 Apollon Oikonomopoulos
            if self.ifaces[ifindex] == iface:
375 1f3139f3 Apollon Oikonomopoulos
                del self.ifaces[ifindex]
376 1f3139f3 Apollon Oikonomopoulos
377 1f3139f3 Apollon Oikonomopoulos
        logging.debug("Removed interface %s" % iface)
378 1f3139f3 Apollon Oikonomopoulos
379 699cc6e3 Apollon Oikonomopoulos
    def dhcp_response(self, i, payload):
380 1f3139f3 Apollon Oikonomopoulos
        """ Generate a reply to a BOOTP/DHCP request
381 1f3139f3 Apollon Oikonomopoulos
382 1f3139f3 Apollon Oikonomopoulos
        """
383 1f3139f3 Apollon Oikonomopoulos
        # Decode the response - NFQUEUE relays IP packets
384 1f3139f3 Apollon Oikonomopoulos
        pkt = IP(payload.get_data())
385 1f3139f3 Apollon Oikonomopoulos
386 1f3139f3 Apollon Oikonomopoulos
        # Get the actual interface from the ifindex
387 1f3139f3 Apollon Oikonomopoulos
        iface = self.ifaces[payload.get_indev()]
388 1f3139f3 Apollon Oikonomopoulos
389 1f3139f3 Apollon Oikonomopoulos
        # Signal the kernel that it shouldn't further process the packet
390 1f3139f3 Apollon Oikonomopoulos
        payload.set_verdict(nfqueue.NF_DROP)
391 699cc6e3 Apollon Oikonomopoulos
392 1f3139f3 Apollon Oikonomopoulos
        # Get the client MAC address
393 1f3139f3 Apollon Oikonomopoulos
        resp = pkt.getlayer(BOOTP).copy()
394 1f3139f3 Apollon Oikonomopoulos
        hlen = resp.hlen
395 1f3139f3 Apollon Oikonomopoulos
        mac = resp.chaddr[:hlen].encode("hex")
396 1f3139f3 Apollon Oikonomopoulos
        mac, _ = re.subn(r'([0-9a-fA-F]{2})', r'\1:', mac, hlen-1)
397 1f3139f3 Apollon Oikonomopoulos
398 1f3139f3 Apollon Oikonomopoulos
        # Server responses are always BOOTREPLYs
399 1f3139f3 Apollon Oikonomopoulos
        resp.op = "BOOTREPLY"
400 1f3139f3 Apollon Oikonomopoulos
        del resp.payload
401 1f3139f3 Apollon Oikonomopoulos
402 1f3139f3 Apollon Oikonomopoulos
        try:
403 1f3139f3 Apollon Oikonomopoulos
            binding = self.clients[mac]
404 1f3139f3 Apollon Oikonomopoulos
        except KeyError:
405 1f3139f3 Apollon Oikonomopoulos
            logging.warn("Invalid client %s on %s" % (mac, iface))
406 1f3139f3 Apollon Oikonomopoulos
            return
407 1f3139f3 Apollon Oikonomopoulos
408 1f3139f3 Apollon Oikonomopoulos
        if iface != binding.iface:
409 1f3139f3 Apollon Oikonomopoulos
            logging.warn("Received spoofed DHCP request for %s from interface"
410 1f3139f3 Apollon Oikonomopoulos
                         " %s instead of %s" %
411 1f3139f3 Apollon Oikonomopoulos
                         (mac, iface, binding.iface))
412 1f3139f3 Apollon Oikonomopoulos
            return
413 1f3139f3 Apollon Oikonomopoulos
414 1f3139f3 Apollon Oikonomopoulos
        resp = Ether(dst=mac, src=self.get_iface_hw_addr(iface))/\
415 699cc6e3 Apollon Oikonomopoulos
               IP(src=DHCP_DUMMY_SERVER_IP, dst=binding.ip)/\
416 1f3139f3 Apollon Oikonomopoulos
               UDP(sport=pkt.dport, dport=pkt.sport)/resp
417 1f3139f3 Apollon Oikonomopoulos
        subnet = self.subnets[binding.link]
418 1f3139f3 Apollon Oikonomopoulos
419 1f3139f3 Apollon Oikonomopoulos
        if not DHCP in pkt:
420 1f3139f3 Apollon Oikonomopoulos
            logging.warn("Invalid request from %s on %s, no DHCP"
421 1f3139f3 Apollon Oikonomopoulos
                         " payload found" % (binding.mac, iface))
422 1f3139f3 Apollon Oikonomopoulos
            return
423 1f3139f3 Apollon Oikonomopoulos
424 1f3139f3 Apollon Oikonomopoulos
        dhcp_options = []
425 1f3139f3 Apollon Oikonomopoulos
        requested_addr = binding.ip
426 1f3139f3 Apollon Oikonomopoulos
        for opt in pkt[DHCP].options:
427 1f3139f3 Apollon Oikonomopoulos
            if type(opt) is tuple and opt[0] == "message-type":
428 1f3139f3 Apollon Oikonomopoulos
                req_type = opt[1]
429 1f3139f3 Apollon Oikonomopoulos
            if type(opt) is tuple and opt[0] == "requested_addr":
430 1f3139f3 Apollon Oikonomopoulos
                requested_addr = opt[1]
431 1f3139f3 Apollon Oikonomopoulos
432 1f3139f3 Apollon Oikonomopoulos
        logging.info("%s from %s on %s" %
433 1f3139f3 Apollon Oikonomopoulos
                    (DHCP_TYPES.get(req_type, "UNKNOWN"), binding.mac, iface))
434 1f3139f3 Apollon Oikonomopoulos
435 1f3139f3 Apollon Oikonomopoulos
        if req_type == DHCPREQUEST and requested_addr != binding.ip:
436 1f3139f3 Apollon Oikonomopoulos
            resp_type = DHCPNAK
437 1f3139f3 Apollon Oikonomopoulos
            logging.info("Sending DHCPNAK to %s on %s: requested %s"
438 1f3139f3 Apollon Oikonomopoulos
                         " instead of %s" %
439 1f3139f3 Apollon Oikonomopoulos
                         (binding.mac, iface, requested_addr, binding.ip))
440 1f3139f3 Apollon Oikonomopoulos
441 1f3139f3 Apollon Oikonomopoulos
        elif req_type in (DHCPDISCOVER, DHCPREQUEST):
442 1f3139f3 Apollon Oikonomopoulos
            resp_type = DHCP_REQRESP[req_type]
443 1f3139f3 Apollon Oikonomopoulos
            resp.yiaddr = self.clients[mac].ip
444 1f3139f3 Apollon Oikonomopoulos
            dhcp_options += [
445 1f3139f3 Apollon Oikonomopoulos
                 ("hostname", binding.hostname),
446 1f3139f3 Apollon Oikonomopoulos
                 ("domain", binding.hostname.split('.', 1)[-1]),
447 1f3139f3 Apollon Oikonomopoulos
                 ("router", subnet.gw),
448 1f3139f3 Apollon Oikonomopoulos
                 ("name_server", "194.177.210.10"),
449 1f3139f3 Apollon Oikonomopoulos
                 ("name_server", "194.177.210.211"),
450 1f3139f3 Apollon Oikonomopoulos
                 ("broadcast_address", str(subnet.broadcast)),
451 1f3139f3 Apollon Oikonomopoulos
                 ("subnet_mask", str(subnet.netmask)),
452 ea915b1a Apollon Oikonomopoulos
                 ("renewal_time", self.lease_renewal),
453 ea915b1a Apollon Oikonomopoulos
                 ("lease_time", self.lease_lifetime),
454 1f3139f3 Apollon Oikonomopoulos
            ]
455 1f3139f3 Apollon Oikonomopoulos
456 1f3139f3 Apollon Oikonomopoulos
        elif req_type == DHCPINFORM:
457 1f3139f3 Apollon Oikonomopoulos
            resp_type = DHCP_REQRESP[req_type]
458 1f3139f3 Apollon Oikonomopoulos
            dhcp_options += [
459 1f3139f3 Apollon Oikonomopoulos
                 ("hostname", binding.hostname),
460 1f3139f3 Apollon Oikonomopoulos
                 ("domain", binding.hostname.split('.', 1)[-1]),
461 1f3139f3 Apollon Oikonomopoulos
                 ("name_server", "194.177.210.10"),
462 1f3139f3 Apollon Oikonomopoulos
                 ("name_server", "194.177.210.211"),
463 1f3139f3 Apollon Oikonomopoulos
            ]
464 1f3139f3 Apollon Oikonomopoulos
465 1f3139f3 Apollon Oikonomopoulos
        elif req_type == DHCPRELEASE:
466 1f3139f3 Apollon Oikonomopoulos
            # Log and ignore
467 1f3139f3 Apollon Oikonomopoulos
            logging.info("DHCPRELEASE from %s on %s" %
468 1f3139f3 Apollon Oikonomopoulos
                         (binding.mac, iface))
469 1f3139f3 Apollon Oikonomopoulos
            return
470 1f3139f3 Apollon Oikonomopoulos
471 1f3139f3 Apollon Oikonomopoulos
        # Finally, always add the server identifier and end options
472 1f3139f3 Apollon Oikonomopoulos
        dhcp_options += [
473 1f3139f3 Apollon Oikonomopoulos
            ("message-type", resp_type),
474 699cc6e3 Apollon Oikonomopoulos
            ("server_id", DHCP_DUMMY_SERVER_IP),
475 1f3139f3 Apollon Oikonomopoulos
            "end"
476 1f3139f3 Apollon Oikonomopoulos
        ]
477 1f3139f3 Apollon Oikonomopoulos
        resp /= DHCP(options=dhcp_options)
478 1f3139f3 Apollon Oikonomopoulos
479 1f3139f3 Apollon Oikonomopoulos
        logging.info("%s to %s (%s) on %s" %
480 1f3139f3 Apollon Oikonomopoulos
                      (DHCP_TYPES[resp_type], mac, binding.ip, iface))
481 1f3139f3 Apollon Oikonomopoulos
        sendp(resp, iface=iface, verbose=False)
482 1f3139f3 Apollon Oikonomopoulos
483 247ad61d Apollon Oikonomopoulos
    def rs_response(self, i, payload):
484 247ad61d Apollon Oikonomopoulos
        """ Generate a reply to a BOOTP/DHCP request
485 247ad61d Apollon Oikonomopoulos
486 247ad61d Apollon Oikonomopoulos
        """
487 247ad61d Apollon Oikonomopoulos
        # Get the actual interface from the ifindex
488 247ad61d Apollon Oikonomopoulos
        iface = self.ifaces[payload.get_indev()]
489 247ad61d Apollon Oikonomopoulos
        ifmac = self.get_iface_hw_addr(iface)
490 247ad61d Apollon Oikonomopoulos
        subnet = self.v6nets[iface]
491 247ad61d Apollon Oikonomopoulos
        ifll = subnet.make_ll64(ifmac)
492 247ad61d Apollon Oikonomopoulos
493 247ad61d Apollon Oikonomopoulos
        # Signal the kernel that it shouldn't further process the packet
494 247ad61d Apollon Oikonomopoulos
        payload.set_verdict(nfqueue.NF_DROP)
495 247ad61d Apollon Oikonomopoulos
496 247ad61d Apollon Oikonomopoulos
        resp = Ether(src=self.get_iface_hw_addr(iface))/\
497 247ad61d Apollon Oikonomopoulos
               IPv6(src=str(ifll))/ICMPv6ND_RA(routerlifetime=14400)/\
498 247ad61d Apollon Oikonomopoulos
               ICMPv6NDOptPrefixInfo(prefix=str(subnet.prefix),
499 247ad61d Apollon Oikonomopoulos
                                     prefixlen=subnet.prefixlen)
500 247ad61d Apollon Oikonomopoulos
501 247ad61d Apollon Oikonomopoulos
        logging.info("RA on %s for %s" % (iface, subnet.net))
502 247ad61d Apollon Oikonomopoulos
        sendp(resp, iface=iface, verbose=False)
503 1f3139f3 Apollon Oikonomopoulos
504 a5c8c1fe Apollon Oikonomopoulos
    def ns_response(self, i, payload):
505 a5c8c1fe Apollon Oikonomopoulos
        """ Generate a reply to an ICMPv6 neighbor solicitation
506 a5c8c1fe Apollon Oikonomopoulos
507 a5c8c1fe Apollon Oikonomopoulos
        """
508 a5c8c1fe Apollon Oikonomopoulos
        # Get the actual interface from the ifindex
509 a5c8c1fe Apollon Oikonomopoulos
        iface = self.ifaces[payload.get_indev()]
510 a5c8c1fe Apollon Oikonomopoulos
        ifmac = self.get_iface_hw_addr(iface)
511 a5c8c1fe Apollon Oikonomopoulos
        subnet = self.v6nets[iface]
512 a5c8c1fe Apollon Oikonomopoulos
        ifll = subnet.make_ll64(ifmac)
513 a5c8c1fe Apollon Oikonomopoulos
514 a5c8c1fe Apollon Oikonomopoulos
        ns = IPv6(payload.get_data())
515 a5c8c1fe Apollon Oikonomopoulos
516 a5c8c1fe Apollon Oikonomopoulos
        if not (subnet.net.overlaps(ns.tgt) or str(ns.tgt) == str(ifll)):
517 a5c8c1fe Apollon Oikonomopoulos
            logging.debug("Received NS for a non-routable IP (%s)" % ns.tgt)
518 a5c8c1fe Apollon Oikonomopoulos
            payload.set_verdict(nfqueue.NF_ACCEPT)
519 a5c8c1fe Apollon Oikonomopoulos
            return 1
520 a5c8c1fe Apollon Oikonomopoulos
521 a5c8c1fe Apollon Oikonomopoulos
        payload.set_verdict(nfqueue.NF_DROP)
522 a5c8c1fe Apollon Oikonomopoulos
523 948d4918 Apollon Oikonomopoulos
        try:
524 948d4918 Apollon Oikonomopoulos
            client_lladdr = ns.lladdr
525 948d4918 Apollon Oikonomopoulos
        except AttributeError:
526 948d4918 Apollon Oikonomopoulos
            return 1
527 948d4918 Apollon Oikonomopoulos
528 948d4918 Apollon Oikonomopoulos
        resp = Ether(src=ifmac, dst=client_lladdr)/\
529 a5c8c1fe Apollon Oikonomopoulos
               IPv6(src=str(ifll), dst=ns.src)/\
530 a5c8c1fe Apollon Oikonomopoulos
               ICMPv6ND_NA(R=1, O=0, S=1, tgt=ns.tgt)/\
531 a5c8c1fe Apollon Oikonomopoulos
               ICMPv6NDOptDstLLAddr(lladdr=ifmac)
532 a5c8c1fe Apollon Oikonomopoulos
533 a5c8c1fe Apollon Oikonomopoulos
        logging.info("NA on %s for %s" % (iface, ns.tgt))
534 a5c8c1fe Apollon Oikonomopoulos
        sendp(resp, iface=iface, verbose=False)
535 a5c8c1fe Apollon Oikonomopoulos
        return 1
536 a5c8c1fe Apollon Oikonomopoulos
537 fa04d422 Apollon Oikonomopoulos
    def send_periodic_ra(self):
538 83027c6b Apollon Oikonomopoulos
        # Use a separate thread as this may take a _long_ time with
539 83027c6b Apollon Oikonomopoulos
        # many interfaces and we want to be responsive in the mean time
540 83027c6b Apollon Oikonomopoulos
        threading.Thread(target=self._send_periodic_ra).start()
541 83027c6b Apollon Oikonomopoulos
542 83027c6b Apollon Oikonomopoulos
    def _send_periodic_ra(self):
543 fa04d422 Apollon Oikonomopoulos
        logging.debug("Sending out periodic RAs")
544 6ca53b5c Apollon Oikonomopoulos
        start = time.time()
545 6ca53b5c Apollon Oikonomopoulos
        i = 0
546 fa04d422 Apollon Oikonomopoulos
        for client in self.clients.values():
547 fa04d422 Apollon Oikonomopoulos
            iface = client.iface
548 fa04d422 Apollon Oikonomopoulos
            ifmac = self.get_iface_hw_addr(iface)
549 6ca53b5c Apollon Oikonomopoulos
            if not ifmac:
550 6ca53b5c Apollon Oikonomopoulos
                continue
551 6ca53b5c Apollon Oikonomopoulos
552 6ca53b5c Apollon Oikonomopoulos
            subnet = self.v6nets[iface]
553 fa04d422 Apollon Oikonomopoulos
            ifll = subnet.make_ll64(ifmac)
554 fa04d422 Apollon Oikonomopoulos
            resp = Ether(src=ifmac)/\
555 fa04d422 Apollon Oikonomopoulos
                   IPv6(src=str(ifll))/ICMPv6ND_RA(routerlifetime=14400)/\
556 fa04d422 Apollon Oikonomopoulos
                   ICMPv6NDOptPrefixInfo(prefix=str(subnet.prefix),
557 fa04d422 Apollon Oikonomopoulos
                                         prefixlen=subnet.prefixlen)
558 fa04d422 Apollon Oikonomopoulos
            try:
559 fa04d422 Apollon Oikonomopoulos
                sendp(resp, iface=iface, verbose=False)
560 fa04d422 Apollon Oikonomopoulos
            except:
561 fa04d422 Apollon Oikonomopoulos
                logging.debug("Periodic RA on %s failed" % iface)
562 6ca53b5c Apollon Oikonomopoulos
            i += 1
563 6ca53b5c Apollon Oikonomopoulos
        logging.debug("Sent %d RAs in %.2f seconds" % (i, time.time() - start))
564 fa04d422 Apollon Oikonomopoulos
565 1f3139f3 Apollon Oikonomopoulos
    def serve(self):
566 1f3139f3 Apollon Oikonomopoulos
        """ Loop forever, serving DHCP requests
567 1f3139f3 Apollon Oikonomopoulos
568 1f3139f3 Apollon Oikonomopoulos
        """
569 1f3139f3 Apollon Oikonomopoulos
        self.build_config()
570 1f3139f3 Apollon Oikonomopoulos
571 1f3139f3 Apollon Oikonomopoulos
        iwfd = self.notifier._fd
572 1f3139f3 Apollon Oikonomopoulos
573 f4b0c05f Apollon Oikonomopoulos
        start = time.time()
574 ea915b1a Apollon Oikonomopoulos
        timeout = self.ra_period
575 f4b0c05f Apollon Oikonomopoulos
        self.send_periodic_ra()
576 fa04d422 Apollon Oikonomopoulos
577 1f3139f3 Apollon Oikonomopoulos
        while True:
578 fa04d422 Apollon Oikonomopoulos
            rlist, _, xlist = select(self.nfq.keys() + [iwfd], [], [], timeout)
579 bf84c4a5 Apollon Oikonomopoulos
            if xlist:
580 bf84c4a5 Apollon Oikonomopoulos
                logging.warn("Warning: Exception on %s" %
581 bf84c4a5 Apollon Oikonomopoulos
                             ", ".join([ str(fd) for fd in xlist]))
582 bf84c4a5 Apollon Oikonomopoulos
583 41a0f754 Apollon Oikonomopoulos
            if rlist:
584 f4b0c05f Apollon Oikonomopoulos
                if iwfd in rlist:
585 41a0f754 Apollon Oikonomopoulos
                # First check if there are any inotify (= configuration change)
586 41a0f754 Apollon Oikonomopoulos
                # events
587 f4b0c05f Apollon Oikonomopoulos
                    self.notifier.read_events()
588 f4b0c05f Apollon Oikonomopoulos
                    self.notifier.process_events()
589 f4b0c05f Apollon Oikonomopoulos
                    rlist.remove(iwfd)
590 1f3139f3 Apollon Oikonomopoulos
591 f4b0c05f Apollon Oikonomopoulos
                for fd in rlist:
592 bf84c4a5 Apollon Oikonomopoulos
                    try:
593 bf84c4a5 Apollon Oikonomopoulos
                        self.nfq[fd].process_pending()
594 bf84c4a5 Apollon Oikonomopoulos
                    except e, msg:
595 bf84c4a5 Apollon Oikonomopoulos
                        logging.warn("Error processing fd %d: %s" % (fd, e))
596 1f3139f3 Apollon Oikonomopoulos
597 fa04d422 Apollon Oikonomopoulos
            # Calculate the new timeout
598 ea915b1a Apollon Oikonomopoulos
            timeout = self.ra_period - (time.time() - start)
599 fa04d422 Apollon Oikonomopoulos
600 fa04d422 Apollon Oikonomopoulos
            if timeout <= 0:
601 41a0f754 Apollon Oikonomopoulos
                start = time.time()
602 fa04d422 Apollon Oikonomopoulos
                self.send_periodic_ra()
603 ea915b1a Apollon Oikonomopoulos
                timeout = self.ra_period - (time.time() - start)
604 fa04d422 Apollon Oikonomopoulos
605 1f3139f3 Apollon Oikonomopoulos
606 f4b0c05f Apollon Oikonomopoulos
607 1f3139f3 Apollon Oikonomopoulos
if __name__ == "__main__":
608 1f3139f3 Apollon Oikonomopoulos
    import optparse
609 651e531d Apollon Oikonomopoulos
    from cStringIO import StringIO
610 1f3139f3 Apollon Oikonomopoulos
    from capng import *
611 1f3139f3 Apollon Oikonomopoulos
    from pwd import getpwnam, getpwuid
612 651e531d Apollon Oikonomopoulos
    from configobj import ConfigObj, flatten_errors
613 651e531d Apollon Oikonomopoulos
614 651e531d Apollon Oikonomopoulos
    import validate
615 651e531d Apollon Oikonomopoulos
616 651e531d Apollon Oikonomopoulos
    validator = validate.Validator()
617 651e531d Apollon Oikonomopoulos
618 651e531d Apollon Oikonomopoulos
    def is_ip_list(value, family=4):
619 651e531d Apollon Oikonomopoulos
        try:
620 651e531d Apollon Oikonomopoulos
            family = int(family)
621 651e531d Apollon Oikonomopoulos
        except ValueError:
622 651e531d Apollon Oikonomopoulos
            raise vaildate.VdtParamError(family)
623 651e531d Apollon Oikonomopoulos
        if isinstance(value, (str, unicode)):
624 651e531d Apollon Oikonomopoulos
            value = [value]
625 651e531d Apollon Oikonomopoulos
        if not isinstance(value, list):
626 651e531d Apollon Oikonomopoulos
            raise validate.VdtTypeError(value)
627 651e531d Apollon Oikonomopoulos
628 651e531d Apollon Oikonomopoulos
        for entry in value:
629 651e531d Apollon Oikonomopoulos
            try:
630 651e531d Apollon Oikonomopoulos
                ip = IPy.IP(entry)
631 651e531d Apollon Oikonomopoulos
            except ValueError:
632 651e531d Apollon Oikonomopoulos
                raise validate.VdtValueError(entry)
633 651e531d Apollon Oikonomopoulos
634 651e531d Apollon Oikonomopoulos
            if ip.version() != family:
635 651e531d Apollon Oikonomopoulos
                raise validate.VdtValueError(entry)
636 651e531d Apollon Oikonomopoulos
        return value
637 651e531d Apollon Oikonomopoulos
638 651e531d Apollon Oikonomopoulos
    validator.functions["ip_addr_list"] = is_ip_list
639 651e531d Apollon Oikonomopoulos
    config_spec = StringIO(CONFIG_SPEC)
640 651e531d Apollon Oikonomopoulos
641 1f3139f3 Apollon Oikonomopoulos
642 1f3139f3 Apollon Oikonomopoulos
    parser = optparse.OptionParser()
643 ea915b1a Apollon Oikonomopoulos
    parser.add_option("-c", "--config", dest="config_file",
644 ea915b1a Apollon Oikonomopoulos
                      help="The location of the data files", metavar="FILE",
645 ea915b1a Apollon Oikonomopoulos
                      default=DEFAULT_CONFIG)
646 1f3139f3 Apollon Oikonomopoulos
    parser.add_option("-d", "--debug", action="store_true", dest="debug",
647 1f3139f3 Apollon Oikonomopoulos
                      help="Turn on debugging messages")
648 1f3139f3 Apollon Oikonomopoulos
    parser.add_option("-f", "--foreground", action="store_false", dest="daemonize",
649 1f3139f3 Apollon Oikonomopoulos
                      default=True, help="Do not daemonize, stay in the foreground")
650 1f3139f3 Apollon Oikonomopoulos
651 1f3139f3 Apollon Oikonomopoulos
652 1f3139f3 Apollon Oikonomopoulos
    opts, args = parser.parse_args()
653 1f3139f3 Apollon Oikonomopoulos
654 1f3139f3 Apollon Oikonomopoulos
    if opts.daemonize:
655 1f3139f3 Apollon Oikonomopoulos
        d = daemon.DaemonContext()
656 1f3139f3 Apollon Oikonomopoulos
        d.open()
657 1f3139f3 Apollon Oikonomopoulos
658 ea915b1a Apollon Oikonomopoulos
    try:
659 651e531d Apollon Oikonomopoulos
        config = ConfigObj(opts.config_file, configspec=config_spec)
660 ea915b1a Apollon Oikonomopoulos
    except:
661 ea915b1a Apollon Oikonomopoulos
        sys.stderr.write("Failed to parse config file %s" % opts.config_file)
662 ea915b1a Apollon Oikonomopoulos
        sys.exit(1)
663 ea915b1a Apollon Oikonomopoulos
664 651e531d Apollon Oikonomopoulos
    results = config.validate(validator)
665 651e531d Apollon Oikonomopoulos
    if results != True:
666 651e531d Apollon Oikonomopoulos
        logging.fatal("Configuration file validation failed! See errors below:")
667 651e531d Apollon Oikonomopoulos
        for (section_list, key, _) in flatten_errors(config, results):
668 651e531d Apollon Oikonomopoulos
            if key is not None:
669 651e531d Apollon Oikonomopoulos
                logging.fatal(" '%s' in section '%s' failed validation" % (key, ", ".join(section_list)))
670 651e531d Apollon Oikonomopoulos
            else:
671 651e531d Apollon Oikonomopoulos
                logging.fatal(" Section '%s' is missing" % ", ".join(section_list))
672 651e531d Apollon Oikonomopoulos
        sys.exit(1)
673 ea915b1a Apollon Oikonomopoulos
674 ea915b1a Apollon Oikonomopoulos
    pidfile = open(config["general"]["pidfile"], "w")
675 1f3139f3 Apollon Oikonomopoulos
    pidfile.write("%s" % os.getpid())
676 1f3139f3 Apollon Oikonomopoulos
    pidfile.close()
677 1f3139f3 Apollon Oikonomopoulos
678 1f3139f3 Apollon Oikonomopoulos
    logger = logging.getLogger()
679 1f3139f3 Apollon Oikonomopoulos
    if opts.debug:
680 1f3139f3 Apollon Oikonomopoulos
        logger.setLevel(logging.DEBUG)
681 1f3139f3 Apollon Oikonomopoulos
    else:
682 1f3139f3 Apollon Oikonomopoulos
        logger.setLevel(logging.INFO)
683 1f3139f3 Apollon Oikonomopoulos
684 1f3139f3 Apollon Oikonomopoulos
    if opts.daemonize:
685 ea915b1a Apollon Oikonomopoulos
        logfile = os.path.join(config["general"]["logdir"], LOG_FILENAME)
686 ea915b1a Apollon Oikonomopoulos
        handler = logging.handlers.RotatingFileHandler(logfile,
687 1f3139f3 Apollon Oikonomopoulos
                                                       maxBytes=2097152)
688 1f3139f3 Apollon Oikonomopoulos
    else:
689 1f3139f3 Apollon Oikonomopoulos
        handler = logging.StreamHandler()
690 1f3139f3 Apollon Oikonomopoulos
691 1f3139f3 Apollon Oikonomopoulos
    handler.setFormatter(logging.Formatter(LOG_FORMAT))
692 1f3139f3 Apollon Oikonomopoulos
    logger.addHandler(handler)
693 1f3139f3 Apollon Oikonomopoulos
694 1f3139f3 Apollon Oikonomopoulos
    logging.info("Starting up")
695 ea915b1a Apollon Oikonomopoulos
696 ea915b1a Apollon Oikonomopoulos
    proxy_opts = {}
697 ea915b1a Apollon Oikonomopoulos
    if config["dhcp"].as_bool("enable_dhcp"):
698 ea915b1a Apollon Oikonomopoulos
        proxy_opts.update({
699 ea915b1a Apollon Oikonomopoulos
            "dhcp_queue_num": config["dhcp"].as_int("dhcp_queue"),
700 ea915b1a Apollon Oikonomopoulos
            "dhcp_lease_lifetime": config["dhcp"].as_int("lease_lifetime"),
701 ea915b1a Apollon Oikonomopoulos
            "dhcp_lease_renewal": config["dhcp"].as_int("lease_renewal"),
702 ea915b1a Apollon Oikonomopoulos
            "dhcp_server_ip": config["dhcp"]["server_ip"],
703 ea915b1a Apollon Oikonomopoulos
            "dhcp_nameservers": config["dhcp"]["nameservers"],
704 ea915b1a Apollon Oikonomopoulos
        })
705 ea915b1a Apollon Oikonomopoulos
706 ea915b1a Apollon Oikonomopoulos
    if config["ipv6"].as_bool("enable_ipv6"):
707 ea915b1a Apollon Oikonomopoulos
        proxy_opts.update({
708 ea915b1a Apollon Oikonomopoulos
            "rs_queue_num": config["ipv6"].as_int("rs_queue"),
709 ea915b1a Apollon Oikonomopoulos
            "ns_queue_num": config["ipv6"].as_int("ns_queue"),
710 ea915b1a Apollon Oikonomopoulos
            "ra_period": config["ipv6"].as_int("ra_period"),
711 ea915b1a Apollon Oikonomopoulos
            "ipv6_nameservers": config["ipv6"]["nameservers"],
712 ea915b1a Apollon Oikonomopoulos
        })
713 ea915b1a Apollon Oikonomopoulos
714 ea915b1a Apollon Oikonomopoulos
    proxy = VMNetProxy(data_path=config["general"]["datapath"], **proxy_opts)
715 1f3139f3 Apollon Oikonomopoulos
716 1f3139f3 Apollon Oikonomopoulos
    # Drop all capabilities except CAP_NET_RAW and change uid
717 1f3139f3 Apollon Oikonomopoulos
    try:
718 ea915b1a Apollon Oikonomopoulos
        uid = getpwuid(config["general"].as_int("user"))
719 1f3139f3 Apollon Oikonomopoulos
    except ValueError:
720 ea915b1a Apollon Oikonomopoulos
        uid = getpwnam(config["general"]["user"])
721 1f3139f3 Apollon Oikonomopoulos
722 1f3139f3 Apollon Oikonomopoulos
    logging.info("Setting capabilities and changing uid")
723 1f3139f3 Apollon Oikonomopoulos
    logging.debug("User: %s, uid: %d, gid: %d" %
724 ea915b1a Apollon Oikonomopoulos
                  (config["general"]["user"], uid.pw_uid, uid.pw_gid))
725 1f3139f3 Apollon Oikonomopoulos
    capng_clear(CAPNG_SELECT_BOTH)
726 1f3139f3 Apollon Oikonomopoulos
    capng_update(CAPNG_ADD, CAPNG_EFFECTIVE|CAPNG_PERMITTED, CAP_NET_RAW)
727 1f3139f3 Apollon Oikonomopoulos
    capng_change_id(uid.pw_uid, uid.pw_gid,
728 1f3139f3 Apollon Oikonomopoulos
                    CAPNG_DROP_SUPP_GRP | CAPNG_CLEAR_BOUNDING)
729 1f3139f3 Apollon Oikonomopoulos
    logging.info("Ready to serve requests")
730 699cc6e3 Apollon Oikonomopoulos
    proxy.serve()
731 1f3139f3 Apollon Oikonomopoulos
732 1f3139f3 Apollon Oikonomopoulos
733 1f3139f3 Apollon Oikonomopoulos
# vim: set ts=4 sts=4 sw=4 et :