Refactor nfdhcpd to support get_physindev()
authorDimitris Aragiorgis <dimara@grnet.gr>
Tue, 7 Aug 2012 14:38:11 +0000 (17:38 +0300)
committerDimitris Aragiorgis <dimara@grnet.gr>
Tue, 7 Aug 2012 14:38:11 +0000 (17:38 +0300)
If get_physindev is supported in nfqueue then the clients are indexed
by their tap ifindex. If not then clients are indexed by their macs.

Signed-off-by: Dimitris Aragiorgis <dimara@grnet.gr>

nfdhcpd

diff --git a/nfdhcpd b/nfdhcpd
index 0de1301..c7396c0 100755 (executable)
--- a/nfdhcpd
+++ b/nfdhcpd
@@ -117,6 +117,33 @@ DHCP_REQRESP = {
     }
 
 
+def get_indev(payload):
+    try:
+        indev_ifindex = payload.get_physindev()
+        if indev_ifindex:
+            logging.debug("Incomming packet from bridge %s", indev_ifindex)
+            return indev_ifindex
+    except AttributeError:
+        #TODO: return error value
+        logging.debug("No get_physindev supported")
+        return 0
+
+    indev_ifindex = payload.get_indev()
+    logging.debug("Incomming packet from tap %s", indev_ifindex)
+
+    return indev_ifindex
+
+def get_binding(proxy, ifindex, mac):
+    try:
+        if proxy.mac_indexed_clients:
+            b = proxy.clients[mac]
+        else:
+            b = proxy.clients[ifindex]
+        return b
+    except KeyError:
+        logging.debug("No client found for mac/ifindex %s/%s", mac, ifindex)
+        return None
+
 def parse_binding_file(path):
     """ Read a client configuration from a tap file
 
@@ -284,6 +311,11 @@ class VMNetProxy(object): # pylint: disable=R0902
                  dhcp_server_ip=DHCP_DUMMY_SERVER_IP, dhcp_nameservers=None,
                  ra_period=DEFAULT_RA_PERIOD, ipv6_nameservers=None):
 
+        try:
+            getattr(nfqueue.payload, 'get_physindev')
+            self.mac_indexed_clients = False
+        except AttributeError:
+            self.mac_indexed_clients = True
         self.data_path = data_path
         self.lease_lifetime = dhcp_lease_lifetime
         self.lease_renewal = dhcp_lease_renewal
@@ -458,24 +490,31 @@ class VMNetProxy(object): # pylint: disable=R0902
             logging.warn("Stale configuration for %s found", tap)
         else:
             if b.is_valid():
-                self.clients[b.mac] = b
+                if self.mac_indexed_clients:
+                    self.clients[b.mac] = b
+                else:
+                    self.clients[ifindex] = b
                 logging.debug("Added client:")
-                logging.debug("%10s %20s %7s %15s", b.hostname, b.mac, b.tap, b.ip)
+                logging.debug("%5s: %10s %20s %7s %15s",
+                               ifindex, b.hostname, b.mac, b.tap, b.ip)
+        logging.debug("\n\n\n\n\n")
         logging.debug("%10s %20s %7s %15s", 'Client', 'MAC', 'TAP', 'IP')
-        for mac in self.clients.keys():
-          b = self.clients[mac]
-          logging.debug("%10s %20s %7s %15s", b.hostname, b.mac, b.tap, b.ip)
+        for b in self.clients.values():
+            logging.debug("%10s %20s %7s %15s", b.hostname, b.mac, b.tap, b.ip)
 
     def remove_tap(self, tap):
         """ Cleanup clients on a removed interface
 
         """
-        for b in self.clients.values():
-            if b.tap == tap:
-                #os.remove(self.data_path+'/'+tap)
-                logging.debug("Removed interface %s", self.data_path+'/'+tap)
-                logging.debug("%10s %20s %7s %15s", b.hostname, b.mac, b.tap, b.ip)
-                del b
+        try:
+            for k in self.clients.keys():
+                if self.clients[k].tap = tap:
+                    logging.debug("Removing client on interface %s", tap)
+                    logging.debug("%10s %20s %7s %15s",
+                                  b.hostname, b.mac, b.tap, b.ip)
+                    del self.clients[k]
+        except:
+            logging.debug("Client on %s disappeared!!!", tap)
 
 
     def dhcp_response(self, i, payload): # pylint: disable=W0613,R0914
@@ -485,7 +524,7 @@ class VMNetProxy(object): # pylint: disable=R0902
         # Decode the response - NFQUEUE relays IP packets
         pkt = IP(payload.get_data())
         logging.debug("IN DHCP RESPONCE")
-        logging.debug(pkt.show())
+        #logging.debug(pkt.show())
 
         # Get the client MAC address
         resp = pkt.getlayer(BOOTP).copy()
@@ -497,16 +536,26 @@ class VMNetProxy(object): # pylint: disable=R0902
         resp.op = "BOOTREPLY"
         del resp.payload
 
-        try:
-            binding = self.clients[mac]
-        except KeyError:
-            logging.warn("Invalid client for mac %s ", mac)
+        indev = get_indev(payload)
+
+        binding = get_binding(self, indev, mac) 
+        if binding is None:
+            # We don't know anything about this interface, so accept the packet
+            # and return
+            logging.debug("Ignoring DHCP request on unknown iface %d", indev)
+            # 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)
 
+        if mac != binding.mac:
+            logging.warn("Recieved spoofed DHCP request for mac %s from tap %s", mac, tap)
+            return
+
         resp = Ether(dst=mac, src=self.get_iface_hw_addr(binding.indev))/\
                IP(src=DHCP_DUMMY_SERVER_IP, dst=binding.ip)/\
                UDP(sport=pkt.dport, dport=pkt.sport)/resp
@@ -536,12 +585,12 @@ class VMNetProxy(object): # pylint: disable=R0902
         if req_type == DHCPREQUEST and requested_addr != binding.ip:
             resp_type = DHCPNAK
             logging.info("Sending DHCPNAK to %s on %s: requested %s"
-                         " instead of %s", binding.mac, binding.tap, requested_addr,
-                         binding.ip)
+                         " instead of %s", binding.mac, binding.tap,
+                         requested_addr, binding.ip)
 
         elif req_type in (DHCPDISCOVER, DHCPREQUEST):
             resp_type = DHCP_REQRESP[req_type]
-            resp.yiaddr = self.clients[mac].ip
+            resp.yiaddr = binding.ip
             dhcp_options += [
                  ("hostname", binding.hostname),
                  ("domain", domainname),
@@ -575,11 +624,6 @@ class VMNetProxy(object): # pylint: disable=R0902
         ]
         resp /= DHCP(options=dhcp_options)
 
-        if payload.get_indev() != self.get_ifindex(binding.indev):
-            logging.warn("Received spoofed DHCP request for %s from interface"
-                         " %s instead of %s", mac, payload.get_indev(), binding.indev)
-            return
-
         logging.info("%s to %s (%s) on %s", DHCP_TYPES[resp_type], mac,
                      binding.ip, binding.tap)
         self.sendp(resp, binding.indev)
@@ -590,14 +634,17 @@ class VMNetProxy(object): # pylint: disable=R0902
         """
         pkt = IPv6(payload.get_data())
         logging.debug("IN RS RESPONCE")
-        logging.debug(pkt.show())
+        #logging.debug(pkt.show())
         mac = pkt.lladdr
         logging.debug("rs for mac %s", mac)
-        try:
-          binding = self.clients[mac]
-        except KeyError:
-            logging.debug("Ignoring router solicitation on"
-                          " for mac %s", mac)
+
+        indev = get_indev(payload)
+
+        binding = get_binding(self, indev, mac)
+        if binding is None:
+            # We don't know anything about this interface, so accept the packet
+            # and return
+            logging.debug("Ignoring router solicitation on 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)
@@ -606,17 +653,21 @@ class VMNetProxy(object): # pylint: disable=R0902
         # Signal the kernel that it shouldn't further process the packet
         payload.set_verdict(nfqueue.NF_DROP)
 
+        if mac != binding.mac:
+            logging.warn("Recieved spoofed RS request for mac %s from tap %s", mac, tap)
+            return
+
         subnet = binding.net6
 
         if subnet.net is None:
           logging.debug("No IPv6 network assigned for the interface")
           return
 
-        ifmac = self.get_iface_hw_addr(binding.indev)
-        ifll = subnet.make_ll64(ifmac)
+        indevmac = self.get_iface_hw_addr(binding.indev)
+        ifll = subnet.make_ll64(indevmac)
 
 
-        resp = Ether(src=ifmac)/\
+        resp = Ether(src=indevmac)/\
                IPv6(src=str(ifll))/ICMPv6ND_RA(routerlifetime=14400)/\
                ICMPv6NDOptPrefixInfo(prefix=str(subnet.prefix),
                                      prefixlen=subnet.prefixlen)
@@ -635,26 +686,33 @@ class VMNetProxy(object): # pylint: disable=R0902
         ns = IPv6(payload.get_data())
         logging.debug("IN NS RESPONCE")
         logging.debug(ns.show())
+        mac = ns.lladdr
 
         logging.debug("dst %s  tgt %s" , ns.dst, ns.tgt)
 
-        try:
-          binding = self.clients[ns.lladdr]
-        except:
-          logging.debug("Ignoring neighbour solicitation for eui64 %s", ns.tgt)
-          # We don't know what to do with this packet, so let the kernel
-          # handle it
-          payload.set_verdict(nfqueue.NF_ACCEPT)
-          return
+        indev = get_indev(payload)
+
+        binding = get_binding(self, indev, mac)
+        if binding is None:
+            # We don't know anything about this interface, so accept the packet
+            # and return
+            logging.debug("Ignoring neighbour solicitation for eui64 %s", ns.tgt)
+            # We don't know what to do with this packet, so let the kernel
+            # handle it
+            payload.set_verdict(nfqueue.NF_ACCEPT)
+            return
+
+        payload.set_verdict(nfqueue.NF_DROP)
+
+        if mac != binding.mac:
+            logging.warn("Recieved spoofed NS request for mac %s from tap %s", mac, tap)
+            return
 
         subnet = binding.net6
         if subnet.net is None:
           logging.debug("No IPv6 network assigned for the interface")
-          payload.set_verdict(nfqueue.NF_ACCEPT)
           return
 
-        payload.set_verdict(nfqueue.NF_DROP)
-
         indevmac = self.get_iface_hw_addr(binding.indev)
 
         ifll = subnet.make_ll64(indevmac)