--- /dev/null
+#
+# Copyright (C) 2009-2010 Thomas Heil <heil@terminal-consulting.de>
+# Copyright (C) 2010-2011 OpenWrt.org
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=l2tpv3tun
+PKG_VERSION:=0.2
+PKG_RELEASE:=1
+
+PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
+PKG_SOURCE_URL:=ftp://www.openl2tp.org/releases
+PKG_MD5SUM:=d3b4a9af652cca9f34d3eea4a7ff6041
+
+PKG_INSTALL:=1
+
+include $(INCLUDE_DIR)/kernel.mk
+include $(INCLUDE_DIR)/package.mk
+
+define Package/l2tpv3tun
+ SECTION:=net
+ CATEGORY:=Network
+ DEPENDS:=+libnl-tiny +ip +kmod-l2tp +kmod-l2tp-ip +kmod-l2tp-eth
+ TITLE:=Control utility for static L2TP v3 (Pseudowire) tunnels
+ MAINTAINER:=Thomas Heil <heil@terminal-consulting.de>
+ URL:=http://www.openl2tp.org
+endef
+
+define Package/l2tpv3tun/description
+ The l2tpv3tun utility is a command line frontend for configuring static
+ L2TP v3 pseudowire tunnels.
+endef
+
+MAKE_FLAGS += \
+ CC="$(TARGET_CC)" \
+ CFLAGS="$(TARGET_CFLAGS) -I$(STAGING_DIR)/usr/include -I$(STAGING_DIR)/usr/include/libnl-tiny/" \
+ LD="$(TARGET_CC)" \
+ LDFLAGS="$(TARGET_LDFLAGS) -L$(STAGING_DIR)/usr/lib/ -lnl-tiny"
+
+define Package/l2tpv3tun/install
+ $(INSTALL_DIR) $(1)/usr/bin
+ $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/l2tpv3tun $(1)/usr/bin/
+ $(INSTALL_DIR) $(1)/lib/network
+ $(INSTALL_BIN) ./files/l2tp.sh $(1)/lib/network/
+endef
+
+$(eval $(call BuildPackage,l2tpv3tun))
--- /dev/null
+# 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"
+ }
+}
--- /dev/null
+--- a/main.c
++++ b/main.c
+@@ -560,6 +560,7 @@ static void usage(void)
+ fprintf(stderr, " session_id ID peer_session_id ID\n");
+ fprintf(stderr, " [ cookie HEXSTR ] [ peer_cookie HEXSTR ]\n");
+ fprintf(stderr, " [ offset OFFSET ] [ peer_offset OFFSET ]\n");
++ fprintf(stderr, " [ ifname IFNAME ]\n");
+ fprintf(stderr, " %s del tunnel tunnel_id ID\n", L2TP_CMD_ROOT);
+ fprintf(stderr, " %s del session tunnel_id ID session_id ID\n", L2TP_CMD_ROOT);
+ fprintf(stderr, " %s show tunnel [ tunnel_id ID ]\n", L2TP_CMD_ROOT);
+@@ -671,6 +672,9 @@ static int parse_args(int argc, char **a
+ p->peer_cookie_len = slen / 2;
+ if (hex2mem(*argv, p->peer_cookie, p->peer_cookie_len) < 0)
+ invarg("cookie must be a hex string\n", *argv);
++ } else if (strcmp(*argv, "ifname") == 0) {
++ NEXT_ARG();
++ p->ifname = *argv;
+ } else if (strcmp(*argv, "tunnel") == 0) {
+ p->tunnel = 1;
+ } else if (strcmp(*argv, "session") == 0) {