Files
rc.router/rc.subr
2026-09-02 05:57:04 -04:00

518 lines
12 KiB
Bash

##########################################################################
##
## The rc.router Project
## Copyright (C) 2026 ADAM David Alan Martin
##
## This program is free software: you can redistribute it and/or modify
## it under the terms of the GNU Affero General Public License as
## published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
##
## You should have received a copy of the
## GNU Affero General Public License along with this program. If not,
## see <http://www.gnu.org/licenses/>.
##
##########################################################################
###########################################################
# Print debugging info, if `debug_conf` is set.
_echo()
{
if [ "${debug_conf}" = "YES" ]
then
echo 1>&2 $*
fi
}
###########################################################
# Log warning info.
_warn()
{
if [ -x /usr/bin/logger ]; then
logger "$0: WARNING: $*"
fi
echo 1>&2 "$0: WARNING: $*"
}
###########################################################
# Repeat a command for multiple arguments
#
# - First param is the name of the function or binary to repeat.
# - Remaining arguments are each passed to the function.
#
# Example: __repeat touch Hello World
__repeat()
{
__func=$1;shift 1
while [ "$1" != "" ]
do
${__func} $1
shift 1
done
}
###########################################################
# Create the specified single interface
#
# This adds the interface to the `cloned_interfaces`
# rc.conf variable
__clone_interface()
{
_echo Adding $1 to cloned interfaces
cloned_interfaces="${cloned_interfaces} $1"
_echo "Cloned interfaces are now \"${cloned_interfaces}\""
}
###########################################################
# Create the specified list of interfaces.
clone_interfaces()
{
__repeat __clone_interface $*
}
###########################################################
# Create the specified interface
# (optionally create more interfaces)
clone_interface()
{
clone_interfaces $*
}
__add_wireguard_interface()
{
_echo "Adding $1 to ${wireguard_interfaces}"
wireguard_enable="YES"
wireguard_interfaces="${wireguard_interfaces} $1"
}
add_wireguard_interfaces()
{
__repeat __add_wireguard_interface $*
}
add_wireguard_interface()
{
add_wireguard_interfaces $*
}
add_wireguard_route()
{
add_route_policy $1 $2
add_wireguard_interface wg$2
}
add_wireguard_route_v4()
{
add_route_policy_v4 $1 $2
# Assuming v6 did this, for now...
#add_wireguard_interface wg$2
}
compute_56_net()
{
_echo Running 56 > /dev/fd/2
_echo 'Computing for `'$1'`' > /dev/fd/2
echo $1 | sha1 | cut -c 1-12 | sed -e 's/\([0-9a-f][0-9a-f]\)/\1:/g' | awk -F: '{print "fd"$1":"$2$3":"$4$5":"$6}'
}
compute_48_net()
{
_echo Running 48 > /dev/fd/2
_echo 'Computing for `'$1'`' > /dev/fd/2
echo $1 | sha1 | cut -c 1-10 | sed -e 's/\([0-9a-f][0-9a-f]\)/\1:/g' | awk -F: '{print "fd"$1":"$2$3":"$4$5":}'
}
###########################################################
# Compute the ULA prefix for the specified subnet size
# using the second argument (string) as a seed.
#
# Note: Only `/56` and `/48` ULA prefixes are supported at
# this time.
compute_net() {
_echo 'Computing for `'$2'`' > /dev/fd/2
case $1 in
56)
compute_56_net "$2"
;;
48)
compute_48_net "$2"
;;
esac
}
compute_v4_net()
{
_prefix_name=v4_net_${1}
_prefix=\${${_prefix_name}}
_subnet=$2
echo "${_prefix}.${_subnet}.1/24"
}
compute_v6_net()
{
_subnet=$2
_prefix=$1
echo "${_prefix}${_subnet}::1"
}
_compute_nets()
{
_card=$1
shift 1
_vlan=$1
shift 1
_base=$((${_vlan}%10))
_net=$((${_vlan}/100))
_subnet=${_net}${_base}
_extra_address=""
if [ -z "${1}" ]
then
#echo "Error in configuration: No network class given"
#exit 1
sleep 0
fi
_class=$1
shift 1
_priv="YES"
while true
do
case ${1} in
"no_ula")
_priv="NO"
;;
"no_isp")
_isp=""
;;
isp=*)
_isp=`echo ${1} | sed -e 's/isp=//'`
;;
*)
break
;;
esac
shift 1
done
while [ -n "${1}" ]
do
_extra_address="${_extra_address} $1"
shift 1
done
}
_add_vlan()
{
_card=$1
_vlan=$2
_vlans_var=vlans_${_card}
eval _vlans="\${${_vlans_var}}"
_echo "XXX ${_vlans} XXX"
_echo "QQQ ${_vlans} QQQ" | grep ${_vlan}
_qres=$?
_echo $_qres
echo "${_vlans}" | grep ${_vlan} > /dev/null
_res=$?
_echo $_res
if [ 0 -ne $_res ]
then
_echo vlans_${_card}="\${vlans_${_card}} ${_vlan}"
eval vlans_${_card}=\"\${vlans_${_card}} ${_vlan}\"
else
_echo "Not adding vlan ${_vlan} to card ${_card}"
fi
}
_add_rtadv()
{
_card=$1
_vlan=$2
_subcard="${_card}.${_vlan}"
_echo "XXX ${rtadvd_interfaces} XXX"
_echo "QQQ ${rtadvd_interfaces} QQQ" | grep ${_subcard}
_qres=$?
_echo $_qres
echo "${rtadvd_interfaces}" | grep ${_subcard} > /dev/null
_res=$?
_echo $_res
if [ 0 -ne $_res ]
then
_echo rtadvd_interfaces="${rtadvd_interfaces} ${_subcard}"
rtadvd_interfaces="${rtadvd_interfaces} ${_subcard}"
else
_echo "Not adding subcard ${_subcard} to ipv6 rtadvd list: ${rtadvd_interfaces}"
fi
}
_add_dhcpv6()
{
_iface=${1}
_isp=${2}
if [ -z "${_isp}" ]
then
_isp=${default_isp}
fi
eval dhcpv6_${_isp}_list=\"\${dhcpv6_${_isp}_list} ${_iface}\"
}
add_dhcpv6()
{
_iface=${1}
_sla=${2}
_isp=${3}
if [ -z "${_isp}" ]
then
_isp=${default_isp}
fi
eval dhcpv6_${_isp}_supplement_list=\"\${dhcpv6_${_isp}_supplement_list} ${_iface}/${_sla}\"
}
_emit_dhcpv6_for_iface()
{
_iface=${1}
_sla=${2}
echo " prefix-interface ${_iface}" >> ${_dhcp_file}
echo " {" >> ${_dhcp_file}
echo " sla-id ${_sla};" >> ${_dhcp_file}
echo " sla-len $((64 - ${_prefix_len}));" >> ${_dhcp_file}
echo " };" >> ${_dhcp_file}
echo "" >> ${_dhcp_file}
}
_emit_dhcpv6_for_isp()
{
_isp=${1}
eval _interface=\"\${dhcp_interface_${_isp}}\"
_n=${2}
eval _prefix_len=\"\${dhcp_prefix_${_isp}}\"
mkdir -p ${router_conf_dir}/gen
_dhcp_file=${router_conf_dir}/gen/dhcp6c.${_isp}.conf
eval _isp_list=\${dhcpv6_${_isp}_list}
eval _isp_supplement_list=\${dhcpv6_${_isp}_supplement_list}
_echo "Building DHCP info for ${_isp} (interfaces: ${_isp_list})"
echo "" > ${_dhcp_file}
echo "id-assoc pd $_n" >> ${_dhcp_file}
echo "{" >> ${_dhcp_file}
echo " prefix ::/${_prefix_len} infinity;" >> ${_dhcp_file}
for _iface_desc in ${_isp_supplement_list}
do
_iface=`echo ${_iface_desc} | sed -e 's;/.*$;;'`
_sla=`echo ${_iface_desc} | sed -e 's;^.*/;;'`
_emit_dhcpv6_for_iface ${_iface} ${_sla}
done
for _iface in ${_isp_list}
do
_vlan=`echo ${_iface} | sed -e 's/^.*\.//'`
_num=`echo ${_vlan} | sed -e 's/\([0-9]\)0\([0-9]\)/\1\2/'`
_vlan_sla=`printf "%d" 0x${_num}`
_emit_dhcpv6_for_iface ${_iface} ${_vlan_sla}
done
echo "};" >> ${_dhcp_file}
echo "" >> ${_dhcp_file}
echo "interface ${_interface} {" >> ${_dhcp_file}
echo " send ia-pd ${_n};" >> ${_dhcp_file}
echo "};" >> ${_dhcp_file}
}
_emit_dhcpv6()
{
_n=0
for _isp in ${dhcp_isps}
do
_emit_dhcpv6_for_isp $_isp ${_n}
_n=$((${_n}+1))
done
}
add_v4_net()
{
_compute_nets $*
_add_vlan $_card $_vlan
_class_group=""
if [ ! -z "${_class}" ]
then
_class_group="group ${_class}"
fi
eval ifconfig_${_card}_${_vlan}=\"$(compute_v4_net $_card $_subnet) group internal ${_class_group}\"
eval dhcpd_ifaces=\"${dhcpd_ifaces} ${_card}.${_vlan}\"
}
_add_v6_prefixes()
{
_next_alias=$1
shift 1
while [ -n "${1}" ]
do
eval ifconfig_${_card}_${_vlan}_alias${_next_alias}=\"inet6 $1/64\"
shift 1
_next_alias=`expr $_next_alias + 1`
done
}
###########################################################
# Create a VLAN on the specified network interface with
# the specififed VLAN-ID, interface group, and optional
# address parameters.
#
# @1 - The underlying network interface to use.
# @2 - The VLAN-ID of the VLAN to create.
# @3 - The network interface group to put this new
# VLAN into. (Uses: `ifconfig @dev group @3`)
# @* - Additional networks to join (or adjustments
# to make).
# - `no_ula` Make this VLAN not use a ULA.
# - `no_isp` Make this VLAN not use any ISP prefixes
# (from DHCPv6)
# - `isp:${isp}` Put this VLAN under the specified ISP
# - `2001:db8:42::` Make this VLAN have `2001:db8:42::/64`
# as a prefix.
#
add_v6_net()
{
_isp=${default_isp}
_compute_nets $*
_add_vlan $_card $_vlan
_add_rtadv $_card $_vlan
_class_group=""
if [ ! -z "${_class}" ]
then
_class_group="group ${_class}"
fi
eval ifconfig_${_card}_${_vlan}_ipv6=\"inet6 fe80::1/64 group internal ${_class_group}\"
_next_alias=0
if [ "${_priv}" = "YES" ]
then
eval ifconfig_${_card}_${_vlan}_alias${_next_alias}=\"inet6 $(compute_v6_net ${secret_ip6_net} ${_subnet})/64\"
_next_alias=1 # UGLY HACK!!! Does BASH increment work in BSD's sh?
fi
if [ -n "${_isp}" ]
then
_add_dhcpv6 $_card.$_vlan ${_isp}
fi
_add_v6_prefixes ${_next_alias} ${_extra_address}
}
add_net()
{
add_v4_net $*
add_v6_net $*
}
option_selected()
{
eval _value=\$${1}
_echo "option_selected: $1 is set to $_value."
case $_value in
# "yes", "true", "on", or "1"
[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1)
echo "YES"
;;
# "no", "false", "off", or "0"
[Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|[Oo][Ff][Ff]|0)
echo "NO"
;;
*)
_warn "\$${1} is not set properly - see rc.conf(5)."
echo "FAILED"
;;
esac
}
build_zoneinfo_for_host()
{
_ISP_PREFIX=${1};shift 1
_MAIN_SUBNET=${1};shift 1
_host=$( echo ${1} | sed -e 's/=.*$//' )
# Not really the MAC, but maybe that's the right way?
# For now it's host=SUFFIX
_MAC=$( echo ${1} | sed -e 's/^.*=//' )
#echo "Host: ${_host} --- MAC: ${_MAC}"
echo "${_host}" IN AAAA ${_ISP_PREFIX}${_MAIN_SUBNET}:${_MAC}
}
build_zoneinfo()
{
_prefix=${1} ; shift 1
_subnet=${1} ; shift 1
echo '$TTL 1h30m'
echo '@ IN SOA '"${localnet_zoneinfo_master_nameserver}"'. '"${localnet_zoneinfo_email}"'. ('
date +'%s'
echo 7200
echo 1200
echo 7200
echo 5400
echo ')'
echo '$ORIGIN '"${localnet_zoneinfo_domain}"'.'
for nameserver in ${localnet_zoneinfo_nameservers}
do
echo '@ IN NS '"${nameserver}"'.'
done
for _hostline in $(cat ${1})
do
#echo ${_prefix} ${_subnet} ${_hostline}
case "${_hostline}" in
"#"*)
continue
;;
esac
build_zoneinfo_for_host ${_prefix} ${_subnet} ${_hostline}
done
}
# Now we compute a prefix from the hash of the network name.
# This lets us be deterministic, yet still know what our
# net name is.
#
#compute_net 56 "${ip6_net_name}"
secret_ip6_net=$(compute_net ${ip6_net_size} "${ip6_net_name}")
. ${router_conf_dir}/pf-framework.subr
# vim: ft=bash