Revision 764f829a nfdhcpd
b/nfdhcpd | ||
---|---|---|
122 | 122 |
def get_indev(payload): |
123 | 123 |
try: |
124 | 124 |
indev_ifindex = payload.get_physindev() |
125 |
logging.debug("get_physindev %s", indev_ifindex) |
|
126 | 125 |
if indev_ifindex: |
127 | 126 |
logging.debug("Incomming packet from bridge %s", indev_ifindex) |
128 | 127 |
return indev_ifindex |
... | ... | |
136 | 135 |
|
137 | 136 |
return indev_ifindex |
138 | 137 |
|
138 |
|
|
139 | 139 |
def get_binding(proxy, ifindex, mac): |
140 | 140 |
try: |
141 | 141 |
if proxy.mac_indexed_clients: |
142 |
logging.debug("get_binding for mac %s", mac)
|
|
142 |
logging.debug("Getting binding for mac %s", mac)
|
|
143 | 143 |
b = proxy.clients[mac] |
144 | 144 |
else: |
145 |
logging.debug("get_binding for ifindex %s", ifindex)
|
|
145 |
logging.debug("Getting binding for ifindex %s", ifindex)
|
|
146 | 146 |
b = proxy.clients[ifindex] |
147 | 147 |
return b |
148 | 148 |
except KeyError: |
149 | 149 |
logging.debug("No client found for mac/ifindex %s/%s", mac, ifindex) |
150 | 150 |
return None |
151 | 151 |
|
152 |
|
|
152 | 153 |
def parse_binding_file(path): |
153 | 154 |
""" Read a client configuration from a tap file |
154 | 155 |
|
155 | 156 |
""" |
157 |
logging.info("Parsing binding file %s", path) |
|
156 | 158 |
try: |
157 | 159 |
iffile = open(path, 'r') |
158 | 160 |
except EnvironmentError, e: |
... | ... | |
173 | 175 |
def get_value(line): |
174 | 176 |
v = line.strip().split('=')[1] |
175 | 177 |
if v == '': |
176 |
return None |
|
178 |
return None
|
|
177 | 179 |
return v |
178 | 180 |
|
179 | 181 |
for line in iffile: |
... | ... | |
204 | 206 |
except: |
205 | 207 |
return None |
206 | 208 |
|
209 |
|
|
207 | 210 |
class ClientFileHandler(pyinotify.ProcessEvent): |
208 | 211 |
def __init__(self, server): |
209 | 212 |
pyinotify.ProcessEvent.__init__(self) |
210 | 213 |
self.server = server |
211 | 214 |
|
212 |
def process_IN_DELETE(self, event): # pylint: disable=C0103 |
|
215 |
def process_IN_DELETE(self, event): # pylint: disable=C0103
|
|
213 | 216 |
""" Delete file handler |
214 | 217 |
|
215 | 218 |
Currently this removes an interface from the watch list |
... | ... | |
217 | 220 |
""" |
218 | 221 |
self.server.remove_tap(event.name) |
219 | 222 |
|
220 |
def process_IN_CLOSE_WRITE(self, event): # pylint: disable=C0103 |
|
223 |
def process_IN_CLOSE_WRITE(self, event): # pylint: disable=C0103
|
|
221 | 224 |
""" Add file handler |
222 | 225 |
|
223 | 226 |
Currently this adds an interface to the watch list |
... | ... | |
316 | 319 |
return self._make_eui64("fe80::", mac) |
317 | 320 |
|
318 | 321 |
|
319 |
class VMNetProxy(object): # pylint: disable=R0902 |
|
320 |
def __init__(self, data_path, dhcp_queue_num=None, # pylint: disable=R0913 |
|
322 |
class VMNetProxy(object): # pylint: disable=R0902
|
|
323 |
def __init__(self, data_path, dhcp_queue_num=None, # pylint: disable=R0913
|
|
321 | 324 |
rs_queue_num=None, ns_queue_num=None, |
322 | 325 |
dhcp_lease_lifetime=DEFAULT_LEASE_LIFETIME, |
323 | 326 |
dhcp_lease_renewal=DEFAULT_LEASE_RENEWAL, |
... | ... | |
396 | 399 |
logging.info("Cleanup finished") |
397 | 400 |
|
398 | 401 |
def _setup_nfqueue(self, queue_num, family, callback): |
399 |
logging.debug("Setting up NFQUEUE for queue %d, AF %s",
|
|
402 |
logging.info("Setting up NFQUEUE for queue %d, AF %s",
|
|
400 | 403 |
queue_num, family) |
401 | 404 |
q = nfqueue.queue() |
402 | 405 |
q.set_callback(callback) |
... | ... | |
405 | 408 |
# This is mandatory for the queue to operate |
406 | 409 |
q.set_mode(nfqueue.NFQNL_COPY_PACKET) |
407 | 410 |
self.nfq[q.get_fd()] = q |
411 |
logging.debug("Successfully set up NFQUEUE %d", queue_num) |
|
408 | 412 |
|
409 | 413 |
def sendp(self, data, dev): |
410 | 414 |
""" Send a raw packet using a layer-2 socket |
411 | 415 |
|
412 | 416 |
""" |
413 |
logging.debug("%s", data) |
|
417 |
logging.debug("Sending raw packet %s", data)
|
|
414 | 418 |
if isinstance(data, BasePacket): |
415 | 419 |
data = str(data) |
416 | 420 |
|
... | ... | |
427 | 431 |
for path in glob.glob(os.path.join(self.data_path, "*")): |
428 | 432 |
self.add_tap(path) |
429 | 433 |
|
430 |
logging.debug("\n\n\n\n\n") |
|
431 |
logging.debug("%10s %20s %7s %15s", 'Client', 'MAC', 'TAP', 'IP') |
|
434 |
logging.debug("%15s %20s %7s %15s", 'Client', 'MAC', 'TAP', 'IP') |
|
432 | 435 |
for b in self.clients.values(): |
433 |
logging.debug("%10s %20s %7s %15s", b.hostname, b.mac, b.tap, b.ip)
|
|
436 |
logging.debug("%15s %20s %7s %15s", b.hostname, b.mac, b.tap, b.ip)
|
|
434 | 437 |
|
435 | 438 |
def get_ifindex(self, iface): |
436 | 439 |
""" Get the interface index from sysfs |
437 | 440 |
|
438 | 441 |
""" |
442 |
logging.debug("Getting ifindex for interface %s from sysfs", iface) |
|
443 |
|
|
439 | 444 |
path = os.path.abspath(os.path.join(SYSFS_NET, iface, "ifindex")) |
440 | 445 |
if not path.startswith(SYSFS_NET): |
441 | 446 |
return None |
... | ... | |
466 | 471 |
|
467 | 472 |
return ifindex |
468 | 473 |
|
469 |
|
|
470 | 474 |
def get_iface_hw_addr(self, iface): |
471 | 475 |
""" Get the interface hardware address from sysfs |
472 | 476 |
|
473 | 477 |
""" |
478 |
logging.debug("Getting mac for iface %s", iface) |
|
474 | 479 |
path = os.path.abspath(os.path.join(SYSFS_NET, iface, "address")) |
475 | 480 |
if not path.startswith(SYSFS_NET): |
476 | 481 |
return None |
... | ... | |
532 | 537 |
except: |
533 | 538 |
logging.debug("Client on %s disappeared!!!", tap) |
534 | 539 |
|
535 |
|
|
536 |
def dhcp_response(self, i, payload): # pylint: disable=W0613,R0914 |
|
540 |
def dhcp_response(self, i, payload): # pylint: disable=W0613,R0914 |
|
537 | 541 |
""" Generate a reply to bnetfilter-queue-deva BOOTP/DHCP request |
538 | 542 |
|
539 | 543 |
""" |
... | ... | |
546 | 550 |
resp = pkt.getlayer(BOOTP).copy() |
547 | 551 |
hlen = resp.hlen |
548 | 552 |
mac = resp.chaddr[:hlen].encode("hex") |
549 |
mac, _ = re.subn(r'([0-9a-fA-F]{2})', r'\1:', mac, hlen-1)
|
|
553 |
mac, _ = re.subn(r'([0-9a-fA-F]{2})', r'\1:', mac, hlen - 1)
|
|
550 | 554 |
|
551 | 555 |
# Server responses are always BOOTREPLYs |
552 | 556 |
resp.op = "BOOTREPLY" |
... | ... | |
564 | 568 |
payload.set_verdict(nfqueue.NF_ACCEPT) |
565 | 569 |
return |
566 | 570 |
|
567 |
|
|
568 | 571 |
# Signal the kernel that it shouldn't further process the packet |
569 | 572 |
payload.set_verdict(nfqueue.NF_DROP) |
570 | 573 |
|
... | ... | |
616 | 619 |
("lease_time", self.lease_lifetime), |
617 | 620 |
] |
618 | 621 |
if subnet.gw: |
619 |
dhcp_options += [("router", subnet.gw)] |
|
622 |
dhcp_options += [("router", subnet.gw)]
|
|
620 | 623 |
dhcp_options += [("name_server", x) for x in self.dhcp_nameservers] |
621 | 624 |
|
622 | 625 |
elif req_type == DHCPINFORM: |
... | ... | |
629 | 632 |
|
630 | 633 |
elif req_type == DHCPRELEASE: |
631 | 634 |
# Log and ignore |
632 |
logging.info("DHCPRELEASE from %s on %s", binding.mac, binding.tap )
|
|
635 |
logging.info("DHCPRELEASE from %s on %s", binding.mac, binding.tap) |
|
633 | 636 |
return |
634 | 637 |
|
635 | 638 |
# Finally, always add the server identifier and end options |
... | ... | |
650 | 653 |
logging.warn("Unkown error during DHCP response on %s: %s", |
651 | 654 |
binding.indev, str(e)) |
652 | 655 |
|
653 |
def rs_response(self, i, payload): # pylint: disable=W0613 |
|
656 |
def rs_response(self, i, payload): # pylint: disable=W0613
|
|
654 | 657 |
""" Generate a reply to a BOOTP/DHCP request |
655 | 658 |
|
656 | 659 |
""" |
660 |
|
|
657 | 661 |
pkt = IPv6(payload.get_data()) |
658 |
logging.debug("IN RS RESPONCE") |
|
659 | 662 |
#logging.debug(pkt.show()) |
660 | 663 |
try: |
661 | 664 |
mac = pkt.lladdr |
... | ... | |
663 | 666 |
logging.debug("Cannot obtain lladdr in rs") |
664 | 667 |
return |
665 | 668 |
|
666 |
logging.debug("rs for mac %s", mac)
|
|
669 |
logging.debug("Generating an rs response for mac %s", mac)
|
|
667 | 670 |
|
668 | 671 |
indev = get_indev(payload) |
669 | 672 |
|
... | ... | |
687 | 690 |
subnet = binding.net6 |
688 | 691 |
|
689 | 692 |
if subnet.net is None: |
690 |
logging.debug("No IPv6 network assigned for the interface") |
|
691 |
return |
|
693 |
logging.debug("No IPv6 network assigned for the interface")
|
|
694 |
return
|
|
692 | 695 |
|
693 | 696 |
indevmac = self.get_iface_hw_addr(binding.indev) |
694 | 697 |
ifll = subnet.make_ll64(indevmac) |
695 | 698 |
if ifll is None: |
696 | 699 |
return |
697 | 700 |
|
698 |
|
|
699 | 701 |
resp = Ether(src=indevmac)/\ |
700 | 702 |
IPv6(src=str(ifll))/ICMPv6ND_RA(routerlifetime=14400)/\ |
701 | 703 |
ICMPv6NDOptPrefixInfo(prefix=str(subnet.prefix), |
... | ... | |
714 | 716 |
logging.warn("Unkown error during RA on %s: %s", |
715 | 717 |
binding.indev, str(e)) |
716 | 718 |
|
717 |
def ns_response(self, i, payload): # pylint: disable=W0613 |
|
719 |
def ns_response(self, i, payload): # pylint: disable=W0613
|
|
718 | 720 |
""" Generate a reply to an ICMPv6 neighbor solicitation |
719 | 721 |
|
720 | 722 |
""" |
723 |
|
|
721 | 724 |
ns = IPv6(payload.get_data()) |
722 |
logging.debug("IN NS RESPONCE") |
|
723 | 725 |
#logging.debug(ns.show()) |
724 | 726 |
try: |
725 | 727 |
mac = ns.lladdr |
... | ... | |
727 | 729 |
logging.debug("Cannot obtain lladdr from ns") |
728 | 730 |
return |
729 | 731 |
|
730 |
logging.debug("dst %s tgt %s" , ns.dst, ns.tgt)
|
|
732 |
logging.debug("Generating ns response, dst: %s tgt: %s", ns.dst, ns.tgt)
|
|
731 | 733 |
|
732 | 734 |
indev = get_indev(payload) |
733 | 735 |
|
... | ... | |
749 | 751 |
|
750 | 752 |
subnet = binding.net6 |
751 | 753 |
if subnet.net is None: |
752 |
logging.debug("No IPv6 network assigned for the interface") |
|
753 |
return |
|
754 |
logging.debug("No IPv6 network assigned for the interface")
|
|
755 |
return
|
|
754 | 756 |
|
755 | 757 |
indevmac = self.get_iface_hw_addr(binding.indev) |
756 | 758 |
|
... | ... | |
790 | 792 |
for binding in self.clients.values(): |
791 | 793 |
tap = binding.tap |
792 | 794 |
indev = binding.indev |
793 |
mac = binding.mac |
|
795 |
# mac = binding.mac
|
|
794 | 796 |
subnet = binding.net6 |
795 | 797 |
if subnet.net is None: |
796 | 798 |
logging.debug("Skipping periodic RA on interface %s," |
... | ... | |
834 | 836 |
|
835 | 837 |
# Yes, we are accessing _fd directly, but it's the only way to have a |
836 | 838 |
# single select() loop ;-) |
837 |
iwfd = self.notifier._fd # pylint: disable=W0212 |
|
839 |
iwfd = self.notifier._fd # pylint: disable=W0212
|
|
838 | 840 |
|
839 | 841 |
start = time.time() |
840 | 842 |
if self.ipv6_enabled: |
... | ... | |
847 | 849 |
rlist, _, xlist = select(self.nfq.keys() + [iwfd], [], [], timeout) |
848 | 850 |
if xlist: |
849 | 851 |
logging.warn("Warning: Exception on %s", |
850 |
", ".join([ str(fd) for fd in xlist]))
|
|
852 |
", ".join([str(fd) for fd in xlist])) |
|
851 | 853 |
|
852 | 854 |
if rlist: |
853 | 855 |
if iwfd in rlist: |
... | ... | |
910 | 912 |
validator.functions["ip_addr_list"] = is_ip_list |
911 | 913 |
config_spec = StringIO(CONFIG_SPEC) |
912 | 914 |
|
913 |
|
|
914 | 915 |
parser = optparse.OptionParser() |
915 | 916 |
parser.add_option("-c", "--config", dest="config_file", |
916 | 917 |
help="The location of the data files", metavar="FILE", |
... | ... | |
921 | 922 |
dest="daemonize", default=True, |
922 | 923 |
help="Do not daemonize, stay in the foreground") |
923 | 924 |
|
924 |
|
|
925 | 925 |
opts, args = parser.parse_args() |
926 | 926 |
|
927 | 927 |
try: |
... | ... | |
1017 | 1017 |
# CAP_NET_ADMIN: we need to send nfqueue packet verdicts to a netlinkgroup |
1018 | 1018 |
capng.capng_clear(capng.CAPNG_SELECT_BOTH) |
1019 | 1019 |
capng.capng_update(capng.CAPNG_ADD, |
1020 |
capng.CAPNG_EFFECTIVE|capng.CAPNG_PERMITTED,
|
|
1020 |
capng.CAPNG_EFFECTIVE | capng.CAPNG_PERMITTED,
|
|
1021 | 1021 |
capng.CAP_NET_ADMIN) |
1022 | 1022 |
capng.capng_change_id(uid.pw_uid, uid.pw_gid, |
1023 |
capng.CAPNG_DROP_SUPP_GRP|capng.CAPNG_CLEAR_BOUNDING)
|
|
1023 |
capng.CAPNG_DROP_SUPP_GRP | capng.CAPNG_CLEAR_BOUNDING)
|
|
1024 | 1024 |
|
1025 | 1025 |
logging.info("Ready to serve requests") |
1026 | 1026 |
try: |
Also available in: Unified diff