b.liu | e958203 | 2025-04-17 19:18:16 +0800 | [diff] [blame^] | 1 | #!/bin/sh |
| 2 | # List of parameters passed through environment |
| 3 | #* reason -- why this script was called, one of: pre-init connect disconnect |
| 4 | #* VPNGATEWAY -- vpn gateway address (always present) |
| 5 | #* TUNDEV -- tunnel device (always present) |
| 6 | #* INTERNAL_IP4_ADDRESS -- address (always present) |
| 7 | #* INTERNAL_IP4_MTU -- mtu (often unset) |
| 8 | #* INTERNAL_IP4_NETMASK -- netmask (often unset) |
| 9 | #* INTERNAL_IP4_NETMASKLEN -- netmask length (often unset) |
| 10 | #* INTERNAL_IP4_NETADDR -- address of network (only present if netmask is set) |
| 11 | #* INTERNAL_IP4_DNS -- list of dns servers |
| 12 | #* INTERNAL_IP4_NBNS -- list of wins servers |
| 13 | #* INTERNAL_IP6_ADDRESS -- IPv6 address |
| 14 | #* INTERNAL_IP6_NETMASK -- IPv6 netmask |
| 15 | #* INTERNAL_IP6_DNS -- IPv6 list of dns servers |
| 16 | #* CISCO_DEF_DOMAIN -- default domain name |
| 17 | #* CISCO_BANNER -- banner from server |
| 18 | #* CISCO_SPLIT_INC -- number of networks in split-network-list |
| 19 | #* CISCO_SPLIT_INC_%d_ADDR -- network address |
| 20 | #* CISCO_SPLIT_INC_%d_MASK -- subnet mask (for example: 255.255.255.0) |
| 21 | #* CISCO_SPLIT_INC_%d_MASKLEN -- subnet masklen (for example: 24) |
| 22 | #* CISCO_SPLIT_INC_%d_PROTOCOL -- protocol (often just 0) |
| 23 | #* CISCO_SPLIT_INC_%d_SPORT -- source port (often just 0) |
| 24 | #* CISCO_SPLIT_INC_%d_DPORT -- destination port (often just 0) |
| 25 | #* CISCO_IPV6_SPLIT_INC -- number of networks in IPv6 split-network-list |
| 26 | #* CISCO_IPV6_SPLIT_INC_%d_ADDR -- IPv6 network address |
| 27 | #* CISCO_IPV6_SPLIT_INC_$%d_MASKLEN -- IPv6 subnet masklen |
| 28 | |
| 29 | HOOKS_DIR=/etc/openconnect |
| 30 | |
| 31 | # FIXMEs: |
| 32 | |
| 33 | # Section A: route handling |
| 34 | |
| 35 | # 1) The 3 values CISCO_SPLIT_INC_%d_PROTOCOL/SPORT/DPORT are currently being ignored |
| 36 | # In order to use them, we'll probably need os specific solutions |
| 37 | # * Linux: iptables -t mangle -I PREROUTING <conditions> -j ROUTE --oif $TUNDEV |
| 38 | # This would be an *alternative* to changing the routes (and thus 2) and 3) |
| 39 | # shouldn't be relevant at all) |
| 40 | # 2) There are two different functions to set routes: generic routes and the |
| 41 | # default route. Why isn't the defaultroute handled via the generic route case? |
| 42 | # 3) In the split tunnel case, all routes but the default route might get replaced |
| 43 | # without getting restored later. We should explicitely check and save them just |
| 44 | # like the defaultroute |
| 45 | # 4) Replies to a dhcp-server should never be sent into the tunnel |
| 46 | |
| 47 | # Section B: Split DNS handling |
| 48 | |
| 49 | # 1) We parse CISCO_SPLIT_DNS and use dnsmasq to set it |
| 50 | |
| 51 | do_connect() { |
| 52 | if [ -n "$CISCO_BANNER" ]; then |
| 53 | logger -t openconnect "Connect Banner:" |
| 54 | echo "$CISCO_BANNER" | while read LINE ; do logger -t openconnect "|" "$LINE" ; done |
| 55 | fi |
| 56 | |
| 57 | proto_init_update "$TUNDEV" 1 |
| 58 | |
| 59 | if [ -n "$INTERNAL_IP4_MTU" ]; then |
| 60 | MTU=$INTERNAL_IP4_MTU |
| 61 | fi |
| 62 | |
| 63 | if [ -z "$MTU" ]; then |
| 64 | MTU=1412 |
| 65 | fi |
| 66 | |
| 67 | proto_add_ipv4_address "$INTERNAL_IP4_ADDRESS" 32 "" "$INTERNAL_IP4_ADDRESS" |
| 68 | |
| 69 | if [ -n "$INTERNAL_IP4_NETMASKLEN" ]; then |
| 70 | proto_add_ipv4_route "$INTERNAL_IP4_NETADDR" "$INTERNAL_IP4_NETMASKLEN" |
| 71 | fi |
| 72 | |
| 73 | # If the netmask is provided, it contains the address _and_ netmask |
| 74 | if [ -n "$INTERNAL_IP6_ADDRESS" ] && [ -z "$INTERNAL_IP6_NETMASK" ]; then |
| 75 | INTERNAL_IP6_NETMASK="$INTERNAL_IP6_ADDRESS/128" |
| 76 | fi |
| 77 | |
| 78 | if [ -n "$INTERNAL_IP6_NETMASK" ]; then |
| 79 | addr="${INTERNAL_IP6_NETMASK%%/*}" |
| 80 | mask="${INTERNAL_IP6_NETMASK##*/}" |
| 81 | [[ "$addr" != "$mask" ]] && proto_add_ipv6_address "$addr" "$mask" |
| 82 | fi |
| 83 | |
| 84 | DNSMASQ_FILE="/tmp/dnsmasq.d/openconnect.$TUNDEV" |
| 85 | LOCAL_DOMAIN=$(uci get dhcp.@dnsmasq[0].domain) |
| 86 | rm -f $DNSMASQ_FILE |
| 87 | if [ -n "$CISCO_SPLIT_DNS" ] && [ -d "/tmp/dnsmasq.d/" ];then |
| 88 | SDNS=`echo $CISCO_SPLIT_DNS|sed 's/,/\n/g'` |
| 89 | echo "$SDNS" | while read i; do |
| 90 | if [ "$i" = "$LOCAL_DOMAIN" ];then |
| 91 | continue |
| 92 | fi |
| 93 | if [ -n "$INTERNAL_IP4_DNS" ];then |
| 94 | for dns in $INTERNAL_IP4_DNS;do |
| 95 | echo "server=/$i/$dns" >> $DNSMASQ_FILE |
| 96 | done |
| 97 | fi |
| 98 | if [ -n "$INTERNAL_IP6_DNS" ];then |
| 99 | for dns in $INTERNAL_IP6_DNS;do |
| 100 | echo "server=/$i/$dns" >> $DNSMASQ_FILE |
| 101 | done |
| 102 | fi |
| 103 | echo "rebind-domain-ok=$i" >> $DNSMASQ_FILE |
| 104 | done |
| 105 | /etc/init.d/dnsmasq restart |
| 106 | else |
| 107 | if [ -n "$INTERNAL_IP4_DNS" ];then |
| 108 | for dns in $INTERNAL_IP4_DNS;do |
| 109 | proto_add_dns_server "$dns" |
| 110 | done |
| 111 | fi |
| 112 | if [ -n "$INTERNAL_IP6_DNS" ];then |
| 113 | for dns in $INTERNAL_IP6_DNS;do |
| 114 | proto_add_dns_server "$dns" |
| 115 | done |
| 116 | fi |
| 117 | if [ -n "$CISCO_DEF_DOMAIN" ] && [ "$CISCO_DEF_DOMAIN" != "$LOCAL_DOMAIN" ];then |
| 118 | if [ -n "$INTERNAL_IP4_DNS" ];then |
| 119 | for dns in $INTERNAL_IP4_DNS;do |
| 120 | echo "server=/$CISCO_DEF_DOMAIN/$dns" >> $DNSMASQ_FILE |
| 121 | done |
| 122 | fi |
| 123 | if [ -n "$INTERNAL_IP6_DNS" ];then |
| 124 | for dns in $INTERNAL_IP6_DNS;do |
| 125 | echo "server=/$CISCO_DEF_DOMAIN/$dns" >> $DNSMASQ_FILE |
| 126 | done |
| 127 | fi |
| 128 | proto_add_dns_search "$CISCO_DEF_DOMAIN" |
| 129 | fi |
| 130 | fi |
| 131 | |
| 132 | if [ -n "$CISCO_SPLIT_INC" ]; then |
| 133 | i=0 |
| 134 | while [ $i -lt $CISCO_SPLIT_INC ] ; do |
| 135 | eval NETWORK="\${CISCO_SPLIT_INC_${i}_ADDR}" |
| 136 | eval NETMASK="\${CISCO_SPLIT_INC_${i}_MASK}" |
| 137 | eval NETMASKLEN="\${CISCO_SPLIT_INC_${i}_MASKLEN}" |
| 138 | if [ $NETWORK != "0.0.0.0" ]; then |
| 139 | proto_add_ipv4_route "$NETWORK" "$NETMASKLEN" |
| 140 | else |
| 141 | proto_add_ipv4_route "0.0.0.0" 0 |
| 142 | fi |
| 143 | i=$(($i + 1)) |
| 144 | done |
| 145 | elif [ -n "$INTERNAL_IP4_ADDRESS" ]; then |
| 146 | proto_add_ipv4_route "0.0.0.0" 0 |
| 147 | fi |
| 148 | if [ -n "$CISCO_IPV6_SPLIT_INC" ]; then |
| 149 | i=0 |
| 150 | while [ $i -lt $CISCO_IPV6_SPLIT_INC ] ; do |
| 151 | eval NETWORK="\${CISCO_IPV6_SPLIT_INC_${i}_ADDR}" |
| 152 | eval NETMASKLEN="\${CISCO_IPV6_SPLIT_INC_${i}_MASKLEN}" |
| 153 | if [ $NETMASKLEN -lt 128 ]; then |
| 154 | proto_add_ipv6_route "$NETWORK" "$NETMASKLEN" |
| 155 | else |
| 156 | proto_add_ipv6_route "::0" 0 |
| 157 | fi |
| 158 | i=$(($i + 1)) |
| 159 | done |
| 160 | elif [ -n "$INTERNAL_IP6_NETMASK" -o -n "$INTERNAL_IP6_ADDRESS" ]; then |
| 161 | proto_add_ipv6_route "::0" 0 |
| 162 | fi |
| 163 | proto_send_update "$INTERFACE" |
| 164 | } |
| 165 | |
| 166 | do_disconnect() { |
| 167 | rm -f "/tmp/dnsmasq.d/openconnect.$TUNDEV" |
| 168 | proto_init_update "$TUNDEV" 0 |
| 169 | proto_send_update "$INTERFACE" |
| 170 | } |
| 171 | |
| 172 | #### Hooks |
| 173 | run_hooks() { |
| 174 | HOOK="$1" |
| 175 | |
| 176 | if [ -d ${HOOKS_DIR}/${HOOK}.d ]; then |
| 177 | for script in ${HOOKS_DIR}/${HOOK}.d/* ; do |
| 178 | [ -f $script ] && . $script |
| 179 | done |
| 180 | fi |
| 181 | } |
| 182 | |
| 183 | #### Main |
| 184 | |
| 185 | if [ -z "$reason" ]; then |
| 186 | logger -t openconnect "this script must be called from vpnc" 1>&2 |
| 187 | exit 1 |
| 188 | fi |
| 189 | if [ -z "$INTERFACE" ]; then |
| 190 | logger -t openconnect "this script must be called for an active interface" |
| 191 | exit 1 |
| 192 | fi |
| 193 | |
| 194 | . /lib/netifd/netifd-proto.sh |
| 195 | |
| 196 | case "$reason" in |
| 197 | pre-init) |
| 198 | run_hooks pre-init |
| 199 | ;; |
| 200 | connect) |
| 201 | run_hooks connect |
| 202 | do_connect |
| 203 | run_hooks post-connect |
| 204 | ;; |
| 205 | disconnect) |
| 206 | run_hooks disconnect |
| 207 | do_disconnect |
| 208 | run_hooks post-disconnect |
| 209 | ;; |
| 210 | reconnect) |
| 211 | run_hooks reconnect |
| 212 | ;; |
| 213 | attempt-reconnect) |
| 214 | run_hooks attempt-reconnect |
| 215 | ;; |
| 216 | *) |
| 217 | logger -t openconnect "unknown reason '$reason'. Maybe vpnc-script is out of date" 1>&2 |
| 218 | exit 1 |
| 219 | ;; |
| 220 | esac |
| 221 | |
| 222 | exit 0 |