>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); } }