The problem with Anyconnect is that it first modifies the routing table, then babysits it and fixes it up should you modify it manually. I found a workaround for this. Works with version 3.1.00495, 3.1.05152, 3.1.05170, and probably anything else in the 3.1 family. May work with other versions, at least similar idea should work assuming the code does not get rewritten. Fortunately for us Cisco has put the babysitter "baby is awake" call into a shared library. So the idea is that we prevent action by vpnagentd via LD_PRELOAD.
First we create a file hack.c
:
#include <sys/socket.h>
#include <linux/netlink.h>
int _ZN27CInterfaceRouteMonitorLinux20routeCallbackHandlerEv()
{
int fd=50; // max fd to try
char buf[8192];
struct sockaddr_nl sa;
socklen_t len = sizeof(sa);
while (fd) {
if (!getsockname(fd, (struct sockaddr *)&sa, &len)) {
if (sa.nl_family == AF_NETLINK) {
ssize_t n = recv(fd, buf, sizeof(buf), MSG_DONTWAIT);
}
}
fd--;
}
return 0;
}
Note: This code works only with Linux. For applying this solution to a macOS machine, see the macOS adapted version.
Then compile it like this:
gcc -o libhack.so -shared -fPIC hack.c
Install libhack.so
into the Cisco library path:
sudo cp libhack.so /opt/cisco/anyconnect/lib/
Bring down the agent:
/etc/init.d/vpnagentd stop
Make sure it really is down
ps auxw | grep vpnagentd
If not, kill -9
just to be sure.
If you have /etc/init.d/vpnagentd
, then fix it up by adding LD_PRELOAD=/opt/cisco/anyconnect/lib/libhack.so
where the underlying executable is being invoked so it looks like this:
LD_PRELOAD=/opt/cisco/anyconnect/lib/libhack.so /opt/cisco/anyconnect/bin/vpnagentd
More modern AnyConnect installations eschew /etc/init.d/vpnagentd
in favor of /lib/systemd/system/vpnagentd.service
, in which case you'll want something like:
sudo mv /opt/cisco/anyconnect/bin/vpnagentd /opt/cisco/anyconnect/bin/vpnagentd.orig &&
{ echo '#!/bin/bash' &&
echo "LD_PRELOAD=$HOME/vpn/libhack.so exec /opt/cisco/anyconnect/bin/vpnagentd.orig"
} | sudo tee /opt/cisco/anyconnect/bin/vpnagentd &&
sudo chmod +x /opt/cisco/anyconnect/bin/vpnagentd
Now start the agent:
/etc/init.d/vpnagentd start
Fix up iptables, because AnyConnect messes with them:
iptables-save | grep -v DROP | iptables-restore
You may want to do something more advanced here to allow access only to certain LAN hosts.
Now fix up the routes as you please, for example:
route add -net 192.168.1.0 netmask 255.255.255.0 dev wlan0
Check to see if they are really there:
route -n
A previous, simpler version of this hack gave a function that only did "return 0;" - that poster noted that "The only side effect that I've observed so far is that vpnagentd is using 100% of CPU as reported by top, but overall CPU is only 3% user and 20% system, and the system is perfectly responsive. I straced it, it seems to be doing two selects in a loop when idle returning from both quickly, but it never reads or writes - I suppose the call that I cut out with LD_PRELOAD was supposed to read. There might be a cleaner way to do it, but it is good enough for me so far. If somebody has a better solution, please share."
The problem with the trivial hack is it caused a single cpu core to be 100% all the time, effectively reducing your hardware cpu thread count by one - whether your vpn connection was active or not. I noticed that the selects the code was doing were on a netlink socket, which sends vpnagentd data when the routing table changes. vpnagentd keeps noticing there's a new message on the netlink socket and calls the routeCallBackHandler to deal with it, but since the trivial hack doesn't clear the new message it just keeps getting called again and again. the new code provided above flushes the netlink data so the endless loop which caused the 100% cpu doesn't happen.
If something does not work, do gdb -p $(pidof vpnagentd)
, once attached:
b socket
c
bt
and see which call you are in. Then just guess which one you want to cut out, add it to hack.c and recompile.
I've had the same problem for a few months. I had to disconnect and had Internet access. Connect so I can have access to corporate resources.... A pain.
My temporary solution was to install a Sonatype Nexus (proxy for dependencies) under Debian Linux and connect with vpnc. Usually you can install it with apt-get/yum etc.
When this solution was not enough, I installed vpnc in my Windows machine with vpnc-fe http: // sourceforge.net/projects/vpncfe/ (VPNc Front-End) on top.
My configuration required a setting for perfect security called "Force-NTT". See this thread. Later you need to put the following script in postconnect.bat:
[Copy in case the thread goes down]
Thank you, rodekerken!
Here's the final result, no manual tinkering necessary to break out the split routes:
@echo off
REM By: capt-tagon
REM
REM Post-Script.bat to take VPNC's Route Table into Windows 7 land. Erases bad route table entries,
REM extracts gateway, assigned IP, Netmask, and Cisco split route entries and resubmits them to
REM Windows 7 so the routing all works. Condensed from other entries on the VPNC-FE forae and combined
REM with information gleaned from VPNC client for Linux to find all the passed parameters useful
REM to making this work. Additional by rodekerken noted below, thank you for helping finish it out.
REM
setlocal enabledelayedexpansion
echo.
echo Post-Script Begin
echo.
REM Gather Connection Data
REM
REM Parameters passed have space at end, see "HELP SET" at Console Prompt for explanation of
REM substring extraction
REM Strip space from TUNDEV, INTERNAL_IP4_ADDRESS, VPNGATEWAY, CISCO_SPLIT_INC
REM This will be broken if VPNC-FE is fixed to not export parameters with the trailing space.
REM
set MyTUNDev=%TUNDEV:~0,-1%
REM By: rodekerken
REM From the top of the router table printout, look for a line like
REM 14...00 ff 5d b0 44 aa ......TAP-Win32 Adapter V9 to find the if number.
REM A way to find out the interface number automatically and extractis this:
for /f "tokens=1 delims=." %%a in ('route print ^|find "TAP-Win32"') do set MyIF=%%a
set MyIP=%INTERNAL_IP4_ADDRESS:~0,-1%
set MyMask=%INTERNAL_IP4_NETMASK:~0,-1%
set MyVPN=%VPNGATEWAY:~0,-1%
REM Number of Routing Table Entries for Split Tunnel
set MyCiscoSplit=%CISCO_SPLIT_INC:~0,-1%
REM Date Time functions
set ANSIDate=%date:~10%%date:~4,2%%date:~7,2%
set StartTime=%time:~0,2%%time:~3,2%%time:~6,2%
REM Display Connection Data
echo Tunnel Device : [%MyTUNDev%]
echo Tunnel IntFace: [%MyIF%]
echo Tunnel IPAddr : [%MyIP%]
echo Tunnel NetMask: [%MyMask%]
echo Tunnel VPN-GW : [%MyVPN%]
echo .
echo Date Connected: [%ANSIDate%]
echo Time Connected: [%StartTime%]
echo .
REM Cisco Routes are 0 index, 6 Routes = 0-5
echo Cisco Split Routes: [%MyCiscoSplit%]
REM Display Bad Route Table
echo.
echo Bad Route Table
route print | find "%MyIP%"
echo.
REM Delete Bad Split Route entries and add back with proper Gateway.
REM Extract Cisco routing table information to feed to "ROUTE ADD"
REM Number of entries passed in Array Index CISCO_SPLIT_INC
REM For x below, value is 0 to CISCO_SPLIT_INC - 1
REM Address is passed in CISCO_SPLIT_INC_x_ADDR
REM NetMask is passed in CISCO_SPLIT_INC_x_MASK
REM
REM By: rodekerken
REM A way to loop and enumerate and evaluate the routes is like this:
set /A MyCiscoSplit-=1
for /L %%i in (0,1,%MyCiscoSplit%) do (
set SplitAddr=CISCO_SPLIT_INC_%%i_ADDR
set SplitMask=CISCO_SPLIT_INC_%%i_MASK
for /f %%a in ('echo !SplitAddr!') do set SplitAddrEval=!%%a!
for /f %%a in ('echo !SplitMask!') do set SplitMaskEval=!%%a!
echo Route%%i !SplitAddrEval! !SplitMaskEval!
route delete !SplitAddrEval! >nul
route add !SplitAddrEval! mask !SplitMaskEval! %MyVPN% metric 1 if %MyIF% >nul
)
REM Display Corrected Route Table
echo.
echo Corrected Route Table
route print | find "%MyVPN%"
echo.
echo Post-Script End
echo.
I've also added this routes after the "FOR":
route add 1111.120.120.0 mask 255.255.255.0 0.0.0.0 metric 1 if %MyIF%
route add 1111.185.19.0 mask 255.255.255.0 0.0.0.0 metric 1 if %MyIF%
route add 10.0.0.0 mask 255.0.0.0 0.0.0.0 metric 1 if %MyIF%
[Please note that the first two routes have incorrect/fake addresses. You need to change it anyway]
For my script to work repeatedly, you need to create a "disconnect.bat" containing the removal of routes:
route delete 1111.120.120.0
route delete 1111.185.19.0
route delete 10.0.0.0
Just my 0.02€
Best Answer
Finally I have figured out the reason for the "sendto: permission denied" error.
The routing table was added correctly. The problem comes from that Cisco AnyConnect VPN Client would automatically block any other traffic by modifying firewall (ipfw) rules on the host, adding one line like
Removing this rule by
will do.
Solution thanks to http://www.petefreitag.com/item/753.cfm