About Blog Contact

Linux client for Peking University's VPN Service

Peking University has a (buggy and unstable) VPN service. The Linux client provided by the school’s IT department takes up a lot of resources, and it redirects all of my traffic through my school’s network slow. So I looked for a better client (preferablly a commandline one), one that can redirect my traffic on demand.

I found out that the VPN protocol used is Pulse Connect Secure. A free software implementation of the protocol exists: openconnect. Yet by default, it also redirects all traffic through the VPN. It establishes a TUN virtual networking interface, and opens a file descriptor on this interface. How to redirect traffic through this interface is determined by a script. Therefore, by changing the script, one can change the way traffic is proxied.

I would like to set up a SOCKS proxy that sends traffic through the VPN. A program exists for this purpose: ocproxy. It opens a SOCKS proxy and tunnels traffic through a TUN interface, using a userspace IP stack, lwip, which is an implementation commonly used on embedded devices.

Both can be installed with package managers:

apt install openconnect
apt install ocproxy

They can also be easily compiled, though there is a caveat with openconnect: to run it, you have to run a script wrapper, which loads all the dynamic libraries under custom pathes, so you can’t just copy and move the executable as you wish. I have no idea why the author choose to organize it in such a messy way.

The script I used is as follows:

#!/bin/bash
# This program starts a VPN to Peking University, and also a SOCKS5 server listening to a port on localhost.
# The requests to the SOCKS5 server are passed through the VPN and accessed from Peking University's end.

USERNAME=1600012345
PASSWORD=your_pass_word

# Where your ocproxy binary is
# https://github.com/cernekee/ocproxy
OCPROXY="$HOME/Software/ocproxy/ocproxy"

# Where your openconnect binary is
# https://www.infradead.org/openconnect/index.html
OPENCONNECT="$HOME/Software/openconnect/openconnect"
SOCKS_PORT=1082

# You can change it to other routes if the default is too slow
# https://its.pku.edu.cn/service_1_vpn.jsp
# Choices:
# CERNET: https://vpnc.pku.edu.cn
# China Telecom: https://vpnt.pku.edu.cn
# China Unicom: https://vpnu.pku.edu.cn
# Dr Peng 电信通: https://vpnp.pku.edu.cn
VPN_ROUTE="https://vpn.pku.edu.cn"

echo ${PASSWORD} | ${OPENCONNECT} --passwd-on-stdin -u ${USERNAME} --protocol=pulse \
	--script-tun --script "${OCPROXY} -D ${SOCKS_PORT} -k 20" ${VPN_ROUTE}

Running this script opens a SOCKS proxy that listens on localhost:1082. --passwd-on-stdin is used because I do not wish to input my password everytime I use the script.

There’s an inherent problem in this VPN: TCP through TCP is a bad idea. Pulse Secure is a layer 3 protocol, it tunnels raw IP packets. Most of the traffic we generate is TCP traffic, and if the underlaying transport is also TCP, then we would be running two reliable transmissions over one another. So retransmission errors in the lower layer TCP would exponentially slow down the upper layer TCP stream being transported. That’s why by default, Pulse Secure attempts to use UDP. However, due to the multiple layers of NAT present in China’s networks, UDP often fails. Every time I use the VPN, it switches to TCP after failing to receive any UDP response. So unless the school changes the VPN to a TCP VPN, like a ssh proxy, this problem will persist.

ocproxy proxy does not support username-password authentication for socks, so I’m planning to implement it. Also, I want to implement SOCKS over TLS, so that I can operate a SOCKS-PKU VPN proxy over the public Internet. I want to use go for its straightforward networking facilities, so all I need is to find a userland IP stack.