X-Git-Url: https://code.grnet.gr/git/snf-nfdhcpd/blobdiff_plain/fac9f92807e30cf006af08b796652795fcb3cae9..98804a5e1ce89e34e2a8206693009099babe6d1d:/nfdhcpd diff --git a/nfdhcpd b/nfdhcpd index 9cd6f9a..c66c435 100755 --- a/nfdhcpd +++ b/nfdhcpd @@ -53,6 +53,9 @@ from scapy.layers.inet6 import IPv6, ICMPv6ND_RA, ICMPv6ND_NA, \ ICMPv6NDOptPrefixInfo, \ ICMPv6NDOptRDNSS from scapy.layers.dhcp import BOOTP, DHCP +from scapy.layers.dhcp6 import DHCP6_Reply, DHCP6OptDNSServers, \ + DHCP6OptServerId, DHCP6OptClientId, \ + DUID_LLT, DHCP6_InfoRequest, DHCP6OptDNSDomains DEFAULT_CONFIG = "/etc/nfdhcpd/nfdhcpd.conf" @@ -91,7 +94,9 @@ enable_ipv6 = boolean(default=True) ra_period = integer(min=1, max=4294967295) rs_queue = integer(min=0, max=65535) ns_queue = integer(min=0, max=65535) +dhcp_queue = integer(min=0, max=65535) nameservers = ip_addr_list(family=6) +domains = force_list(default=None) """ @@ -349,12 +354,13 @@ class Subnet(object): class VMNetProxy(object): # pylint: disable=R0902 def __init__(self, data_path, dhcp_queue_num=None, # pylint: disable=R0913 - rs_queue_num=None, ns_queue_num=None, + rs_queue_num=None, ns_queue_num=None, dhcpv6_queue_num=None, dhcp_lease_lifetime=DEFAULT_LEASE_LIFETIME, dhcp_lease_renewal=DEFAULT_LEASE_RENEWAL, - dhcp_domain='', + dhcp_domain=None, dhcp_server_ip=DHCP_DUMMY_SERVER_IP, dhcp_nameservers=None, - ra_period=DEFAULT_RA_PERIOD, ipv6_nameservers=None): + ra_period=DEFAULT_RA_PERIOD, ipv6_nameservers=None, + dhcpv6_domains=None): try: getattr(nfqueue.payload, 'get_physindev') @@ -377,6 +383,11 @@ class VMNetProxy(object): # pylint: disable=R0902 else: self.ipv6_nameservers = ipv6_nameservers + if dhcpv6_domains is None: + self.dhcpv6_domains = [] + else: + self.dhcpv6_domains = dhcpv6_domains + self.ipv6_enabled = False self.clients = {} @@ -405,6 +416,10 @@ class VMNetProxy(object): # pylint: disable=R0902 self._setup_nfqueue(ns_queue_num, AF_INET6, self.ns_response, 10) self.ipv6_enabled = True + if dhcpv6_queue_num is not None: + self._setup_nfqueue(dhcpv6_queue_num, AF_INET6, self.dhcpv6_response, 10) + self.ipv6_enabled = True + def get_binding(self, ifindex, mac): try: if self.mac_indexed_clients: @@ -535,11 +550,13 @@ class VMNetProxy(object): # pylint: disable=R0902 if b.is_valid(): if self.mac_indexed_clients: self.clients[b.mac] = b + k = b.mac else: self.clients[ifindex] = b - logging.debug(" - Added client:") - logging.debug(" + %5s: %10s %20s %7s %15s", - ifindex, b.hostname, b.mac, b.tap, b.ip) + k = ifindex + logging.info(" - Added client:") + logging.info(" + %10s | %20s %20s %10s %20s %40s", + k, b.hostname, b.mac, b.tap, b.ip, b.eui64) def remove_tap(self, tap): """ Cleanup clients on a removed interface @@ -550,8 +567,8 @@ class VMNetProxy(object): # pylint: disable=R0902 if cl.tap == tap: logging.info("Removing client %s and closing socket on %s", cl.hostname, cl.tap) - logging.debug(" - %10s | %10s %20s %10s %20s", - k, cl.hostname, cl.mac, cl.tap, cl.ip) + logging.info(" - %10s | %20s %20s %10s %20s %40s", + k, cl.hostname, cl.mac, cl.tap, cl.ip, cl.eui64) cl.socket.close() del self.clients[k] except: @@ -694,6 +711,81 @@ class VMNetProxy(object): # pylint: disable=R0902 logging.warn(" - Unkown error during DHCP response on %s (%s): %s", binding.tap, binding.hostname, str(e)) + def dhcpv6_response(self, arg1, arg2=None): # pylint: disable=W0613 + + logging.info(" * Processing pending DHCPv6 request") + # Workaround for supporting both squeezy's nfqueue-bindings-python + # and wheezy's python-nfqueue because for some reason the function's + # signature has changed and has broken compatibility + # See bug http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=718894 + if arg2: + payload = arg2 + else: + payload = arg1 + pkt = IPv6(payload.get_data()) + indev = get_indev(payload) + + #TODO: figure out how to find the src mac + mac = None + binding = self.get_binding(indev, mac) + if binding is None: + # We don't know anything about this interface, so accept the packet + # and return + logging.debug(" - Ignoring dhcpv6 request for mac %s", mac) + # We don't know what to do with this packet, so let the kernel + # handle it + payload.set_verdict(nfqueue.NF_ACCEPT) + return + + # Signal the kernel that it shouldn't further process the packet + payload.set_verdict(nfqueue.NF_DROP) + + subnet = binding.net6 + + if subnet.net is None: + logging.debug(" - No IPv6 network assigned for tap %s", binding.tap) + return + + indevmac = self.get_iface_hw_addr(binding.indev) + ifll = subnet.make_ll64(indevmac) + if ifll is None: + return + + ofll = subnet.make_ll64(binding.mac) + if ofll is None: + return + + logging.info(" - Generating DHCPv6 response for host %s (mac %s) on tap %s", + binding.hostname, binding.mac, binding.tap) + + if self.dhcpv6_domains: + domains = self.dhcpv6_domains + else: + domains = [binding.hostname.split('.', 1)[-1]] + + # We do this in order not to caclulate optlen ourselves + dnsdomains = str(DHCP6OptDNSDomains(dnsdomains=domains)) + dnsservers = str(DHCP6OptDNSServers(dnsservers=self.ipv6_nameservers)) + + resp = Ether(src=indevmac, dst=binding.mac)/\ + IPv6(tc=192, src=str(ifll), dst=str(ofll))/\ + UDP(sport=pkt.dport, dport=pkt.sport)/\ + DHCP6_Reply(trid=pkt[DHCP6_InfoRequest].trid)/\ + DHCP6OptClientId(duid=pkt[DHCP6OptClientId].duid)/\ + DHCP6OptServerId(duid=DUID_LLT(lladdr=indevmac, timeval=time.time()))/\ + DHCP6OptDNSDomains(dnsdomains)/\ + DHCP6OptDNSServers(dnsservers) + + try: + binding.sendp(resp) + except socket.error, e: + logging.warn(" - DHCPv6 on %s (%s) failed: %s", + binding.tap, binding.hostname, str(e)) + except Exception, e: + logging.warn(" - Unkown error during DHCPv6 on %s (%s): %s", + binding.tap, binding.hostname, str(e)) + + def rs_response(self, arg1, arg2=None): # pylint: disable=W0613 """ Generate a reply to a BOOTP/DHCP request @@ -750,7 +842,7 @@ class VMNetProxy(object): # pylint: disable=R0902 binding.hostname, mac, binding.tap) resp = Ether(src=indevmac)/\ - IPv6(src=str(ifll))/ICMPv6ND_RA(routerlifetime=14400)/\ + IPv6(src=str(ifll))/ICMPv6ND_RA(O=1, routerlifetime=14400)/\ ICMPv6NDOptPrefixInfo(prefix=str(subnet.prefix), prefixlen=subnet.prefixlen) @@ -866,7 +958,7 @@ class VMNetProxy(object): # pylint: disable=R0902 if ifll is None: continue resp = Ether(src=indevmac)/\ - IPv6(src=str(ifll))/ICMPv6ND_RA(routerlifetime=14400)/\ + IPv6(src=str(ifll))/ICMPv6ND_RA(O=1, routerlifetime=14400)/\ ICMPv6NDOptPrefixInfo(prefix=str(subnet.prefix), prefixlen=subnet.prefixlen) if self.ipv6_nameservers: @@ -954,11 +1046,11 @@ class VMNetProxy(object): # pylint: disable=R0902 timeout = self.ra_period - (time.time() - start) def print_clients(self): - logging.info("%10s %20s %20s %10s %20s", - 'Key', 'Client', 'MAC', 'TAP', 'IP') + logging.info("%10s %20s %20s %10s %20s %40s", + 'Key', 'Client', 'MAC', 'TAP', 'IP', 'IPv6') for k, cl in self.clients.items(): - logging.info("%10s | %20s %20s %10s %20s", - k, cl.hostname, cl.mac, cl.tap, cl.ip) + logging.info("%10s | %20s %20s %10s %20s %40s", + k, cl.hostname, cl.mac, cl.tap, cl.ip, cl.eui64) @@ -1111,10 +1203,12 @@ if __name__ == "__main__": if config["ipv6"].as_bool("enable_ipv6"): proxy_opts.update({ + "dhcpv6_queue_num": config["ipv6"].as_int("dhcp_queue"), "rs_queue_num": config["ipv6"].as_int("rs_queue"), "ns_queue_num": config["ipv6"].as_int("ns_queue"), "ra_period": config["ipv6"].as_int("ra_period"), "ipv6_nameservers": config["ipv6"]["nameservers"], + "dhcpv6_domains": config["ipv6"]["domains"], }) # pylint: disable=W0142