From 97314acabc8069f8469fd035435c6837d015b0d0 Mon Sep 17 00:00:00 2001
From: david <david@e0a8ed71-7df4-0310-8962-fdc924857419>
Date: Thu, 11 Mar 2010 20:04:21 +0000
Subject: [PATCH] Change the way route_dst works to allow static routes to
 override interface addresses and netmasks.

Before, route_dst worked like this:
  1) Check destination address against all interfaces, with special-case
     handling for local addresses.
  2) Check destination address against routing table.

Now it works like this:
  1) Check destination address against local addresses.
  2) Check destination address against routing table.
  3) Check destination address against all interfaces.

This allows a routing table entry to override an interface, for example
in this configuration:

  ************************INTERFACES************************
  DEV  (SHORT) IP/MASK         TYPE     UP MAC
  eth0 (eth0)  192.168.0.21/24 ethernet up 00:00:00:00:00:00

  **************************ROUTES**************************
  DST/MASK       DEV  GATEWAY
  192.168.0.3/32 eth0 192.168.0.1
  192.168.0.0/24 eth0

192.168.0.3 will not be considered directly connected, but will have its
traffic routed through 192.168.0.1.

The logic for determining when an address is directly connected has
changed. Instead of considering an address directly connected whenever
it matches an interface netmask, we consider it so when the gateway
address is 0.0.0.0 or when the gateway address is exactly equal to the
interface address. The proper way to do this would be to look at the "G"
flag from the routing table, but libdnet doesn't retain that.


git-svn-id: https://svn.nmap.org/nmap@16970 e0a8ed71-7df4-0310-8962-fdc924857419
---
--- a/tcpip.cc	2008-09-04 16:41:59.000000000 +0200
+++ b/tcpip.cc	2013-06-12 11:11:14.808964217 +0200
@@ -3185,6 +3185,31 @@
   return routes;
 }
 
+static bool match_netmask(u32 addr1, u32 addr2, u32 mask) {
+  return (addr1 & mask) == (addr2 & mask);
+}
+
+static bool match_netmask_bits(u32 addr1, u32 addr2, int bits) {
+  return match_netmask(addr1, addr2, htonl((unsigned long) (0 - 1) << (32 - bits)));
+}
+
+static bool match_netmask_bits(const struct sockaddr_in *addr1,
+  const struct sockaddr_in *addr2, int bits) {
+  return match_netmask_bits(addr1->sin_addr.s_addr, addr2->sin_addr.s_addr, bits);
+}
+
+static struct interface_info *find_loopback_iface(struct interface_info *ifaces,
+  int numifaces) {
+  int i;
+
+  for (i = 0; i < numifaces; i++) {
+    if (ifaces[i].device_type == devt_loopback)
+      return &ifaces[i];
+  }
+
+  return NULL;
+}
+
 /* Takes a destination address (dst) and tries to determine the
    source address and interface necessary to route to this address.
    If no route is found, false is returned and rnfo is undefined.  If
@@ -3193,15 +3218,13 @@
    options set by user (o.spoofsource, o.device) */
 bool route_dst(const struct sockaddr_storage *const dst, struct route_nfo *rnfo) {
   struct interface_info *ifaces;
-  struct interface_info *iface = NULL;
+  struct interface_info *iface;
   int numifaces = 0;
   struct sys_route *routes;
   struct sockaddr_storage spoofss;
   size_t spoofsslen;
   int numroutes = 0;
-  int ifnum;
   int i;
-  u32 mask;
   struct sockaddr_in *ifsin, *dstsin;
   if (!dst) fatal("%s passed a NULL dst address", __func__);
   dstsin = (struct sockaddr_in *)dst;
@@ -3218,93 +3241,87 @@
     assert(o.device[0] != '\0');
   }
 
-  //first we check to see if the host is directly connected,
-  //if not, we have to check the routing table
-  //also, if the user specified a device we have to check the routing table
-  if(!*o.device) {
-    ifaces = getinterfaces(&numifaces);
-    /* I suppose that I'll first determine whether it is a direct connect
-       instance */
-    for(ifnum=0; ifnum < numifaces; ifnum++) {
-      ifsin = (struct sockaddr_in *) &ifaces[ifnum].addr;
-      if (ifsin->sin_family != AF_INET) continue;
-      if (dstsin->sin_addr.s_addr == ifsin->sin_addr.s_addr && 
-	  ifaces[ifnum].device_type != devt_loopback) {
-	/* Trying to scan one of the machine's own interfaces -- we need to use
-	   the localhost device for this */
-	for(i=0; i < numifaces; i++)
-	  if (ifaces[i].device_type == devt_loopback)
-	    break;
-	if (i < numifaces) {
-	  rnfo->direct_connect = true;
-	  memcpy(&rnfo->ii, &ifaces[i], sizeof(rnfo->ii));
-	  /* But the source address we want to use is the target addy */
-	  if (!o.spoofsource)
-	    memcpy(&rnfo->srcaddr, &ifaces[ifnum].addr, sizeof(rnfo->srcaddr));
-	  return true;
-	}
-	/* Hmmm ... no localhost -- I guess I'll just try using the device
-	   itself */
-      }
-      mask = htonl((unsigned long) (0-1) << (32 - ifaces[ifnum].netmask_bits));
-      if ((ifsin->sin_addr.s_addr & mask) == (dstsin->sin_addr.s_addr & mask)) {
-	rnfo->direct_connect = true;
-	memcpy(&rnfo->ii, &ifaces[ifnum], sizeof(rnfo->ii));
-	if (!o.spoofsource) {
-	  memcpy(&rnfo->srcaddr, &ifaces[ifnum].addr, sizeof(rnfo->srcaddr));
-	}
-	return true;
-	  }
-    }
-  }
-
-  //assume the device isnt directly connected until we know it is
-  //the only case it could be is if the user specified an interface
-  rnfo->direct_connect = false;
-
   if (*o.device) {
     iface = getInterfaceByName(o.device);
-    if (!iface) {
+    if (!iface)
       fatal("Could not find interface %s which was specified by -e", o.device);
-    }
-    /* Is it directly connected? */
-    mask = htonl((unsigned long) (0-1) << (32 - iface->netmask_bits));
-    ifsin = (struct sockaddr_in *) &(iface->addr);
-	if ((ifsin->sin_addr.s_addr & mask) == (dstsin->sin_addr.s_addr & mask)){
-      rnfo->direct_connect = true;
-      memcpy(&rnfo->ii, iface, sizeof(rnfo->ii));
-	  if (!o.spoofsource)
-	    memcpy(&rnfo->srcaddr, &(iface->addr), sizeof(rnfo->srcaddr));
-	  return true;
-	}
+  } else {
+    iface = NULL;
   }
 
   routes = getsysroutes(&numroutes);
-  /* Now we simply go through the list and take the first match */
+  ifaces = getinterfaces(&numifaces);
+
+  /* First check if dst is one of the localhost's own addresses. We need to use
+     a localhost device for these. */
+  for (i = 0; i < numifaces; i++) {
+    struct interface_info *loopback;
+
+    ifsin = (struct sockaddr_in *) &ifaces[i].addr;
+    if (dstsin->sin_addr.s_addr != ifsin->sin_addr.s_addr)
+      continue;
+    if (iface != NULL && strcmp(ifaces[i].devname, iface->devname) != 0)
+      continue;
+
+    if (ifaces[i].device_type == devt_loopback)
+      loopback = &ifaces[i];
+    else
+      loopback = find_loopback_iface(ifaces, numifaces);
+    if (loopback == NULL)
+      /* Hmmm ... no localhost -- move on to the routing table. */
+      break;
+
+    rnfo->ii = *loopback;
+    rnfo->direct_connect = true;
+    /* But the source address we want to use is the target address. */
+    if (!o.spoofsource)
+      rnfo->srcaddr = ifaces[i].addr;
+
+    return true;
+  }
+
+  /* Go through the routing table and take the first match. getsysroutes sorts
+     so more-specific routes come first. */
   for(i=0; i < numroutes; i++) {
-    if ((routes[i].dest & routes[i].netmask) == (dstsin->sin_addr.s_addr & routes[i].netmask)) {
-      /* Yay, found a matching route. */
-      if(*o.device){
-	if(strcmp(routes[i].device->devname, iface->devname) != 0) {
-	  //ignore routes that arent on the device we specified!
-	  continue;
-	} else {
-	  if (!o.spoofsource)
-	    memcpy(&rnfo->srcaddr, &(iface->addr), sizeof(rnfo->srcaddr));
-	  memcpy(&rnfo->ii, iface, sizeof(rnfo->ii));
-	}
-      } else {
-	memcpy(&rnfo->ii, routes[i].device, sizeof(rnfo->ii));
-	if (!o.spoofsource) {
-	  memcpy(&rnfo->srcaddr, &routes[i].device->addr, sizeof(rnfo->srcaddr));
-	}
-      }
-      ifsin = (struct sockaddr_in *) &rnfo->nexthop;
-      ifsin->sin_family = AF_INET;
-      ifsin->sin_addr.s_addr = routes[i].gw.s_addr;
-      return true;
-    }
+    if (!match_netmask(dstsin->sin_addr.s_addr, routes[i].dest, routes[i].netmask))
+      continue;
+    /* Ignore routes that aren't on the device we specified. */
+    if (iface != NULL && strcmp(routes[i].device->devname, iface->devname) != 0)
+      continue;
+
+    rnfo->ii = *routes[i].device;
+    /* At this point we don't whether this route is direct or indirect ("G" flag
+       in netstat). We guess that a route is direct when the gateway address is
+       0.0.0.0 or it exactly matches the interface address. */
+    rnfo->direct_connect = (routes[i].gw.s_addr == 0) ||
+      (routes[i].gw.s_addr == ((struct sockaddr_in *) &routes[i].device->addr)->sin_addr.s_addr);
+    if (!o.spoofsource)
+      rnfo->srcaddr = routes[i].device->addr;
+    ifsin = (struct sockaddr_in *) &rnfo->nexthop;
+    ifsin->sin_family = AF_INET;
+    ifsin->sin_addr = routes[i].gw;
+
+    return true;
   }
+
+  /* No match on routes. Try interfaces directly. */
+  for (i = 0; i < numifaces; i++) {
+    if (ifaces[i].addr.ss_family != AF_INET)
+      continue;
+    ifsin = (struct sockaddr_in *) &ifaces[i].addr;
+    if (!match_netmask_bits(dstsin, ifsin, ifaces[i].netmask_bits))
+      continue;
+    if (iface != NULL && strcmp(ifaces[i].devname, iface->devname) != 0)
+      continue;
+
+    rnfo->ii = ifaces[i];
+    rnfo->direct_connect = true;
+    if (!o.spoofsource)
+      rnfo->srcaddr = ifaces[i].addr;
+
+    return true;
+  }
+
   return false;
 }
 
