#!/bin/bash PROGNAME=$(basename $0) TABLE_FILE=$(cd $(dirname $0) && pwd)/$PROGNAME.conf main() { local I # Defaults for options VERBOSELEVEL=10 # Process options # Process arguments CCD_SCRIPT="$1" # Sanity checks and derivations [ -f $TABLE_FILE ] || error "$TABLE_FILE: not accessible" { OUTPUT=$(bash -c ". $TABLE_FILE && echo OK" 2>&1) && [ "X$OUTPUT" = XOK ]; } || error "$TABLE_FILE: not loadable (output was: $OUTPUT)" . $TABLE_FILE # Guts # Server stuff: add route to remote *host's* *local LAN* IP address. # (This loop actually only processes one entry, but it looks more symmetrical # do doing a loop to find the matching index and then an if-we-went-off-the-end-of # the loop if statement.) debug 10 "main: server stuff ..." for ((I=0; I<${#TABLE[*]}; I+=5)); do eval "VPN_CLIENT_ID=\"\${TABLE[$I]}\"" eval "VPN_CLIENT_NET_TYPE=\"\${TABLE[$I+1]}\"" eval "VPN_CLIENT_LAN_IP=\"\${TABLE[$I+2]}\"" eval "VPN_CLIENT_LAN_NW=\"\${TABLE[$I+3]}\"" eval "VPN_CLIENT_LAN_NM=\"\${TABLE[$I+4]}\"" # We're only interested in the details of the client that's connecting. [ "X$common_name" = "X$VPN_CLIENT_ID" ] || continue # Clients not on private LANs would have their VPN connection (i.e. the # tunnel) disrupted by attempting to route to them over the VPN tunnel. [ $VPN_CLIENT_NET_TYPE = private ] || continue # Do it! CLEANUP_SCRIPT=/tmp/$common_name-cleanup debug 10 "main: $common_name: writing cleanup script to $CLEANUP_SCRIPT ..." { echo "#!/bin/bash" echo "echo \"$CLEANUP_SCRIPT: cleaning up ...\"" echo "route del -net $VPN_CLIENT_LAN_NW netmask $VPN_CLIENT_LAN_NM gw $route_vpn_gateway" echo "iptables -t nat -D POSTROUTING -s $ifconfig_pool_remote_ip -j SNAT --to-source=$VPN_CLIENT_LAN_IP -m comment --comment \"$common_name\"" echo "exec rm -f $CLEANUP_SCRIPT" } > $CLEANUP_SCRIPT chmod 755 $CLEANUP_SCRIPT debug 10 "main: $common_name: telling kernel how use openvpn to to reach client's local LAN ..." route add -net $VPN_CLIENT_LAN_NW netmask $VPN_CLIENT_LAN_NM gw $route_vpn_gateway # We don't undo this anywhere because too complicated to track if other # tunnels need it. echo 1 > /proc/sys/net/ipv4/ip_forward debug 10 "main: $common_name: adding SNAT to VPN server to change IP sent from client's tunnel interface o client's local LAN interface ..." iptables -t nat -A POSTROUTING -s $ifconfig_pool_remote_ip -j SNAT --to-source=$VPN_CLIENT_LAN_IP -m comment --comment "$common_name" # Kernel got route to client network above but openvpn needs to be told too. debug 10 "main: $common_name: telling openvpn software that it *should* handle traffic for client's local LAN ..." echo "iroute $VPN_CLIENT_LAN_NW $VPN_CLIENT_LAN_NM" >> $CCD_SCRIPT done # Client-side stuff; push routes for *other* private LANs to client debug 10 "main: client stuff ..." for ((I=0; I<${#TABLE[*]}; I+=5)); do eval "VPN_CLIENT_ID=\"\${TABLE[$I]}\"" eval "VPN_CLIENT_LAN_IP=\"\${TABLE[$I+1]}\"" eval "VPN_CLIENT_NET_TYPE=\"\${TABLE[$I+2]}\"" eval "VPN_CLIENT_LAN_NW=\"\${TABLE[$I+3]}\"" eval "VPN_CLIENT_LAN_NM=\"\${TABLE[$I+4]}\"" # We're interested in the details of the private networks *except* that of # the client that's connecting (because that client already has a route for # the client's local LAN). [ "X$common_name" != "X$VPN_CLIENT_ID" ] || continue debug 10 "main: $common_name: telling client that it can reach $VPN_CLIENT_LAN_NW over VPN ..." echo "push \"route $VPN_CLIENT_LAN_NW $VPN_CLIENT_LAN_NM\"" >> $CCD_SCRIPT done } # Standard message functions msg() { if [ -t 2 ]; then echo "$PROGNAME: $2" >&2; fi; logger -p local0.$1 "$2"; echo "$2" >> /tmp/qqq; } debug() { [ $VERBOSELEVEL -lt $1 ] || msg debug "DEBUG[$1]: $2" >&2; } info() { [ $VERBOSELEVEL -lt 3 ] || msg info "INFO: $1" >&2; } warning() { [ $VERBOSELEVEL -lt 2 ] || msg warning "WARNING: $1" >&2; } error() { [ $VERBOSELEVEL -lt 1 ] || msg error "ERROR: $1" >&2; exit 1; } internal() { msg crit "INTERNAL ERROR: $1" >&2; exit 2; } main "$@" CCD_SCRIPT="$1" main "$@"