Last active
June 27, 2024 07:56
-
-
Save riyad/390694af9d8bf8ef150428594467fd9a to your computer and use it in GitHub Desktop.
OpenWRT firewall script to configure network prefix translation for IPv6
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/sh | |
# | |
# Author: Riyad Preukschas <riyad@informatik.uni-bremen.de> | |
# License: Mozilla Public License 2.0 | |
# SPDX-License-Identifier: MPL-2.0 | |
# | |
# OpenWRT firewall script for configuring NPTv6 (network prefix translation). | |
# | |
# # Installation | |
# | |
# - Install the `iptables-mod-nat-extra` package | |
# - Save this script to /etc/firewall.npt6 on your OpenWRT device. | |
# - Run the following commands to | |
# - to add this script to the backup archive | |
# - instruct the firewall to run this script when it reloads | |
# - restart the firewall to pick up the new configuration | |
# | |
# ``` | |
# cat << "EOF" >> /etc/sysupgrade.conf | |
# /etc/firewall.npt6 | |
# EOF | |
# uci -q delete firewall.npt6 | |
# uci set firewall.npt6="include" | |
# uci set firewall.npt6.path="/etc/firewall.npt6" | |
# uci set firewall.npt6.reload="1" | |
# uci commit firewall | |
# /etc/init.d/firewall restart | |
# ``` | |
# | |
# # Configuration | |
# | |
# You can configure NPT mappings by adding config sections to /etc/config/firewall: | |
# | |
# ``` | |
# config npt6 | |
# option src_interface 'lan' # interface name as found in /etc/config/network or the "Network > Interfaces" page of LuCI. | |
# option dest_interface 'wan6_wg' # see above | |
# option output '1' # map outgoing connections (src -> dest). (default '1') | |
# # This should be enough to connect to devices in the destination network. | |
# option input '0' # (optional) map incoming connections (dest -> src). (default '0') | |
# # WARNING: You should have firewall rules denying unsolicited | |
# # connections especially when `dest_interface` connects to the Internet. | |
# # e.g. OpenWRT's "WAN" firewall zone should deny these | |
# # connections by default (i.e. Input: "reject"). | |
# # You should probably use specific "Port Forwards" | |
# # or "Traffic Rules" to poke holes into your firewall. | |
# ``` | |
# | |
# You can add multiple sections if you want to use NTP between multiple source and destination networks. | |
# | |
# Run `/etc/init.d/firewall reload` to apply new configurations. | |
# | |
. /lib/functions.sh | |
. /lib/functions/network.sh | |
set -e -o pipefail | |
DEBUG=${DEBUG:-0} | |
log() { | |
logger -t npt6 -s "${@}" | |
} | |
debug() { | |
if [ $DEBUG -ne 0 ]; then | |
log "DEBUG: ${@}" | |
fi | |
} | |
ip6t() { | |
ip6tables "${@}" | |
} | |
ip6t_add() { | |
if ! ip6t -C "${@}" &> /dev/null; then | |
ip6t -I "${@}" | |
fi | |
} | |
nat6_init() { | |
log "Resetting NETMAP rules" | |
# NOTE: the original script used iptables-save here | |
ip6tables-save -t nat \ | |
| sed -e "/\sNETMAP\s/d" \ | |
| ip6tables-restore -T nat | |
} | |
npt6_forwarding_output() { | |
local src_network="${1}" | |
local dest_network="${2}" | |
} | |
handle_npt6() { | |
# ${config_id} contains the ID of the current section | |
local config_id="${1}" | |
local src_network | |
config_get src_network "${config_id}" src_interface | |
local dest_network | |
config_get dest_network "${config_id}" dest_interface | |
# Enable forwarding via NPT6 | |
local output | |
config_get_bool output "${config_id}" output 1 | |
local input | |
config_get_bool input "${config_id}" input 0 | |
log "Firewall config=\"${config_id}\" src_interface=\"${src_network}\" dest_interface=\"${dest_network}\" output=\"${output}\" input=\"${input}\"." | |
if [ "${output}" -eq 0 -a "${input}" -eq 0 ]; then | |
return 0 | |
fi | |
if [ "$(uci get network.${src_network}.ip6class 2>/dev/null)" != "local" ]; then | |
log "Warning: network '${src_network}' option ip6class should be 'local'!" | |
fi | |
# FIXME: add warning requiring dhcp.*.ra_default='2'?, ...ra='server'?, ...dhcpv6 'server'? | |
local dest_device | |
if ! network_get_device dest_device "${dest_network}"; then | |
debug "... failed. Destination network '${dest_network}' has no device." | |
return 0 | |
fi | |
local done_net_dev | |
for done_net_dev in ${DONE_NETWORK_DEVICES_O}; do | |
if [ "${done_net_dev}" = "${dest_device}" ]; then | |
log " ... skipping: already configured device '${dest_device}', so leaving as is." | |
return 0 | |
fi | |
done | |
log "Configuring NPTv6 for '${src_network}' -> '${dest_network}' via '${dest_device}' ..." | |
local dest_subnet | |
if ! network_get_subnet6 dest_subnet ${dest_device}; then | |
log " ... skipping: Destination network '${dest_network}' has no IPv6 subnet." | |
return 0 | |
fi | |
debug "Using '${dest_device}' subnet: ${dest_subnet}" | |
local src_device | |
network_get_device src_device "${src_network}" || return 0 | |
local src_subnet | |
network_get_subnet6 src_subnet ${src_network} || return 0 | |
debug "Using '${src_network}' subnet: ${src_subnet}" | |
# `-r` prevents error: "-e expression #1, char 20: invalid reference \1 on `s' command's RHS" | |
# `.*` is needed to match (and replace) the whole line | |
local src_prefix="$(echo "${src_subnet}" | sed -nr 's/.*(\/\d+)$/\1/p')" | |
local dest_prefix="$(echo "${dest_subnet}" | sed -nr 's/.*(\/\d+)$/\1/p')" | |
if [ "${src_prefix}" != "${dest_prefix}" ]; then | |
log " ... ignoring: subnets for '${src_network}' and '${dest_network}' don't have the same IPv6 prefix length." | |
return 0 | |
fi | |
if [ "${output}" -eq 1 ]; then | |
log "Mapping output from ${src_subnet} (${src_network}) to $dest_subnet ($dest_device)" | |
ip6t_add POSTROUTING -t nat -o "${dest_device}" \ | |
-s "${src_subnet}" -j NETMAP --to "${dest_subnet}" \ | |
-m comment --comment "!fw3: NPTv6 output: ${src_network} -> ${dest_device}" | |
fi | |
if [ "${input}" -eq 1 ]; then | |
log "Mapping input from $dest_subnet ($dest_device) to ${src_subnet} (${src_network})" | |
ip6t_add PREROUTING -t nat -i "${dest_device}" \ | |
-d "${dest_subnet}" -j NETMAP --to "${src_subnet}" \ | |
-m comment --comment "!fw3: NPTv6 input: ${dest_device} -> ${src_network}" | |
fi | |
log "Configuring NPTv6 for '${src_network}' -> '${dest_network}' via '${dest_device}' ... done" | |
} | |
main() { | |
nat6_init | |
config_load firewall | |
CONFIG_APPEND=1 config_load network | |
config_foreach handle_npt6 npt6 | |
} | |
main "${@}" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment