1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
|
#!/bin/sh
#
# © 2009 David Woodhouse <dwmw2@infradead.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
################
#
# This is a replacement for the standard vpnc-script for use with vpnc
# and openconnect. It sets up VPN routing which doesn't screw over the
# _normal_ routing of the box.
# It sets up a new network namespace for the VPN to use, and it runs
# pTRTd (http://www.litech.org/ptrtd/) in that namespace. Connections
# to the VPN are actually made as IPv6 connections, and pTRTd handles
# converting them to Legacy IP and forwarding them.
# The full range of Legacy IP addresses on the VPN is available to the
# host through a tiny corner of the IPv6 address space; for this purpose
# we use fec0:0:0:ffff:0:0:xxyy:zzww to represent the address xx.yy.zz.ww
# on the VPN.
# TODO: Either use totd (ftp://ftp.pasta.cs.uit.no/pub/Vermicelli/) or
# preferably extend dnsmasq to handle DNS for us. We want the following:
# - A queries for non-existent VPN hosts return NXDOMAIN (no munging)
# - A queries for _extant_ VPN hosts return NOERROR instead of the result.
# - AAAA queries return the A result, converted to the above IPv6 space.
# - PTR queries within the IPv6 space converted appropriately.
IP="`command -v ip | grep '^/'`"
connect_parent()
{
# XXX: How do we work out what it _really_ is?
export PARENT_NETNS=$$
# XXX: Make sure it doesn't exist in this namespace already
export RETURNDEV=x$TUNDEV
./netunshare $0 $@ &
CHILDPID=$!
# XXX: If we do this too soon (before the unshare), we're just
# giving it to our _own_ netns. which achieves nothing.
# So give it away until we _can't_ give it away any more.
while $IP link set $TUNDEV netns $CHILDPID 2>/dev/null; do
sleep 0.1
done
# Wait for the ptrtd tundev to appear in this namespace
while ! $IP link show $RETURNDEV >/dev/null 2>&1 ; do
sleep 1
done
$IP link set $RETURNDEV up
$IP addr add fe80::1 dev $RETURNDEV
$IP route add fec0:0:0:ffff::/64 dev $RETURNDEV
# XXX: Local hack -- my company's VPN server returns
# "foo.company.com" instead of just "company.com".
CISCO_DEF_DOMAIN=`echo $CISCO_DEF_DOMAIN | cut -f2- -d.`
# Work out the IPv6 address of the nameservers...
IPV6NS=
for NS in $INTERNAL_IP4_DNS; do
A=`echo $NS | cut -f1 -d.`
B=`echo $NS | cut -f2 -d.`
C=`echo $NS | cut -f3 -d.`
D=`echo $NS | cut -f4 -d.`
THISNS=`printf fec0:0:0:ffff:0:0:%02x%02x:%02x%02x $A $B $C $D`
IPV6NS="$THISNS $IPV6NS"
DNSMASQ_CMDLINE="-S /$CISCO_DEF_DOMAIN/$IPV6NS $DNSMASQ_CMDLINE"
done
echo IPv6 DNS: $IPV6NS
# XXX: Add totd-like capability to dnsmasq
dnsmasq $DNSMASQ_CMDLINE
}
connect()
{
if [ -z "$PARENT_NETNS" ]; then
connect_parent
exit 0
fi
# Wait for the tundev to appear in this namespace
while ! $IP link show $TUNDEV >/dev/null 2>&1 ; do
sleep 0.1
done
# Set up Legacy IP in the new namespace
$IP link set lo up
$IP link set $TUNDEV up
$IP -4 addr add $INTERNAL_IP4_ADDRESS dev $TUNDEV
$IP -4 route add default dev $TUNDEV
if [ "$INTERNAL_IP4_MTU" != "" ]; then
$IP link set $TUNDEV mtu $INTERNAL_IP4_MTU
fi
# ifconfig
# route
# For debugging, really. Lets you ssh into the netns with
# ssh fec0:0:0:ffff:0:0:7f00:1
/usr/sbin/sshd -D &
SSHD_PID=$!
# Start ptrtd
ptrtd -i tun:$RETURNDEV -d >/dev/null 2>&1 &
PTRTD_PID=$!
# Wait for the ptrtd to make its device
while ! $IP link show $TUNDEV >/dev/null 2>&1 ; do
sleep 0.1
done
# Now give the ptrtd device back to the parent
$IP link set $RETURNDEV down
$IP link set $RETURNDEV netns $PARENT_NETNS
#Hm, this doesn't work because the tundev doesn't go away when it should
#while ip link show $TUNDEV 2> /dev/null ; do
# sleep 1
#done
# Wait for ptrtd to die (which it will when disconnect() kills its tun)
wait $PTRTD_PID
kill -TERM $SSHD_PID
# Wait a while to avoid tun BUG() if we quit and the netns goes away
# before vpnc/openconnect closes its tun fd.
sleep 1
}
disconnect()
{
RETURNDEV=x$TUNDEV
# This will kill ptrtd inside the netns, leaving the script to clean up
$IP link del $RETURNDEV
# XXX: Undo the dnsmasq stuff.
killall dnsmasq
# XXX: properly.
}
case $reason in
connect)
connect
;;
disconnect)
disconnect
;;
esac
|