In some networks (e.g. at public events using open wifi) I want
traffic not destined for the local network to go through a VPN.

If the local network uses DHCP, I usually end up killing dhclient
because it's impossible to renew the lease without changing the
default route.

To be clear, I end up with a command sequence like:

 dhclient wpi0  # get a local address
 netstat -rn -finet | grep ^default  # figure out local router
 route delete default # don't use local router for everything
 route add -host $VPN_SERVER_IP $LOCAL_ROUTER # just for this host
 openvpn config-file # start VPN, i am too dumb for ipsec
 route add default $ROUTER_BEHIND_VPN # use my trusted router as default
 pkill -x dhclient # kill dhclient to avoid default route change later

The problem is that the lease expires at some point and things
usually stop working until I perform the same tedious configuration
dance again.

The patch below adds a new option to dhclient.conf (the VPN server is
at 10.1.1.1 in this example):

  interface "wpi0" {
          host-route 10.1.1.1;
  }

Instead of adding a default route dhclient will add a host route
to the specified address. So the above sequence becomes:

 dhclient wpi0  # get a local address, add route to VPN server
 openvpn config-file # start VPN
 route add default $ROUTER_BEHIND_VPN # use my trusted router as default

And the configuration remains stable.

Is this a good approach?
Does anyone else think this is useful?


Index: clparse.c
===================================================================
RCS file: /cvs/src/sbin/dhclient/clparse.c,v
retrieving revision 1.37
diff -u -p -r1.37 clparse.c
--- clparse.c   4 Apr 2011 11:14:52 -0000       1.37
+++ clparse.c   12 Aug 2011 18:04:02 -0000
@@ -151,7 +151,8 @@ read_client_leases(void)
  *     interface-declaration |
  *     TOK_LEASE client-lease-statement |
  *     TOK_ALIAS client-lease-statement |
- *     TOK_REJECT reject-statement
+ *     TOK_REJECT reject-statement |
+ *     TOK_HOST_ROUTE host-route-statement
  */
 void
 parse_client_statement(FILE *cfile)
@@ -234,6 +235,9 @@ parse_client_statement(FILE *cfile)
        case TOK_REJECT:
                parse_reject_statement(cfile);
                return;
+       case TOK_HOST_ROUTE:
+               parse_host_route_statement(cfile);
+               return;
        default:
                parse_warn("expecting a statement.");
                skip_to_semi(cfile);
@@ -739,6 +743,25 @@ parse_reject_statement(FILE *cfile)
                token = next_token(&val, cfile);
        } while (token == ',');
 
+       if (token != ';') {
+               parse_warn("expecting semicolon.");
+               skip_to_semi(cfile);
+       }
+}
+
+void
+parse_host_route_statement(FILE *cfile)
+{
+       char *val;
+       int token;
+
+       if (!parse_ip_addr(cfile, &config->host_route)) {
+               parse_warn("expecting IP address.");
+               skip_to_semi(cfile);
+               return;
+       }
+
+       token = next_token(&val, cfile);
        if (token != ';') {
                parse_warn("expecting semicolon.");
                skip_to_semi(cfile);
Index: conflex.c
===================================================================
RCS file: /cvs/src/sbin/dhclient/conflex.c,v
retrieving revision 1.13
diff -u -p -r1.13 conflex.c
--- conflex.c   17 Dec 2006 17:41:56 -0000      1.13
+++ conflex.c   12 Aug 2011 18:00:51 -0000
@@ -324,6 +324,7 @@ static const struct keywords {
        { "filename",                           TOK_FILENAME },
        { "fixed-address",                      TOK_FIXED_ADDR },
        { "hardware",                           TOK_HARDWARE },
+       { "host-route",                         TOK_HOST_ROUTE },
        { "initial-interval",                   TOK_INITIAL_INTERVAL },
        { "interface",                          TOK_INTERFACE },
        { "lease",                              TOK_LEASE },
Index: dhclient-script
===================================================================
RCS file: /cvs/src/sbin/dhclient/dhclient-script,v
retrieving revision 1.22
diff -u -p -r1.22 dhclient-script
--- dhclient-script     9 Apr 2011 19:53:00 -0000       1.22
+++ dhclient-script     12 Aug 2011 18:06:00 -0000
@@ -60,11 +60,16 @@ delete_old_routes() {
 add_new_routes() {
        route -q $rdomain -n flush -inet -iface $interface
        for router in $new_routers; do
-               if [ "$new_ip_address" = "$router" ]; then
-                       route -q $rdomain add default -iface $router
+               if [ -n "$host_route" -a "$host_route" != "0.0.0.0" ]; then
+                       new_route="-host $host_route $router"
                else
-                       route -q $rdomain add default $router
+                       if [ "$new_ip_address" = "$router" ]; then
+                               new_route="default -iface $router"
+                       else
+                               new_route="default $router"
+                       fi
                fi
+               route -q $rdomain add $new_route
                # 2nd and subsequent default routers error out, so explicitly
                # stop processing the list after the first one.
                break
Index: dhclient.c
===================================================================
RCS file: /cvs/src/sbin/dhclient/dhclient.c,v
retrieving revision 1.141
diff -u -p -r1.141 dhclient.c
--- dhclient.c  11 May 2011 14:38:36 -0000      1.141
+++ dhclient.c  12 Aug 2011 18:06:57 -0000
@@ -1604,6 +1604,9 @@ supersede:
        }
        snprintf(tbuf, sizeof(tbuf), "%d", (int)lease->expiry);
        script_set_env(prefix, "expiry", tbuf);
+
+       if (config->host_route.len)
+               script_set_env("", "host_route", piaddr(config->host_route));
 }
 
 void
Index: dhclient.conf.5
===================================================================
RCS file: /cvs/src/sbin/dhclient/dhclient.conf.5,v
retrieving revision 1.21
diff -u -p -r1.21 dhclient.conf.5
--- dhclient.conf.5     9 Apr 2011 19:53:00 -0000       1.21
+++ dhclient.conf.5     13 Aug 2011 19:18:56 -0000
@@ -430,6 +430,13 @@ If no lease is acquired, the script is u
 any, and also called once if no valid lease can be identified.
 For more information, see
 .Xr dhclient.leases 5 .
+.It Ic host-route Ar ip-address ;
+The
+.Ic host-route
+statement causes the DHCP client to install a host route to the specified
+IP address instead of installing a default route.
+This is useful if the default route points to a VPN tunnel terminated
+at the specified IP address instead of a router on the local network.
 .El
 .Sh EXAMPLES
 The following configuration file is used on a laptop
Index: dhcpd.h
===================================================================
RCS file: /cvs/src/sbin/dhclient/dhcpd.h,v
retrieving revision 1.73
diff -u -p -r1.73 dhcpd.h
--- dhcpd.h     11 May 2011 14:38:36 -0000      1.73
+++ dhcpd.h     12 Aug 2011 18:03:23 -0000
@@ -150,6 +150,7 @@ struct client_config {
        enum { IGNORE, ACCEPT, PREFER }
                                 bootp_policy;
        struct iaddrlist        *reject_list;
+       struct iaddr             host_route;
 };
 
 struct client_state {
@@ -344,3 +345,4 @@ void parse_client_lease_declaration(FILE
 int parse_option_decl(FILE *, struct option_data *);
 void parse_string_list(FILE *, struct string_list **, int);
 void parse_reject_statement(FILE *);
+void parse_host_route_statement(FILE *);
Index: dhctoken.h
===================================================================
RCS file: /cvs/src/sbin/dhclient/dhctoken.h,v
retrieving revision 1.5
diff -u -p -r1.5 dhctoken.h
--- dhctoken.h  15 May 2006 08:10:57 -0000      1.5
+++ dhctoken.h  12 Aug 2011 18:03:38 -0000
@@ -79,6 +79,7 @@
 #define TOK_REJECT             292
 #define TOK_FDDI               293
 #define TOK_LINK_TIMEOUT       294
+#define TOK_HOST_ROUTE         295
 
 #define is_identifier(x)       ((x) >= TOK_FIRST_TOKEN &&      \
                                 (x) != TOK_STRING &&   \

Reply via email to