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 && \