Saturday, January 23, 2010

How to use a VPN which forces routing to your employer's network AND YET access your local network at the same time



My employer is paranoid.
A lot of them are these days.

This employer provides a VPN access that forces the use of the corporate gateway as the default gateway and which sets the route to this default gateway to use a metric of 1.

This leads to the following result: When my laptop (provided by my employer) is connected to my employer's VPN behind my home network firewall/NAT/router, I have no access to the various devices on my home network, including local printer/scanner and various multimedia devices.
Very annoying indeed.

So I have found a way to circumvent that.

Basically, what I do it that I set a standard VPN connection instead of using my employer's provided wrapper (above MS Windows VPN links). And then I change the routing table so that my default gateway is the one of my home router (or the hotel router) and 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":





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%

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

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%

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 but blogger seems to truncate the end of lines...

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= | /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;
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.

No comments: