diff --git a/LICENSE b/LICENSE index da3a492..d7c3f54 100644 --- a/LICENSE +++ b/LICENSE @@ -220,7 +220,7 @@ If you develop a new program, and you want it to be of the greatest possible use To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. rc.router - Copyright (C) 2026 adam + 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. diff --git a/README.md b/README.md index 8664faa..2f3e9c1 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,121 @@ # rc.router -FreeBSD's Init/RC system could use some extensions for easy configuration of homelab routers. \ No newline at end of file +The FreeBSD Init/RC system could use some extensions for easy configuration of homelab +and SOHO routers. + +These files provide such a set of extensions. These extensions, while providing a lot +of advanced functionality, are not meant for anything other than edge routers. +"Core router" functionality (which bridges between transit networks) is not provided +for by this configuration mechanism. + +`rc.router` requires FreeBSD-15 or greater. (It might work for some configurations +on FreeBSD-14, but I no longer run any routers with it on FreeBSD-14.) + +## How to setup (Easy dual-stack example With Git) + +Executing this sequence of sample commands will setup a basic Dual-stack router +for Verizon FIOS. There are a few pre-canned ISP configuration files which help +guide what settings are needed for which ISPs. (Mostly this impacts IPv6 settings +not IPv4 settings.) + +``` +$ cd /etc +$ mkdir router-conf +$ cd router-conf +$ git init . +$ echo << EOF > rc.conf +# Configure a basic external v4 ISP +external_card="em0" + +add_canned_isp ${external_card} fios_1 verizon_fios + + +# Configure a basic internal v4 network +internal_card="re0" +add_net ${internal_card} 0 main + +EOF +$ git add rc.conf +$ git submodule add rc.router https://gitea.nerdland.org/adam/rc.router +$ git commit -m "FIRST POST!!!" +$ echo 'rc_router_dir="/etc/router-conf/rc.router"' >> /etc/rc.conf +$ echo 'rc_router_conf_dif="/etc/router-conf"' >> /etc/rc.conf +$ echo ". ${rc_router_dir}/rc.entry" >> /etc/rc.conf +``` + +## How to setup (Easy v4-only example With Git) + +Executing this sequence of sample commands will setup a basic v4 NAT/dhcp router. + +``` +$ cd /etc +$ mkdir router-conf +$ cd router-conf +$ git init . +$ echo << EOF > rc.conf +# Configure a basic external v4 ISP +external_card="em0" +add_v4_isp ${external_card} dhcp + + + +# Configure a basic internal v4 network +internal_card="re0" +v4_net="172.26" + +add_v4_net ${internal_card} 0 main +EOF +$ git add rc.conf +$ git submodule add rc.router https://gitea.nerdland.org/adam/rc.router +$ git commit -m "FIRST POST!!!" +$ echo 'rc_router_dir="/etc/router-conf/rc.router"' >> /etc/rc.conf +$ echo 'rc_router_conf_dif="/etc/router-conf"' >> /etc/rc.conf +$ echo ". ${rc_router_dir}/rc.entry" >> /etc/rc.conf +``` + +In this example, `em0` will be the uplink card, and will dhcpv4 configure itself. `re0` is +the primary internal interface card, which will be configured to host DHCP for internal +`172.26.0.0/24` addresses, in the basic default pool of `172.26.0.100` thru `172.26.0.200`. +The router's internal address in this subnet is `172.26.0.1/24`. And `8.8.8.8` will be used +as the default resolver for everyone within this subnet. + +## Interoperation with regular FreeBSD `init` and `rc.conf` configuration + +These scripts are not meant to replace the FreeBSD init script mechanisms. Instead these +scripts seek to augment the system. The scripts provide ways to set clusters of related +config variables (and to generate other related configuration files) such that hybrid +configurations can be made. The various commands, such as `add_v4_net` result in the +setting and updating of various `rc.conf` variables as-if the user simply wrote the correct +configuration settings at that point in the `rc.conf` file. + +As such, these mechanisms are meant to co-exist with manual variable setting and manually +maintained configuration files for things such as DHCP. This permits an adoption path +other than "big bang cutover". The `rc.router` commands are careful to update-by-append +to certain variables, rather than to assume direct ownership of all variables. Thus, +`add_net xy0 402` and `ifconfig_xy0_43_alias0="192.168.10.4/24"` could be safely +intermingled. (Right now, the scripts assume ownership over specific related +variables for interfaces and vlans that they create.) + +## IPv6 posture and readiness + +`rc.router` is designed as an IPv6-first package. While IPv4-only networks are still +possible with this package, the defaults are tuned for and assume IPv6. Pre-canned +router configurations should gracefully (albeit with some timeouts) fail back to v4-only +modes, when IPv6 is not available from those ISPs, due to CPE concerns. + +IPv6 configuration with `rc.router` permits DHCPv6 `IA_PD` with Router Advertisements or +static configuration for the ISP side. For the internal side, SLAAC is the only +configuration provided. DHCPv6 internally for `IA_NA` is not implemented. `rc.router` +is not expliticly incompatible with DHCPv6 internal provisioning, but no effort has +been made to implement or test it. Given that a large number of consumer devices +(Android, in particular) refuse to provision via DHCPv6, SLAAC must be used internally +for any deployment to accommodate the widest swathe of client devices. + +`rc.router` installs `tayga` and users are encouraged to use its `map` feature in +the configuration file to provide IPv4 addresses for IPv6-only hosts, or vice-versa. +This is intended to permit incremental migration for IPv4-only homelabs to dual-stack +and IPv6-only deployments. + +The DHCPv4 configuration generator is capable of emitting Option 108 for IPv6-mostly +deployments. +. diff --git a/defaults/rc.conf b/defaults/rc.conf new file mode 100644 index 0000000..d77042f --- /dev/null +++ b/defaults/rc.conf @@ -0,0 +1,43 @@ +#!/bin/sh + +# This is rc.conf defaults for rc.router - a file full of useful +# variables that you can set to change the default startup behavior +# of your system. You should not edit this file! Put any +# overrides into one of the ${rc_conf_files} instead and you will +# be able to update these defaults later without spamming your +# local configuration information. +# +# The ${rc_conf_files} files should only contain values which override +# values set in this file. This eases the upgrade path when defaults +# are changed and new features are added. +# +# All arguments must be in double or single quotes. +# +# For a more detailed explanation of all the rc.conf variables, please +# refer to the rc.conf(5) manual page. +# +# For the `rc.router` project, router configurations should be tuned in +# `${rc_router_conf_dir}/rc.conf`. + +rc_router_debug="NO" + +generate_internal_dhcpv4_enable='YES' +generate_isp_dhcp_enable='YES' +generate_rtadvd_conf_enable="YES" + +rc_router_gen_dir=${rc_router_dir}/gen + +localnet_enable='NO' +rtadvd_pltimedecr='NO' +rtadvd_vltimedecr='NO' + +v4_net="192.168" +dhcpv4_pool_lo="100" +dhcpv4_pool_hi="200" + +rc_router_network_name="Default Network Name" +rc_router_ula_prefix_size="48" + +rtsold_flags="-F" + +# vim: ft=bash diff --git a/harness/etc/rc.conf b/harness/etc/rc.conf new file mode 100644 index 0000000..3d1202d --- /dev/null +++ b/harness/etc/rc.conf @@ -0,0 +1,11 @@ +#!/bin/sh + +# This emulates a minimal `/etc/rc.conf` that one would create in FreeBSD to +# hand off control to `rc.router`. + +rc_router_dir="${RC_ROUTER_HARNESS}/.." +rc_router_conf_dir="${RC_ROUTER_HARNESS}/../test-conf/" + +. ${rc_router_dir}/rc.entry + +# vim: ft=bash diff --git a/harness/etc/rc.subr b/harness/etc/rc.subr new file mode 100644 index 0000000..fe8be72 --- /dev/null +++ b/harness/etc/rc.subr @@ -0,0 +1,29 @@ +#!/bin/sh + +# This file exists to let me test the function of these scripts outside +# of the FreeBSD RC environment. +# +# Just enough bare minimum facilities are present, to permit my rc.d scripts +# to run. I mostly use this to test the scripts which generate configuration +# files. + +load_rc_config() +{ + true +} + + +run_rc_command() +{ + if [ -z "${1}" ] + then + echo "Error. Must supply an rc.d script command." + exit 254 + fi + eval \${${1}_cmd} +} + + +. ${RC_ROUTER_HARNESS}/etc/rc.conf + +# vim: ft=bash diff --git a/hints/verizon_fios b/hints/verizon_fios new file mode 100644 index 0000000..8d50aa1 --- /dev/null +++ b/hints/verizon_fios @@ -0,0 +1,5 @@ +#!/bin/sh + +v6_prefix_len="56" + +# vim: ft=bash diff --git a/hints/xfinity b/hints/xfinity new file mode 100644 index 0000000..0fbb3dc --- /dev/null +++ b/hints/xfinity @@ -0,0 +1,5 @@ +#!/bin/sh + +v6_prefix_len="60" + +# vim: ft=bash diff --git a/pf-framework.subr b/pf-framework.subr new file mode 100755 index 0000000..9453f25 --- /dev/null +++ b/pf-framework.subr @@ -0,0 +1,73 @@ +_genfile="${route_policy_file}" +if [ -z "${_genfile}" ] +then + _genfile="/dev/fd/1" +fi + +__install_route_policy() +{ + iface=$1;shift 1 + network=$1;shift 1 + table=$1;shift 1 + + echo "match out on ${iface} from !fd00::/8 to ${network} rtable ${table}" >> $1 + echo "match in on ${iface} from ${network} to !fd00::/8 rtable ${table}" >> $1 +} + +__install_route_policy_v4() +{ + iface=$1;shift 1 + network=$1;shift 1 + table=$1;shift 1 + + echo "match out on ${iface} to ${network} rtable ${table}" >> $1 + echo "match in on ${iface} from ${network} rtable ${table}" >> $1 +} + +_install_route_policy() +{ + iface=$1;shift 1 + split=`echo $1 | sed -e 's/->/ /g'`;shift 1 + + __install_route_policy $iface ${split} $* +} + +_install_route_policy_v4() +{ + iface=$1;shift 1 + split=`echo $1 | sed -e 's/->/ /g'`;shift 1 + + __install_route_policy_v4 $iface ${split} $* +} + +install_route_policy() +{ + _install_route_policy internal $* ${_genfile} +} + +install_route_policy_v4() +{ + _install_route_policy_v4 internal $* ${_genfile} +} + + +add_route_policy() +{ + route_policies="${route_policies} $1->$2" +} + +add_route_policy_v4() +{ + route_policies_v4="${route_policies_v4} $1->$2" +} + +clear_all_policies() +{ + echo -n > $_genfile +} + +#add_route_policy 2602:f6a8:1::/48@9 +#add_route_policy 2001:db8:f6a8:1::/64@42 + + +# vim: ft=bash diff --git a/rc.d/generate_internal_dhcpv4 b/rc.d/generate_internal_dhcpv4 new file mode 100755 index 0000000..02388af --- /dev/null +++ b/rc.d/generate_internal_dhcpv4 @@ -0,0 +1,35 @@ +#!/bin/sh +# +# $FreeBSD$ +# +# PROVIDE: generate_internal_dhcpv4 +# REQUIRE: netif +# BEFORE: isc-dhcp +# KEYWORD: shutdown +# + +. ${RC_ROUTER_HARNESS}/etc/rc.subr + +name='generate_internal_dhcpv4' + +start_cmd='generate_dhcp_start' +stop_cmd='generate_dhcp_stop' +rcvar='generate_internal_dhcpv4_enable' + +load_rc_config 'generate_internal_dhcpv4' + +generate_dhcp_start() +{ + _emit_dhcpv4 +} + +generate_dhcp_stop() +{ + true +} + +################ Epilogue + +run_rc_command "$1" + +# vim: ft=bash diff --git a/rc.d/generate_isp_dhcp b/rc.d/generate_isp_dhcp new file mode 100755 index 0000000..76ec30a --- /dev/null +++ b/rc.d/generate_isp_dhcp @@ -0,0 +1,36 @@ +#!/bin/sh +# +# $FreeBSD$ +# +# PROVIDE: generate_isp_dhcp +# REQUIRE: netif +# BEFORE: dhcp6c +# KEYWORD: shutdown +# + +. ${RC_ROUTER_HARNESS}/etc/rc.subr + +name='generate_isp_dhcp' + +start_cmd='generate_isp_dhcp_start' +stop_cmd='generate_isp_dhcp_stop' +rcvar='generate_isp_dhcp_enable' + +load_rc_config 'generate_isp_dhcp' + +generate_isp_dhcp_start() +{ + _emit_dhcpv6 + cat ${rc_router_gen_dir}/dhcp6c.*.conf > ${rc_router_gen_dir}/dhcp6c.conf +} + +generate_isp_dhcp_stop() +{ + true +} + +################ Epilogue + +run_rc_command "$1" + +# vim: ft=bash diff --git a/rc.d/generate_rtadvd_conf b/rc.d/generate_rtadvd_conf new file mode 100755 index 0000000..cb35fb1 --- /dev/null +++ b/rc.d/generate_rtadvd_conf @@ -0,0 +1,131 @@ +#!/bin/sh +# +# $FreeBSD$ +# +# PROVIDE: generate_rtadvd_conf +# REQUIRE: dhcp6c +# BEFORE: rtadvd +# KEYWORD: shutdown +# + +. ${RC_ROUTER_HARNESS}/etc/rc.subr + +name='generate_rtadvd_conf' + +start_cmd='generate_rtadvd_conf_start' +stop_cmd='generate_rtadvd_conf_stop' +rcvar='generate_rtadvd_conf_enable' + +load_rc_config 'generate_rtadvd_conf' + +[ -z "$generate_rtadvd_conf_enable" ] && generate_rtadvd_conf_enable='NO' + +_prepare_var() +{ + var=$1 + opt=$2 + + eval _value=\${${var}} + eval _${opt}="" + if [ -n "${_value}" ] + then + _text="${opt}=\\\"${_value}\\\"" + eval _${opt}=\"${_text}\" + fi +} + +_prepare_num() +{ + var=$1 + opt=$2 + + eval _value=\${${var}} + eval _${opt}\="" + if [ -n "${_value}" ] + then + _text="${opt}\#${_value}" + eval _${opt}=\"${_text}\" + fi +} + +_prepare_opt() +{ + var=$1 + opt=$2 + + eval _${opt}\="" + if [ `option_selected ${var}` = "YES" ] + then + _text="${opt}" + eval _${opt}=\"${_text}\" + fi +} + + +_build_rtadv() +{ + gen_rtadv_dir=${rc_router_gen_dir} + rtadvd_conf=${gen_rtadv_dir}/rtadvd.conf + mkdir -p ${gen_rtadv_dir} + + _search_list="" + if [ -n "${rtadvd_dns_search_list}" ] + then + _search_list=$( echo ${rtadvd_dns_search_list} | sed -e 's/ */,/g' -e 's/^,//' -e 's/,$//' ) + fi + + _prepare_var _search_list dnssl + + _prepare_var rtadvd_pref64_prefix pref64 + + _prepare_num rtadvd_mininterval mininterval + _prepare_num rtadvd_maxinterval maxinterval + + _prepare_num rtadvd_pltime pltime + _prepare_opt rtadvd_pltimedecr pltimedecr + + _prepare_num rtadvd_vltime vltime + _prepare_opt rtadvd_vltimedecr vltimedecr + + _prepare_num rtadvd_rltime rltime + + cat ${rc_router_dir}/templates/rtadvd.conf \ + | sed \ + -e "s/@PREF64@/${_pref64}/" \ + -e "s/@MININTERVAL@/${_mininterval}/" \ + -e "s/@MAXINTERVAL@/${_maxinterval}/" \ + -e "s/@PLTIME@/${_pltime}/" \ + -e "s/@PLTIME_DECR@/${_pltimedecr}/" \ + -e "s/@VLTIME@/${_vltime}/" \ + -e "s/@VLTIME_DECR@/${_vltimedecr}/" \ + -e "s/@RLTIME@/${_rltime}/" \ + -e "s/@DNS_SEARCHLIST@/${_dnssl}/" \ + -e "s/@DNS_SERVER@/${rtadvd_dns_server}/" \ + > ${rtadvd_conf} + + + echo "" >> ${rtadvd_conf} + echo "" >> ${rtadvd_conf} + + for interface in ${rtadvd_interfaces} + do + echo "${interface}:tc=settings" >> ${rtadvd_conf} + done +} + + +generate_rtadvd_conf_start() +{ + _build_rtadv +} + +generate_rtadvd_conf_stop() +{ + true +} + +################ Epilogue + +run_rc_command "$1" + +# vim: ft=bash diff --git a/rc.d/localnet b/rc.d/localnet new file mode 100755 index 0000000..b3f2c7a --- /dev/null +++ b/rc.d/localnet @@ -0,0 +1,103 @@ +#!/bin/sh +# +# $FreeBSD$ +# +# PROVIDE: localnet +# REQUIRE: dhcp6c +# BEFORE: named +# KEYWORD: shutdown +# + +# The purpose of this script is to generate various ISP prefix dependent files. +# +# Specifically, the DNS (BIND) acls file for handling various views, a local ISP +# range address for the router itself (on a dedicated loopback device), and +# attach any static addresses for this machine to another loopback device. + +. ${RC_ROUTER_HARNESS}/etc/rc.subr + + +name='localnet' + +extra_commands="rebuild_zoneinfo" +start_cmd='localnet_start' +stop_cmd='localnet_stop' +rebuild_zoneinfo_cmd="localnet_build_zoneinfo" +rcvar='localnet_enable' + +load_rc_config 'localnet' + + +localnet_build_zoneinfo() +{ + compute_addrs + localnet_build_zoneinfo_impl +} + + +localnet_build_zoneinfo_impl() +{ + #build_zoneinfo ${isp_prefix} 31 ${router_conf_dir}/namedb/local-machines.defs + build_zoneinfo ${isp_prefix} 31 ${rc_router_conf_dir}/namedb/local-machines.defs > ${rc_router_gen_dir}/zone.hosts.nerdland.org +} + +localnet_build_acls() +{ + gen_acls_dir=${rc_router_gen_dir}/namedb + mkdir -p ${gen_acls_dir} + cat ${rc_router_dir}/templates/named.acls.conf \ + | sed \ + -e "s/@ISP_PREFIX@/${isp_prefix}/g" \ + -e "s/@ULA_PREFIX@/${secret_ip6_net}/g" \ + > ${gen_acls_dir}/acls.conf +} + +compute_addrs() { + + loop=0 + while [ -z "${v6addr}" -a ${loop} -lt 5 ] + do + [ ${loop} -ne 0 ] && sleep 5 + # todo: Adjust our ipv6 hint... get from a var? + v6addr=$(ifconfig ${localnet_first_nic} | grep "inet6 [23]...:" | head -1 | awk '{print $2}') + echo "V6 addr is: ${v6addr}" + loop=$(( ${loop} + 1 )) + done + + echo "V6 addr is: ${v6addr}" + three_quads=$(echo ${v6addr} | awk -F ':' '{print $1":"$2":"$3}') + echo "First three quads are: ${three_quads}" + last_quad=$(echo ${v6addr} | awk -F ':' '{print $4}') + echo "Last quad is: ${last_quad}" + last_prefix=$(echo ${last_quad} | rev | awk '{print $1"0000"}' | cut -c 3,4 | rev | sed -e 's/^0//') + echo "Last prefix is: ${last_prefix}" + + isp_prefix="${three_quads}:${last_prefix}" + base_addr=" inet6 ${isp_prefix}ff::1" +} + + +localnet_start() { + cp ${rc_router_conf_dir}/resolv.conf.master ${rc_router_conf_dir}/resolv.conf + compute_addrs + ifconfig lo1 create ${base_addr} prefer_source + + # DO NOT USE prefer_source for this address... + # It will cause confusion for the wireguard tunnels underneath... + # It also could cause DNS recursion to fail -- routing as the nested + # connections... + + localnet_build_acls + localnet_build_zoneinfo_impl +} + +localnet_stop() { + compute_addrs + ifconfig lo0 ${base_addr} delete + + ifconfig lo1 destroy +} + +run_rc_command "$1" + +# vim: ft=bash diff --git a/rc.entry b/rc.entry new file mode 100644 index 0000000..44adf9d --- /dev/null +++ b/rc.entry @@ -0,0 +1,25 @@ +#!/bin/sh + +if [ -z "${rc_router_dir}" ] +then + echo "Unable to proceed -- rc_router_dir is not set." >& /dev/fd/2 + echo "You must set this variable before sourcing `rc.router/rc.entry`" >& /dev/fd/2 + exit 254 +fi + +if [ -z "${rc_router_conf_dir}" ] +then + echo "Unable to proceed -- rc_router_conf_dir is not set." >& /dev/fd/2 + echo "You must set this variable before sourcing `rc.router/rc.entry`" >& /dev/fd/2 + exit 254 +fi + +. ${rc_router_dir}/rc.subr + +. ${rc_router_dir}/defaults/rc.conf + +. ${rc_router_conf_dir}/rc.conf + +_rc_router_epilogue + +# vim: ft=bash diff --git a/rc.subr b/rc.subr new file mode 100644 index 0000000..845c3b6 --- /dev/null +++ b/rc.subr @@ -0,0 +1,629 @@ +_debug() +{ + echo 1>&2 $* +} + +_echo() +{ + if [ "${rc_router_debug}" = "YES" ] + then + _debug $* + fi +} + +_warn() +{ + if [ -x /usr/bin/logger ]; then + logger "$0: WARNING: $*" + fi + echo 1>&2 "$0: WARNING: $*" +} + +__repeat() +{ + __func=$1;shift 1 + + while [ "$1" != "" ] + do + ${__func} $1 + shift 1 + done +} + +__clone_interface() +{ + _echo Adding $1 to cloned interfaces + cloned_interfaces="${cloned_interfaces} $1" + _echo "Cloned interfaces are now \"${cloned_interfaces}\"" +} + +clone_interfaces() +{ + __repeat __clone_interface $* +} + +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 | _hash | 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 | _hash | 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_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() +{ + _card=$(echo ${_iface} | sed -e's/\.[0-9]*//') + eval _net=\${v4_net_${_card}} + if [ -z "${_net}" ] + then + _net=${v4_net} + fi + _prefix=${_net} + _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 + 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 + echo "No ISP found..." + exit 255 + 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 ${rc_router_gen_dir} + _dhcp_file=${rc_router_gen_dir}/dhcp6c.${_isp}.conf + eval _isp_list=\${dhcpv6_${_isp}_list} + _echo "Checking ${_isp} against default: ${default_isp}" + if [ "${_isp}" = "${default_isp}" ] + then + _isp_list="${dhcpv6_default_list} ${_isp_list}" + fi + eval _isp_supplement_list=\${dhcpv6_${_isp}_supplement_list} + + _echo "Building DHCP info for ${_isp} (interfaces: ${_isp_list})" + + echo "######################" > ${_dhcp_file} + echo "# Configuration for ${_isp} on ${_interface}" >> ${_dhcp_file} + 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} + echo "" >> ${_dhcp_file} + echo "" >> ${_dhcp_file} +} + +_emit_dhcpv6() +{ + _n=0 + + _echo "Emitting DHCPv6, and default ISP is: ${default_isp}" + for _isp in ${dhcp_isps} + do + _emit_dhcpv6_for_isp $_isp ${_n} + _n=$((${_n}+1)) + done + +} + +add_v4_isp() +{ + _iface="${1}" ; shift 1 + eval ifconfig_${_iface}="$*" + +} + +add_v6_isp() +{ + _echo "ISP added: $*" + _iface="${1}" ; shift 1 + _name="${1}" ; shift 1 + _isp_type="${1}" ; shift 1 + eval _prefix_len="\${prefix_len_${_isp_type}}" + if [ "${1}" = "default" ] + then + _echo "Saw default, and overriding to ${_isp}" + default_isp="${_name}" ; shift 1 + fi + + if [ -z "${default_isp}" ] + then + default_isp=${_name} + fi + + if [ "${default_isp}" = "${_name}" ] + then + rtsold_enable="YES" + ipv6_default_interface="${_iface}" + ipv6_cpe_wanif="${_iface}" + ipv6_isp_interfaces="${ipv6_isp_interfaces} ${_iface}" + dhcp_6c_enable="YES" + dhcp6c_config="${rc_router_gen_dir}/dhcp6c.conf" + fi + + + dhcp6c_interfaces="${dhcp6c_interfaces} ${_iface}" + + eval dhcp_interface_${_name}="${_iface}" + eval dhcp_prefix_${_name}="${_prefix_len}" + dhcp_isps="${dhcp_isps} ${_name}" +} + +add_canned_isp() +{ + load_isp_hints # Make sure ISP hints are loaded, first + + _iface="${1}" ; shift 1 + _name="${1}" ; shift 1 + _isp="${1}" ; shift 1 + + _v4_isp_config="\${rc_router_v4_isp_config_${_isp}}" # DHCP, usually + + add_v4_isp ${_iface} ${_v4_isp_config} + add_v6_isp ${_iface} ${_name} ${_isp} $* +} + +_emit_dhcpv4_for_iface() +{ + _iface=${1} + _vlan=${2} + + # Get a newline in there, to make output more readable + echo + + _base=$((${_vlan}%10)) + _net=$((${_vlan}/100)) + _subnet=${_net}${_base} + eval _net=\${v4_net_${_iface}} + if [ -z "${_net}" ] + then + _net=${v4_net} + fi + + eval _lo="\${dhcpv4_pool_lo_${_iface}}" + eval _hi="\${dhcpv4_pool_hi_${_iface}}" + + [ -z "${_lo}" ] && _lo=${dhcpv4_pool_lo} + [ -z "${_hi}" ] && _hi=${dhcpv4_pool_hi} + + _prefix="${_net}.${_subnet}" + + echo "subnet ${_prefix}.0 netmask 255.255.255.0" + echo "{" + echo " option routers ${_prefix}.1;" + echo " range ${_prefix}.${_lo} ${_prefix}.${_hi};" + echo "}" +} + +_emit_dhcpv4_defaults() +{ + echo "option v6-mostly code 108 = integer 32;" + + [ -z "${enable_v6_mostly}" ] && enable_v6_mostly="NO" + + if [ `option_selected enable_v6_mostly` = "YES" ] + then + echo "option v6-mostly 7200;" + fi + + if [ -n "${dhcpv4_dns}" ] + then + echo "option domain-name-servers ${dhcpv4_dns};" + fi + + if [ -n "${dhcpv4_default_lease_time}" ] + then + echo "option default-lease-time ${dhcpv4_default_lease_time};" + fi + + if [ -n "${dhcpv4_max_lease_time}" ] + then + echo "option max-lease-time ${dhcpv4_max_lease_time};" + fi +} + +_emit_dhcpv4() +{ + _dhcp_file=${rc_router_gen_dir}/dhcpd.subnets.conf + echo "" > ${_dhcp_file} + _emit_dhcpv4_defaults >> ${_dhcp_file} + + + for _iface in ${dhcpd_ifaces} + do + _iface_root=$( echo ${_iface} | sed -e 's/\.[0-9][0-9]*//' ) + _vlan=$( echo ${_iface} | sed -e 's/[a-z][a-z]*[0-9][0-9]*\.//' ) + _emit_dhcpv4_for_iface ${_iface_root} ${_vlan} >> ${_dhcp_file} + 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 +} + +add_v6_net() +{ + _isp="" + _compute_nets $* + _add_vlan $_card $_vlan + _add_rtadv $_card $_vlan + + _compute_ula_prefix + + _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} + else + _echo "Adding ${_card}.${_vlan} to default (which is ${default_isp} at this time)" + _add_dhcpv6 ${_card}.${_vlan} default + 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 ns.nerdland.org. adam.imp-net.work. (' + date +'%s' + + echo 7200 + echo 1200 + echo 7200 + echo 5400 + + echo ')' + + echo '$ORIGIN hosts.nerdland.org.' + + echo '@ IN NS xns.nerdland.org.' + + for _hostline in $(cat ${1}) + do + #echo ${_prefix} ${_subnet} ${_hostline} + case "${_hostline}" in + "#"*) + continue + ;; + esac + + build_zoneinfo_for_host ${_prefix} ${_subnet} ${_hostline} + done +} + +_hash() +{ + sha1sum +} + +# 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_ula_prefix() +{ + secret_ip6_net=$(compute_net ${rc_router_ula_prefix_size} "${rc_router_network_name}") +} + +load_isp_hints() +{ + for _hint in $(ls ${rc_router_dir}/hints/*) + do + v4_setup="dhcp" # v4 usually defaults to DHCP + _isp=$( basename ${_hint} ) + . ${rc_router_dir}/hints/${_isp} + + eval rc_router_v4_isp_config_${_isp}=${v4_setup} + eval prefix_len_${_isp}=${v6_prefix_len} + done +} + +_rc_router_epilogue() +{ + rtsold_flags="${rtsold_flags} ${ipv6_isp_interfaces}" +} + +#. ${router_conf_dir}/pf-framework.subr + +# vim: ft=bash diff --git a/rc.test b/rc.test new file mode 100755 index 0000000..8b84905 --- /dev/null +++ b/rc.test @@ -0,0 +1,22 @@ +#!/bin/sh +. ${RC_ROUTER_HARNESS}/etc/rc.conf + +echo "Which nic?" +read nic + +nic=`echo $nic | sed -e 's/\./_/g'` + +echo "V6 net is: ${secret_ip6_net}" + +eval echo "ifconfig_${nic}: \${ifconfig_${nic}}" +eval echo "ifconfig_${nic}_ipv6: \${ifconfig_${nic}_ipv6}" +eval echo "ifconfig_${nic}_alias0: \${ifconfig_${nic}_alias0}" +eval echo "ifconfig_${nic}_alias1: \${ifconfig_${nic}_alias1}" +eval echo "ifconfig_${nic}_alias2: \${ifconfig_${nic}_alias2}" +eval echo "ifconfig_${nic}_alias3: \${ifconfig_${nic}_alias3}" +eval echo "ifconfig_${nic}_alias4: \${ifconfig_${nic}_alias4}" +echo "rtadvd_interfaces: ${rtadvd_interfaces}" +echo "wireguard_interfaces: ${wireguard_interfaces}" +echo "cloned_interfaces: ${cloned_interfaces}" + +set diff --git a/templates/rtadvd.conf b/templates/rtadvd.conf new file mode 100644 index 0000000..670f0b8 --- /dev/null +++ b/templates/rtadvd.conf @@ -0,0 +1,11 @@ +settings:\ + :@PREF64@:\ + :@MININTERVAL@:\ + :@MAXINTERVAL@:\ + :@PLTIME@:\ + :@PLTIME_DECR@:\ + :@VLTIME@:\ + :@VLTIME_DECR@:\ + :@RLTIME@:\ + :@DNS_SEARCHLIST@:\ + :dnssl="@DNS_SERVER@":