ASR_BASE

Change-Id: Icf3719cc0afe3eeb3edc7fa80a2eb5199ca9dda1
diff --git a/package/network/ipv6/464xlat/Makefile b/package/network/ipv6/464xlat/Makefile
new file mode 100644
index 0000000..ab09b1e
--- /dev/null
+++ b/package/network/ipv6/464xlat/Makefile
@@ -0,0 +1,43 @@
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=464xlat
+PKG_RELEASE:=13
+
+PKG_SOURCE_DATE:=2018-01-16
+PKG_MAINTAINER:=Hans Dedecker <dedeckeh@gmail.com>
+PKG_LICENSE:=GPL-2.0
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/464xlat
+  SECTION:=net
+  CATEGORY:=Network
+  DEPENDS:=@IPV6 +kmod-nat46 +ip
+  TITLE:=464xlat CLAT support
+endef
+
+define Build/Prepare
+	$(call Build/Prepare/Default)
+	$(CP) ./src/* $(PKG_BUILD_DIR)/
+endef
+
+define Build/Compile
+	$(MAKE) -C $(PKG_BUILD_DIR) \
+		CC="$(TARGET_CC)" \
+		CFLAGS="$(TARGET_CFLAGS) -Wall" \
+		LDFLAGS="$(TARGET_LDFLAGS)"
+endef
+
+define Package/464xlat/description
+  464xlat provides support to deploy limited IPv4 access services to mobile
+  and wireline IPv6-only edge networks without encapsulation (RFC6877)
+endef
+
+define Package/464xlat/install
+	$(INSTALL_DIR) $(1)/lib/netifd/proto
+	$(INSTALL_BIN) ./files/464xlat.sh $(1)/lib/netifd/proto/464xlat.sh
+	$(INSTALL_DIR) $(1)/sbin
+	$(INSTALL_BIN) $(PKG_BUILD_DIR)/464xlatcfg $(1)/sbin
+endef
+
+$(eval $(call BuildPackage,464xlat))
diff --git a/package/network/ipv6/464xlat/files/464xlat.sh b/package/network/ipv6/464xlat/files/464xlat.sh
new file mode 100755
index 0000000..903f202
--- /dev/null
+++ b/package/network/ipv6/464xlat/files/464xlat.sh
@@ -0,0 +1,118 @@
+#!/bin/sh
+# 464xlat.sh - 464xlat CLAT
+#
+# Copyright (c) 2015 Steven Barth <cyrus@openwrt.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2
+# as published by the Free Software Foundation
+#
+# 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.
+
+[ -n "$INCLUDE_ONLY" ] || {
+	. /lib/functions.sh
+	. /lib/functions/network.sh
+	. ../netifd-proto.sh
+	init_proto "$@"
+}
+
+proto_464xlat_setup() {
+	local cfg="$1"
+	local iface="$2"
+	local link="464-$cfg"
+
+	local ip6addr ip6prefix tunlink zone
+	json_get_vars ip6addr ip6prefix tunlink zone
+
+	[ "$zone" = "-" ] && zone=""
+
+	( proto_add_host_dependency "$cfg" "::" "$tunlink" )
+
+	if [ -z "$tunlink" ] && ! network_find_wan6 tunlink; then
+		proto_notify_error "$cfg" "NO_WAN_LINK"
+		return
+	fi
+	network_get_device tundev "$tunlink"
+
+	ip6addr=$(464xlatcfg "$link" "$tundev" "$ip6prefix" 192.0.0.1 $ip6addr)
+	if [ -z "$ip6addr" ]; then
+		proto_notify_error "$cfg" "CLAT_CONFIG_FAILED"
+		return
+	fi
+
+	ip -6 rule del from all lookup local
+	ip -6 rule add from all lookup local pref 1
+	ip -6 rule add to $ip6addr lookup prelocal pref 0
+	echo "$ip6addr" > /tmp/464-$cfg-anycast
+
+	proto_init_update "$link" 1
+	proto_add_ipv4_route "0.0.0.0" 0 "" "" 2048
+	proto_add_ipv6_route $ip6addr 96 "" "" "" "" 254
+
+	proto_add_data
+	[ -n "$zone" ] && json_add_string zone "$zone"
+
+	json_add_array firewall
+		[ -z "$zone" ] && zone=$(fw3 -q network $iface 2>/dev/null)
+
+		json_add_object ""
+			json_add_string type nat
+			json_add_string target SNAT
+			json_add_string family inet
+			json_add_string snat_ip 192.0.0.1
+		json_close_object
+		[ -n "$zone" ] && {
+			json_add_object ""
+				json_add_string type rule
+				json_add_string family inet6
+				json_add_string proto all
+				json_add_string direction in
+				json_add_string dest "$zone"
+				json_add_string src "$zone"
+				json_add_string src_ip $ip6addr
+				json_add_string target ACCEPT
+			json_close_object
+		}
+	json_close_array
+	proto_close_data
+
+	proto_send_update "$cfg"
+}
+
+proto_464xlat_teardown() {
+	local cfg="$1"
+	local link="464-$cfg"
+
+	[ -f /tmp/464-$cfg-anycast ] || return
+	local ip6addr=$(cat /tmp/464-$cfg-anycast)
+
+	464xlatcfg "$link"
+
+	rm -rf /tmp/464-$cfg-anycast
+	[ -n "$ip6addr" ] && ip -6 rule del to $ip6addr lookup prelocal
+
+	if [ -z "$(ls /tmp/464-*-anycast 2>&-)" ]; then
+		ip -6 rule del from all lookup local
+		ip -6 rule add from all lookup local pref 0
+	fi
+
+	# Kill conntracks SNATed to 192.0.0.1
+	echo 192.0.0.1 > /proc/net/nf_conntrack
+}
+
+proto_464xlat_init_config() {
+	no_device=1
+	available=1
+
+	proto_config_add_string "ip6prefix"
+	proto_config_add_string "ip6addr"
+	proto_config_add_string "tunlink"
+	proto_config_add_string "zone"
+}
+
+[ -n "$INCLUDE_ONLY" ] || {
+        add_protocol 464xlat
+}
diff --git a/package/network/ipv6/464xlat/src/464xlatcfg.c b/package/network/ipv6/464xlat/src/464xlatcfg.c
new file mode 100644
index 0000000..76cd90a
--- /dev/null
+++ b/package/network/ipv6/464xlat/src/464xlatcfg.c
@@ -0,0 +1,167 @@
+/* 464xlatcfg.c
+ *
+ * Copyright (c) 2015 Steven Barth <cyrus@openwrt.org>
+ * Copyright (c) 2017 Hans Dedecker <dedeckeh@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation
+ *
+ * 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.
+ */
+
+#include <netinet/icmp6.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <stdio.h>
+#include <netdb.h>
+
+static void sighandler(__attribute__((unused)) int signal)
+{
+}
+
+int main(int argc, const char *argv[])
+{
+	char buf[INET6_ADDRSTRLEN], prefix[INET6_ADDRSTRLEN + 4];
+	int pid;
+
+	if (argc <= 1) {
+		fprintf(stderr, "Usage: %s <name> [ifname] [ipv6prefix] [ipv4addr] [ipv6addr]\n", argv[0]);
+		return 1;
+	}
+
+	snprintf(buf, sizeof(buf), "/var/run/%s.pid", argv[1]);
+	FILE *fp = fopen(buf, "r");
+	if (fp) {
+		if (fscanf(fp, "%d", &pid) == 1)
+			kill(pid, SIGTERM);
+
+		unlink(buf);
+		fclose(fp);
+	}
+
+	if (!argv[2])
+		return 0;
+
+	if (!argv[3] || !argv[4] || !(fp = fopen(buf, "wx")))
+		return 1;
+
+	signal(SIGTERM, SIG_DFL);
+	setvbuf(fp, NULL, _IOLBF, 0);
+	fprintf(fp, "%d\n", getpid());
+
+	prefix[sizeof(prefix) - 1] = 0;
+	strncpy(prefix, argv[3], sizeof(prefix) - 1);
+
+	if (!prefix[0]) {
+		struct addrinfo hints = { .ai_family = AF_INET6 }, *res;
+		if (getaddrinfo("ipv4only.arpa", NULL, &hints, &res) || !res) {
+			sleep(3);
+			if (getaddrinfo("ipv4only.arpa", NULL, &hints, &res) || !res)
+				return 2;
+		}
+
+		struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)res->ai_addr;
+		inet_ntop(AF_INET6, &sin6->sin6_addr, prefix, sizeof(prefix) - 4);
+		strcat(prefix, "/96");
+		freeaddrinfo(res);
+	}
+
+	int i = 0;
+	int sock;
+	struct sockaddr_in6 saddr;
+
+	do {
+		socklen_t saddrlen = sizeof(saddr);
+		struct icmp6_filter filt;
+
+		sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
+		ICMP6_FILTER_SETBLOCKALL(&filt);
+		setsockopt(sock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt, sizeof(filt));
+		setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, argv[2], strlen(argv[2]));
+		memset(&saddr, 0, sizeof(saddr));
+		saddr.sin6_family = AF_INET6;
+		saddr.sin6_addr.s6_addr32[0] = htonl(0x2001);
+		saddr.sin6_addr.s6_addr32[1] = htonl(0xdb8);
+		if (connect(sock, (struct sockaddr*)&saddr, sizeof(saddr)) ||
+				getsockname(sock, (struct sockaddr*)&saddr, &saddrlen))
+			return 3;
+
+		if (!IN6_IS_ADDR_LINKLOCAL(&saddr.sin6_addr) || argv[5])
+			break;
+
+		close(sock);
+		sleep(3);
+		i++;
+	} while (i < 3);
+
+	struct ipv6_mreq mreq = {saddr.sin6_addr, if_nametoindex(argv[2])};
+	if (!argv[5]) {
+		if (IN6_IS_ADDR_LINKLOCAL(&mreq.ipv6mr_multiaddr))
+			return 5;
+
+		srandom(mreq.ipv6mr_multiaddr.s6_addr32[0] ^ mreq.ipv6mr_multiaddr.s6_addr32[1] ^
+				mreq.ipv6mr_multiaddr.s6_addr32[2] ^ mreq.ipv6mr_multiaddr.s6_addr32[3]);
+		mreq.ipv6mr_multiaddr.s6_addr32[2] = random();
+		mreq.ipv6mr_multiaddr.s6_addr32[3] = 0;
+	} else if (inet_pton(AF_INET6, argv[5], &mreq.ipv6mr_multiaddr) != 1) {
+		return 1;
+	}
+
+	if (setsockopt(sock, SOL_IPV6, IPV6_JOIN_ANYCAST, &mreq, sizeof(mreq)))
+		return 3;
+
+	inet_ntop(AF_INET6, &mreq.ipv6mr_multiaddr, buf, sizeof(buf));
+	fputs(buf, stdout);
+	fputc('\n', stdout);
+	fflush(stdout);
+
+	FILE *nat46 = fopen("/proc/net/nat46/control", "w");
+	if (!nat46 || fprintf(nat46, "add %s\nconfig %s local.style RFC6052 local.v4 %s/32 local.v6 %s/96 "
+			"remote.style RFC6052 remote.v6 %s\n", argv[1], argv[1], argv[4], buf, prefix) < 0 ||
+			fclose(nat46))
+		return 4;
+
+	FILE *toe_nat46 = fopen("/sys/kernel/debug/toe/nat46", "w");
+	if (toe_nat46) {
+		fprintf(toe_nat46, "add %s\nconfig %s local.v6 %s/96 remote.v6 %s\n",
+				argv[1], argv[1], buf, prefix);
+		fclose(toe_nat46);
+	}
+
+	if (!(pid = fork())) {
+		fclose(fp);
+		fclose(stdin);
+		fclose(stdout);
+		fclose(stderr);
+		chdir("/");
+		setsid();
+		signal(SIGTERM, sighandler);
+		pause();
+
+		nat46 = fopen("/proc/net/nat46/control", "w");
+		if (nat46) {
+			fprintf(nat46, "del %s\n", argv[1]);
+			fclose(nat46);
+		}
+
+		toe_nat46 = fopen("/sys/kernel/debug/toe/nat46", "w");
+		if (toe_nat46) {
+			fprintf(toe_nat46, "del %s\n", argv[1]);
+			fclose(toe_nat46);
+		}
+	} else {
+		rewind(fp);
+		fprintf(fp, "%d\n", pid);
+	}
+
+	return 0;
+}
diff --git a/package/network/ipv6/464xlat/src/Makefile b/package/network/ipv6/464xlat/src/Makefile
new file mode 100644
index 0000000..3950a6b
--- /dev/null
+++ b/package/network/ipv6/464xlat/src/Makefile
@@ -0,0 +1,8 @@
+all: 464xlatcfg
+
+464xlatcfg: 464xlatcfg.c
+	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
+
+clean:
+	rm -f 464xlatcfg
+
diff --git a/package/network/ipv6/6in4/Makefile b/package/network/ipv6/6in4/Makefile
new file mode 100644
index 0000000..b70da23
--- /dev/null
+++ b/package/network/ipv6/6in4/Makefile
@@ -0,0 +1,42 @@
+#
+# Copyright (C) 2010-2015 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:=6in4
+PKG_RELEASE:=29
+PKG_LICENSE:=GPL-2.0
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/6in4
+  SECTION:=net
+  CATEGORY:=Network
+  DEPENDS:=@IPV6 +kmod-sit +uclient-fetch +resolveip
+  TITLE:=IPv6-in-IPv4 configuration support
+  MAINTAINER:=Jo-Philipp Wich <jo@mein.io>
+  PKGARCH:=all
+endef
+
+define Package/6in4/description
+Provides support for 6in4 tunnels in /etc/config/network.
+Refer to http://wiki.openwrt.org/doc/uci/network for
+configuration details.
+endef
+
+define Build/Compile
+endef
+
+define Build/Configure
+endef
+
+define Package/6in4/install
+	$(INSTALL_DIR) $(1)/lib/netifd/proto
+	$(INSTALL_BIN) ./files/6in4.sh $(1)/lib/netifd/proto/6in4.sh
+endef
+
+$(eval $(call BuildPackage,6in4))
diff --git a/package/network/ipv6/6in4/files/6in4.sh b/package/network/ipv6/6in4/files/6in4.sh
new file mode 100755
index 0000000..dd055ec
--- /dev/null
+++ b/package/network/ipv6/6in4/files/6in4.sh
@@ -0,0 +1,182 @@
+#!/bin/sh
+# 6in4.sh - IPv6-in-IPv4 tunnel backend
+# Copyright (c) 2010-2015 OpenWrt.org
+
+[ -n "$INCLUDE_ONLY" ] || {
+	. /lib/functions.sh
+	. /lib/functions/network.sh
+	. ../netifd-proto.sh
+	init_proto "$@"
+}
+
+# Function taken from 6to4 package (6to4.sh), flipped returns
+test_6in4_rfc1918()
+{
+	local oIFS="$IFS"; IFS="."; set -- $1; IFS="$oIFS"
+	[ $1 -eq  10 ] && return 1
+	[ $1 -eq 192 ] && [ $2 -eq 168 ] && return 1
+	[ $1 -eq 172 ] && [ $2 -ge  16 ] && [ $2 -le  31 ] && return 1
+
+	# RFC 6598
+	[ $1 -eq 100 ] && [ $2 -ge  64 ] && [ $2 -le 127 ] && return 1
+
+	return 0
+}
+
+proto_6in4_update() {
+	sh -c '
+		timeout=5
+
+		(while [ $((timeout--)) -gt 0 ]; do
+			sleep 1
+			kill -0 $$ || exit 0
+		done; kill -9 $$) 2>/dev/null &
+
+		exec "$@"
+	' "$1" "$@"
+}
+
+proto_6in4_add_prefix() {
+	append "$3" "$1"
+}
+
+proto_6in4_setup() {
+	local cfg="$1"
+	local iface="$2"
+	local link="6in4-$cfg"
+	local remoteip
+
+	local mtu ttl tos ipaddr peeraddr ip6addr ip6prefix ip6prefixes tunlink tunnelid username password updatekey device nohostroute
+	json_get_vars mtu ttl tos ipaddr peeraddr ip6addr tunlink tunnelid username password updatekey device nohostroute
+	json_for_each_item proto_6in4_add_prefix ip6prefix ip6prefixes
+
+	[ -n "$device" ] && link="$device"
+
+	[ -z "$peeraddr" ] && {
+		proto_notify_error "$cfg" "MISSING_PEER_ADDRESS"
+		proto_block_restart "$cfg"
+		return
+	}
+
+	remoteip=$(resolveip -t 10 -4 "$peeraddr")
+
+	if [ -z "$remoteip" ]; then
+		proto_notify_error "$cfg" "PEER_RESOLVE_FAIL"
+		return
+	fi
+
+	for ip in $remoteip; do
+		peeraddr=$ip
+		break
+	done
+
+	if [ "${nohostroute}" != "1" ]; then
+		( proto_add_host_dependency "$cfg" "$peeraddr" "$tunlink" )
+	fi
+
+	[ -z "$ipaddr" ] && {
+		local wanif="$tunlink"
+		if [ -z "$wanif" ] && ! network_find_wan wanif; then
+			proto_notify_error "$cfg" "NO_WAN_LINK"
+			return
+		fi
+
+		if ! network_get_ipaddr ipaddr "$wanif"; then
+			proto_notify_error "$cfg" "NO_WAN_LINK"
+			return
+		fi
+	}
+
+	proto_init_update "$link" 1
+
+	[ -n "$ip6addr" ] && {
+		local local6="${ip6addr%%/*}"
+		local mask6="${ip6addr##*/}"
+		[ "$local6" = "$mask6" ] && mask6=
+		proto_add_ipv6_address "$local6" "$mask6"
+		proto_add_ipv6_route "::" 0 "" "" "" "$local6/$mask6"
+	}
+
+	for ip6prefix in $ip6prefixes; do
+		proto_add_ipv6_prefix "$ip6prefix"
+		proto_add_ipv6_route "::" 0 "" "" "" "$ip6prefix"
+	done
+
+	proto_add_tunnel
+	json_add_string mode sit
+	json_add_int mtu "${mtu:-1280}"
+	json_add_int ttl "${ttl:-64}"
+	[ -n "$tos" ] && json_add_string tos "$tos"
+	json_add_string local "$ipaddr"
+	json_add_string remote "$peeraddr"
+	[ -n "$tunlink" ] && json_add_string link "$tunlink"
+	proto_close_tunnel
+
+	proto_send_update "$cfg"
+
+	[ -n "$tunnelid" -a -n "$username" -a \( -n "$password" -o -n "$updatekey" \) ] && {
+		[ -n "$updatekey" ] && password="$updatekey"
+
+		local http="http"
+		local urlget="uclient-fetch"
+		local urlget_opts="-qO-"
+		local ca_path="${SSL_CERT_DIR:-/etc/ssl/certs}"
+
+		[ -f /lib/libustream-ssl.so ] && http=https
+		[ "$http" = "https" -a -z "$(find $ca_path -name "*.0" 2>/dev/null)" ] && {
+			urlget_opts="$urlget_opts --no-check-certificate"
+		}
+
+		local url="$http://ipv4.tunnelbroker.net/nic/update?hostname=$tunnelid"
+		
+		test_6in4_rfc1918 "$ipaddr" && {
+			local url="${url}&myip=${ipaddr}"
+		}
+
+		local try=0
+		local max=3
+
+		(
+			set -o pipefail
+			while [ $((++try)) -le $max ]; do
+				if proto_6in4_update $urlget $urlget_opts --user="$username" --password="$password" "$url" 2>&1 | \
+					sed -e 's,^Killed$,timeout,' -e "s,^,update $try/$max: ," | \
+					logger -t "$link";
+				then
+					logger -t "$link" "updated"
+					return 0
+				fi
+				sleep 5
+			done
+			logger -t "$link" "update failed"
+		)
+	}
+}
+
+proto_6in4_teardown() {
+	local cfg="$1"
+}
+
+proto_6in4_init_config() {
+	no_device=1
+	available=1
+
+	proto_config_add_string "ipaddr"
+	proto_config_add_string "ip6addr"
+	proto_config_add_array "ip6prefix"
+	proto_config_add_string "peeraddr"
+	proto_config_add_string "tunlink"
+	proto_config_add_string "tunnelid"
+	proto_config_add_string "username"
+	proto_config_add_string "password"
+	proto_config_add_string "updatekey"
+	proto_config_add_int "mtu"
+	proto_config_add_int "ttl"
+	proto_config_add_string "tos"
+	proto_config_add_string "device"
+	proto_config_add_boolean "nohostroute"
+}
+
+[ -n "$INCLUDE_ONLY" ] || {
+	add_protocol 6in4
+}
diff --git a/package/network/ipv6/6rd/Makefile b/package/network/ipv6/6rd/Makefile
new file mode 100644
index 0000000..3ab8198
--- /dev/null
+++ b/package/network/ipv6/6rd/Makefile
@@ -0,0 +1,48 @@
+#
+# Copyright (C) 2010-2012 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:=6rd
+PKG_RELEASE:=13
+PKG_LICENSE:=GPL-2.0
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/6rd
+  SECTION:=net
+  CATEGORY:=Network
+  DEPENDS:=@IPV6 +kmod-sit
+  TITLE:=6rd configuration support
+  MAINTAINER:=Steven Barth <cyrus@openwrt.org>
+  PKGARCH:=all
+endef
+
+define Package/6rd/description
+Provides support for 6rd tunnels in /etc/config/network.
+Refer to http://wiki.openwrt.org/doc/uci/network for
+configuration details.
+endef
+
+define Build/Configure
+endef
+
+define Build/Compile
+	$(MAKE) -C $(PKG_BUILD_DIR) \
+		CC="$(TARGET_CC)" \
+		CFLAGS="$(TARGET_CFLAGS) -Wall" \
+		LDFLAGS="$(TARGET_LDFLAGS)"
+endef
+
+define Package/6rd/install
+	$(INSTALL_DIR) $(1)/usr/sbin
+	$(INSTALL_BIN) $(PKG_BUILD_DIR)/6rdcalc $(1)/usr/sbin/
+	$(INSTALL_DIR) $(1)/lib/netifd/proto
+	$(INSTALL_BIN) ./files/6rd.sh $(1)/lib/netifd/proto/6rd.sh
+endef
+
+$(eval $(call BuildPackage,6rd))
diff --git a/package/network/ipv6/6rd/files/6rd.sh b/package/network/ipv6/6rd/files/6rd.sh
new file mode 100644
index 0000000..dad6111
--- /dev/null
+++ b/package/network/ipv6/6rd/files/6rd.sh
@@ -0,0 +1,106 @@
+#!/bin/sh
+# 6rd.sh - IPv6-in-IPv4 tunnel backend
+# Copyright (c) 2010-2012 OpenWrt.org
+
+[ -n "$INCLUDE_ONLY" ] || {
+	. /lib/functions.sh
+	. /lib/functions/network.sh
+	. ../netifd-proto.sh
+	init_proto "$@"
+}
+
+proto_6rd_setup() {
+	local cfg="$1"
+	local iface="$2"
+	local link="6rd-$cfg"
+
+	local mtu df ttl tos ipaddr peeraddr ip6prefix ip6prefixlen ip4prefixlen tunlink zone
+	json_get_vars mtu df ttl tos ipaddr peeraddr ip6prefix ip6prefixlen ip4prefixlen tunlink zone
+
+	[ -z "$ip6prefix" -o -z "$peeraddr" ] && {
+		proto_notify_error "$cfg" "MISSING_ADDRESS"
+		proto_block_restart "$cfg"
+		return
+	}
+
+	( proto_add_host_dependency "$cfg" "$peeraddr" "$tunlink" )
+
+	[ -z "$ipaddr" ] && {
+		local wanif="$tunlink"
+		if [ -z $wanif ] && ! network_find_wan wanif; then
+			proto_notify_error "$cfg" "NO_WAN_LINK"
+			return
+		fi
+
+		if ! network_get_ipaddr ipaddr "$wanif"; then
+			proto_notify_error "$cfg" "NO_WAN_LINK"
+			return
+		fi
+	}
+
+	# Determine the relay prefix.
+	local ip4prefixlen="${ip4prefixlen:-0}"
+	local ip4prefix IP PREFIX NETWORK NETMASK BROADCAST
+	ipcalc "$ipaddr/$ip4prefixlen" && ip4prefix="$NETWORK"
+
+	# Determine our IPv6 address.
+	local ip6subnet=$(6rdcalc "$ip6prefix/$ip6prefixlen" "$ipaddr/$ip4prefixlen")
+	local ip6addr="${ip6subnet%%::*}::1"
+
+	# Determine the IPv6 prefix
+	local ip6lanprefix="$ip6subnet/$(($ip6prefixlen + 32 - $ip4prefixlen))"
+
+	proto_init_update "$link" 1
+	proto_add_ipv6_address "$ip6addr" "$ip6prefixlen"
+	proto_add_ipv6_prefix "$ip6lanprefix"
+
+	proto_add_ipv6_route "::" 0 "::$peeraddr" 4096 "" "$ip6addr/$ip6prefixlen"
+	proto_add_ipv6_route "::" 0 "::$peeraddr" 4096 "" "$ip6lanprefix"
+
+	proto_add_tunnel
+	json_add_string mode sit
+	json_add_int mtu "${mtu:-1280}"
+	json_add_boolean df "${df:-1}"
+	json_add_int ttl "${ttl:-64}"
+	[ -n "$tos" ] && json_add_string tos "$tos"
+	json_add_string local "$ipaddr"
+	[ -n "$tunlink" ] && json_add_string link "$tunlink"
+
+	json_add_object 'data'
+	json_add_string prefix "$ip6prefix/$ip6prefixlen"
+	json_add_string relay-prefix "$ip4prefix/$ip4prefixlen"
+	json_close_object
+
+	proto_close_tunnel
+
+	proto_add_data
+	[ -n "$zone" ] && json_add_string zone "$zone"
+	proto_close_data
+
+	proto_send_update "$cfg"
+}
+
+proto_6rd_teardown() {
+	local cfg="$1"
+}
+
+proto_6rd_init_config() {
+	no_device=1
+	available=1
+
+	proto_config_add_int "mtu"
+	proto_config_add_boolean "df"
+	proto_config_add_int "ttl"
+	proto_config_add_string "tos"
+	proto_config_add_string "ipaddr"
+	proto_config_add_string "peeraddr"
+	proto_config_add_string "ip6prefix"
+	proto_config_add_string "ip6prefixlen"
+	proto_config_add_string "ip4prefixlen"
+	proto_config_add_string "tunlink"
+	proto_config_add_string "zone"
+}
+
+[ -n "$INCLUDE_ONLY" ] || {
+	add_protocol 6rd
+}
diff --git a/package/network/ipv6/6rd/src/6rdcalc.c b/package/network/ipv6/6rd/src/6rdcalc.c
new file mode 100644
index 0000000..87bc397
--- /dev/null
+++ b/package/network/ipv6/6rd/src/6rdcalc.c
@@ -0,0 +1,126 @@
+/*
+ * Utility used to calculate the 6rd subnet.
+ *
+ * Copyright 2012, Stéphan Kochen <stephan@kochen.nl>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/errno.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+#define INET_PREFIXSTRLEN (INET_ADDRSTRLEN+3)
+#define INET6_PREFIXSTRLEN (INET6_ADDRSTRLEN+4)
+
+static void print_usage()
+{
+	fprintf(stderr, "Usage: 6rdcalc <v6 prefix>/<mask> <v4 address>/<mask>\n");
+	exit(1);
+}
+
+static void print_error()
+{
+	fprintf(stderr, "%s", strerror(errno));
+	exit(1);
+}
+
+static void parse_str(int af, char *str, void *addr, unsigned long *mask)
+{
+	int ret;
+	char *slash;
+
+	/* Split the address at the slash. */
+	if ((slash = strchr(str, '/')) == NULL)
+		print_usage();
+	*slash = '\0';
+
+	/* Parse the address. */
+	if ((ret = inet_pton(af, str, addr)) != 1) {
+		if (ret == 0)
+			print_usage();
+		else
+			print_error();
+	}
+
+	/* Parse the mask. */
+	*mask = strtoul(slash+1, NULL, 10);
+	if ((af == AF_INET  && *mask >  32) ||
+		(af == AF_INET6 && *mask > 128))
+		print_usage();
+}
+
+int main(int argc, const char **argv)
+{
+	char v6str[INET6_PREFIXSTRLEN], v4str[INET_PREFIXSTRLEN];
+	struct in6_addr v6;
+	struct in_addr v4;
+	unsigned long v6it, v4it, mask;
+	unsigned char *byte4, *byte6;
+	unsigned char bit4, bit6;
+
+	/* Check parameters. */
+	if (argc != 3)
+		print_usage();
+
+	/* Parse the v6 address. */
+	strncpy(v6str, argv[1], INET6_PREFIXSTRLEN);
+	v6str[INET6_PREFIXSTRLEN-1] = '\0';
+	parse_str(AF_INET6, v6str, &v6, &v6it);
+
+	/* Parse the v4 address */
+	strncpy(v4str, argv[2], INET_PREFIXSTRLEN);
+	v6str[INET_PREFIXSTRLEN-1] = '\0';
+	parse_str(AF_INET, v4str, &v4, &v4it);
+
+	/* Check if the combined mask is within bounds. */
+	mask = (32 - v4it) + v6it;
+	if (mask > 128)
+		print_usage();
+
+	/* Combine the addresses. */
+	while (v4it < 32) {
+		byte6 = (unsigned char *)(&v6.s6_addr) + (v6it >> 3);
+		byte4 = (unsigned char *)(&v4.s_addr)  + (v4it >> 3);
+		bit6 = 128 >> (v6it & 0x07);
+		bit4 = 128 >> (v4it & 0x07);
+
+		if (*byte4 & bit4)
+			*byte6 |= bit6;
+		else
+			*byte6 &= ~bit6;
+
+		v4it++; v6it++;
+	}
+
+	/* Clear remaining bits. */
+	while (v6it < 128) {
+		byte6 = (unsigned char *)(&v6.s6_addr) + (v6it >> 3);
+		bit6 = 128 >> (v6it & 0x07);
+
+		*byte6 &= ~bit6;
+
+		v6it++;
+	}
+
+	/* Print the subnet prefix. */
+	if (inet_ntop(AF_INET6, &v6, v6str, sizeof(v6str)) == NULL)
+		print_error();
+	printf("%s/%lu\n", v6str, mask);
+	return 0;
+}
diff --git a/package/network/ipv6/6rd/src/Makefile b/package/network/ipv6/6rd/src/Makefile
new file mode 100644
index 0000000..2881d43
--- /dev/null
+++ b/package/network/ipv6/6rd/src/Makefile
@@ -0,0 +1,7 @@
+all: 6rdcalc
+
+6rdcalc: 6rdcalc.c
+	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
+
+clean:
+	rm -f 6rdcalc
diff --git a/package/network/ipv6/6to4/Makefile b/package/network/ipv6/6to4/Makefile
new file mode 100644
index 0000000..dfb66aa
--- /dev/null
+++ b/package/network/ipv6/6to4/Makefile
@@ -0,0 +1,42 @@
+#
+# Copyright (C) 2010-2012 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:=6to4
+PKG_RELEASE:=13
+PKG_LICENSE:=GPL-2.0
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/6to4
+  SECTION:=net
+  CATEGORY:=Network
+  DEPENDS:=@IPV6 +kmod-sit
+  TITLE:=IPv6-to-IPv4 configuration support
+  MAINTAINER:=Jo-Philipp Wich <xm@subsignal.org>
+  PKGARCH:=all
+endef
+
+define Package/6to4/description
+Provides support for 6to4 tunnels in /etc/config/network.
+Refer to https://openwrt.org/docs/guide-user/base-system/basic-networking
+configuration details.
+endef
+
+define Build/Compile
+endef
+
+define Build/Configure
+endef
+
+define Package/6to4/install
+	$(INSTALL_DIR) $(1)/lib/netifd/proto
+	$(INSTALL_BIN) ./files/6to4.sh $(1)/lib/netifd/proto/6to4.sh
+endef
+
+$(eval $(call BuildPackage,6to4))
diff --git a/package/network/ipv6/6to4/files/6to4.sh b/package/network/ipv6/6to4/files/6to4.sh
new file mode 100755
index 0000000..a5d0567
--- /dev/null
+++ b/package/network/ipv6/6to4/files/6to4.sh
@@ -0,0 +1,98 @@
+#!/bin/sh
+# 6to4.sh - IPv6-in-IPv4 tunnel backend
+# Copyright (c) 2010-2012 OpenWrt.org
+
+[ -n "$INCLUDE_ONLY" ] || {
+	. /lib/functions.sh
+	. /lib/functions/network.sh
+	. ../netifd-proto.sh
+	init_proto "$@"
+}
+
+find_6to4_prefix() {
+	local ip4="$1"
+	local oIFS="$IFS"; IFS="."; set -- $ip4; IFS="$oIFS"
+
+	printf "2002:%02x%02x:%02x%02x\n" $1 $2 $3 $4
+}
+
+test_6to4_rfc1918()
+{
+	local oIFS="$IFS"; IFS="."; set -- $1; IFS="$oIFS"
+	[ $1 -eq  10 ] && return 0
+	[ $1 -eq 192 ] && [ $2 -eq 168 ] && return 0
+	[ $1 -eq 172 ] && [ $2 -ge  16 ] && [ $2 -le  31 ] && return 0
+
+	# RFC 6598
+	[ $1 -eq 100 ] && [ $2 -ge  64 ] && [ $2 -le 127 ] && return 0
+
+	return 1
+}
+
+proto_6to4_setup() {
+	local cfg="$1"
+	local iface="$2"
+	local link="6to4-$cfg"
+
+	local mtu ttl tos ipaddr
+	json_get_vars mtu ttl tos ipaddr
+
+	( proto_add_host_dependency "$cfg" 0.0.0.0 )
+
+	local wanif
+	if ! network_find_wan wanif; then
+		proto_notify_error "$cfg" "NO_WAN_LINK"
+		return
+	fi
+
+	[ -z "$ipaddr" ] && {
+		if ! network_get_ipaddr ipaddr "$wanif"; then
+			proto_notify_error "$cfg" "NO_WAN_ADDRESS"
+			return
+		fi
+	}
+
+	test_6to4_rfc1918 "$ipaddr" && {
+		proto_notify_error "$cfg" "INVALID_LOCAL_ADDRESS"
+		return
+	}
+
+	# find our local prefix
+	local prefix6=$(find_6to4_prefix "$ipaddr")
+	local local6="$prefix6::1"
+
+	proto_init_update "$link" 1
+	proto_add_ipv6_address "$local6" 16
+	proto_add_ipv6_prefix "$prefix6::/48"
+
+	proto_add_ipv6_route "::" 0 "::192.88.99.1" "" "" "$local6/16"
+	proto_add_ipv6_route "::" 0 "::192.88.99.1" "" "" "$prefix6::/48"
+
+	proto_add_tunnel
+	json_add_string mode sit
+	json_add_int mtu "${mtu:-1280}"
+	json_add_int ttl "${ttl:-64}"
+	[ -n "$tos" ] && json_add_string tos "$tos"
+	json_add_string local "$ipaddr"
+	proto_close_tunnel
+
+	proto_send_update "$cfg"
+}
+
+proto_6to4_teardown() {
+	local cfg="$1"
+}
+
+proto_6to4_init_config() {
+	no_device=1
+	available=1
+
+	proto_config_add_string "ipaddr"
+	proto_config_add_int "mtu"
+	proto_config_add_int "ttl"
+	proto_config_add_string "tos"
+}
+
+[ -n "$INCLUDE_ONLY" ] || {
+	add_protocol 6to4
+}
diff --git a/package/network/ipv6/ds-lite/Makefile b/package/network/ipv6/ds-lite/Makefile
new file mode 100644
index 0000000..37c3449
--- /dev/null
+++ b/package/network/ipv6/ds-lite/Makefile
@@ -0,0 +1,42 @@
+#
+# Copyright (C) 2013 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:=ds-lite
+PKG_RELEASE:=9
+PKG_LICENSE:=GPL-2.0
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/ds-lite
+  SECTION:=net
+  CATEGORY:=Network
+  DEPENDS:=@IPV6 +kmod-ip6-tunnel +resolveip
+  TITLE:=IPv4 over IPv6 (RFC2473 and DS-Lite) configuration support
+  MAINTAINER:=Steven Barth <steven@midlink.org>
+  PKGARCH:=all
+endef
+
+define Package/ds-lite/description
+Provides support for IPv4 over IPv6 (RFC2473 and DS-Lite) in /etc/config/network.
+Refer to http://wiki.openwrt.org/doc/uci/network for
+configuration details.
+endef
+
+define Build/Compile
+endef
+
+define Build/Configure
+endef
+
+define Package/ds-lite/install
+	$(INSTALL_DIR) $(1)/lib/netifd/proto
+	$(INSTALL_BIN) ./files/dslite.sh $(1)/lib/netifd/proto/dslite.sh
+endef
+
+$(eval $(call BuildPackage,ds-lite))
diff --git a/package/network/ipv6/ds-lite/files/dslite.sh b/package/network/ipv6/ds-lite/files/dslite.sh
new file mode 100644
index 0000000..325401b
--- /dev/null
+++ b/package/network/ipv6/ds-lite/files/dslite.sh
@@ -0,0 +1,143 @@
+#!/bin/sh
+# dslite.sh - IPv4-in-IPv6 tunnel backend for ipip6 and ds-lite
+# Copyright (c) 2013 OpenWrt.org
+# Copyright (c) 2013 Steven Barth <steven@midlink.org>
+# Copyright (c) 2021 Kenji Uno <ku@digitaldolphins.jp>
+# Copyright (c) 2024 Arayuki Mago <ms@missing233.com>
+
+[ -n "$INCLUDE_ONLY" ] || {
+	. /lib/functions.sh
+	. /lib/functions/network.sh
+	. ../netifd-proto.sh
+	init_proto "$@"
+}
+
+tnl_setup() {
+	local cfg="$1"
+	local iface="$2"
+	local tnl_type="$3"
+	local ip4addr="$4"
+	local ip4gateway="$5"
+	local link="$tnl_type-$cfg"
+	local remoteip6
+
+	local mtu ttl peeraddr ip6addr tunlink zone weakif encaplimit
+	json_get_vars mtu ttl peeraddr ip6addr tunlink zone weakif encaplimit
+
+	[ -z "$peeraddr" ] && {
+		proto_notify_error "$cfg" "MISSING_ADDRESS"
+		proto_block_restart "$cfg"
+		return
+	}
+
+	( proto_add_host_dependency "$cfg" "::" "$tunlink" )
+
+	remoteip6=$(resolveip -6 "$peeraddr")
+	if [ -z "$remoteip6" ]; then
+		sleep 3
+		remoteip6=$(resolveip -6 "$peeraddr")
+		if [ -z "$remoteip6" ]; then
+			proto_notify_error "$cfg" "AFTR_DNS_FAIL"
+			return
+		fi
+	fi
+
+	for ip6 in $remoteip6; do
+		peeraddr=$ip6
+		break
+	done
+
+	[ -z "$ip6addr" ] && {
+		local wanif="$tunlink"
+		if [ -z "$wanif" ] && ! network_find_wan6 wanif; then
+			proto_notify_error "$cfg" "NO_WAN_LINK"
+			return
+		fi
+
+		if ! network_get_ipaddr6 ip6addr "$wanif"; then
+			[ -z "$weakif" ] && weakif="lan"
+			if ! network_get_ipaddr6 ip6addr "$weakif"; then
+				proto_notify_error "$cfg" "NO_WAN_LINK"
+				return
+			fi
+		fi
+	}
+
+	proto_init_update "$link" 1
+	proto_add_ipv4_route "0.0.0.0" 0
+	proto_add_ipv4_address "$ip4addr" "" "" "$ip4gateway"
+
+	proto_add_tunnel
+	json_add_string mode ipip6
+	json_add_int mtu "${mtu:-1280}"
+	json_add_int ttl "${ttl:-64}"
+	json_add_string local "$ip6addr"
+	json_add_string remote "$peeraddr"
+	[ -n "$tunlink" ] && json_add_string link "$tunlink"
+	json_add_object "data"
+	  [ -n "$encaplimit" ] && json_add_string encaplimit "$encaplimit"
+	json_close_object
+	proto_close_tunnel
+
+	proto_add_data
+	[ -n "$zone" ] && json_add_string zone "$zone"
+
+	if [ "$tnl_type" = "ds" ]; then
+		json_add_array firewall
+			json_add_object ""
+				json_add_string type nat
+				json_add_string target ACCEPT
+			json_close_object
+		json_close_array
+	fi
+
+	proto_close_data
+
+	proto_send_update "$cfg"
+}
+
+init_config() {
+	no_device=1
+	available=1
+
+	proto_config_add_string "ip6addr"
+	proto_config_add_string "peeraddr"
+	proto_config_add_string "tunlink"
+	proto_config_add_int "mtu"
+	proto_config_add_int "ttl"
+	proto_config_add_string "encaplimit"
+	proto_config_add_string "zone"
+	proto_config_add_string "weakif"
+}
+
+proto_ipip6_init_config() {
+	init_config
+	proto_config_add_string "ip4ifaddr"
+}
+
+proto_ipip6_setup() {
+	local ip4ifaddr
+	json_get_vars ip4ifaddr
+	tnl_setup "$1" "$2" "ipip6" "$ip4ifaddr" "0.0.0.0"
+}
+
+proto_ipip6_teardown() {
+	local cfg="$1"
+}
+
+proto_dslite_init_config() {
+	init_config
+}
+
+proto_dslite_setup() {
+	tnl_setup "$1" "$2" "ds" "192.0.0.2" "192.0.0.1"
+}
+
+proto_dslite_teardown() {
+	local cfg="$1"
+}
+
+[ -n "$INCLUDE_ONLY" ] || {
+	add_protocol ipip6
+	add_protocol dslite
+}
diff --git a/package/network/ipv6/map/Makefile b/package/network/ipv6/map/Makefile
new file mode 100644
index 0000000..f73b5ee
--- /dev/null
+++ b/package/network/ipv6/map/Makefile
@@ -0,0 +1,40 @@
+#
+# Copyright (C) 2014-2015 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:=map
+PKG_RELEASE:=7
+PKG_LICENSE:=GPL-2.0
+
+include $(INCLUDE_DIR)/package.mk
+include $(INCLUDE_DIR)/cmake.mk
+
+define Package/map
+  SECTION:=net
+  CATEGORY:=Network
+  DEPENDS:=@IPV6 +kmod-ip6-tunnel +libubox +libubus +iptables-mod-conntrack-extra +kmod-nat46
+  TITLE:=MAP-E/MAP-T and Lightweight 4over6 configuration support
+  MAINTAINER:=Hans Dedecker <dedeckeh@gmail.com>
+  PROVIDES:=map-t
+endef
+
+define Package/map/description
+ Provides support for MAP-E (RFC7597), MAP-T (RFC7599) and
+ Lightweight 4over6 (RFC7596) in /etc/config/network.
+ MAP combines address and port translation with the tunneling
+ of IPv4 packets over an IPv6 network
+endef
+
+define Package/map/install
+	$(INSTALL_DIR) $(1)/lib/netifd/proto
+	$(INSTALL_BIN) ./files/map.sh $(1)/lib/netifd/proto/map.sh
+	$(INSTALL_DIR) $(1)/usr/sbin
+	$(INSTALL_BIN) $(PKG_BUILD_DIR)/mapcalc $(1)/usr/sbin/
+endef
+
+$(eval $(call BuildPackage,map))
diff --git a/package/network/ipv6/map/files/map.sh b/package/network/ipv6/map/files/map.sh
new file mode 100755
index 0000000..652c306
--- /dev/null
+++ b/package/network/ipv6/map/files/map.sh
@@ -0,0 +1,245 @@
+#!/bin/sh
+# map.sh - IPv4-in-IPv6 tunnel backend
+#
+# Author: Steven Barth <cyrus@openwrt.org>
+# Copyright (c) 2014 cisco Systems, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2
+# as published by the Free Software Foundation
+#
+# 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.
+
+[ -n "$INCLUDE_ONLY" ] || {
+	. /lib/functions.sh
+	. /lib/functions/network.sh
+	. ../netifd-proto.sh
+	init_proto "$@"
+}
+
+proto_map_setup() {
+	local cfg="$1"
+	local iface="$2"
+	local link="map-$cfg"
+
+	local maptype type legacymap mtu ttl tunlink zone encaplimit
+	local rule ipaddr ip4prefixlen ip6prefix ip6prefixlen peeraddr ealen psidlen psid offset
+	json_get_vars maptype type legacymap mtu ttl tunlink zone encaplimit
+	json_get_vars rule ipaddr ip4prefixlen ip6prefix ip6prefixlen peeraddr ealen psidlen psid offset
+
+	[ "$zone" = "-" ] && zone=""
+
+	# Compatibility with older config: use $type if $maptype is missing
+	[ -z "$maptype" ] && maptype="$type"
+	[ -z "$maptype" ] && maptype="map-e"
+	[ -z "$ip4prefixlen" ] && ip4prefixlen=32
+
+	( proto_add_host_dependency "$cfg" "::" "$tunlink" )
+
+	# fixme: handle RA/DHCPv6 address race for LW
+	[ "$maptype" = lw4o6 ] && sleep 5
+
+	if [ -z "$rule" ]; then
+		rule="type=$maptype,ipv6prefix=$ip6prefix,prefix6len=$ip6prefixlen,ipv4prefix=$ipaddr,prefix4len=$ip4prefixlen"
+		[ -n "$psid" ] && rule="$rule,psid=$psid"
+		[ -n "$psidlen" ] && rule="$rule,psidlen=$psidlen"
+		[ -n "$offset" ] && rule="$rule,offset=$offset"
+		[ -n "$ealen" ] && rule="$rule,ealen=$ealen"
+		if [ "$maptype" = "map-t" ]; then
+			rule="$rule,dmr=$peeraddr"
+		else
+			rule="$rule,br=$peeraddr"
+		fi
+	fi
+
+	echo "rule=$rule" > /tmp/map-$cfg.rules
+	RULE_DATA=$(LEGACY="$legacymap" mapcalc ${tunlink:-\*} $rule)
+	if [ "$?" != 0 ]; then
+		proto_notify_error "$cfg" "INVALID_MAP_RULE"
+		proto_block_restart "$cfg"
+		return
+	fi
+
+	echo "$RULE_DATA" >> /tmp/map-$cfg.rules
+	eval $RULE_DATA
+
+	if [ -z "$RULE_BMR" ]; then
+		proto_notify_error "$cfg" "NO_MATCHING_PD"
+		proto_block_restart "$cfg"
+		return
+	fi
+
+	k=$RULE_BMR
+	if [ "$maptype" = "lw4o6" -o "$maptype" = "map-e" ]; then
+		proto_init_update "$link" 1
+		proto_add_ipv4_address $(eval "echo \$RULE_${k}_IPV4ADDR") "" "" ""
+
+		proto_add_tunnel
+		json_add_string mode ipip6
+		json_add_int mtu "${mtu:-1280}"
+		json_add_int ttl "${ttl:-64}"
+		json_add_string local $(eval "echo \$RULE_${k}_IPV6ADDR")
+		json_add_string remote $(eval "echo \$RULE_${k}_BR")
+		json_add_string link $(eval "echo \$RULE_${k}_PD6IFACE")
+		json_add_object "data"
+			[ -n "$encaplimit" ] && json_add_string encaplimit "$encaplimit"
+			if [ "$maptype" = "map-e" ]; then
+				json_add_array "fmrs"
+				for i in $(seq $RULE_COUNT); do
+					[ "$(eval "echo \$RULE_${i}_FMR")" != 1 ] && continue
+					json_add_object ""
+					json_add_string prefix6 "$(eval "echo \$RULE_${i}_IPV6PREFIX")/$(eval "echo \$RULE_${i}_PREFIX6LEN")"
+					json_add_string prefix4 "$(eval "echo \$RULE_${i}_IPV4PREFIX")/$(eval "echo \$RULE_${i}_PREFIX4LEN")"
+					json_add_int ealen $(eval "echo \$RULE_${i}_EALEN")
+					json_add_int offset $(eval "echo \$RULE_${i}_OFFSET")
+					json_close_object
+				done
+				json_close_array
+			fi
+		json_close_object
+
+
+		proto_close_tunnel
+	elif [ "$maptype" = "map-t" -a -f "/proc/net/nat46/control" ]; then
+		proto_init_update "$link" 1
+		local style="MAP"
+		[ "$legacymap" = 1 ] && style="MAP0"
+
+		echo add $link > /proc/net/nat46/control
+		local cfgstr="local.style $style local.v4 $(eval "echo \$RULE_${k}_IPV4PREFIX")/$(eval "echo \$RULE_${k}_PREFIX4LEN")"
+		cfgstr="$cfgstr local.v6 $(eval "echo \$RULE_${k}_IPV6PREFIX")/$(eval "echo \$RULE_${k}_PREFIX6LEN")"
+		cfgstr="$cfgstr local.ea-len $(eval "echo \$RULE_${k}_EALEN") local.psid-offset $(eval "echo \$RULE_${k}_OFFSET")"
+		cfgstr="$cfgstr remote.v4 0.0.0.0/0 remote.v6 $(eval "echo \$RULE_${k}_DMR") remote.style RFC6052 remote.ea-len 0 remote.psid-offset 0"
+		echo config $link $cfgstr > /proc/net/nat46/control
+
+		for i in $(seq $RULE_COUNT); do
+			[ "$(eval "echo \$RULE_${i}_FMR")" != 1 ] && continue
+			local cfgstr="remote.style $style remote.v4 $(eval "echo \$RULE_${i}_IPV4PREFIX")/$(eval "echo \$RULE_${i}_PREFIX4LEN")"
+			cfgstr="$cfgstr remote.v6 $(eval "echo \$RULE_${i}_IPV6PREFIX")/$(eval "echo \$RULE_${i}_PREFIX6LEN")"
+			cfgstr="$cfgstr remote.ea-len $(eval "echo \$RULE_${i}_EALEN") remote.psid-offset $(eval "echo \$RULE_${i}_OFFSET")"
+			echo insert $link $cfgstr > /proc/net/nat46/control
+		done
+	else
+		proto_notify_error "$cfg" "UNSUPPORTED_TYPE"
+		proto_block_restart "$cfg"
+	fi
+
+	proto_add_ipv4_route "0.0.0.0" 0
+	proto_add_data
+	[ -n "$zone" ] && json_add_string zone "$zone"
+
+	json_add_array firewall
+	  if [ -z "$(eval "echo \$RULE_${k}_PORTSETS")" ]; then
+	    json_add_object ""
+	      json_add_string type nat
+	      json_add_string target SNAT
+	      json_add_string family inet
+	      json_add_string snat_ip $(eval "echo \$RULE_${k}_IPV4ADDR")
+	    json_close_object
+	  else
+	    for portset in $(eval "echo \$RULE_${k}_PORTSETS"); do
+              for proto in icmp tcp udp; do
+	        json_add_object ""
+	          json_add_string type nat
+	          json_add_string target SNAT
+	          json_add_string family inet
+	          json_add_string proto "$proto"
+                  json_add_boolean connlimit_ports 1
+                  json_add_string snat_ip $(eval "echo \$RULE_${k}_IPV4ADDR")
+                  json_add_string snat_port "$portset"
+	        json_close_object
+              done
+	    done
+	  fi
+	  if [ "$maptype" = "map-t" ]; then
+		[ -z "$zone" ] && zone=$(fw3 -q network $iface 2>/dev/null)
+
+		[ -n "$zone" ] && {
+			json_add_object ""
+				json_add_string type rule
+				json_add_string family inet6
+				json_add_string proto all
+				json_add_string direction in
+				json_add_string dest "$zone"
+				json_add_string src "$zone"
+				json_add_string src_ip $(eval "echo \$RULE_${k}_IPV6ADDR")
+				json_add_string target ACCEPT
+			json_close_object
+			json_add_object ""
+				json_add_string type rule
+				json_add_string family inet6
+				json_add_string proto all
+				json_add_string direction out
+				json_add_string dest "$zone"
+				json_add_string src "$zone"
+				json_add_string dest_ip $(eval "echo \$RULE_${k}_IPV6ADDR")
+				json_add_string target ACCEPT
+			json_close_object
+		}
+		proto_add_ipv6_route $(eval "echo \$RULE_${k}_IPV6ADDR") 128
+	  fi
+	json_close_array
+	proto_close_data
+
+	proto_send_update "$cfg"
+
+	if [ "$maptype" = "lw4o6" -o "$maptype" = "map-e" ]; then
+		json_init
+		json_add_string name "${cfg}_"
+		json_add_string ifname "@$(eval "echo \$RULE_${k}_PD6IFACE")"
+		json_add_string proto "static"
+		json_add_array ip6addr
+		json_add_string "" "$(eval "echo \$RULE_${k}_IPV6ADDR")"
+		json_close_array
+		json_close_object
+		ubus call network add_dynamic "$(json_dump)"
+	fi
+}
+
+proto_map_teardown() {
+	local cfg="$1"
+	local link="map-$cfg"
+
+	json_get_var type type
+
+	[ -z "$maptype" ] && maptype="$type"
+	[ -z "$maptype" ] && maptype="map-e"
+
+	case "$maptype" in
+		"map-e"|"lw4o6") ifdown "${cfg}_" ;;
+		"map-t") [ -f "/proc/net/nat46/control" ] && echo del $link > /proc/net/nat46/control ;;
+	esac
+
+	rm -f /tmp/map-$cfg.rules
+}
+
+proto_map_init_config() {
+	no_device=1
+	available=1
+
+	proto_config_add_string "maptype"
+	proto_config_add_string "rule"
+	proto_config_add_string "ipaddr"
+	proto_config_add_int "ip4prefixlen"
+	proto_config_add_string "ip6prefix"
+	proto_config_add_int "ip6prefixlen"
+	proto_config_add_string "peeraddr"
+	proto_config_add_int "ealen"
+	proto_config_add_int "psidlen"
+	proto_config_add_int "psid"
+	proto_config_add_int "offset"
+	proto_config_add_boolean "legacymap"
+
+	proto_config_add_string "tunlink"
+	proto_config_add_int "mtu"
+	proto_config_add_int "ttl"
+	proto_config_add_string "zone"
+	proto_config_add_string "encaplimit"
+}
+
+[ -n "$INCLUDE_ONLY" ] || {
+        add_protocol map
+}
diff --git a/package/network/ipv6/map/src/CMakeLists.txt b/package/network/ipv6/map/src/CMakeLists.txt
new file mode 100644
index 0000000..a839373
--- /dev/null
+++ b/package/network/ipv6/map/src/CMakeLists.txt
@@ -0,0 +1,29 @@
+cmake_minimum_required(VERSION 2.8.1)
+
+project(mapcalc C)
+
+set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -std=c99")
+
+FIND_PATH(ubus_include_dir libubus.h)
+INCLUDE_DIRECTORIES(${ubus_include_dir})
+
+add_definitions(-D_GNU_SOURCE -Wall -Wno-gnu -Wextra)
+
+add_executable(mapcalc mapcalc.c)
+target_link_libraries(mapcalc ubus ubox)
+
+install(TARGETS mapcalc DESTINATION sbin/)
+
+
+# Packaging rules
+set(CPACK_PACKAGE_VERSION "1")
+set(CPACK_PACKAGE_CONTACT "Steven Barth <steven@midlink.org>")
+set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "hnetd")
+set(CPACK_GENERATOR "DEB;RPM;STGZ")
+set(CPACK_STRIP_FILES true)
+
+SET(CPACK_DEBIAN_PACKAGE_VERSION ${CPACK_PACKAGE_VERSION})
+set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}_${CPACK_DEBIAN_PACKAGE_VERSION}")
+
+include(CPack)
diff --git a/package/network/ipv6/map/src/mapcalc.c b/package/network/ipv6/map/src/mapcalc.c
new file mode 100644
index 0000000..66610c4
--- /dev/null
+++ b/package/network/ipv6/map/src/mapcalc.c
@@ -0,0 +1,418 @@
+/*
+ * mapcalc - MAP parameter calculation
+ *
+ * Author: Steven Barth <cyrus@openwrt.org>
+ * Copyright (c) 2014-2015 cisco Systems, Inc.
+ * Copyright (c) 2015 Steven Barth <cyrus@openwrt.org>
+ * Copyright (c) 2018 Hans Dedecker <dedeckeh@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <libubus.h>
+#include <libubox/utils.h>
+
+
+struct blob_attr *dump = NULL;
+
+enum {
+	DUMP_ATTR_INTERFACE,
+	DUMP_ATTR_MAX
+};
+
+static const struct blobmsg_policy dump_attrs[DUMP_ATTR_MAX] = {
+	[DUMP_ATTR_INTERFACE] = { .name = "interface", .type = BLOBMSG_TYPE_ARRAY },
+};
+
+
+enum {
+	IFACE_ATTR_INTERFACE,
+	IFACE_ATTR_PREFIX,
+	IFACE_ATTR_ADDRESS,
+	IFACE_ATTR_MAX,
+};
+
+static const struct blobmsg_policy iface_attrs[IFACE_ATTR_MAX] = {
+	[IFACE_ATTR_INTERFACE] = { .name = "interface", .type = BLOBMSG_TYPE_STRING },
+	[IFACE_ATTR_PREFIX] = { .name = "ipv6-prefix", .type = BLOBMSG_TYPE_ARRAY },
+	[IFACE_ATTR_ADDRESS] = { .name = "ipv6-address", .type = BLOBMSG_TYPE_ARRAY },
+};
+
+
+enum {
+	PREFIX_ATTR_ADDRESS,
+	PREFIX_ATTR_MASK,
+	PREFIX_ATTR_MAX,
+};
+
+static const struct blobmsg_policy prefix_attrs[PREFIX_ATTR_MAX] = {
+	[PREFIX_ATTR_ADDRESS] = { .name = "address", .type = BLOBMSG_TYPE_STRING },
+	[PREFIX_ATTR_MASK] = { .name = "mask", .type = BLOBMSG_TYPE_INT32 },
+};
+
+static int bmemcmp(const void *av, const void *bv, size_t bits)
+{
+	const uint8_t *a = av, *b = bv;
+	size_t bytes = bits / 8;
+	bits %= 8;
+
+	int res = memcmp(a, b, bytes);
+	if (res == 0 && bits > 0)
+		res = (a[bytes] >> (8 - bits)) - (b[bytes] >> (8 - bits));
+
+	return res;
+}
+
+static void bmemcpy(void *av, const void *bv, size_t bits)
+{
+	uint8_t *a = av;
+	const uint8_t *b = bv;
+
+	size_t bytes = bits / 8;
+	bits %= 8;
+	memcpy(a, b, bytes);
+
+	if (bits > 0) {
+		uint8_t mask = (1 << (8 - bits)) - 1;
+		a[bytes] = (a[bytes] & mask) | ((~mask) & b[bytes]);
+	}
+}
+
+static void bmemcpys64(void *av, const void *bv, size_t frombits, size_t nbits)
+{
+	uint64_t buf = 0;
+	const uint8_t *b = bv;
+	size_t frombyte = frombits / 8, tobyte = (frombits + nbits) / 8;
+
+	memcpy(&buf, &b[frombyte], tobyte - frombyte + 1);
+	buf = cpu_to_be64(be64_to_cpu(buf) << (frombits % 8));
+
+	bmemcpy(av, &buf, nbits);
+}
+
+static void handle_dump(struct ubus_request *req __attribute__((unused)),
+		int type __attribute__((unused)), struct blob_attr *msg)
+{
+	struct blob_attr *tb[DUMP_ATTR_INTERFACE];
+	blobmsg_parse(dump_attrs, DUMP_ATTR_MAX, tb, blob_data(msg), blob_len(msg));
+
+	if (!tb[DUMP_ATTR_INTERFACE])
+		return;
+
+	dump = blob_memdup(tb[DUMP_ATTR_INTERFACE]);
+}
+
+static void match_prefix(int *pdlen, struct in6_addr *pd, struct blob_attr *cur,
+		const struct in6_addr *ipv6prefix, int prefix6len, bool lw4o6)
+{
+	struct blob_attr *d;
+	unsigned drem;
+
+	if (!cur || blobmsg_type(cur) != BLOBMSG_TYPE_ARRAY || !blobmsg_check_attr(cur, false))
+		return;
+
+	blobmsg_for_each_attr(d, cur, drem) {
+		struct blob_attr *ptb[PREFIX_ATTR_MAX];
+		blobmsg_parse(prefix_attrs, PREFIX_ATTR_MAX, ptb,
+				blobmsg_data(d), blobmsg_data_len(d));
+
+		if (!ptb[PREFIX_ATTR_ADDRESS] || !ptb[PREFIX_ATTR_MASK])
+			continue;
+
+		struct in6_addr prefix = IN6ADDR_ANY_INIT;
+		int mask = blobmsg_get_u32(ptb[PREFIX_ATTR_MASK]);
+		inet_pton(AF_INET6, blobmsg_get_string(ptb[PREFIX_ATTR_ADDRESS]), &prefix);
+
+		// lw4over6 /128-address-as-PD matching madness workaround
+		if (lw4o6 && mask == 128)
+			mask = 64;
+
+		if (*pdlen < mask && mask >= prefix6len &&
+				!bmemcmp(&prefix, ipv6prefix, prefix6len)) {
+			bmemcpy(pd, &prefix, mask);
+			*pdlen = mask;
+		} else if (lw4o6 && *pdlen < prefix6len && mask < prefix6len &&
+				!bmemcmp(&prefix, ipv6prefix, mask)) {
+			bmemcpy(pd, ipv6prefix, prefix6len);
+			*pdlen = prefix6len;
+		}
+	}
+}
+
+enum {
+	OPT_TYPE,
+	OPT_FMR,
+	OPT_EALEN,
+	OPT_PREFIX4LEN,
+	OPT_PREFIX6LEN,
+	OPT_IPV6PREFIX,
+	OPT_IPV4PREFIX,
+	OPT_OFFSET,
+	OPT_PSIDLEN,
+	OPT_PSID,
+	OPT_BR,
+	OPT_DMR,
+	OPT_PD,
+	OPT_PDLEN,
+	OPT_MAX
+};
+
+static char *const token[] = {
+	[OPT_TYPE] = "type",
+	[OPT_FMR] = "fmr",
+	[OPT_EALEN] = "ealen",
+	[OPT_PREFIX4LEN] = "prefix4len",
+	[OPT_PREFIX6LEN] = "prefix6len",
+	[OPT_IPV6PREFIX] = "ipv6prefix",
+	[OPT_IPV4PREFIX] = "ipv4prefix",
+	[OPT_OFFSET] = "offset",
+	[OPT_PSIDLEN] = "psidlen",
+	[OPT_PSID] = "psid",
+	[OPT_BR] = "br",
+	[OPT_DMR] = "dmr",
+	[OPT_PD] = "pd",
+	[OPT_PDLEN] = "pdlen",
+	[OPT_MAX] = NULL
+};
+
+
+int main(int argc, char *argv[])
+{
+	int status = 0;
+	const char *iface = argv[1];
+
+	const char *legacy_env = getenv("LEGACY");
+	bool legacy = legacy_env && atoi(legacy_env);
+
+
+	if (argc < 3) {
+		fprintf(stderr, "Usage: %s <interface|*> <rule1> [rule2] [...]\n", argv[0]);
+		return 1;
+	}
+
+	uint32_t network_interface;
+	struct ubus_context *ubus = ubus_connect(NULL);
+	if (ubus) {
+		ubus_lookup_id(ubus, "network.interface", &network_interface);
+		ubus_invoke(ubus, network_interface, "dump", NULL, handle_dump, NULL, 5000);
+	}
+
+	int rulecnt = 0;
+	for (int i = 2; i < argc; ++i) {
+		bool lw4o6 = false;
+		bool fmr = false;
+		int ealen = -1;
+		int addr4len = 32;
+		int prefix4len = 32;
+		int prefix6len = -1;
+		int pdlen = -1;
+		struct in_addr ipv4prefix = {INADDR_ANY};
+		struct in_addr ipv4addr = {INADDR_ANY};
+		struct in6_addr ipv6addr = IN6ADDR_ANY_INIT;
+		struct in6_addr ipv6prefix = IN6ADDR_ANY_INIT;
+		struct in6_addr pd = IN6ADDR_ANY_INIT;
+		int offset = -1;
+		int psidlen = -1;
+		int psid = -1;
+		uint16_t psid16 = 0;
+		const char *dmr = NULL;
+		const char *br = NULL;
+
+		for (char *rule = strdup(argv[i]); *rule; ) {
+			char *value;
+			int intval;
+			int idx = getsubopt(&rule, token, &value);
+			errno = 0;
+
+			if (idx == OPT_TYPE) {
+				lw4o6 = (value && !strcmp(value, "lw4o6"));
+			} else if (idx == OPT_FMR) {
+				fmr = true;
+			} else if (idx == OPT_EALEN && (intval = strtoul(value, NULL, 0)) <= 48 && !errno) {
+				ealen = intval;
+			} else if (idx == OPT_PREFIX4LEN && (intval = strtoul(value, NULL, 0)) <= 32 && !errno) {
+				prefix4len = intval;
+			} else if (idx == OPT_PREFIX6LEN && (intval = strtoul(value, NULL, 0)) <= 128 && !errno) {
+				prefix6len = intval;
+			} else if (idx == OPT_IPV4PREFIX && inet_pton(AF_INET, value, &ipv4prefix) == 1) {
+				// dummy
+			} else if (idx == OPT_IPV6PREFIX && inet_pton(AF_INET6, value, &ipv6prefix) == 1) {
+				// dummy
+			} else if (idx == OPT_PD && inet_pton(AF_INET6, value, &pd) == 1) {
+				// dummy
+			} else if (idx == OPT_OFFSET && (intval = strtoul(value, NULL, 0)) <= 16 && !errno) {
+				offset = intval;
+			} else if (idx == OPT_PSIDLEN && (intval = strtoul(value, NULL, 0)) <= 16 && !errno) {
+				psidlen = intval;
+			} else if (idx == OPT_PDLEN && (intval = strtoul(value, NULL, 0)) <= 128 && !errno) {
+				pdlen = intval;
+			} else if (idx == OPT_PSID && (intval = strtoul(value, NULL, 0)) <= 65535 && !errno) {
+				psid = intval;
+			} else if (idx == OPT_DMR) {
+				dmr = value;
+			} else if (idx == OPT_BR) {
+				br = value;
+			} else {
+				if (idx == -1 || idx >= OPT_MAX)
+					fprintf(stderr, "Skipped invalid option: %s\n", value);
+				else
+					fprintf(stderr, "Skipped invalid value %s for option %s\n",
+							value, token[idx]);
+			}
+		}
+
+		if (offset < 0)
+			offset = (lw4o6) ? 0 : (legacy) ? 4 : 6;
+
+		// LW4over6 doesn't have an EALEN and has no psid-autodetect
+		if (lw4o6) {
+			if (psidlen < 0)
+				psidlen = 0;
+
+			ealen = psidlen;
+		}
+
+		// Find PD
+		if (pdlen < 0) {
+			struct blob_attr *c;
+			unsigned rem;
+			blobmsg_for_each_attr(c, dump, rem) {
+				struct blob_attr *tb[IFACE_ATTR_MAX];
+				blobmsg_parse(iface_attrs, IFACE_ATTR_MAX, tb, blobmsg_data(c), blobmsg_data_len(c));
+
+				if (!tb[IFACE_ATTR_INTERFACE] || (strcmp(argv[1], "*") && strcmp(argv[1],
+						blobmsg_get_string(tb[IFACE_ATTR_INTERFACE]))))
+					continue;
+
+				match_prefix(&pdlen, &pd, tb[IFACE_ATTR_PREFIX], &ipv6prefix, prefix6len, lw4o6);
+
+				if (lw4o6)
+					match_prefix(&pdlen, &pd, tb[IFACE_ATTR_ADDRESS], &ipv6prefix, prefix6len, lw4o6);
+
+				if (pdlen >= 0) {
+					iface = blobmsg_get_string(tb[IFACE_ATTR_INTERFACE]);
+					break;
+				}
+			}
+		}
+
+		if (ealen < 0 && pdlen >= 0)
+			ealen = pdlen - prefix6len;
+
+		if (psidlen <= 0) {
+			psidlen = ealen - (32 - prefix4len);
+			if (psidlen < 0)
+				psidlen = 0;
+
+			psid = -1;
+		}
+
+		if (prefix4len < 0 || prefix6len < 0 || ealen < 0 || psidlen > 16 || ealen < psidlen) {
+			fprintf(stderr, "Skipping invalid or incomplete rule: %s\n", argv[i]);
+			status = 1;
+			continue;
+		}
+
+		if (psid < 0 && psidlen >= 0 && pdlen >= 0) {
+			bmemcpys64(&psid16, &pd, prefix6len + ealen - psidlen, psidlen);
+			psid = be16_to_cpu(psid16);
+		}
+
+		if (psidlen > 0) {
+			psid = psid >> (16 - psidlen);
+			psid16 = cpu_to_be16(psid);
+			psid = psid << (16 - psidlen);
+		}
+
+		if (pdlen >= 0 || ealen == psidlen) {
+			bmemcpys64(&ipv4addr, &pd, prefix6len, ealen - psidlen);
+			ipv4addr.s_addr = htonl(ntohl(ipv4addr.s_addr) >> prefix4len);
+			bmemcpy(&ipv4addr, &ipv4prefix, prefix4len);
+
+			if (prefix4len + ealen < 32)
+				addr4len = prefix4len + ealen;
+		}
+
+		if (pdlen < 0 && !fmr) {
+			fprintf(stderr, "Skipping non-FMR without matching PD: %s\n", argv[i]);
+			status = 1;
+			continue;
+		} else if (pdlen >= 0) {
+			size_t v4offset = (legacy) ? 9 : 10;
+			memcpy(&ipv6addr.s6_addr[v4offset], &ipv4addr, 4);
+			memcpy(&ipv6addr.s6_addr[v4offset + 4], &psid16, 2);
+			bmemcpy(&ipv6addr, &pd, pdlen);
+		}
+
+		++rulecnt;
+		char ipv4addrbuf[INET_ADDRSTRLEN];
+		char ipv4prefixbuf[INET_ADDRSTRLEN];
+		char ipv6prefixbuf[INET6_ADDRSTRLEN];
+		char ipv6addrbuf[INET6_ADDRSTRLEN];
+		char pdbuf[INET6_ADDRSTRLEN];
+
+		inet_ntop(AF_INET, &ipv4addr, ipv4addrbuf, sizeof(ipv4addrbuf));
+		inet_ntop(AF_INET, &ipv4prefix, ipv4prefixbuf, sizeof(ipv4prefixbuf));
+		inet_ntop(AF_INET6, &ipv6prefix, ipv6prefixbuf, sizeof(ipv6prefixbuf));
+		inet_ntop(AF_INET6, &ipv6addr, ipv6addrbuf, sizeof(ipv6addrbuf));
+		inet_ntop(AF_INET6, &pd, pdbuf, sizeof(pdbuf));
+
+		printf("RULE_%d_FMR=%d\n", rulecnt, fmr);
+		printf("RULE_%d_EALEN=%d\n", rulecnt, ealen);
+		printf("RULE_%d_PSIDLEN=%d\n", rulecnt, psidlen);
+		printf("RULE_%d_OFFSET=%d\n", rulecnt, offset);
+		printf("RULE_%d_PREFIX4LEN=%d\n", rulecnt, prefix4len);
+		printf("RULE_%d_PREFIX6LEN=%d\n", rulecnt, prefix6len);
+		printf("RULE_%d_IPV4PREFIX=%s\n", rulecnt, ipv4prefixbuf);
+		printf("RULE_%d_IPV6PREFIX=%s\n", rulecnt, ipv6prefixbuf);
+
+		if (pdlen >= 0) {
+			printf("RULE_%d_IPV6PD=%s\n", rulecnt, pdbuf);
+			printf("RULE_%d_PD6LEN=%d\n", rulecnt, pdlen);
+			printf("RULE_%d_PD6IFACE=%s\n", rulecnt, iface);
+			printf("RULE_%d_IPV6ADDR=%s\n", rulecnt, ipv6addrbuf);
+			printf("RULE_BMR=%d\n", rulecnt);
+		}
+
+		if (ipv4addr.s_addr) {
+			printf("RULE_%d_IPV4ADDR=%s\n", rulecnt, ipv4addrbuf);
+			printf("RULE_%d_ADDR4LEN=%d\n", rulecnt, addr4len);
+		}
+
+
+		if (psidlen > 0 && psid >= 0) {
+			printf("RULE_%d_PORTSETS='", rulecnt);
+			for (int k = (offset) ? 1 : 0; k < (1 << offset); ++k) {
+				int start = (k << (16 - offset)) | (psid >> offset);
+				int end = start + (1 << (16 - offset - psidlen)) - 1;
+
+				if (start == 0)
+					start = 1;
+
+				if (start <= end)
+					printf("%d-%d ", start, end);
+			}
+			printf("'\n");
+		}
+
+		if (dmr)
+			printf("RULE_%d_DMR=%s\n", rulecnt, dmr);
+
+		if (br)
+			printf("RULE_%d_BR=%s\n", rulecnt, br);
+	}
+
+	printf("RULE_COUNT=%d\n", rulecnt);
+	return status;
+}
diff --git a/package/network/ipv6/odhcp6c/Makefile b/package/network/ipv6/odhcp6c/Makefile
new file mode 100644
index 0000000..811e98c
--- /dev/null
+++ b/package/network/ipv6/odhcp6c/Makefile
@@ -0,0 +1,60 @@
+#
+# Copyright (C) 2012-2015 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:=odhcp6c
+PKG_RELEASE:=1
+
+PKG_SOURCE_PROTO:=git
+PKG_SOURCE_URL=$(PROJECT_GIT)/project/odhcp6c.git
+PKG_SOURCE_DATE:=2024-09-25
+PKG_SOURCE_VERSION:=b6ae9ffaeb0e18e9fa3d5be62faa8d3c9f340a58
+PKG_MIRROR_HASH:=ef1bdc53d79744771854456132f99a3ee345d178bc5c4b6261f2817fb0003a15
+PKG_MAINTAINER:=Hans Dedecker <dedeckeh@gmail.com>
+PKG_LICENSE:=GPL-2.0
+
+include $(INCLUDE_DIR)/package.mk
+include $(INCLUDE_DIR)/cmake.mk
+
+CMAKE_OPTIONS += \
+	-DUSE_LIBUBOX=on
+
+ifneq ($(CONFIG_PACKAGE_odhcp6c_ext_cer_id),0)
+  CMAKE_OPTIONS += -DEXT_CER_ID=$(CONFIG_PACKAGE_odhcp6c_ext_cer_id)
+endif
+
+define Package/odhcp6c
+  SECTION:=net
+  CATEGORY:=Network
+  TITLE:=Embedded DHCPv6-client for OpenWrt
+  DEPENDS:=@IPV6 +libubox
+endef
+
+define Package/odhcp6c/config
+  config PACKAGE_odhcp6c_ext_cer_id
+    int "CER-ID Extension ID (0 = disabled)"
+    depends on PACKAGE_odhcp6c
+    default 0
+endef
+
+define Package/odhcp6c/conffiles
+/etc/odhcp6c.user
+/etc/odhcp6c.user.d/
+endef
+
+define Package/odhcp6c/install
+	$(INSTALL_DIR) $(1)/usr/sbin/
+	$(INSTALL_BIN) $(PKG_BUILD_DIR)/odhcp6c $(1)/usr/sbin/
+	$(INSTALL_DIR) $(1)/lib/netifd/proto
+	$(INSTALL_BIN) ./files/dhcpv6.sh $(1)/lib/netifd/proto/dhcpv6.sh
+	$(INSTALL_BIN) ./files/dhcpv6.script $(1)/lib/netifd/
+	$(INSTALL_DIR) $(1)/etc/odhcp6c.user.d/
+	$(INSTALL_CONF) ./files/odhcp6c.user $(1)/etc/
+endef
+
+$(eval $(call BuildPackage,odhcp6c))
diff --git a/package/network/ipv6/odhcp6c/files/dhcpv6.script b/package/network/ipv6/odhcp6c/files/dhcpv6.script
new file mode 100755
index 0000000..f92f891
--- /dev/null
+++ b/package/network/ipv6/odhcp6c/files/dhcpv6.script
@@ -0,0 +1,232 @@
+#!/bin/sh
+[ -z "$2" ] && echo "Error: should be run by odhcpc6c" && exit 1
+. /lib/functions.sh
+. /lib/netifd/netifd-proto.sh
+
+setup_interface () {
+	local device="$1"
+	local prefsig=""
+	local addrsig=""
+
+	# Apply IPv6 / ND configuration
+	HOPLIMIT=$(cat /proc/sys/net/ipv6/conf/$device/hop_limit)
+	[ -n "$RA_HOPLIMIT" -a -n "$HOPLIMIT" ] && [ "$RA_HOPLIMIT" -gt "$HOPLIMIT" ] && echo "$RA_HOPLIMIT" > /proc/sys/net/ipv6/conf/$device/hop_limit
+	[ -n "$RA_MTU" ] && [ "$RA_MTU" -ge 1280 ] && echo "$RA_MTU" > /proc/sys/net/ipv6/conf/$device/mtu 2>/dev/null
+	[ -n "$RA_REACHABLE" ] && [ "$RA_REACHABLE" -gt 0 ] && echo "$RA_REACHABLE" > /proc/sys/net/ipv6/neigh/$device/base_reachable_time_ms
+	[ -n "$RA_RETRANSMIT" ] && [ "$RA_RETRANSMIT" -gt 0 ] && echo "$RA_RETRANSMIT" > /proc/sys/net/ipv6/neigh/$device/retrans_time_ms
+
+	proto_init_update "*" 1
+
+	# Merge RA-DNS
+	for radns in $RA_DNS; do
+		local duplicate=0
+		for dns in $RDNSS; do
+			[ "$radns" = "$dns" ] && duplicate=1
+		done
+		[ "$duplicate" = 0 ] && RDNSS="$RDNSS $radns"
+	done
+
+	for dns in $RDNSS; do
+		proto_add_dns_server "$dns"
+	done
+
+	for radomain in $RA_DOMAINS; do
+		local duplicate=0
+		for domain in $DOMAINS; do
+			[ "$radomain" = "$domain" ] && duplicate=1
+		done
+		[ "$duplicate" = 0 ] && DOMAINS="$DOMAINS $radomain"
+	done
+
+	for domain in $DOMAINS; do
+		proto_add_dns_search "$domain"
+	done
+
+	for prefix in $PREFIXES; do
+		proto_add_ipv6_prefix "$prefix"
+		prefsig="$prefsig ${prefix%%,*}"
+		local entry="${prefix#*/}"
+		entry="${entry#*,}"
+		entry="${entry#*,}"
+		local valid="${entry%%,*}"
+
+		if [ -z "$RA_ADDRESSES" -a -z "$RA_ROUTES" -a \
+				-z "$RA_DNS" -a "$FAKE_ROUTES" = 1 ]; then
+			RA_ROUTES="::/0,$SERVER,$valid,4096"
+		fi
+	done
+
+	for prefix in $USERPREFIX; do
+		proto_add_ipv6_prefix "$prefix"
+	done
+
+	# Merge addresses
+	for entry in $RA_ADDRESSES; do
+		local duplicate=0
+		local addr="${entry%%/*}"
+		for dentry in $ADDRESSES; do
+			local daddr="${dentry%%/*}"
+			[ "$addr" = "$daddr" ] && duplicate=1
+		done
+		[ "$duplicate" = "0" ] && ADDRESSES="$ADDRESSES $entry"
+	done
+
+	for entry in $ADDRESSES; do
+		local addr="${entry%%/*}"
+		entry="${entry#*/}"
+		local mask="${entry%%,*}"
+		entry="${entry#*,}"
+		local preferred="${entry%%,*}"
+		entry="${entry#*,}"
+		local valid="${entry%%,*}"
+
+		proto_add_ipv6_address "$addr" "$mask" "$preferred" "$valid" 1
+		addrsig="$addrsig $addr/$mask"
+
+		if [ -z "$RA_ADDRESSES" -a -z "$RA_ROUTES" -a \
+				-z "$RA_DNS" -a "$FAKE_ROUTES" = 1 ]; then
+			RA_ROUTES="::/0,$SERVER,$valid,4096"
+		fi
+
+		# RFC 7278
+		if [ "$mask" -eq 64 -a -z "$PREFIXES" -a -n "$EXTENDPREFIX" ]; then
+			proto_add_ipv6_prefix "$addr/$mask,$preferred,$valid"
+
+			local raroutes=""
+			for route in $RA_ROUTES; do
+				local prefix="${route%%/*}"
+				local entry="${route#*/}"
+				local pmask="${entry%%,*}"
+				entry="${entry#*,}"
+				local gw="${entry%%,*}"
+
+				[ -z "$gw" -a "$mask" = "$pmask" ] && {
+					case "$addr" in
+						"${prefix%*::}"*) continue;;
+					esac
+				}
+				raroutes="$raroutes $route"
+			done
+			RA_ROUTES="$raroutes"
+		fi
+	done
+
+	for entry in $RA_ROUTES; do
+		local duplicate=$NOSOURCEFILTER
+		local addr="${entry%%/*}"
+		entry="${entry#*/}"
+		local mask="${entry%%,*}"
+		entry="${entry#*,}"
+		local gw="${entry%%,*}"
+		entry="${entry#*,}"
+		local valid="${entry%%,*}"
+		entry="${entry#*,}"
+		local metric="${entry%%,*}"
+
+		for xentry in $RA_ROUTES; do
+			local xprefix="${xentry%%,*}"
+			xentry="${xentry#*,}"
+			local xgw="${xentry%%,*}"
+
+			[ -n "$gw" -a -z "$xgw" -a "$addr/$mask" = "$xprefix" ] && duplicate=1
+		done
+
+		if [ -z "$gw" -o "$duplicate" = 1 ]; then
+			proto_add_ipv6_route "$addr" "$mask" "$gw" "$metric" "$valid"
+		else
+			proto_add_ipv6_route "$addr" "$mask" "$gw" "$metric" "$valid" "::/128"
+			for prefix in $PREFIXES $ADDRESSES; do
+				local paddr="${prefix%%,*}"
+				proto_add_ipv6_route "$addr" "$mask" "$gw" "$metric" "$valid" "$paddr"
+			done
+		fi
+	done
+
+	proto_add_data
+	[ -n "$CER" ] && json_add_string cer "$CER"
+	[ -n "$PASSTHRU" ] && json_add_string passthru "$PASSTHRU"
+	[ -n "$ZONE" ] && json_add_string zone "$ZONE"
+	proto_close_data
+
+	proto_send_update "$INTERFACE"
+
+	MAPTYPE=""
+	MAPRULE=""
+
+	if [ -n "$MAPE" -a -f /lib/netifd/proto/map.sh ]; then
+		MAPTYPE="map-e"
+		MAPRULE="$MAPE"
+	elif [ -n "$MAPT" -a -f /lib/netifd/proto/map.sh -a -f /proc/net/nat46/control ]; then
+		MAPTYPE="map-t"
+		MAPRULE="$MAPT"
+	elif [ -n "$LW4O6" -a -f /lib/netifd/proto/map.sh ]; then
+		MAPTYPE="lw4o6"
+		MAPRULE="$LW4O6"
+	fi
+
+	[ -n "$ZONE" ] || ZONE=$(fw3 -q network $INTERFACE 2>/dev/null)
+
+	if [ "$IFACE_MAP" != 0 -a -n "$MAPTYPE" -a -n "$MAPRULE" ]; then
+		[ -z "$IFACE_MAP" -o "$IFACE_MAP" = 1 ] && IFACE_MAP=${INTERFACE}_4
+		json_init
+		json_add_string name "$IFACE_MAP"
+		json_add_string ifname "@$INTERFACE"
+		json_add_string proto map
+		json_add_string type "$MAPTYPE"
+		json_add_string _prefsig "$prefsig"
+		[ "$MAPTYPE" = lw4o6 ] && json_add_string _addrsig "$addrsig"
+		json_add_string rule "$MAPRULE"
+		json_add_string tunlink "$INTERFACE"
+		[ -n "$ZONE_MAP" ] || ZONE_MAP=$ZONE
+		[ -n "$ZONE_MAP" ] && json_add_string zone "$ZONE_MAP"
+		[ -n "$ENCAPLIMIT_MAP" ] && json_add_string encaplimit "$ENCAPLIMIT_MAP"
+		[ -n "$IFACE_MAP_DELEGATE" ] && json_add_boolean delegate "$IFACE_MAP_DELEGATE"
+		json_close_object
+		ubus call network add_dynamic "$(json_dump)"
+	elif [ -n "$AFTR" -a "$IFACE_DSLITE" != 0 -a -f /lib/netifd/proto/dslite.sh ]; then
+		[ -z "$IFACE_DSLITE" -o "$IFACE_DSLITE" = 1 ] && IFACE_DSLITE=${INTERFACE}_4
+		json_init
+		json_add_string name "$IFACE_DSLITE"
+		json_add_string ifname "@$INTERFACE"
+		json_add_string proto "dslite"
+		json_add_string peeraddr "$AFTR"
+		json_add_string tunlink "$INTERFACE"
+		[ -n "$ZONE_DSLITE" ] || ZONE_DSLITE=$ZONE
+		[ -n "$ZONE_DSLITE" ] && json_add_string zone "$ZONE_DSLITE"
+		[ -n "$ENCAPLIMIT_DSLITE" ] && json_add_string encaplimit "$ENCAPLIMIT_DSLITE"
+		[ -n "$IFACE_DSLITE_DELEGATE" ] && json_add_boolean delegate "$IFACE_DSLITE_DELEGATE"
+		json_close_object
+		ubus call network add_dynamic "$(json_dump)"
+	fi
+
+	# TODO: $SNTP_IP $SIP_IP $SNTP_FQDN $SIP_DOMAIN
+}
+
+teardown_interface() {
+	proto_init_update "*" 0
+	proto_send_update "$INTERFACE"
+}
+
+case "$2" in
+	bound)
+		teardown_interface "$1"
+		setup_interface "$1"
+	;;
+	informed|updated|rebound)
+		setup_interface "$1"
+	;;
+	ra-updated)
+		[ -n "$ADDRESSES$RA_ADDRESSES$PREFIXES$USERPREFIX" ] && setup_interface "$1"
+	;;
+	started|stopped|unbound)
+		teardown_interface "$1"
+	;;
+esac
+
+# user rules
+[ -f /etc/odhcp6c.user ] && . /etc/odhcp6c.user "$@"
+for f in /etc/odhcp6c.user.d/*; do
+  [ -f "$f" ] && (. "$f" "$@")
+done
+
+exit 0
diff --git a/package/network/ipv6/odhcp6c/files/dhcpv6.sh b/package/network/ipv6/odhcp6c/files/dhcpv6.sh
new file mode 100755
index 0000000..484851c
--- /dev/null
+++ b/package/network/ipv6/odhcp6c/files/dhcpv6.sh
@@ -0,0 +1,139 @@
+#!/bin/sh
+
+. /lib/functions.sh
+. ../netifd-proto.sh
+init_proto "$@"
+
+proto_dhcpv6_init_config() {
+	renew_handler=1
+
+	proto_config_add_string 'reqaddress:or("try","force","none")'
+	proto_config_add_string 'reqprefix:or("auto","no",range(0, 64))'
+	proto_config_add_string clientid
+	proto_config_add_string 'reqopts:list(uinteger)'
+	proto_config_add_string 'defaultreqopts:bool'
+	proto_config_add_string 'noslaaconly:bool'
+	proto_config_add_string 'forceprefix:bool'
+	proto_config_add_string 'extendprefix:bool'
+	proto_config_add_string 'norelease:bool'
+	proto_config_add_string 'noserverunicast:bool'
+	proto_config_add_string 'noclientfqdn:bool'
+	proto_config_add_string 'noacceptreconfig:bool'
+	proto_config_add_array 'ip6prefix:list(ip6addr)'
+	proto_config_add_string iface_dslite
+	proto_config_add_string zone_dslite
+	proto_config_add_string encaplimit_dslite
+	proto_config_add_string iface_map
+	proto_config_add_string zone_map
+	proto_config_add_string encaplimit_map
+	proto_config_add_string zone
+	proto_config_add_string 'ifaceid:ip6addr'
+	proto_config_add_string "userclass"
+	proto_config_add_string "vendorclass"
+	proto_config_add_array "sendopts:list(string)"
+	proto_config_add_boolean delegate
+	proto_config_add_int skpriority
+	proto_config_add_int "soltimeout"
+	proto_config_add_boolean fakeroutes
+	proto_config_add_boolean sourcefilter
+	proto_config_add_boolean keep_ra_dnslifetime
+	proto_config_add_int "ra_holdoff"
+	proto_config_add_boolean verbose
+}
+
+proto_dhcpv6_add_prefix() {
+	append "$3" "$1"
+}
+
+proto_dhcpv6_add_sendopts() {
+	[ -n "$1" ] && append "$3" "-x$1"
+}
+
+proto_dhcpv6_setup() {
+	local config="$1"
+	local iface="$2"
+
+	local reqaddress reqprefix clientid reqopts defaultreqopts noslaaconly forceprefix extendprefix norelease noserverunicast noclientfqdn noacceptreconfig ip6prefix ip6prefixes iface_dslite iface_map ifaceid userclass vendorclass sendopts delegate zone_dslite zone_map zone encaplimit_dslite encaplimit_map skpriority soltimeout fakeroutes sourcefilter keep_ra_dnslifetime ra_holdoff verbose
+	json_get_vars reqaddress reqprefix clientid reqopts defaultreqopts noslaaconly forceprefix extendprefix norelease noserverunicast noclientfqdn noacceptreconfig iface_dslite iface_map ifaceid userclass vendorclass delegate zone_dslite zone_map zone encaplimit_dslite encaplimit_map skpriority soltimeout fakeroutes sourcefilter keep_ra_dnslifetime ra_holdoff verbose
+	json_for_each_item proto_dhcpv6_add_prefix ip6prefix ip6prefixes
+
+	# Configure
+	local opts=""
+	[ -n "$reqaddress" ] && append opts "-N$reqaddress"
+
+	[ -z "$reqprefix" -o "$reqprefix" = "auto" ] && reqprefix=0
+	[ "$reqprefix" != "no" ] && append opts "-P$reqprefix"
+
+	[ -n "$clientid" ] && append opts "-c$clientid"
+
+	[ "$defaultreqopts" = "0" ] && append opts "-R"
+
+	[ "$noslaaconly" = "1" ] && append opts "-S"
+
+	[ "$forceprefix" = "1" ] && append opts "-F"
+
+	[ "$norelease" = "1" ] && append opts "-k"
+
+	[ "$noserverunicast" = "1" ] && append opts "-U"
+
+	[ "$noclientfqdn" = "1" ] && append opts "-f"
+
+	[ "$noacceptreconfig" = "1" ] && append opts "-a"
+
+	[ -n "$ifaceid" ] && append opts "-i$ifaceid"
+
+	[ -n "$vendorclass" ] && append opts "-V$vendorclass"
+
+	[ -n "$userclass" ] && append opts "-u$userclass"
+
+	[ "$keep_ra_dnslifetime" = "1" ] && append opts "-L"
+
+	[ -n "$skpriority" ] && append opts "-K$skpriority"
+
+	[ -n "$ra_holdoff" ] && append opts "-m$ra_holdoff"
+
+	[ "$verbose" = "1" ] && append opts "-v"
+
+	local opt
+	for opt in $reqopts; do
+		append opts "-r$opt"
+	done
+
+	json_for_each_item proto_dhcpv6_add_sendopts sendopts opts
+
+	append opts "-t${soltimeout:-120}"
+
+	[ -n "$ip6prefixes" ] && proto_export "USERPREFIX=$ip6prefixes"
+	[ -n "$iface_dslite" ] && proto_export "IFACE_DSLITE=$iface_dslite"
+	[ -n "$iface_map" ] && proto_export "IFACE_MAP=$iface_map"
+	[ "$delegate" = "0" ] && proto_export "IFACE_DSLITE_DELEGATE=0"
+	[ "$delegate" = "0" ] && proto_export "IFACE_MAP_DELEGATE=0"
+	[ -n "$zone_dslite" ] && proto_export "ZONE_DSLITE=$zone_dslite"
+	[ -n "$zone_map" ] && proto_export "ZONE_MAP=$zone_map"
+	[ -n "$zone" ] && proto_export "ZONE=$zone"
+	[ -n "$encaplimit_dslite" ] && proto_export "ENCAPLIMIT_DSLITE=$encaplimit_dslite"
+	[ -n "$encaplimit_map" ] && proto_export "ENCAPLIMIT_MAP=$encaplimit_map"
+	[ "$fakeroutes" != "0" ] && proto_export "FAKE_ROUTES=1"
+	[ "$sourcefilter" = "0" ] && proto_export "NOSOURCEFILTER=1"
+	[ "$extendprefix" = "1" ] && proto_export "EXTENDPREFIX=1"
+
+	proto_export "INTERFACE=$config"
+	proto_run_command "$config" odhcp6c \
+		-s /lib/netifd/dhcpv6.script \
+		$opts $iface
+}
+
+proto_dhcpv6_renew() {
+	local interface="$1"
+	# SIGUSR1 forces odhcp6c to renew its lease
+	local sigusr1="$(kill -l SIGUSR1)"
+	[ -n "$sigusr1" ] && proto_kill_command "$interface" $sigusr1
+}
+
+proto_dhcpv6_teardown() {
+	local interface="$1"
+	proto_kill_command "$interface"
+}
+
+add_protocol dhcpv6
+
diff --git a/package/network/ipv6/odhcp6c/files/odhcp6c.user b/package/network/ipv6/odhcp6c/files/odhcp6c.user
new file mode 100644
index 0000000..f15a1b1
--- /dev/null
+++ b/package/network/ipv6/odhcp6c/files/odhcp6c.user
@@ -0,0 +1 @@
+# This script is sourced by odhcp6c's dhcpv6.script at every DHCPv6 event.
diff --git a/package/network/ipv6/thc-ipv6/Makefile b/package/network/ipv6/thc-ipv6/Makefile
new file mode 100644
index 0000000..b26b050
--- /dev/null
+++ b/package/network/ipv6/thc-ipv6/Makefile
@@ -0,0 +1,61 @@
+#
+# Copyright (C) 2009-2015 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:=thc-ipv6
+PKG_VERSION:=3.8
+PKG_RELEASE:=1
+PKG_LICENSE:=GPL-3.0
+
+PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
+PKG_SOURCE_URL:=@GITHUB/vanhauser-thc/THC-Archive/master/Tools
+PKG_HASH:=b60be61a8b0a944a66e3b719704b4c03c1bc2c22f32d5d21e99e434c82a9d769
+
+PKG_MAINTAINER:=Jo-Philipp Wich <jo@mein.io>
+
+include $(INCLUDE_DIR)/package.mk
+
+THC_APPLETS := \
+	address6 alive6 covert_send6 covert_send6d denial6 detect-new-ip6     \
+	detect_sniffer6 dnsdict6 dnsrevenum6 dos-new-ip6                      \
+	dump_router6 exploit6 fake_advertise6 fake_dhcps6 fake_dns6d          \
+	fake_dnsupdate6 fake_mipv6 fake_mld26 fake_mld6 fake_mldrouter6       \
+	fake_router26 fake_router6 fake_solicitate6 flood_advertise6          \
+	flood_dhcpc6 flood_mld26 flood_mld6 flood_mldrouter6 flood_router26   \
+	flood_router6 flood_solicitate6 fragmentation6 fuzz_ip6 fuzz_dhcpc6   \
+	fuzz_dhcps6 implementation6 implementation6d inverse_lookup6          \
+	kill_router6 ndpexhaust6 node_query6 parasite6 passive_discovery6     \
+	randicmp6 redir6 rsmurf6 sendpees6 sendpeesmp6 smurf6 thcping6        \
+	toobig6 trace6 toobigsniff6 flood_unreach6 connect6
+
+THC_DEPENDS_dnsdict6 := +libpthread
+THC_DEPENDS_thcping6 := +librt
+
+define BuildTool
+  define Package/thc-ipv6-$(subst _,-,$(1))
+    TITLE:=THC-IPv6 $(1) utility
+    SECTION:=net
+    CATEGORY:=Network
+    DEPENDS:=+libpcap $(THC_DEPENDS_$(1))
+    URL:=https://github.com/vanhauser-thc/thc-ipv6
+    SUBMENU:=THC-IPv6 attack and analyzing toolkit
+  endef
+
+  define Package/thc-ipv6-$(subst _,-,$(1))/description
+    This package contains the $(1) utility of the THC-IPv6 toolkit.
+  endef
+
+  define Package/thc-ipv6-$(subst _,-,$(1))/install
+	$(INSTALL_DIR) $$(1)/usr/sbin
+	$(INSTALL_BIN) $(PKG_BUILD_DIR)/$(1) $$(1)/usr/sbin/$(1)
+  endef
+
+  $$(eval $$(call BuildPackage,thc-ipv6-$(subst _,-,$(1))))
+endef
+
+$(foreach a,$(THC_APPLETS),$(eval $(call BuildTool,$(a))))
diff --git a/package/network/ipv6/thc-ipv6/patches/100-no-ssl.patch b/package/network/ipv6/thc-ipv6/patches/100-no-ssl.patch
new file mode 100644
index 0000000..118c2a7
--- /dev/null
+++ b/package/network/ipv6/thc-ipv6/patches/100-no-ssl.patch
@@ -0,0 +1,10 @@
+--- a/Makefile
++++ b/Makefile
+@@ -1,6 +1,6 @@
+ # Comment out if openssl-dev is not present
+ # of if you want to compile statc
+-HAVE_SSL=yes
++#HAVE_SSL=yes
+ 
+ # comment in if you want to compile static tools
+ #STATIC=-static
diff --git a/package/network/ipv6/thc-ipv6/patches/101-remove-march-native.patch b/package/network/ipv6/thc-ipv6/patches/101-remove-march-native.patch
new file mode 100644
index 0000000..b397f40
--- /dev/null
+++ b/package/network/ipv6/thc-ipv6/patches/101-remove-march-native.patch
@@ -0,0 +1,11 @@
+--- a/Makefile
++++ b/Makefile
+@@ -7,7 +7,7 @@
+ 
+ #CC=gcc
+ #CFLAGS=-g
+-CFLAGS+=-g -O3 -march=native -flto -falign-functions -falign-jumps -falign-loops -falign-labels -freorder-blocks
++CFLAGS+=-g -O3 -flto -falign-functions -falign-jumps -falign-loops -falign-labels -freorder-blocks
+ CFLAGS+=$(if $(HAVE_SSL),-D_HAVE_SSL,)
+ LDFLAGS+=-lpcap $(if $(HAVE_SSL),-lssl -lcrypto,)
+ PROGRAMS=parasite6 dos-new-ip6 detect-new-ip6 fake_router6 fake_advertise6 fake_solicitate6 fake_mld6 fake_mld26 fake_mldrouter6 flood_mldrouter6 fake_mipv6 redir6 smurf6 alive6 toobig6 rsmurf6 implementation6 implementation6d sendpees6 sendpeesmp6 randicmp6 fuzz_ip6 flood_mld6 flood_mld26 flood_router6 flood_advertise6 flood_solicitate6 trace6 exploit6 denial6 fake_dhcps6 flood_dhcpc6 fake_dns6d fragmentation6 kill_router6 fake_dnsupdate6 ndpexhaust6 detect_sniffer6 dump_router6 fake_router26 flood_router26 passive_discovery6 dnsrevenum6 inverse_lookup6 node_query6 address6 covert_send6 covert_send6d inject_alive6 firewall6 ndpexhaust26 fake_pim6 thcsyn6 redirsniff6 flood_redir6 four2six dump_dhcp6 flood_rs6 fuzz_dhcps6 fuzz_dhcpc6 toobigsniff6 flood_unreach6 connect6