diff --git a/docs/index.rst b/docs/index.rst index 037679ae..285b0fab 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -57,6 +57,7 @@ Several Freifunk communities in Germany use Gluon as the foundation of their Fre :caption: Packages :maxdepth: 1 + package/gluon-464xlat-clat package/gluon-client-bridge package/gluon-config-mode-domain-select package/gluon-ebtables-filter-multicast diff --git a/docs/package/gluon-464xlat-clat.rst b/docs/package/gluon-464xlat-clat.rst new file mode 100644 index 00000000..a9473f34 --- /dev/null +++ b/docs/package/gluon-464xlat-clat.rst @@ -0,0 +1,32 @@ +gluon-464xlat-clat +================== + +This package provides the kernel module and functionality required to support +ipv4 clients on an ipv6-only backbone. + +Assumptions +----------- + +* Clients will be given IPv4 addresses by a dhcp daemon that runs on each node. + gluon-ddhcpd is a great choice for this. +* There is a component on the network that does plat on the default network + 64:ff9b::/96. https://github.com/FreifunkMD/jool-docker.git can do this. + +Limitations +----------- +* When roaming, clients will experience temporary loss of IPv4 connectivity + +site.conf +--------- + +clat_range : mandatory + - infrastructure net (ULA) from which a /96 clat prefix will be generated. + - This must be a /48 prefix. + - This can be the same for each community and is pre-registered at https://wiki.freifunk.net/IP-Netze#IPv6 as part of fdff:ffff:ff00::/40 + +Example:: + + { + clat_range = 'fdff:ffff:ffff::/48', + } + diff --git a/package/gluon-464xlat-clat/Makefile b/package/gluon-464xlat-clat/Makefile new file mode 100644 index 00000000..80a0f9a1 --- /dev/null +++ b/package/gluon-464xlat-clat/Makefile @@ -0,0 +1,15 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=gluon-464xlat-clat +PKG_VERSION:=1 + +include ../gluon.mk + +PKG_CONFIG_DEPENDS += $(GLUON_I18N_CONFIG) + +define Package/gluon-464xlat-clat + TITLE:=Support translating IPv4 addresses to IPv6 using 464xlat + DEPENDS:=+gluon-core +kmod-nat46 +endef + +$(eval $(call BuildPackageGluon,gluon-464xlat-clat)) diff --git a/package/gluon-464xlat-clat/check_site.lua b/package/gluon-464xlat-clat/check_site.lua new file mode 100644 index 00000000..66518c55 --- /dev/null +++ b/package/gluon-464xlat-clat/check_site.lua @@ -0,0 +1,2 @@ +need_string(in_domain({'clat_range'})) +need_string_match(in_domain({'next_node', 'ip4'}), '^%d+.%d+.%d+.%d+$', true) diff --git a/package/gluon-464xlat-clat/files/50-clat-sysctl.conf b/package/gluon-464xlat-clat/files/50-clat-sysctl.conf new file mode 100644 index 00000000..fb259454 --- /dev/null +++ b/package/gluon-464xlat-clat/files/50-clat-sysctl.conf @@ -0,0 +1,2 @@ +net.ipv4.conf.clat.accept_local=1 +net.ipv6.conf.clat.use_oif_addrs_only=1 diff --git a/package/gluon-464xlat-clat/files/etc/sysctl.d/50-clat-sysctl.conf b/package/gluon-464xlat-clat/files/etc/sysctl.d/50-clat-sysctl.conf new file mode 100644 index 00000000..fb259454 --- /dev/null +++ b/package/gluon-464xlat-clat/files/etc/sysctl.d/50-clat-sysctl.conf @@ -0,0 +1,2 @@ +net.ipv4.conf.clat.accept_local=1 +net.ipv6.conf.clat.use_oif_addrs_only=1 diff --git a/package/gluon-464xlat-clat/files/lib/netifd/proto/xlat464clat.sh b/package/gluon-464xlat-clat/files/lib/netifd/proto/xlat464clat.sh new file mode 100755 index 00000000..c81e92e1 --- /dev/null +++ b/package/gluon-464xlat-clat/files/lib/netifd/proto/xlat464clat.sh @@ -0,0 +1,171 @@ +#!/bin/sh +# Copyright 2018 Vincent Wiemann +# +# 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. + +CTR=/proc/net/nat46/control + +[ -n "$INCLUDE_ONLY" ] || { + . /lib/functions.sh + . ../netifd-proto.sh + init_proto "$@" +} + +proto_464xlatclat_init_config() { + proto_config_add_string "zone" + proto_config_add_string "zone4" + proto_config_add_string "local_style" + proto_config_add_string "local_v4" + proto_config_add_string "local_v6" + proto_config_add_string "local_ea_len" + proto_config_add_string "local_psid" + proto_config_add_boolean "local_fmr_flag" + proto_config_add_string "remote_style" + proto_config_add_string "remote_v4" + proto_config_add_string "remote_v6" + proto_config_add_string "remote_ea_len" + proto_config_add_string "remote_psid" + proto_config_add_string "ip4table" + proto_config_add_boolean "debug" + available=1 + no_device=1 +} + +proto_464xlatclat_setup() { + local config="$1" + local clat_cfg="config ${config}" + local local_style + local local_v4 + local local_v6 + local local_ea_len + local local_psid + local local_fmr + local remote_style + local remote_v4 + local remote_v6 + local remote_ea_len + local remote_psid + local zone + local zone4 + local ip4table + + config_load network + config_get ip4table "${config}" "ip4table" + config_get zone "${config}" "zone" + config_get zone4 "${config}" "zone4" + config_get_bool debug "${config}" "debug" 0 + config_get local_style "${config}" "local_style" + config_get local_v4 "${config}" "local_v4" + config_get local_v6 "${config}" "local_v6" + config_get local_ea_len "${config}" "local_ea_len" + config_get local_psid "${config}" "local_psid_offset" + config_get_bool local_fmr "${config}" "local_fmr_flag" 0 + config_get remote_style "${config}" "remote_style" + config_get remote_v4 "${config}" "remote_v4" + config_get remote_v6 "${config}" "remote_v6" + config_get remote_ea_len "${config}" "remote_ea_len" + config_get remote_psid "${config}" "remote_psid_offset" + config_get local_ip "${config}" "local_ip" + + zone="${zone:-wan}" + zone4="${zone4:-lan}" + local_v6="${local_v6:-fd00:13:37:13:37::/96}" + remote_v4="${remote_v4:-0.0.0.0/0}" + + [ "$debug" -ne 0 ] && clat_cfg="$clat_cfg debug 1" + [ "${local_fmr}" -ne 0 ] && clat_cfg="$clat_cfg local.fmr-flag 1" + + clat_cfg="${clat_cfg} local.style ${local_style:-RFC6052} local.v4 ${local_v4:-192.168.1.0/24} local.v6 ${local_v6}" + clat_cfg="${clat_cfg} local.ea-len ${local_ea_len:-0} local.psid-offset ${local_psid:-0}" + clat_cfg="${clat_cfg} remote.style ${remote_style:-RFC6052} remote.v4 ${remote_v4} remote.v6 ${remote_v6:-64:ff9b::/96}" + clat_cfg="${clat_cfg} remote.ea-len ${remote_ea_len:-0} remote.psid-offset ${remote_psid:-0}" + + echo "add ${config}" > ${CTR} + echo "${clat_cfg}" > ${CTR} + + [ "${ip4table}" ] && ip -4 rule add from "${local_v4}" lookup "${ip4table}" + + proto_init_update "${config}" 1 + + # add routes + case "${local_v6}" in + *:*/*) + proto_add_ipv6_route "${local_v6%%/*}" "${local_v6##*/}" + ;; + *:*) + proto_add_ipv6_route "${local_v6%%/*}" "128" + ;; + esac + + case "${remote_v4}" in + *.*/*) + proto_add_ipv4_route "${remote_v4%%/*}" "${remote_v4##*/}" "" "" 2048 + ;; + *.*) + proto_add_ipv4_route "${remote_v4%%/*}" "32" "" "" 2048 + ;; + esac + + proto_add_data + [ "${zone}" != "-" ] && json_add_string zone "${zone}" + + json_add_array firewall + # if forwarding within "zone" and between zone<->zone4 is allowed you can set zone = "-" + if [ "${zone}" != "-" ]; then + 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 "$local_v6" + json_add_string target ACCEPT + json_close_object + # if a forwarding between zone and zone4 exists (e.g. lan<->wan) you can set zone4 = "-" + if [ "${zone4}" != "-" ]; then + json_add_object "" + json_add_string type rule + json_add_string family inet + json_add_string proto all + json_add_string direction out + json_add_string dest "${zone}" + [ "$remote_v4" != "0.0.0.0/0" ] && json_add_string dest_ip "$remote_v4" + json_add_string src "${zone4}" + json_add_string src_ip "$local_v4" + json_add_string target ACCEPT + json_close_object + fi + fi + + json_close_array + + proto_close_data + + proto_send_update "$config" + + # this rule relies on the ll-address being set. This rule will set the + # src-address for icmp packets to the ipv4 next-node address + clat_ll_ip="$(ip -o a s dev clat |cut -d" " -f7|cut -d"/" -f1)/128" + clat_cfg="insert $config local.v4 ${local_ip} local.v6 :: local.style NONE" + clat_cfg="${clat_cfg} local.ea-len 0 local.psid-offset 0" + clat_cfg="${clat_cfg} remote.v4 ${local_ip} remote.v6 $clat_ll_ip" + clat_cfg="${clat_cfg} remote.style NONE remote.ea-len 0 remote.psid-offset 0" + echo "${clat_cfg}" > ${CTR} +} + +proto_464xlatclat_teardown() { + local config="$1" + echo "del ${config}" > ${CTR} +} + +[ -n "$INCLUDE_ONLY" ] || { + add_protocol 464xlatclat +} diff --git a/package/gluon-464xlat-clat/luasrc/lib/gluon/upgrade/450-gluon-xlat464-clat b/package/gluon-464xlat-clat/luasrc/lib/gluon/upgrade/450-gluon-xlat464-clat new file mode 100755 index 00000000..4bdc6197 --- /dev/null +++ b/package/gluon-464xlat-clat/luasrc/lib/gluon/upgrade/450-gluon-xlat464-clat @@ -0,0 +1,39 @@ +#!/usr/bin/lua +local site = require 'gluon.site' +local sysconfig = require 'gluon.sysconfig' +local uci = require('simple-uci').cursor() + +local f = io.open("/etc/iproute2/rt_tables", "r") +if f then + if not f:read("*a"):find("10\tclat") then + f:close() + f = io.open("/etc/iproute2/rt_tables", "a") + if f then + f:write("\n10\tclat\n") + end + end + f:close() +end + +local plat_prefix = uci:get('network', 'plat', 'local_v6') or "64:ff9b::/96" + +uci:set('network', 'local_node', 'ip4table', 'clat') + +local appendix = sysconfig.primary_mac:gsub('%:', '') +appendix = string.format('%x:%x:%x', tonumber(appendix:sub(1, 4), 16), + tonumber(appendix:sub(5, 8), 16), + tonumber(appendix:sub(9, 12), 16)) +local clat_prefix = string.format('%s%s::/96', site.clat_range():gsub(':/.+', ''), appendix) + +uci:delete('network', 'clat') +uci:section('network', 'interface', 'clat', { + proto = '464xlatclat', + local_v4 = site.prefix4(), + local_v6 = clat_prefix, + remote_v6 = plat_prefix, + zone = "local_client", + zone4 = "local_client", + ip4table = "clat", + local_ip = site.next_node.ip4() .. '/32' +}) +uci:save('network')