VPN: use default gateway AND access your local network

Published:
Sometimes, you want your microsoft VPN to route all the traffic to the remote network. Usually your employer network. This makes it possible to access all the nodes inside this remote LAN, even if they have no "public DNS" entries.
To do so, you would configure the VPN connexion so that it becomes your "default gateway".
Nice.
It works great, you can connect to all the nodes in the remote LAN.
But now you need to print something on your local printer. It's a networked printer, on YOUR LAN.
Since all your traffic is routed to the remote LAN, you'll never reach anything on YOUR LAN.
This example uses a networked printer but it can be a network attached storage, a file server, a media server... Anything that is available on your LAN.
So what you usually do?
You disconnect the VPN, and voila.
But know, when you realize that you need to connect to your remote LAN again, you loose the connection to your own LAN.
Especially frustrating if you need to access some Intranet. You would have to make local copies (or print web pages to pdf...), disconnect, use the local copies. It can even become a security concern, because things that should not leave the Intranet are now on your LAN.

The problem is that with a VPN that uses the default gateway on the remote network, this forces the use of the remote gateway as the default gateway and this sets the route to this default gateway to use a metric of 1.

Fortunately, there is at least one solution:

Basically, what I do is that I set a standard VPN connection, that I customize so that it will not use the remote LAN default gateway. And then I change the routing table so that my default gateway stays my home router (or the hotel router) and at the same time, all the traffic to my employers network go through the VPN link.

My employers owns 2 C-Class Internet ranges so the routing to my employer's network is easy to figure out.

OTOH, when specifying a route in Windows, one must know the IP address and ID of the interface to use for these packets. And a VPN interface (actually a WAN PPP interface) has the nasty habit of changing its ID each time it is launched, and usually, it will also get a different IP address each time. So I developed a small utility to recover this ID and store it in an environment variable.

Then, I just have to invoke the corresponding "route change" or "route add" commands to add the routes to my employers C-Class and to make these route using the VPN interface.

It may seem more complicated than it is.

1st thing:
Create the VPN connexion using standard MS Windows VPN.
In my case, my employer uses a simple PPTP tunnel, so it is very easy. But L2TP should be as easy. IPSEC might be a little more complex, but if you use IPSEC, you might skip this step and just change the routing as described below.

In order to set a Windows based VPN, you can use this tutorial:
https://people.chem.umass.edu/wiki/index.php?title=VPN_-_Connect_from_Windows_XP

One thing you must NOT forget is to uncheck the "Use default gateway on remote network":

400px-Winxp-tcpip-advanced-gatew.png

This is the last screen of the tutorial mentioned above... If you do not know how to get there, just follow the tutorial above.


2nd thing:
Change the routing table

I use a .bat file like this one (I changed the real network classes...) :

NICIndex.exe /IPPrefix=193.105.13. /Type=PPP > %temp%\SetPPP.bat
                      if exist %temp%\SetPPP.bat call %temp%\SetPPP.bat
                      route add 192.105.13.0 MASK 255.255.255.0 %NICIP% Metric 50 IF %NICIDX%
                      route add 192.105.14.0 MASK 255.255.255.0 %NICIP% Metric 50 IF %NICIDX%

Open in new window

What you miss is my NICIndex tool that can be downloaded from this site, just following the link above.
It is a Delphi program which uses WMI to get the network adapters information.
It gets the ID of the first interface it founds which IP address begins with the parameter passed to it. In the example above:
NICIndex.exe /IPPrefix=193.105.13. /type=PPP
will get the interface ID of the first network interface which type is "PPP" (Point to point protocol, which is the type of VPN interfaces. The other types that you might use are "Ethernet" and, maybe, "TokenRing"...) that has an IP address beginning with 193.105.13
It will display this ID in the form
SET NICIDX=

for instance SET NICIDX=0x2000A
It also displays the interface IP address in the form
SET NICIP=193.195.13.127

Actually, NICPPPIndex.exe displays something like
SET NICIDX=
SET NICIP=
SET NICIP=193.195.13.127
SET NICIDX=0x2000A

So calling it this way:

NICIndex.exe /IPPrefix=193.105.13. /Type=PPP > %temp%\SetPPP.bat

Open in new window


you create a SetPPP.bat file in your temp folder.
When you call this SetPPP.bat file, you create the NICIP and NICIDX environment variables that you need to tune your routing table.
Thus my "VPNRoute.bat" file:

NICIndex.exe /IPPrefix=193.105.13. /Type=PPP > %temp%\SetPPP.bat
                      if exist %temp%\SetPPP.bat call %temp%\SetPPP.bat
                      route add 192.105.13.0 MASK 255.255.255.0 %NICIP% Metric 50 IF %NICIDX%
                      route add 192.105.14.0 MASK 255.255.255.0 %NICIP% Metric 50 IF %NICIDX%

Open in new window

In order for this to work, you need the NICIndex.exe file. And it must be in your PATH.


3rd thing:
Now connect it !

The only thing you have to do is to launch the VPN connection. When it is OK, launch the VPNRoute.bat file (for instance copy it on your desktop, after having modified it to suit your particular networking configuration)


Open Source !

My NICIndex source file, in Delphi, is here. Very simple.
NICIndex Source is available for download here:
http://www.filefactory.com/file/b13g5c3/n/NICIndex.dpr

I also include the full source text hereafter

program NICIndex;
                      
                      
                      {$APPTYPE CONSOLE}
                      
                      uses
                        SysUtils,
                        Windows;
                      
                      const
                        MAX_ADAPTER_NAME_LENGTH        = 256;
                        MAX_ADAPTER_DESCRIPTION_LENGTH = 128;
                        MAX_ADAPTER_ADDRESS_LENGTH     = 8;
                      
                        TnTYPE:array[0..28] of AnsiString=('','','','','','','Ethernet','','','Token ring','','','','','','FDDI','','','','','','','','PPP','Loopback','','','','Slip');
                      
                      type
                        PrIP_ADDRESS=^TrIP_ADDRESS;
                        TrIP_ADDRESS=record
                          Next:PrIP_ADDRESS;
                          IpAddress:array[0..15] of Char;
                          IpMask:array[0..15] of Char;
                          Context:DWORD;
                        end;
                      
                        PrADAPTER=^TrADAPTER;
                        TrADAPTER=record
                          FNext:PrADAPTER;
                          FComboIndex:DWORD;
                          FAdapterName:array[0..MAX_ADAPTER_NAME_LENGTH+3] of Char;
                          FDescription:array[0..MAX_ADAPTER_DESCRIPTION_LENGTH+3] of Char;
                          FAddrLen:UINT;
                          FAddress:array[0..MAX_ADAPTER_ADDRESS_LENGTH-1] of Byte;
                          FIndex:DWORD;
                          FType:UINT;
                          FDHCPEnabled:UINT;
                          FCurrentIpAddress:PrIP_ADDRESS;
                          FIpAddressList:TrIP_ADDRESS;
                          FGatewayList:TrIP_ADDRESS;
                          FDHCPServer:TrIP_ADDRESS;
                          FHaveWins:BOOL;
                          FPrimaryWinsServer:TrIP_ADDRESS;
                          FSecondaryWinsServer:TrIP_ADDRESS;
                          FLeaseObtained:Longint;
                          FLeaseExpires:Longint;
                        end;
                      
                      function GetAdaptersInfo(pAdapterInfo:PrADAPTER;pOutputBuffer:PULONG):DWORD;stdcall;external 'IPHlpAPI.dll' name 'GetAdaptersInfo';
                      
                      
                      var
                        Size:DWORD;
                        Info,P:PrADAPTER;
                        Found:Boolean=False;
                        PPPIP: string;
                        IPPrefix, NICType: string;
                        I: byte;
                      
                      
                      begin
                        Writeln(ErrOutput,ExtractFileName(ParamStr(0))+'   Usage: ');
                        Writeln(ErrOutput,ExtractFileName(ParamStr(0))+'   [/IPPREFIX=<IP address Prefix> | /Type=<Interface Type>]');
                        Writeln(ErrOutput,'Examples: '+ExtractFileName(ParamStr(0))+' /IPPREFIX=192.168.4.');
                        Writeln(ErrOutput,'          '+ExtractFileName(ParamStr(0))+' /Type=Ethernet');
                        Writeln(ErrOutput,'          '+ExtractFileName(ParamStr(0))+' /IPPREFIX=192.168.6. /Type=PPP');
                      
                        Size:=0;
                        P:=nil;
                        GetAdaptersInfo(P,@Size);
                        GetMem(P,Size);
                        GetAdaptersInfo(P,@Size);
                        writeln('SET NICIDX=');
                        writeln('SET NICIP=');
                        PPPIP:='';
                        IPPrefix:='';;
                        NICType:='';
                        for I:=1 to ParamCount do
                        begin
                          if Uppercase(Copy(ParamStr(I),1,Length('/IPPREFIX=')))='/IPPREFIX=' then IPPREFIX:=Copy(ParamStr(I),Length('/IPPREFIX=')+1,Length(ParamStr(I))-Length('/IPPREFIX='));
                          if Uppercase(Copy(ParamStr(I),1,Length('/Type=')))='/TYPE=' then NICType:=Copy(ParamStr(I),Length('/Type=')+1,Length(ParamStr(I))-Length('/Type='));
                        end;
                      {$IFDEF DEBUG}
                        Writeln(ErrOutput,'IPPrefix='+IPPrefix);
                        Writeln(ErrOutput,'NICType='+NICType);
                      {$ENDIF}
                        try
                          Info:=P;
                          if Assigned(P) then
                      // just printout - not needed
                          repeat
                            PPPIP:=PChar(@Info^.FIpAddressList.IpAddress);
                            if (IPPrefix<>'') and (NICType<>'') then Found:= (UpperCase(TnTYPE[Info^.FType])=UpperCase(NICType)) and (Pos(IPPrefix,PPPIP)=1)
                            else
                            begin
                             if NICType<>'' then Found:=(UpperCase(TnTYPE[Info^.FType])=UpperCase(NICType))
                             else if IPPrefix<>'' then Found:=(Pos(IPPrefix,PPPIP)=1);
                            end;
                      
                            if (Found) then
                            begin
                              writeln('SET NICIP=',PPPIP);
                              writeln('SET NICIDX=',Format('0x%x',[Info^.FIndex]));
                              exit;
                            end;
                            Info:=Info^.FNext;
                          until not Assigned(Info);
                        finally
                          FreeMem(P);
                        end;
                      end.

Open in new window


  NICIndex.zip
1
13,733 Views

Comments (4)

Commented:
Those scripts look like they could be quite useful, but I'm a bit confused at your motivation for all this.
I use Microsoft VPNs (PPTP) all the time and I don't lose connection to my LAN(at least not my own subnet) when tunneled.  Are you maybe using the same exact subnet locally as the remote network?  And the problem with short name DNS resolution not working when not tunelling all traffic is usually resolved by adding a DNS suffix for your internal domain name to the VPN adapter's TCP/IP settings.  One of the major weaknesses of the PPTP VPN is that you can't push routes to the client like you can with the Cisco, Sonicwall, or really almost any other solutions.  So if your not tunnelling all traffic then the resources you want to access need to be in the same subnet as the one you are receiving via the VPN.

Author

Commented:
As long as you have only one subnet in your remote location and you did not check the "use default gateway" for the VPN, you don't care about the routes:
The Tunnel wil lcreate a route to your subnet, and even if the metric is not smaller than teh default route, since there is a route especially set, the traffic to your subnet will be routed through the tunnel.
The issue is when you have several subnets.
Because then, only the traffic to the subnet that homes your VPN pseudo-adapter will be routed through this tunnel.
You can avoid that by using the VPN's default gateway but then, you may loose connectivity to your local network.
My scripts and utilities are useful for that purpose.

They are also useful to change the metric of a local/private network, if this network provides a default gateway that you don't want to use.
For instance, my own lab's networks are 192.168.13.0 and 192.168.14.0. And when I connect to my lab's network from my laptop, which is also connected to the rest of the company's network, I do not want to use its default gateway.
Then I use the following script (my laptop will get an IP address in the 192.168.13.0 network) :

 
NICIndex /IPPREFIX=192.168.13. /Type=Ethernet > %TEMP%\NICIdx.bat
Call %TEMP%\NICIdx.bat
route change 0.0.0.0 MASK 0.0.0.0 192.168.13.254 if %NICIdx% METRIC 30
route add 192.168.14.0 MASK 255.255.255.0 192.168.13.252 if %NICIdx% METRIC 30

Open in new window

Commented:
I see your point now.  Yes, if you have multiple subnets remotely and locally then you'll need more complicated routing entries than what PPTP gives you.  And to my knowledge there is no way to pass those routes to the client with PPTP/RRAS.  Many other VPN solutions do allow you to pass routes to the client though.  Cisco & Sonicwall are just a few I can think of.

Author

Commented:
Yes, other VPN techniques allow other ways to solve this kind of issues.

However, the article is also a kind of tutorial for using routes, changing them in scripts and allow programmatic access to NIC configuration data under Windows.

Thanks for your comments they make the article clearer !

Have a question about something in this article? You can receive help directly from the article author. Sign up for a free trial to get started.