--- a/net/l2tpv3tun/files/l2tp.sh +++ b/net/l2tpv3tun/files/l2tp.sh @@ -1,1 +1,214 @@ - +# l2tp.sh - L2TPv3 tunnel backend +# Copyright (c) 2010 OpenWrt.org + +l2tp_next_tunnel_id() { + local max=0 + local val + for val in $( + local l + l2tpv3tun show tunnel | while read l; do + case "$l" in + Tunnel*,*encap*) l="${l#Tunnel }"; echo "${l%%,*}";; + esac + done + ); do + [ "$val" -gt "$max" ] && max="$val" + done + echo $((max + 1)) +} + +l2tp_next_session_id() { + local tunnel="$1" + local max=0 + local val + for val in $( + local l + l2tpv3tun show session${tunnel:+ tunnel_id "$tunnel"} | while read l; do + case "$l" in + Session*in*) l="${l#Session }"; echo "${l%% *}";; + esac + done + ); do + [ "$val" -gt "$max" ] && max="$val" + done + echo $((max + 1)) +} + +l2tp_tunnel_exists() { + test -n "$(l2tpv3tun show tunnel tunnel_id "$1" 2>/dev/null)" +} + +l2tp_session_exists() { + test -n "$(l2tpv3tun show session tunnel_id "$1" session_id "$2" 2>/dev/null)" +} + +l2tp_ifname() { + l2tpv3tun show session tunnel_id "$1" session_id "$2" 2>/dev/null | \ + sed -ne 's/^.*interface name: //p' +} + +l2tp_lock() { + lock /var/lock/l2tp-setup +} + +l2tp_unlock() { + lock -u /var/lock/l2tp-setup +} + +l2tp_log() { + logger -t "ifup-l2tp" "$@" +} + + +# Hook into scan_interfaces() to synthesize a .device option +# This is needed for /sbin/ifup to properly dispatch control +# to setup_interface_l2tp() even if no .ifname is set in +# the configuration. +scan_l2tp() { + local dev + config_get dev "$1" device + config_set "$1" device "${dev:+$dev }l2tp-$1" +} + +coldplug_interface_l2tp() { + setup_interface_l2tp "l2tp-$1" "$1" +} + +setup_interface_l2tp() { + local iface="$1" + local cfg="$2" + local link="l2tp-$cfg" + + l2tp_lock + + # prevent recursion + local up="$(uci_get_state network "$cfg" up 0)" + [ "$up" = 0 ] || { + l2tp_unlock + return 0 + } + + local tunnel_id + config_get tunnel_id "$cfg" tunnel_id + [ -n "$tunnel_id" ] || { + tunnel_id="$(l2tp_next_tunnel_id)" + uci_set_state network "$cfg" tunnel_id "$tunnel_id" + l2tp_log "No tunnel ID specified, assuming $tunnel_id" + } + + local peer_tunnel_id + config_get peer_tunnel_id "$cfg" peer_tunnel_id + [ -n "$peer_tunnel_id" ] || { + peer_tunnel_id="$tunnel_id" + uci_set_state network "$cfg" peer_tunnel_id "$peer_tunnel_id" + l2tp_log "No peer tunnel ID specified, assuming $peer_tunnel_id" + } + + local encap + config_get encap "$cfg" encap udp + + local sport dport + [ "$encap" = udp ] && { + config_get sport "$cfg" sport 1701 + config_get dport "$cfg" dport 1701 + } + + local peeraddr + config_get peeraddr "$cfg" peeraddr + [ -z "$peeraddr" ] && config_get peeraddr "$cfg" peer6addr + + local localaddr + case "$peeraddr" in + *:*) config_get localaddr "$cfg" local6addr ;; + *) config_get localaddr "$cfg" localaddr ;; + esac + + [ -n "$localaddr" -a -n "$peeraddr" ] || { + l2tp_log "Missing local or peer address for tunnel $cfg - skipping" + return 1 + } + + ( + while ! l2tp_tunnel_exists "$tunnel_id"; do + [ -n "$sport" ] && l2tpv3tun show tunnel 2>/dev/null | grep -q "ports: $sport/" && { + l2tp_log "There already is a tunnel with src port $sport - skipping" + l2tp_unlock + return 1 + } + + l2tpv3tun add tunnel tunnel_id "$tunnel_id" peer_tunnel_id "$peer_tunnel_id" \ + encap "$encap" local "$localaddr" remote "$peeraddr" \ + ${sport:+udp_sport "$sport"} ${dport:+udp_dport "$dport"} + + # Wait for tunnel + sleep 1 + done + + + local session_id + config_get session_id "$cfg" session_id + [ -n "$session_id" ] || { + session_id="$(l2tp_next_session_id "$tunnel_id")" + uci_set_state network "$cfg" session_id "$session_id" + l2tp_log "No session ID specified, assuming $session_id" + } + + local peer_session_id + config_get peer_session_id "$cfg" peer_session_id + [ -n "$peer_session_id" ] || { + peer_session_id="$session_id" + uci_set_state network "$cfg" peer_session_id "$peer_session_id" + l2tp_log "No peer session ID specified, assuming $peer_session_id" + } + + + while ! l2tp_session_exists "$tunnel_id" "$session_id"; do + l2tpv3tun add session ifname "$link" tunnel_id "$tunnel_id" \ + session_id "$session_id" peer_session_id "$peer_session_id" + + # Wait for session + sleep 1 + done + + + local dev + config_get dev "$cfg" device + + local ifn + config_get ifn "$cfg" ifname + + uci_set_state network "$cfg" ifname "${ifn:-$dev}" + uci_set_state network "$cfg" device "$dev" + + local mtu + config_get mtu "$cfg" mtu 1462 + + local ttl + config_get ttl "$cfg" ttl + + ip link set mtu "$mtu" ${ttl:+ ttl "$ttl"} dev "$link" + + # IP setup inherited from proto static + prepare_interface "$link" "$cfg" + setup_interface_static "${ifn:-$dev}" "$cfg" + + ip link set up dev "$link" + + uci_set_state network "$cfg" up 1 + l2tp_unlock + ) & +} + +stop_interface_l2tp() { + local cfg="$1" + local link="l2tp-$cfg" + + local tunnel=$(uci_get_state network "$cfg" tunnel_id) + local session=$(uci_get_state network "$cfg" session_id) + + [ -n "$tunnel" ] && [ -n "$session" ] && { + l2tpv3tun del session tunnel_id "$tunnel" session_id "$session" + l2tpv3tun del tunnel tunnel_id "$tunnel" + } +} +