Statistics
| Branch: | Tag: | Revision:

root / nfdhcpd @ f2c7bb76

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