Skip to content

Commit

Permalink
gluon-464xlat-clat: initial commit
Browse files Browse the repository at this point in the history
This provides native ipv4 to clients on a network having an ipv6-only
backend, relying on external plat. This allows to use clients in
ipv6-only networks that would otherwise refuse to connect without a
valid ipv4 route.

Limitations:
* External plat must be provided, for example by jool - for example by
  https://github.com/FreifunkMD/jool-docker.git
* This implementation will break ipv4 connections when clients roam.
  • Loading branch information
christf committed Jan 22, 2020
1 parent 35a0e3f commit ebbd407
Show file tree
Hide file tree
Showing 8 changed files with 264 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
32 changes: 32 additions & 0 deletions docs/package/gluon-464xlat-clat.rst
Original file line number Diff line number Diff line change
@@ -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',
}

15 changes: 15 additions & 0 deletions package/gluon-464xlat-clat/Makefile
Original file line number Diff line number Diff line change
@@ -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))
2 changes: 2 additions & 0 deletions package/gluon-464xlat-clat/check_site.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
need_string(in_domain({'clat_range'}))
need_string_match(in_domain({'next_node', 'ip4'}), '^%d+.%d+.%d+.%d+$', true)
2 changes: 2 additions & 0 deletions package/gluon-464xlat-clat/files/50-clat-sysctl.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
net.ipv4.conf.clat.accept_local=1
net.ipv6.conf.clat.use_oif_addrs_only=1
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
net.ipv4.conf.clat.accept_local=1
net.ipv6.conf.clat.use_oif_addrs_only=1
171 changes: 171 additions & 0 deletions package/gluon-464xlat-clat/files/lib/netifd/proto/xlat464clat.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
#!/bin/sh
# Copyright 2018 Vincent Wiemann <vincent.wiemann@ironai.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.

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
}
Original file line number Diff line number Diff line change
@@ -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')

0 comments on commit ebbd407

Please sign in to comment.