[patch] dhclient: support for RFC 3442 "Local Subnet Routes"

classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

[patch] dhclient: support for RFC 3442 "Local Subnet Routes"

Matthew Dempsky-3
From RFC 3442:

   Local Subnet Routes

       In some cases more than one IP subnet may be configured on a link.
       In such cases, a host whose IP address is in one IP subnet in the
       link could communicate directly with a host whose IP address is in a
       different IP subnet on the same link.  In cases where a client is
       being assigned an IP address on an IP subnet on such a link, for each
       IP subnet in the link other than the IP subnet on which the client
       has been assigned the DHCP server MAY be configured to specify a
       router IP address of 0.0.0.0.

       For example, consider the case where there are three IP subnets
       configured on a link: 10.0.0/24, 192.168.0/24, 10.0.21/24.  If the
       client is assigned an IP address of 10.0.21.17, then the server could
       include a route with a destination of 10.0.0/24 and a router address
       of 0.0.0.0, and also a route with a destination of 192.168.0/24 and a
       router address of 0.0.0.0.

       A DHCP client whose underlying TCP/IP stack does not provide this
       capability MUST ignore routes in the Classless Static Routes option
       whose router IP address is 0.0.0.0.  Please note that the behavior
       described here only applies to the Classless Static Routes option,
       not to the Static Routes option nor the Router option.

The patch below adds support for these routes to dhclient.  It
refactors the existing magic direct-route incantation I added in
r1.272, and reuses it in add_classless_static-routes().

I've experimentally verified that this makes OpenBSD work on Google
Compute Engine again.  E.g., a DHCP lease with:

    fixed-address 10.240.142.165;
    option subnet-mask 255.255.255.255;
    option classless-static-routes 10.240.0.1/32 0.0.0.0, 0.0.0.0/0 10.240.0.1;

now results in a routing table like:

    Internet:
    Destination        Gateway            Flags   Refs      Use   Mtu  Prio Iface
    default            10.240.0.1         UGS        0        0     -     8 vio0
    10.240.0.1         link#1             UHLc       1        0     -     8 vio0
    10.240.0.1/32      link#1             UCS        1        0     -     8 vio0
    10.240.142.165     42:01:0a:f0:8e:a5  UHLl       0        0     -     1 lo0
    10.240.142.165/32  link#1             UC         0        0     -     4 vio0
    127/8              127.0.0.1          UGRS       0        0 32768     8 lo0
    127.0.0.1          127.0.0.1          UH         1        0 32768     4 lo0
    224/4              127.0.0.1          URS        0        0 32768     8 lo0

ok?

Index: dhclient.c
===================================================================
RCS file: /cvs/src/sbin/dhclient/dhclient.c,v
retrieving revision 1.319
diff -u -p -r1.319 dhclient.c
--- dhclient.c 11 Aug 2014 18:41:13 -0000 1.319
+++ dhclient.c 1 Oct 2014 00:47:22 -0000
@@ -108,9 +108,10 @@ struct client_lease *clone_lease(struct
 void socket_nonblockmode(int);
 void apply_ignore_list(char *);
 
+void add_direct_route(int, struct in_addr, struct in_addr, struct in_addr);
 void add_default_route(int, struct in_addr, struct in_addr);
 void add_static_routes(int, struct option_data *);
-void add_classless_static_routes(int, struct option_data *);
+void add_classless_static_routes(int, struct option_data *, struct in_addr);
 
 int compare_lease(struct client_lease *, struct client_lease *);
 void set_lease_times(struct client_lease *);
@@ -872,10 +873,12 @@ bind_lease(void)
  add_address(ifi->name, ifi->rdomain, client->active->address, mask);
  if (options[DHO_CLASSLESS_STATIC_ROUTES].len) {
  add_classless_static_routes(ifi->rdomain,
-    &options[DHO_CLASSLESS_STATIC_ROUTES]);
+    &options[DHO_CLASSLESS_STATIC_ROUTES],
+    client->active->address);
  } else if (options[DHO_CLASSLESS_MS_STATIC_ROUTES].len) {
  add_classless_static_routes(ifi->rdomain,
-    &options[DHO_CLASSLESS_MS_STATIC_ROUTES]);
+    &options[DHO_CLASSLESS_MS_STATIC_ROUTES],
+    client->active->address);
  } else {
  opt = &options[DHO_ROUTERS];
  if (opt->len >= sizeof(gateway)) {
@@ -883,17 +886,13 @@ bind_lease(void)
  gateway.s_addr = ((struct in_addr *)opt->data)->s_addr;
 
  /*
- * If we were given a /32 IP assignment, then make sure
- * the gateway address is routable with equivalent of
- *
- *     route add -net $gw -netmask 255.255.255.255 \
- *         -cloning -iface $addr
+ * To be compatible with ISC DHCP behavior on Linux, if
+ * we were given a /32 IP assignment, then add a /32
+ * direct route for the gateway to make it routable.
  */
  if (mask.s_addr == INADDR_BROADCAST) {
- add_route(ifi->rdomain, gateway, mask,
-    client->active->address,
-    RTA_DST | RTA_NETMASK | RTA_GATEWAY,
-    RTF_CLONING | RTF_STATIC);
+ add_direct_route(ifi->rdomain, gateway, mask,
+    client->active->address);
  }
 
  add_default_route(ifi->rdomain, client->active->address,
@@ -2368,6 +2367,18 @@ priv_write_file(struct imsg_write_file *
 }
 
 /*
+ * add_direct_route is the equivalent of
+ *
+ *     route add -net $gateway -netmask $mask -cloning -iface $iface
+ */
+void
+add_direct_route(int rdomain, struct in_addr gateway, struct in_addr mask, struct in_addr iface)
+{
+ add_route(rdomain, gateway, mask, iface,
+    RTA_DST | RTA_NETMASK | RTA_GATEWAY, RTF_CLONING | RTF_STATIC);
+}
+
+/*
  * add_default_route is the equivalent of
  *
  * route -q $rdomain add default -iface $router
@@ -2424,7 +2435,9 @@ add_static_routes(int rdomain, struct op
  }
 }
 
-void add_classless_static_routes(int rdomain, struct option_data *opt)
+void
+add_classless_static_routes(int rdomain, struct option_data *opt,
+    struct in_addr iface)
 {
  struct in_addr dest, netmask, gateway;
  int bits, bytes, i;
@@ -2454,11 +2467,11 @@ void add_classless_static_routes(int rdo
  i += sizeof(gateway);
 
  if (gateway.s_addr == INADDR_ANY)
- continue; /* OBSD TCP/IP doesn't support this. */
-
- add_route(rdomain, dest, netmask, gateway,
-    RTA_DST | RTA_GATEWAY | RTA_NETMASK,
-    RTF_GATEWAY | RTF_STATIC);
+ add_direct_route(rdomain, dest, netmask, iface);
+ else
+ add_route(rdomain, dest, netmask, gateway,
+    RTA_DST | RTA_GATEWAY | RTA_NETMASK,
+    RTF_GATEWAY | RTF_STATIC);
  }
 }