#!/bin/bash
# -*- mode: sh; sh-basic-offset: 2; indent-tabs-mode: t -*-
# vim:set ft=sh sw=2 ts=2:
#
# Test suite for 08-ipv6-prefix
#   - tests functions used by dhclient
#
# shellcheck disable=SC2034,SC1090,SC2123

NMG_XTEST=${NMG_XTEST:-conf/nmg_xtest}
{ [[ -r ${NMG_XTEST} ]] && . "${NMG_XTEST}"; } ||
  { echo >&2 "Unable to load ${NMG_XTEST}"; exit 2; }

xtest::group1::life() {
  local var CUR_TIME base_time

  shtest::title "Lifetime Function Tests (life group)"

  # needed for a ip lifetime calc test
  local -x DUMMY_MOCK_OUTPUT="date @ARGS@"
  local NMDATE="${TEST_BIN}/dummy-mock"

  base_time=$("$NMDATE" +%s) || shtest::fatal "${NMDATE} not available"
  [[ ${base_time} == "date '+%s'" ]] || shtest::fatal "${NMDATE} failed"
  base_time=1234567
  DUMMY_MOCK_OUTPUT=${base_time}

  var='bogus'
  CUR_TIME=''
  xwrap2 "0" ipv6_life_calc var "0" "${base_time}"
  xtest S1 t "returns true"
  vtest S1v "0" "sets retvar to 0"
  ftest S1f "" "should not log"
  [[ ${CUR_TIME} ]] && (( CUR_TIME > 0 ))
  xtest S1g t "should set \$CUR_TIME"

  xwrap2 "<max-int> 0" ipv6_life_calc var 4294967295 "0"
  xtest S2 t "returns true"
  vtest S2v "forever" "sets retvar to forever"

  xwrap2 "0 <max-int>" ipv6_life_calc var "0" "4294967295"
  xtest S3 1 "returns 1"
  ftest S3f "" "should not log"

  (( base_time = CUR_TIME - 100 )) || :

  xwrap2 "300 <time-100>" ipv6_life_calc var "300" "${base_time}"
  xtest S4 t "returns true"
  vtest S4v "200" "should return 200"

  xwrap2 "50 <time-100>" ipv6_life_calc var "50" "${base_time}"
  xtest S5 t "returns true"
  vtest S5v "0" "should return 0"

  CUR_TIME=0
  xwrap2 "<failed-time>" ipv6_life_calc var "0" "0"
  xtest S6 1 "returns 1"
  CUR_TIME=''

  return 0
}

xtest::group1::prefix() {

  local aref=() Lans=() Reserved=() Assigned=() out var var2

  shtest::title "Prefix Function Tests (life group)"

  xwrap2 "0/64 0/64" ipv6_prefix_overlap 0 64 0/64
  xtest P1 t "returns true"
  xwrap2 "2/64 1/63" ipv6_prefix_overlap 2 64 1/63
  xtest P2 t "returns true"
  xwrap2 "1/64 1/64" ipv6_prefix_overlap 1 64 1/63
  xtest P3 f "returns false"
  xwrap2 "2/64 1/63" ipv6_prefix_overlap 2 64 1/63
  xtest P4 t "returns true"
  xwrap2 "4/64 1/62" ipv6_prefix_overlap 4 64 1/62
  xtest P5 t "returns true"
  xwrap2 "7/64 1/62" ipv6_prefix_overlap 7 64 1/62
  xtest P6 t "returns true"
  xwrap2 "7/64 4/62" ipv6_prefix_overlap 7 64 4/62
  xtest P7 f "returns false"

  xwrap2 "br0 56" ipv6_lan_node_load "br0" "56"
  xtest P21 t "returns true"
  ftest P21f "" "does not log"
  aref=("br0 63 auto 0")
  atest P21l Lans aref "sets Lans"
  aref=("0/63")
  atest P21r Reserved aref "sets Reserved"

  xwrap2 "br1 56" ipv6_lan_node_load "br1" "56"
  xtest P22 t "returns true"
  ftest P22f "" "does not log"
  aref=("br0 63 auto 0" "br1 64 auto auto")
  atest P22l Lans aref "adds to Lans"
  aref=("0/63")
  atest P22r Reserved aref "doesn't add to Reserved"

  Lans=() Reserved=()
  xwrap2 "br0 64" ipv6_lan_node_load "br0" "64"
  xtest P23 t "returns true"
  ftest P23f "br0 not configured, LAN_PREFIX_LEN 63 < available 64" \
        "log info"
  aref=("br0")
  atest P23l Lans aref "sets Lans"
  aref=()
  atest P23r Reserved aref "doesn't set Reserved"

  # create temp config file
  local LAN_CONFIG_PAT=${XFILE}
  xrm "$XFILE"
  echo "LAN_SITE=bogus" > "$XFILE"

  Lans=() Reserved=()
  xwrap2 "<bad-site>" ipv6_lan_node_load "brx" "64"
  xtest P24 t "returns true"
  xread_value out <<-EOF
	ERR: nmg::2dec: invalid <value> '0xbogus'
	ERR: brx config error, invalid LAN_SITE 'bogus'
	EOF
  ftest P24f "$out" "log error"
  aref=("brx")
  atest P24l Lans aref "sets Lans"
  aref=()
  atest P24r Reserved aref "doesn't set Reserved"

  echo "LAN_PREFIX_LEN=0" > "$XFILE"
  Lans=() Reserved=()

  xwrap2 "<bad-plen>" ipv6_lan_node_load "brx" "64"
  xtest P25 t "returns true"
  ftest P25f "ERR: brx config error, invalid LAN_PREFIX_LEN '0'" "log error"
  aref=("brx")
  atest P25l Lans aref "sets Lans"
  aref=()
  atest P25r Reserved aref "doesn't set Reserved"

  echo "LAN_PREFIX_LEN=128" > "$XFILE"
  Lans=() Reserved=()

  xwrap2 "<128 plen>" ipv6_lan_node_load "brx" "64"
  xtest P26 t "returns true"
  ftest P26f "" "does not log"
  aref=("brx 128 auto 0")
  atest P26l Lans aref "sets Lans"
  aref=()
  atest P26r Reserved aref "doesn't set Reserved"

  echo "LAN_PREFIX_LEN=63"$'\n'"LAN_SITE=7" > "$XFILE"
  Lans=() Reserved=()

  xwrap2 "<site 7> 61" ipv6_lan_node_load "brx" "61"
  xtest P27 t "returns true"
  ftest P27f "" "does not log"
  aref=("brx 63 auto 3")
  atest P27l Lans aref "sets Lans (site 3)"
  aref=("3/63")
  atest P27r Reserved aref "sets Reserved"

  Lans=() Reserved=()

  xwrap2 "<site 7> 63" ipv6_lan_node_load "brx" "63"
  xtest P28 t "returns true"
  ftest P28f "" "does not log"
  aref=("brx 63 auto 0")
  atest P28l Lans aref "sets Lans (site 0)"
  aref=("0/63")
  atest P28r Reserved aref "sets Reserved"

  var="bogus"
  xwrap2 "64 64 0/64" ipv6_find_freesite var 64 64 "0/64"
  xtest P31 f "returns false"
  ftest P31f "" "does not log"
  vtest P31v "bogus" "does not set retvar"

  xwrap2 "64 64" ipv6_find_freesite var 64 64
  xtest P32 t "returns true"
  vtest P32v "0" "sets retvar 0"

  xwrap2 "62 64 <2-63>" ipv6_find_freesite var 62 64 "0/63" "1/63"
  xtest P33 f "returns false"

  xwrap2 "62 64 <1 63>" ipv6_find_freesite var 62 64 "0/63"
  xtest P34 t "returns true"
  vtest P34v "2" "sets retvar 2"

  xwrap2 "62 64 <'1' free>" ipv6_find_freesite var 62 64 "1/63" "0/64"
  xtest P35 t "returns true"
  vtest P35v "1" "sets retvar 1"

  xwrap2 "62 64 <'3' free>" ipv6_find_freesite var 62 64 "0/63" "2/64"
  xtest P36 t "returns true"
  vtest P36v "3" "sets retvar 3"

  xwrap2 "64 64 <empty>" ipv6_find_freesite var 64 64 "" ""
  xtest P37 t "returns true"
  vtest P37v "0" "sets retvar 0"

  Assigned=() Reserved=("0/63")

  xwrap2 "62 64 <0/63 reserved>" ipv6_find_autosite var 62 64
  xtest P41 t "returns true"
  vtest P41v "2" "sets retvar 2 (avoids reserved)"

  Assigned=("0/63" "2/64") Reserved=("3/64")

  xwrap2 "62 64 <0/63 2/64 used, 3/64 reserved>" ipv6_find_autosite var 62 64
  xtest P42 t "returns true"
  vtest P42v "3" "sets retvar 3 (uses reserved)"

  Assigned=() Reserved=()

  xwrap2 "62 63" ipv6_find_autosite var 62 63
  xtest P43 t "returns true"
  vtest P43v "0" "sets retvar 0"

  Assigned=("0/63") Reserved=() var="bogus"

  xwrap2 "63 64 <used 63>" ipv6_find_autosite var 63 64
  xtest P44 f "returns false"
  vtest P44v "bogus" "does not set retvar"
  ftest P44f "" "does not log"

  local new_ip6_prefix="2001:db8:33::/63"
  Assigned=("2/64") Reserved=("0/63") var=""

  xwrap2 "<reserved 0/63>" ipv6_lan_node_calc var var2 br0 63 auto 0
  xtest P51 t "returns true"
  vtest P51v "2001:db8:33:0:32fb:93c5:6555:845a/63" "sets address"
  ltest P51s "var2" "0/63" "sets site 0/63"
  ftest P51f "" "does not log"
  aref=("2/64" "0/63")
  atest P51a Assigned aref "appends to Assigned"

  Assigned=("0/63" "2/64") Reserved=("0/63" "1/63") var='orig' var2='orig'

  xwrap2 "<unavail pfx>" ipv6_lan_node_calc var var2 br0 63 auto 1
  xtest P52 t "returns true"
  vtest P52v "orig" "does not set address"
  ltest P52s "var2" "orig" "does not set site"
  ftest P52f "br0 cannot be configured, no /63 prefixes available" "logs info"

  Assigned=("1/63") Reserved=("1/63" "2/64") var='' var2=''

  xwrap2 "<unavail reserved 2/64>" ipv6_lan_node_calc var var2 br0 64 auto 2
  xtest P53 t "returns true"
  vtest P53v "2001:db8:33:0:32fb:93c5:6555:845a/64" "set address"
  ltest P53s "var2" "0/64" "sets site 0/64"
  ftest P53f "" "does not log"

  new_ip6_prefix="2001:db8:33::/62"
  Assigned=("0/63") Reserved=("0/63" "2/64") var='' var2=''

  xwrap2 "<autosite>" ipv6_lan_node_calc var var2 br0 64 auto auto
  xtest P54 t "returns true"
  vtest P54v "2001:db8:33:3:32fb:93c5:6555:845a/64" "set address"
  ltest P54s "var2" "3/64" "sets site 3/64"
  ftest P54f "" "does not log"

  Assigned=("0/63" "3/64") Reserved=("0/63" "2/64") var='' var2=''

  xwrap2 "<autosite, only reserved>" ipv6_lan_node_calc var var2 br0 64 \
	 auto auto
  xtest P55 t "returns true"
  vtest P55v "2001:db8:33:2:32fb:93c5:6555:845a/64" "set retvar to reserved"
  ltest P55s "var2" "2/64" "sets site 2/64"
  ftest P55f "" "does not log"

  Assigned=("0/63" "3/64" "2/64") var="orig" var2="orig"

  xwrap2 "<autosite, unavail>" ipv6_lan_node_calc var var2 br0 64 auto auto
  xtest P56 t "returns true"
  vtest P56v "orig" "does not set address"
  ltest P56s "var2" "orig" "does not set site"
  ftest P56f "br0 cannot be configured, no /64 prefixes available" "logs info"

  ipv6_lan_reset_config
}

xtest::onexit::addr() {
  [[ ${FORWARDING_PAT-} ]] || return 0
  xrm "${FORWARDING_PAT/@NODE@/br0}"
}

xtest::group2::addr() {

  local out state state1 CUR_TIME='' Forwarding=1

  shtest::title "LAN Address Tests (addr group)"

  [[ ${FORWARDING_PAT-} ]]
  xtest A0 t "checking FORWARDING_PAT"
  shtest::last_check_ok || return 1

  state1=${FORWARDING_PAT/@NODE@/br0}
  xrm "$state1"
  printf '' >>"$state1"

  shtest::check_file A0c "$state1" "" "created forwarding test file"
  shtest::last_check_ok || return 1

  xwrap2 "<addr>" ipv6_lan_add_addr br0 "inet6:2001:db8:100::1/64"
  xtest A1 t "returns true"
  xread_value out <<-EOF
	Adding 2001:db8:100::1/64 to br0
	${NMG_IP} => ip '-6' 'addr' 'add' '2001:db8:100::1/64' 'dev' 'br0'
	EOF
  ftest A1f "$out" "adds address"
  shtest::check_file A1w "$state1" "1" "writes 1 to forwarding file"

  # cleanup
  Forwarding=0
  xrm "$state1"

  CUR_TIME=3060

  xread_value state <<-EOF
	inet6:2001:db8:100::1/64
	valid-life:240
	pref-life:120
	life-start:3000
	EOF

  xwrap2 "<addr> <lifetimes set>" ipv6_lan_add_addr br0 "$state"
  xtest A2 t "returns true"
  xread_value out <<-EOF
	Adding 2001:db8:100::1/64 to br0
	${NMG_IP} => ip '-6' 'addr' 'add' '2001:db8:100::1/64' 'dev' 'br0'\
 'valid_lft' '180' 'preferred_lft' '60'
	EOF
  ftest A2f "$out" "adds address with lifetimes"

  CUR_TIME=3300

  xwrap2 "<addr> <expired lifetimes>" ipv6_lan_add_addr br0 "$state"
  xtest A3 t "returns true"
  ftest A3f "" "does not add address"

  xwrap2 "<addr>" ipv6_lan_del_addr br0 "fdc0:4455:b240::1/64"
  xtest A11 t "returns true"
  xread_value out <<-EOF
	Removing fdc0:4455:b240::1/64 from br0
	${NMG_IP} => ip '-6' 'addr' 'del' 'fdc0:4455:b240::1/64' 'dev' 'br0'
	EOF
  ftest A11f "$out" "removes address"

  xwrap2 "br0" ipv6_lan_read_config br0
  xtest A21 t "returns true"
  ltest A21a "LAN_SITE" "ab80" "sets LAN_SITE"
  ltest A21b "LAN_PREFIX_LEN" "63" "sets LAN_PREFIX_LEN"
  ltest A21c "LAN_NODE" "auto" "uses defaults"

  ipv6_lan_reset_config
  xtest::onexit::addr
}

xtest::onexit::lan() {
  [[ ${LAN_STATE_PAT-} ]] || return 0
  local LAN_STATE_PAT="${LAN_STATE_PAT%-dhc}-dhc"
  xrm "${LAN_STATE_PAT/@LAN@-from-@ID@-@WAN@/br0-from-8899-wan0}"
  xrm "${LAN_STATE_PAT/@LAN@-from-@ID@-@WAN@/br0-from-1122-wan0}"
  xrm "${LAN_STATE_PAT/@LAN@-from-@ID@-@WAN@/br1-from-8899-wan0}"
  xrm "${LAN_STATE_PAT/@LAN@-from-@ID@-@WAN@/br1-from-1122-wan0}"
  xrm "${LAN_STATE_PAT/@LAN@-from-@ID@-@WAN@/brx-from-8899-wan0}"
  xrm "${LAN_STATE_PAT/@LAN@-from-@ID@-@WAN@/brx-from-1122-wan0}"
  local LAN_DDNS_STATE_PAT="${LAN_DDNS_STATE_PAT%-dhc}-dhc"
  xrm "${LAN_DDNS_STATE_PAT/@LAN@-from-@WAN@-@RREC@/br1-from-wan0-AAAA}"
}

xtest::group3::lan() {

  local out CUR_TIME='' state1 state2 state3 state4 state5 state6 dstate1

  shtest::title "LAN Prefix Assignment Tests (lan group)"

  [[ ${LAN_STATE_PAT-} ]]
  xtest L1 t "checking LAN_STATE_PAT"
  shtest::last_check_ok || return 1

  local LAN_STATE_PAT="${LAN_STATE_PAT}-dhc"
  local LAN_DDNS_STATE_PAT="${LAN_DDNS_STATE_PAT}-dhc"

  state1=${LAN_STATE_PAT/@LAN@-from-@ID@-@WAN@/br0-from-8899-wan0}
  state2=${LAN_STATE_PAT/@LAN@-from-@ID@-@WAN@/br0-from-1122-wan0}
  state3=${LAN_STATE_PAT/@LAN@-from-@ID@-@WAN@/br1-from-8899-wan0}
  state4=${LAN_STATE_PAT/@LAN@-from-@ID@-@WAN@/br1-from-1122-wan0}
  state5=${LAN_STATE_PAT/@LAN@-from-@ID@-@WAN@/brx-from-8899-wan0}
  state6=${LAN_STATE_PAT/@LAN@-from-@ID@-@WAN@/brx-from-1122-wan0}
  dstate1=${LAN_DDNS_STATE_PAT/@LAN@-from-@WAN@-@RREC@/br1-from-wan0-AAAA}
  xrm "$state1" "$state2" "$state3" "$state4" "$state5" "$state6"
  xrm "$dstate1"

  WAN_LAN_INTFS="brx br0"
  CUR_TIME=3060

  local new_ip6_prefix="2001:db8:100::/56" new_iaid="88:99"
  local new_max_life=240 new_preferred_life=120 new_life_starts=3000

  xwrap2 "<br0 brx, 88:99>" ipv6_dhc_bind
  xtest L2 t "returns true"
  xread_value out <<-EOF
	Prefix on wan0: 2001:db8:100::/56
	Adding 2001:db8:100:0:32fb:93c5:6555:845a/63 to br0
	${NMG_IP} => ip '-6' 'addr' 'add'\
 '2001:db8:100:0:32fb:93c5:6555:845a/63' 'dev' 'br0' 'valid_lft' '180'\
 'preferred_lft' '60'
	${NMG_RADVD_TRIGGER} => radvd triggered
	EOF
  ftest L2f "$out" "adds address"
  xread_value out <<-EOF
	inet6:2001:db8:100:0:32fb:93c5:6555:845a/63
	site6:0/63
	valid-life:240
	pref-life:120
	life-start:3000
	EOF
  shtest::check_file L2i "$state1" "$out" "creates br0 88:99 state file"
  xread_value out <<-EOF
	inet6:2001:db8:100:2::/64
	site6:2/64
	valid-life:240
	pref-life:120
	life-start:3000
	EOF
  shtest::check_file L2m "$state5" "$out" \
		     "creates brx 88:99 state file (prefix)"

  new_ip6_prefix="2001:db8:200::/56" new_iaid="11:22"

  xwrap2 "<brx br0, 11:22>" ipv6_prefix_bind
  xtest L3 t "returns true"
  xread_value out <<-EOF
	Prefix on wan0: 2001:db8:200::/56
	Adding 2001:db8:200:0:32fb:93c5:6555:845a/63 to br0
	${NMG_IP} => ip '-6' 'addr' 'add'\
 '2001:db8:200:0:32fb:93c5:6555:845a/63' 'dev' 'br0' 'valid_lft' '180'\
 'preferred_lft' '60'
	${NMG_RADVD_TRIGGER} => radvd triggered
	EOF
  ftest L3f "$out" "adds address"
  xread_value out <<-EOF
	inet6:2001:db8:200:0:32fb:93c5:6555:845a/63
	site6:0/63
	valid-life:240
	pref-life:120
	life-start:3000
	EOF
  shtest::check_file L3j "$state2" "$out" "creates state file"
  xread_value out <<-EOF
	inet6:2001:db8:200:2::/64
	site6:2/64
	valid-life:240
	pref-life:120
	life-start:3000
	EOF
  shtest::check_file L3n "$state6" "$out" \
		     "creates brx 11:22 state file (prefix)"

  # reverse lan order, check existing assignments are kept
  WAN_LAN_INTFS="brx br1"
  new_ip6_prefix="2001:db8:200::/56" new_iaid="11:22" new_preferred_life=''

  xwrap2 "<brx br1, 11:22>" ipv6_prefix_bind
  xtest L4 t "returns true"
  xread_value out <<-EOF
	Prefix on wan0: 2001:db8:200::/56
	Adding 2001:db8:200:0:32fb:93c5:555:1/64 to br1
	${NMG_IP} => ip '-6' 'addr' 'add'\
 '2001:db8:200:0:32fb:93c5:555:1/64' 'dev' 'br1'
	Removing home.example.test AAAA
	${NMDDNS_NSUPDATE} => nsupdate '-t' '2'
	server 127.0.0.1
	zone example.test
	update delete home.example.test AAAA
	send
	${NMG_RADVD_TRIGGER} => radvd triggered
	EOF
  ftest L4f "$out" "adds address"
  [[ -e $state2 ]]
  xtest L4j f "removes br0 11:22 state file"
  xread_value out <<-EOF
	inet6:2001:db8:200:0:32fb:93c5:555:1/64
	site6:0/64
	EOF
  shtest::check_file L4l "$state4" "$out" "creates br1 11:22 state"
  xread_value out <<-EOF
	inet6:2001:db8:200:2::/64
	site6:2/64
	EOF
  shtest::check_file L4n "$state6" "$out" "honors brx 11:22 assignment"

  # reset
  xtest::onexit::lan

  WAN_LAN_INTFS="br1 br0"

  new_ip6_prefix="2001:db8:200::/56" new_iaid="11:22" new_preferred_life=''

  xwrap2 "<br1>" ipv6_prefix_bind
  xtest L5 t "returns true"
  xread_value out <<-EOF
	Prefix on wan0: 2001:db8:200::/56
	Replacing 2001:db8:200:2:32fb:93c5:555:1/64 on br1
	${NMG_IP} => ip '-6' 'addr' 'replace'\
 '2001:db8:200:2:32fb:93c5:555:1/64' 'dev' 'br1'
	Adding 2001:db8:200:0:32fb:93c5:6555:845a/63 to br0
	${NMG_IP} => ip '-6' 'addr' 'add'\
 '2001:db8:200:0:32fb:93c5:6555:845a/63' 'dev' 'br0'
	Setting home.example.test AAAA to 2001:db8:200:2:32fb:93c5:555:1
	${NMDDNS_NSUPDATE} => nsupdate '-t' '2'
	server 127.0.0.1
	zone example.test
	update delete home.example.test AAAA
	update add home.example.test 600 AAAA 2001:db8:200:2:32fb:93c5:555:1
	send
	${NMG_RADVD_TRIGGER} => radvd triggered
	EOF
  ftest L5f "${out}" "adds address, updates DNS"
  [[ -e "$state1" ]]
  xtest L5i f "br0 88:99 state file should not exist"
  [[ -e "$state2" ]]
  xread_value out <<-EOF
	inet6:2001:db8:200:0:32fb:93c5:6555:845a/63
	site6:0/63
	EOF
  shtest::check_file L5j "$state2" "$out" "creates br0 11:22 state file"
  [[ -e "$state3" ]]
  xtest L5k f "br1 88:99 state file should not exist"
  xread_value out <<-EOF
	inet6:2001:db8:200:2:32fb:93c5:555:1/64
	site6:2/64
	EOF
  shtest::check_file L5l "$state4" "$out" "creates br1 11:22 state file"
  shtest::check_file \
    L5d "$dstate1" "2001:db8:200:2:32fb:93c5:555:1" "create ddns state file"
  xrm "$state1" "$state2" "$state3" "$state4" "$dstate1"

  local old_ip6_prefix="2001:db8:100::/56" old_iaid="88:99"
  echo "inet6:2001:db8:100:2:32fb:93c5:555:1/64" > "$state3"
  new_ip6_prefix="2001:db8:200::/56" new_iaid="11:22" new_preferred_life=''

  xwrap2 "<br1-with-old>" ipv6_prefix_bind
  xtest L6 t "returns true"
  xread_value out <<-EOF
	Prefix on wan0: 2001:db8:200::/56 (old: 2001:db8:100::/56)
	Removing 2001:db8:100:2:32fb:93c5:555:1/64 from br1
	${NMG_IP} => ip '-6' 'addr' 'del'\
 '2001:db8:100:2:32fb:93c5:555:1/64' 'dev' 'br1'
	Replacing 2001:db8:200:2:32fb:93c5:555:1/64 on br1
	${NMG_IP} => ip '-6' 'addr' 'replace'\
 '2001:db8:200:2:32fb:93c5:555:1/64' 'dev' 'br1'
	Adding 2001:db8:200:0:32fb:93c5:6555:845a/63 to br0
	${NMG_IP} => ip '-6' 'addr' 'add'\
 '2001:db8:200:0:32fb:93c5:6555:845a/63' 'dev' 'br0'
	Setting home.example.test AAAA to 2001:db8:200:2:32fb:93c5:555:1
	${NMDDNS_NSUPDATE} => nsupdate '-t' '2'
	server 127.0.0.1
	zone example.test
	update delete home.example.test AAAA
	update add home.example.test 600 AAAA 2001:db8:200:2:32fb:93c5:555:1
	send
	${NMG_RADVD_TRIGGER} => radvd triggered
	EOF
  ftest L6f "${out}" "removes old, adds new, updates DNS"
  [[ -e "$state1" ]]
  xtest L6i f "br0 88:99 state file should not exist"
  [[ -e "$state2" ]]
  xread_value out <<-EOF
	inet6:2001:db8:200:0:32fb:93c5:6555:845a/63
	site6:0/63
	EOF
  shtest::check_file \
    L6j "$state2" "$out" "creates br0 11:22 state file"
  [[ -e "$state3" ]]
  xtest L6k f "br1 88:99 state file should not exist"
  xread_value out <<-EOF
	inet6:2001:db8:200:2:32fb:93c5:555:1/64
	site6:2/64
	EOF
  shtest::check_file \
    L6l "$state4" "$out" "creates br1 11:22 state file"
  shtest::check_file \
    L6d "$dstate1" "2001:db8:200:2:32fb:93c5:555:1" "create ddns state file"

  unset old_ip6_prefix old_iaid

  new_ip6_prefix="2001:db8:100::/56" new_iaid="88:99"

  xwrap2 "<br1>" ipv6_prefix_bind
  xtest L7 t "returns true"
  xread_value out <<-EOF
	Prefix on wan0: 2001:db8:100::/56
	Replacing 2001:db8:100:2:32fb:93c5:555:1/64 on br1
	${NMG_IP} => ip '-6' 'addr' 'replace'\
 '2001:db8:100:2:32fb:93c5:555:1/64' 'dev' 'br1'
	Adding 2001:db8:100:0:32fb:93c5:6555:845a/63 to br0
	${NMG_IP} => ip '-6' 'addr' 'add'\
 '2001:db8:100:0:32fb:93c5:6555:845a/63' 'dev' 'br0'
	Setting home.example.test AAAA to\
 2001:db8:200:2:32fb:93c5:555:1,2001:db8:100:2:32fb:93c5:555:1
	${NMDDNS_NSUPDATE} => nsupdate '-t' '2'
	server 127.0.0.1
	zone example.test
	update delete home.example.test AAAA
	update add home.example.test 600 AAAA 2001:db8:200:2:32fb:93c5:555:1
	update add home.example.test 600 AAAA 2001:db8:100:2:32fb:93c5:555:1
	send
	${NMG_RADVD_TRIGGER} => radvd triggered
	EOF
  ftest L7f "${out}" "adds address, updates DNS"
  xread_value out <<-EOF
	inet6:2001:db8:100:0:32fb:93c5:6555:845a/63
	site6:0/63
	EOF
  shtest::check_file L7i "$state1" "$out" "creates br0 88:99 state file"
  xread_value out <<-EOF
	inet6:2001:db8:200:0:32fb:93c5:6555:845a/63
	site6:0/63
	EOF
  shtest::check_file L7j "$state2" "$out" "br0 11:22 state file remains"
  xread_value out <<-EOF
	inet6:2001:db8:100:2:32fb:93c5:555:1/64
	site6:2/64
	EOF
  shtest::check_file L7k "$state3" "$out" "creates br1 88:99 state file"
  xread_value out <<-EOF
	inet6:2001:db8:200:2:32fb:93c5:555:1/64
	site6:2/64
	EOF
  shtest::check_file L7l "$state4" "$out" "br1 11:22 state file remains"
  shtest::check_file \
    L7d "$dstate1" \
    "2001:db8:200:2:32fb:93c5:555:1,2001:db8:100:2:32fb:93c5:555:1" \
    "updates ddns state file"

  unset new_ip6_prefix new_iaid

  local cur_ip6_prefix="2001:db8:100::/56" cur_iaid="88:99" cur_max_life=180
  local cur_life_starts=3000

  # prefix on br1 88:99 (eg br1 down when prefix assigned)
  xcat > "$state3" <<-EOF
	inet6:2001:db8:100:2::/64
	site6:2/64
	EOF
  # prefix on brx 88:99
  xcat > "$state5" <<-EOF
	inet6:2001:db8:100:fe::/63
	site6:fe/63
	EOF

  WAN_LAN_INTFS="br0 brx br1"

  xwrap2 "<88:99>" ipv6_dhc_depref
  xtest L11 t "returns true"
  xread_value out <<-EOF
	Prefix devalued on wan0: 2001:db8:100::/56
	Adding 2001:db8:100:0:32fb:93c5:6555:845a/63 to br0
	${NMG_IP} => ip '-6' 'addr' 'replace'\
 '2001:db8:100:0:32fb:93c5:6555:845a/63' 'dev' 'br0' 'valid_lft' '120'\
 'preferred_lft' '0'
	Changing 2001:db8:100:2:32fb:93c5:555:1/64 on br1
	${NMG_IP} => ip '-6' 'addr' 'change'\
 '2001:db8:100:2:32fb:93c5:555:1/64' 'dev' 'br1' 'valid_lft' '120'\
 'preferred_lft' '0'
	Setting home.example.test AAAA to 2001:db8:200:2:32fb:93c5:555:1
	${NMDDNS_NSUPDATE} => nsupdate '-t' '2'
	server 127.0.0.1
	zone example.test
	update delete home.example.test AAAA
	update add home.example.test 600 AAAA 2001:db8:200:2:32fb:93c5:555:1
	send
	${NMG_RADVD_TRIGGER} => radvd triggered
	EOF
  ftest L11f "$out" "sets address lifetime 0, removes from dns"
  xread_value out <<-EOF
	inet6:2001:db8:100:0:32fb:93c5:6555:845a/63
	site6:0/63
	valid-life:180
	pref-life:0
	life-start:3000
	EOF
  shtest::check_file L11i "$state1" "$out" "updates br0 88:99 state file"
  xread_value out <<-EOF
	inet6:2001:db8:100:2:32fb:93c5:555:1/64
	site6:2/64
	valid-life:180
	pref-life:0
	life-start:3000
	EOF
  shtest::check_file L11k "$state3" "$out" \
		     "updates br1 88:99 state file with address"
  xread_value out <<-EOF
	inet6:2001:db8:100:fe::/63
	site6:fe/63
	valid-life:180
	pref-life:0
	life-start:3000
	EOF
  shtest::check_file L11m "$state5" "$out" \
		     "updates brx 88:99 state file (prefix)"
  shtest::check_file \
    L11d "$dstate1" "2001:db8:200:2:32fb:93c5:555:1" \
    "updates ddns state file"

  WAN_LAN_INTFS="br1"

  xwrap2 "<br1, 88:99>" ipv6_prefix_depref
  xtest L12 t "returns true"
  xread_value out <<-EOF
	Prefix devalued on wan0: 2001:db8:100::/56
	Changing 2001:db8:100:2:32fb:93c5:555:1/64 on br1
	${NMG_IP} => ip '-6' 'addr' 'change'\
 '2001:db8:100:2:32fb:93c5:555:1/64' 'dev' 'br1' 'valid_lft' '120'\
 'preferred_lft' '0'
	Setting home.example.test AAAA to 2001:db8:200:2:32fb:93c5:555:1
	${NMDDNS_NSUPDATE} => nsupdate '-t' '2'
	server 127.0.0.1
	zone example.test
	update delete home.example.test AAAA
	update add home.example.test 600 AAAA 2001:db8:200:2:32fb:93c5:555:1
	send
	${NMG_RADVD_TRIGGER} => radvd triggered
	EOF
  ftest L12f "$out" "sets lifetime 0, removes dead addr"
  [[ -e $state1 ]]
  xtest L12i f "removes br0 88:99 state file"
  xread_value out <<-EOF
	inet6:2001:db8:100:2:32fb:93c5:555:1/64
	site6:2/64
	valid-life:180
	pref-life:0
	life-start:3000
	EOF
  shtest::check_file L12k "$state3" "$out" "updates br1 88:99 state file"
  [[ -e $state5 ]]
  xtest L12m f "removes brx 88:99 state file"

  unset cur_ip6_prefix cur_iaid

  old_ip6_prefix="2001:db8:100::/56" old_iaid="88:99"

  xwrap2 "<88:99>" ipv6_dhc_release
  xtest L21 t "returns true"
  xread_value out <<-EOF
	Prefix removed from wan0 - 2001:db8:100::/56
	Removing 2001:db8:100:2:32fb:93c5:555:1/64 from br1
	${NMG_IP} => ip '-6' 'addr' 'del'\
 '2001:db8:100:2:32fb:93c5:555:1/64' 'dev' 'br1'
	Setting home.example.test AAAA to 2001:db8:200:2:32fb:93c5:555:1
	${NMDDNS_NSUPDATE} => nsupdate '-t' '2'
	server 127.0.0.1
	zone example.test
	update delete home.example.test AAAA
	update add home.example.test 600 AAAA 2001:db8:200:2:32fb:93c5:555:1
	send
	${NMG_RADVD_TRIGGER} => radvd triggered
	EOF
  ftest L21f "$out" "removes address"
  [[ -e $state3 ]]
  xtest L21k f "br1 88:99 state file removed"
  shtest::check_file \
    L21d "$dstate1" "2001:db8:200:2:32fb:93c5:555:1" \
    "updates ddns state file"

  unset old_ip6_prefix old_iaid

  xwrap2 "" ipv6_prefix_flush
  xtest L31 t "returns true"
  xread_value out <<-EOF
	Removing 2001:db8:200:2:32fb:93c5:555:1/64 from br1
	${NMG_IP} => ip '-6' 'addr' 'del'\
 '2001:db8:200:2:32fb:93c5:555:1/64' 'dev' 'br1'
	Removing home.example.test AAAA
	${NMDDNS_NSUPDATE} => nsupdate '-t' '2'
	server 127.0.0.1
	zone example.test
	update delete home.example.test AAAA
	send
	${NMG_RADVD_TRIGGER} => radvd triggered
	EOF
  ftest L31f "$out" "removes address and dns"
  [[ -e $state2 ]]
  xtest L31i f "br0 11:22 state file removed"
  [[ -e $state4 ]]
  xtest L31k f "br1 11:22 state file removed"
  [[ -e $state6 ]]
  xtest L31m f "brx 11:22 state file removed"
  [[ -e $dstate1 ]]
  xtest L31d f "ddns state file removed"

  # cleanup
  ipv6_lan_reset_config
  nmddns_reset_config
  xtest::onexit::lan
}

xtest::onexit::lan_dad() {
  [[ ${LAN_STATE_PAT-} ]] || return 0
  local LAN_STATE_PAT="${LAN_STATE_PAT%-dhc}-dhc"
  xrm "${LAN_STATE_PAT/@LAN@-from-@ID@-@WAN@/eth3-from-aabb-wan0=aabb}"
}

xtest::group3::lan_dad() {

  local state1 out

  local LAN_STATE_PAT="${LAN_STATE_PAT}-dhc"
  state1=${LAN_STATE_PAT/@LAN@-from-@ID@-@WAN@/eth3-from-aabb-wan0=aabb}
  xrm "$state1"

  shtest::title "LAN Prefix DAD Failure Tests (lan_dad group)"

  # Call ipv6_dhc_bind with prefix assignment that fails DAD on LAN

  # check DAD failed
  WAN_LAN_INTFS="eth3"

  local new_ip6_prefix="2001:db8:5::/56" new_iaid="aa:bb"

  xwrap2 F1"<eth3, aa:bb>" ipv6_dhc_bind
  xtest F1 t "returns true"
  xread_value out <<-EOF
	Prefix on wan0: 2001:db8:5::/56
	Replacing 2001:db8:5:0::1/64 on eth3
	${NMG_IP} => ip '-6' 'addr' 'replace' '2001:db8:5:0::1/64' 'dev' 'eth3'
	ERR: Address 2001:db8:5:0::1/64 has conflict on eth3
	Removing 2001:db8:5:0::1/64 from eth3
	${NMG_IP} => ip '-6' 'addr' 'del' '2001:db8:5:0::1/64' 'dev' 'eth3'
	${NMG_RADVD_TRIGGER} => radvd triggered
	EOF
  ftest F1f "$out" "adds/removes ip"
  [[ -e $state1 ]]
  xtest F1s f "should not create state file"

  ipv6_lan_reset_config
  xtest::onexit::lan_dad
}

xtest::onexit::wan_start() {
  [[ ${WAN_STATE_PAT-} ]] || return 0
  local file="${WAN_STATE_PAT%-dhc}-dhc"
  file="${file/@WAN@/wan0}"
  xrm "${file/@ID@/abc}"
  xrm "${file/@ID@/cde}"
}

xtest::group3::wan_start() {

  local CUR_TIME='' out state1 state2
  local CONNECTION_UUID=''

  shtest::title "WAN Address Assignment Tests (wan_start group)"

  [[ ${WAN_STATE_PAT-} ]]
  xtest W0 t "checking WAN_STATE_PAT"
  shtest::last_check_ok || return 1

  local WAN_STATE_PAT="${WAN_STATE_PAT}-dhc"
  # @WAN@ replaced on load
  state1=${WAN_STATE_PAT/@WAN@/${interface}}
  state2=${state1/@ID@/cde}
  state1=${state1/@ID@/abc}
  xrm "$state1" "$state2"

  # interface is wan0
  xwrap2 "" ipv6_check_nm
  xtest W1 t "returns true"
  ftest W1f "" "does not log"
  [[ $NMCLI ]]
  xtest W1n t "NMCLI is set"

  local new_ip6_address='2001:db8:800::2' new_ip6_prefixlen=64 new_iaid='a:b:c'

  xwrap2 "<exiting addr>" ipv6_dhc_bind
  xtest W11 t "returns true"
  ftest W11f "Address on wan0: 2001:db8:800::2/64" "does nothing"
  shtest::check_file W11s "$state1" "inet6:2001:db8:800::2/64" \
                     "creates state file"

  local new_max_life=3600 new_life_starts=3000 new_preferred_life=1800
  CUR_TIME=3000

  xwrap2 "<new lifetimes>" ipv6_addr_bind
  xtest W12 t "returns true"
  xread_value out <<-EOF
	Address on wan0: 2001:db8:800::2/64
	Changing 2001:db8:800::2/64 on wan0
	${NMG_IP} => ip '-6' 'addr' 'change' '2001:db8:800::2/64'\
 'dev' 'wan0' 'valid_lft' '3600' 'preferred_lft' '1800'
	EOF
  ftest W12f "$out" "updates lifetimes"
  xread_value out <<-EOF
	inet6:2001:db8:800::2/64
	valid-life:3600
	pref-life:1800
	life-start:3000
	EOF
  shtest::check_file W12s "$state1" "$out" "creates state file"

  local old_ip6_address='2001:db8:800::2' old_ip6_prefixlen=64 old_iaid='a:b:c'
  new_ip6_address='2001:db8:aaa::2' new_ip6_prefixlen=64 new_iaid='c:d:e'
  xrm "$state2"

  xwrap2 "<moving addr>" ipv6_addr_bind
  xtest W13 t "returns true"
  xread_value out <<-EOF
	Address on wan0: 2001:db8:aaa::2/64 (old: 2001:db8:800::2/64)
	${NMCLI} => nmcli 'conn' 'modify' '--temporary' '${isp_uuid}'\
 'ipv6.method' 'manual' 'ipv6.addresses' '2001:db8:aaa::2/64'\
 'ipv6.dns' '' 'ipv6.dns-search' ''
	${NMCLI} => nmcli 'device' 'reapply' 'wan0'
	ERR: DAD timeout for 2001:db8:aaa::2/64
	${NMCLI} => nmcli 'conn' 'modify' '--temporary' '${isp_uuid}'\
 'ipv6.method' 'link-local' 'ipv6.addresses' '' 'ipv6.dns' ''\
 'ipv6.dns-search' ''
	${NMCLI} => nmcli 'device' 'reapply' 'wan0'
	EOF
  ftest W13f "${out}" "adds, then removes new addr"
  [[ -e ${state1} ]]
  shtest::check_result W13o f "removes old state"
  [[ -e ${state2} ]]
  shtest::check_result W13n f "does not create new state"

  unset old_ip6_address old_ip6_prefixlen old_iaid
  new_ip6_address='2001:db8:800::2' new_ip6_prefixlen=64 new_iaid='c:d:e'
  unset new_max_life new_life_starts new_preferred_life
  local WAN_STATIC_IP6='2001:db8:55::1/64, 2001:db8:100::1/64'
  local WAN_STATIC_DNS6='2001:db8:aaaa::1, 2001:db8:bbbb::1'
  local WAN_STATIC_DNS6_SEARCH='home.lan, office.lan'
  xrm "$state2"

  xwrap2 "<static dns>" ipv6_addr_bind
  xtest W14 t "returns true"
  xread_value out <<-EOF
	Address on wan0: 2001:db8:800::2/64
	${NMCLI} => nmcli 'conn' 'modify' '--temporary' '${isp_uuid}'\
 'ipv6.method' 'manual' 'ipv6.addresses'\
 '2001:db8:55::1/64,2001:db8:100::1/64,2001:db8:800::2/64'\
 'ipv6.dns' '2001:db8:aaaa::1,2001:db8:bbbb::1'\
 'ipv6.dns-search' 'home.lan,office.lan'
	${NMCLI} => nmcli 'device' 'reapply' 'wan0'
	EOF
  ftest W14f "${out}" "adds static/dhcp addrs, static dns"
  shtest::check_file W14s "$state2" "inet6:2001:db8:800::2/64" \
                     "creates state file"

  local new_dhcp6_name_servers='2001:db8:111::1 2001:db8:222::1'
  local new_dhcp6_domain_search='dns-search.lan dns-search2.lan'
  xrm "$state2"

  xwrap2 "<static/dhcp dns>" ipv6_addr_bind
  xtest W15 t "returns true"
  xread_value out <<-EOF
	Address on wan0: 2001:db8:800::2/64
	${NMCLI} => nmcli 'conn' 'modify' '--temporary' '${isp_uuid}'\
 'ipv6.method' 'manual' 'ipv6.addresses'\
 '2001:db8:55::1/64,2001:db8:100::1/64,2001:db8:800::2/64' 'ipv6.dns'\
 '2001:db8:aaaa::1,2001:db8:bbbb::1,2001:db8:111::1,2001:db8:222::1'\
 'ipv6.dns-search' 'home.lan,office.lan,dns-search.lan,dns-search2.lan'
	${NMCLI} => nmcli 'device' 'reapply' 'wan0'
	EOF
  ftest W15f "${out}" "adds static/dhcp addrs/dns"
  xread_value out <<-EOF
	inet6:2001:db8:800::2/64
	dns:2001:db8:111::1,2001:db8:222::1
	dns-search:dns-search.lan,dns-search2.lan
	EOF
  shtest::check_file W15s "$state2" "$out" "creates state file"

  WAN_STATIC_IP6='2001:db8:55::1/64'
  WAN_STATIC_DNS6='2001:db8:aaaa::1'
  WAN_STATIC_DNS6_SEARCH='home.lan'
  local -x NMCLI_MOCK_FORCE_ipv6_ignore_auto_dns=yes
  xrm "$state2"

  xwrap2 "<no-auto-dns>" ipv6_addr_bind
  xtest W16 t "returns true"
  xread_value out <<-EOF
	Address on wan0: 2001:db8:800::2/64
	${NMCLI} => nmcli 'conn' 'modify' '--temporary' '${isp_uuid}'\
 'ipv6.method' 'manual' 'ipv6.addresses'\
 '2001:db8:55::1/64,2001:db8:800::2/64' 'ipv6.dns' '2001:db8:aaaa::1'\
 'ipv6.dns-search' 'home.lan'
	${NMCLI} => nmcli 'device' 'reapply' 'wan0'
	EOF
  ftest W16f "${out}" "adds static/dhcp addrs, static dns"
  xread_value out <<-EOF
	inet6:2001:db8:800::2/64
	dns:2001:db8:111::1,2001:db8:222::1
	dns-search:dns-search.lan,dns-search2.lan
	EOF
  shtest::check_file W16s "$state2" "$out" "creates state file (with dns)"

  unset WAN_STATIC_IP6 WAN_STATIC_DNS6 WAN_STATIC_DNS6_SEARCH
  unset NMCLI_MOCK_FORCE_ipv6_ignore_auto_dns
  xrm "$state2"

  xwrap2 "<dhcp only dns>" ipv6_addr_bind
  xtest W17 t "returns true"
  xread_value out <<-EOF
	Address on wan0: 2001:db8:800::2/64
	${NMCLI} => nmcli 'conn' 'modify' '--temporary' '${isp_uuid}'\
 'ipv6.method' 'manual' 'ipv6.addresses' '2001:db8:800::2/64'\
 'ipv6.dns' '2001:db8:111::1,2001:db8:222::1'\
 'ipv6.dns-search' 'dns-search.lan,dns-search2.lan'
	${NMCLI} => nmcli 'device' 'reapply' 'wan0'
	EOF
  ftest W17f "${out}" "adds dhcp addrs/dns"
  xread_value out <<-EOF
	inet6:2001:db8:800::2/64
	dns:2001:db8:111::1,2001:db8:222::1
	dns-search:dns-search.lan,dns-search2.lan
	EOF
  shtest::check_file W17s "$state2" "$out" "creates state file"

  unset new_dhcp6_name_servers new_dhcp6_domain_search
  local -x NMCLI_MOCK_FORCE_ipv6_addresses='2001:db8:100::2/64'
  new_max_life=3600 new_life_starts=3000 new_preferred_life=1800
  CUR_TIME=3000
  xrm "$state2"

  xwrap2 "<new ip/lifetimes>" ipv6_addr_bind
  xtest W18 t "returns true"
  xread_value out <<-EOF
	Address on wan0: 2001:db8:800::2/64
	${NMCLI} => nmcli 'conn' 'modify' '--temporary' '${isp_uuid}'\
 'ipv6.method' 'manual' 'ipv6.addresses' '2001:db8:800::2/64'\
 'ipv6.dns' '' 'ipv6.dns-search' ''
	${NMCLI} => nmcli 'device' 'reapply' 'wan0'
	Changing 2001:db8:800::2/64 on wan0
	${NMG_IP} => ip '-6' 'addr' 'change' '2001:db8:800::2/64'\
 'dev' 'wan0' 'valid_lft' '3600' 'preferred_lft' '1800'
	EOF
  ftest W18f "$out" "updates lifetimes"
  xread_value out <<-EOF
	inet6:2001:db8:800::2/64
	valid-life:3600
	pref-life:1800
	life-start:3000
	EOF
  shtest::check_file W18s "$state2" "$out" "creates state file"

  unset new_max_life new_life_starts new_preferred_life
  new_ip6_address='2001:db8:800::2' new_ip6_prefixlen=64 new_iaid='a:b:c'

  xwrap2 "<2nd ip>" ipv6_addr_bind
  xtest W19 t "returns true"
  xread_value out <<-EOF
	Address on wan0: 2001:db8:800::2/64
	${NMCLI} => nmcli 'conn' 'modify' '--temporary' '${isp_uuid}'\
 'ipv6.method' 'manual' 'ipv6.addresses'\
 '2001:db8:800::2/64,2001:db8:800::2/64' 'ipv6.dns' '' 'ipv6.dns-search' ''
	${NMCLI} => nmcli 'device' 'reapply' 'wan0'
	Changing 2001:db8:800::2/64 on wan0
	${NMG_IP} => ip '-6' 'addr' 'change' '2001:db8:800::2/64'\
 'dev' 'wan0' 'valid_lft' '3600' 'preferred_lft' '1800'
	EOF
  ftest W19f "$out" "updated NM, lifetimes"
  shtest::check_file W19s "$state1" "inet6:2001:db8:800::2/64" "creates abc state file"

  nmddns_reset_config
  xtest::onexit::wan_start
}

xtest::onexit::wan_depref() {
  [[ ${WAN_STATE_PAT-} ]] || return 0
  local file="${WAN_STATE_PAT%-dhc}-dhc"
  file="${file/@WAN@/wan0}"
  xrm "${file/@ID@/123}"
}

xtest::group3::wan_depref() {

  local out CUR_TIME=3100 state1
  local CONNECTION_UUID=''

  shtest::title "WAN Address Depref Tests (wan_depref group)"

  [[ ${WAN_STATE_PAT-} ]]
  xtest E0 t "checking WAN_STATE_PAT"
  shtest::last_check_ok || return 1

  local WAN_STATE_PAT="${WAN_STATE_PAT}-dhc"
  state1=${WAN_STATE_PAT/@WAN@/${interface}}
  state1=${state1/@ID@/123}
  xrm "$state1"

  local -x NMCLI_MOCK_FORCE_ipv6_addresses='2001:db8:100::2/64'
  local cur_ip6_address='2001:db8:800::2' cur_ip6_prefixlen=64 cur_iaid='1:2:3'
  local cur_max_life=1800 cur_life_starts=3000

  xwrap2 "<depref new>" ipv6_dhc_depref
  xtest E1 t "returns true"
  xread_value out <<-EOF
	Address devalued on wan0: 2001:db8:800::2/64
	${NMCLI} => nmcli 'conn' 'modify' '--temporary' '${isp_uuid}'\
 'ipv6.method' 'manual' 'ipv6.addresses' '2001:db8:800::2/64'\
 'ipv6.dns' '' 'ipv6.dns-search' ''
	${NMCLI} => nmcli 'device' 'reapply' 'wan0'
	Changing 2001:db8:800::2/64 on wan0
	${NMG_IP} => ip '-6' 'addr' 'change' '2001:db8:800::2/64'\
 'dev' 'wan0' 'valid_lft' '1700' 'preferred_lft' '0'
	EOF
  ftest E1f "$out" "updates lifetimes"
  xread_value out <<-EOF
	inet6:2001:db8:800::2/64
	valid-life:1800
	pref-life:0
	life-start:3000
	EOF
  shtest::check_file E1s "$state1" "$out" "creates state file"

  unset NMCLI_MOCK_FORCE_ipv6_addresses
  xrm "$state1"

  xwrap2 "<depref exist>" ipv6_addr_depref
  xtest E2 t "returns true"
  xread_value out <<-EOF
	Address devalued on wan0: 2001:db8:800::2/64
	Changing 2001:db8:800::2/64 on wan0
	${NMG_IP} => ip '-6' 'addr' 'change' '2001:db8:800::2/64'\
 'dev' 'wan0' 'valid_lft' '1700' 'preferred_lft' '0'
	EOF
  ftest E2f "$out" "updates lifetimes"
  xread_value out <<-EOF
	inet6:2001:db8:800::2/64
	valid-life:1800
	pref-life:0
	life-start:3000
	EOF
  shtest::check_file E2s "$state1" "$out" "creates state file"

  nmddns_reset_config
  xtest::onexit::wan_depref
}

xtest::onexit::wan_ddns() {
  [[ ${WAN_STATE_PAT-} ]] || return 0
  local file="${WAN_STATE_PAT%-dhc}-dhc"
  file=${file/@WAN@/eth0}
  xrm "${file/@ID@/1a1a}"
  xrm "${file/@ID@/2b2b}"
}

xtest::group3::wan_ddns() {

  local out state1 state2 interface=eth0
  local CONNECTION_UUID=''

  shtest::title "WAN Address DDNS Tests (wan_ddns group)"

  local WAN_STATE_PAT="${WAN_STATE_PAT}-dhc"
  xtest D0 t "checking WAN_STATE_PAT"
  shtest::last_check_ok || return 1
  state1=${WAN_STATE_PAT/@WAN@/${interface}}
  state2=${state1/@ID@/2b2b}
  state1=${state1/@ID@/1a1a}
  xrm "$state1"

  local new_ip6_address='2001:db8:871a:28c1::1' new_ip6_prefixlen=64 new_iaid='1a:1a'
  local -x NMCLI_MOCK_FORCE_ipv6_method="manual"
  local -x NMCLI_MOCK_FORCE_ipv6_addresses="2001:db8:871a:28c1::1/64"
  local -x IP_MOCK_OUTPUT
  xread_value IP_MOCK_OUTPUT <<-EOF
	inet6 2001:db8:871a:28c1::1/64 scope global
	valid_lft forever preferred_lft forever
	EOF

  xwrap2 "<exiting addr>" ipv6_addr_bind
  xtest D1 t "returns true"
  xread_value out <<-EOF
	Address on eth0: 2001:db8:871a:28c1::1/64
	Setting eth0.example.test AAAA to 2001:db8:871a:28c1::1
	${NMDDNS_NSUPDATE} => nsupdate '-t' '2'
	server 127.0.0.1
	zone example.test
	update delete eth0.example.test AAAA
	update add eth0.example.test 600 AAAA 2001:db8:871a:28c1::1
	send
	EOF
  ftest D1f "$out" "adds DNS"
  shtest::check_file D1y "$state1" "inet6:2001:db8:871a:28c1::1/64" \
                     "creates state file"

  local new_ip6_address='2001:db8:4860:4860::8888'
  local new_ip6_prefixlen=64 new_iaid='2b:2b'
  local -x NMCLI_MOCK_FORCE_ipv6_addresses="2001:db8:871a:28c1::1/64,\
 2001:db8:4860:4860::8888/64"
  xread_value IP_MOCK_OUTPUT <<-EOF
	inet6 2001:db8:871a:28c1::1/64 scope global
	valid_lft forever preferred_lft forever
	inet6 2001:db8:4860:4860::8888/64 scope global
	valid_lft forever preferred_lft forever
	EOF

  xwrap2 "<2nd addr>" ipv6_addr_bind
  xtest D2 t "returns true"
  xread_value out <<-EOF
	Address on eth0: 2001:db8:4860:4860::8888/64
	Setting eth0.example.test AAAA to\
 2001:db8:871a:28c1::1,2001:db8:4860:4860::8888
	${NMDDNS_NSUPDATE} => nsupdate '-t' '2'
	server 127.0.0.1
	zone example.test
	update delete eth0.example.test AAAA
	update add eth0.example.test 600 AAAA 2001:db8:871a:28c1::1
	update add eth0.example.test 600 AAAA 2001:db8:4860:4860::8888
	send
	EOF
  ftest D2f "$out" "adds DNS"
  shtest::check_file D2y "$state1" "inet6:2001:db8:871a:28c1::1/64" \
                     "creates 1a1a state file"
  shtest::check_file D2z "$state2" "inet6:2001:db8:4860:4860::8888/64" \
                     "creates 2b2b state file"

  unset new_ip6_address new_ip6_prefixlen new_iaid
  local old_ip6_address='2001:db8:871a:28c1::1'
  local old_ip6_prefixlen=64 old_iaid='1a:1a'
  local -x DIG_MOCK_OUTPUT="2001:db8:871a:28c1::1,2001:db8:4860:4860::8888"

  xwrap2 "<1/2 removed>" ipv6_dhc_release
  xtest D11 t "returns true"
  xread_value out <<-EOF
	Address removed from eth0: 2001:db8:871a:28c1::1/64
	${NMCLI} => nmcli 'conn' 'modify' '--temporary' '${office_uuid}'\
 'ipv6.method' 'manual' 'ipv6.addresses' '2001:db8:4860:4860::8888/64'\
 'ipv6.dns' '' 'ipv6.dns-search' ''
	${NMCLI} => nmcli 'device' 'reapply' 'eth0'
	Setting eth0.example.test AAAA to 2001:db8:4860:4860::8888
	${NMDDNS_NSUPDATE} => nsupdate '-t' '2'
	server 127.0.0.1
	zone example.test
	update delete eth0.example.test AAAA
	update add eth0.example.test 600 AAAA 2001:db8:4860:4860::8888
	send
	EOF
  ftest D11f "$out" "removes 1a1a from NM/DNS"
  [[ -e "$state1" ]]
  xtest D11y f "1a1a state file removed"
  [[ -e "$state2" ]]
  xtest D11z t "2b2b state file remains"

  old_ip6_address='2001:db8:4860:4860::8888'
  old_ip6_prefixlen=64 old_iaid='2b:2b'
  NMCLI_MOCK_FORCE_ipv6_addresses="2001:db8:4860:4860::8888/64"
  DIG_MOCK_OUTPUT="2001:db8:4860:4860::8888"

  xwrap2 "<2nd removed>" ipv6_addr_release
  xtest D12 t "returns true"
  xread_value out <<-EOF
	Address removed from eth0: 2001:db8:4860:4860::8888/64
	${NMCLI} => nmcli 'conn' 'modify' '--temporary' '${office_uuid}'\
 'ipv6.method' 'link-local' 'ipv6.addresses' '' 'ipv6.dns' ''\
 'ipv6.dns-search' ''
	${NMCLI} => nmcli 'device' 'reapply' 'eth0'
	Removing eth0.example.test AAAA
	${NMDDNS_NSUPDATE} => nsupdate '-t' '2'
	server 127.0.0.1
	zone example.test
	update delete eth0.example.test AAAA
	send
	EOF
  ftest D12f "$out" "removes 2b2b from NM/DNS"
  [[ -e "$state2" ]]
  xtest D12z f "2b2b state file removed"

  # restore state files
  echo "inet6:2001:db8:871a:28c1::1/64" >"$state1"
  echo "inet6:2001:db8:4860:4860::8888/64" >"$state2"

  xwrap2 "" ipv6_addrs_flush
  xtest D21 t "returns true"
  xread_value out <<-EOF
	${NMCLI} => nmcli 'conn' 'modify' '--temporary' '${office_uuid}'\
 'ipv6.method' 'link-local' 'ipv6.addresses' '' 'ipv6.dns' ''\
 'ipv6.dns-search' ''
	Removing eth0.example.test AAAA
	${NMDDNS_NSUPDATE} => nsupdate '-t' '2'
	server 127.0.0.1
	zone example.test
	update delete eth0.example.test AAAA
	send
	EOF
  ftest D21f "$out" "removes 1a1a/2b2b from NM/DNS (no apply)"
  [[ -e "$state1" ]]
  xtest D21y f "1a1a state file removed"
  [[ -e "$state2" ]]
  xtest D21z f "2b2b state file removed"

  nmddns_reset_config
  xtest::onexit::wan_ddns
}

xtest::onexit() {
  [[ ${WAN_CONNSTATE_PAT-} ]] && {
    local WAN_CONNSTATE_PAT="${WAN_CONNSTATE_PAT%-dhc}-dhc"
    xrm "${WAN_CONNSTATE_PAT/@WAN@/wan0}"
    xrm "${WAN_CONNSTATE_PAT/@WAN@/eth0}"
  }
  xrm "${XFILE-}"
}

xmain() {
  local XFILE="$TEST_OUT/prefix-script file" WAN_LAN_INTFS=''
  local TEST_RADVD_TRIGGER="$TEST_BIN/dummy-mock"
  local -x DUMMY_MOCK_OUTPUT="radvd triggered"

  # set so wan config is loaded (dhclient mode)
  local -x interface=wan0 reason=TEST

  # disable helper so we get nsupdate results
  local TEST_DDNS_HELPER=''

  xload_script "${SRC_ROOT}/usr/lib/NetworkManager/dispatcher.d/08-ipv6-prefix"

  [[ ${WAN_LAN_INTFS-} && ${WAN_CONNSTATE_PAT-} ]] ||
    xtest::fail "Config file failed to load"

  # create state file created when dhclient spawned
  local WAN_CONNSTATE_PAT="${WAN_CONNSTATE_PAT}-dhc"
  local office_uuid="fd8bc2bb-9f55-47d6-a394-174f4ee4fe69"
  echo "conn-uuid:${office_uuid}" >"${WAN_CONNSTATE_PAT/@WAN@/eth0}"
  local isp_uuid="9613d84e-2555-4e7f-a6c2-2a7da5f4d07b"
  echo "conn-uuid:${isp_uuid}" >"${WAN_CONNSTATE_PAT/@WAN@/wan0}"

  xtest::run_tests "ipv6-prefix-dhclient-test Summary" "$@"
  local rc=$?

  xtest::onexit
  nmddns_cleanup

  return $rc
}

xstart "$@"
