@@ -434,6 +434,28 @@ get_new_macaddr() {
434434 echo $NEWMAC
435435}
436436
437+ get_ipv6addr () {
438+ ip -6 addr show $1 scope global | sed -e ' s/ /\n/g' | sed -ne ' /inet6/{n;p}'
439+ }
440+
441+ get_new_ipv6addr () {
442+ local OLDIP NEWIP LAST_CHUNK i
443+ OLDIP=$( get_ipv6addr $INTERNET_IFACE )
444+ OLDIP=" ${OLDIP%/* } "
445+ mutex_lock
446+ for i in {1..65535}; do
447+ if [[ " $OLDIP " == ::* ]]; then
448+ NEWIP=" ${OLDIP} ::$( printf %04x $i ) "
449+ else
450+ LAST_CHUNK=$( printf %d 0x${OLDIP##*: } )
451+ NEWIP=" ${OLDIP%:* } :$( printf %02x $(( ($LAST_CHUNK + $i ) % 65536 )) ) "
452+ ip -6 addr show $1 scope global | sed -e ' s/ /\n/g' | sed -ne ' /inet6/{n;p}' | grep " $NEWIP " > /dev/null 2>&1 || break
453+ fi
454+ done
455+ mutex_unlock
456+ echo $NEWIP
457+ }
458+
437459# start haveged when needed
438460haveged_watchdog () {
439461 local show_warn=1
@@ -819,8 +841,10 @@ _cleanup() {
819841 iptables -w -D INPUT -p udp -m udp --dport 67 -j ACCEPT
820842 if [[ $IPV6 -ne 0 ]]; then
821843 ip6tables -w -D INPUT -p udp -m udp --dport 67 -j ACCEPT
822- ip -6 route del " $INTERNET6 " /" $PREFIXLEN6 " dev $WIFI_IFACE
823- ip -6 route replace " $INTERNET6 " /" $PREFIXLEN6 " dev $INTERNET_IFACE
844+ # Restore original subnet to internet-facing interface
845+ ip -6 addr del " $INTERNET6 " /128 dev ${INTERNET_IFACE}
846+ ip -6 addr add " $INTERNET6 " /" $PREFIXLEN6 " dev ${INTERNET_IFACE}
847+ ip -6 route replace " $INTERNET6 " /" $PREFIXLEN6 " dev ${INTERNET_IFACE}
824848 fi
825849 fi
826850
@@ -1603,16 +1627,12 @@ if [[ "$SHARE_METHOD" == "bridge" ]]; then
16031627 fi
16041628elif [[ $IPV6 -ne 0 ]]; then
16051629 echo " Looking up IPv6 address of internet interface..."
1606- NETADDR6=$( ip -6 addr show $INTERNET_IFACE scope global | sed -e ' s/ /\n/g ' | sed -ne ' /inet6/{n;p} ' )
1630+ NETADDR6=$( get_ipv6addr $INTERNET_IFACE )
16071631 if [[ -n " $NETADDR6 " ]]; then
16081632 INTERNET6=" ${NETADDR6%/* } "
16091633 PREFIXLEN6=" ${NETADDR6#*/ } "
1610- # FIXME: this is a dodgy/bad way to pick an IPv6 address.
1611- # We want a different IPv6 address for the AP (internal),
1612- # compared to our external IPv6 address, but they need to be
1613- # in the same /64 subnet. This is the current ugly kludge.
1614- GATEWAY6=" ${INTERNET6%:* } " :$( printf " %04x" " $(( 0 x${INTERNET6##*: } + 1 )) " )
1615- echo " Got $INTERNET6 /$PREFIXLEN6 . Will route $GATEWAY6 /$PREFIXLEN6 to clients."
1634+ GATEWAY6=$( get_new_ipv6addr $INTERNET_IFACE )
1635+ echo " Internet interface is $INTERNET6 /$PREFIXLEN6 . Will route $GATEWAY6 /$PREFIXLEN6 to wireless clients."
16161636 else
16171637 echo " No IPv6 address found. Disabling IPv6."
16181638 IPV6=0
@@ -1834,11 +1854,14 @@ if [[ "$SHARE_METHOD" != "bridge" ]]; then
18341854 ip link set up dev ${WIFI_IFACE} || die " $VIRTDIEMSG "
18351855 ip addr add ${GATEWAY} /24 broadcast ${GATEWAY% .* } .255 dev ${WIFI_IFACE} || die " $VIRTDIEMSG "
18361856 if [[ $IPV6 -ne 0 ]]; then
1837- # Keep only /128 for Internet-facing IPv6 address
1857+ # Normally, the Internet-facing device has been assigned a /64 subnet (saved in INTERNET6/PREFIXLEN6).
1858+ # We want to keep only /128 for the Internet-facing device, but route the rest of the subnet to
1859+ # the AP, so that it can be used by clients via DHCPv6 or SLAAC.
1860+
18381861 ip -6 addr del " $INTERNET6 " /" $PREFIXLEN6 " dev ${INTERNET_IFACE} || die " $VIRTDIEMSG "
18391862 ip -6 addr add " $INTERNET6 " /128 dev ${INTERNET_IFACE} || die " $VIRTDIEMSG "
18401863 ip -6 route replace " $INTERNET6 " /128 dev ${INTERNET_IFACE} || die " $VIRTDIEMSG "
1841- # While routing full IPv6 subnet to clients
1864+
18421865 ip -6 addr add " $GATEWAY6 " /" $PREFIXLEN6 " dev ${WIFI_IFACE} || die " $VIRTDIEMSG "
18431866 ip -6 route replace " $INTERNET6 " /" $PREFIXLEN6 " dev ${WIFI_IFACE} || die " $VIRTDIEMSG "
18441867 fi
0 commit comments