test_container_devices_nic_ipvlan() {
  ensure_import_testimage
  ensure_has_localhost_remote "${INCUS_ADDR}"

  if ! incus info | grep 'network_ipvlan: "true"' ; then
    echo "==> SKIP: No IPVLAN support"
    return
  fi

  ctName="nt$$"
  ipRand=$(shuf -i 0-9 -n 1)

  # Test ipvlan support to offline container (hot plugging not supported).
  ip link add "${ctName}" type dummy

  # Record how many nics we started with.
  startNicCount=$(find /sys/class/net | wc -l)

  # Check that starting IPVLAN container.
  sysctl net.ipv6.conf."${ctName}".proxy_ndp=1
  sysctl net.ipv6.conf."${ctName}".forwarding=1
  sysctl net.ipv4.conf."${ctName}".forwarding=1
  incus init testimage "${ctName}"
  incus config device add "${ctName}" eth0 nic \
    nictype=ipvlan \
    parent=${ctName} \
    ipv4.address="192.0.2.1${ipRand}" \
    ipv6.address="2001:db8::1${ipRand}" \
    ipv4.gateway=auto \
    ipv6.gateway=auto \
    mtu=1400
  incus start "${ctName}"

  # Check custom MTU is applied.
  if ! incus exec "${ctName}" -- ip link show eth0 | grep "mtu 1400" ; then
    echo "mtu invalid"
    false
  fi

  incus stop "${ctName}" --force

  # Check that MTU is inherited from parent device when not specified on device.
  ip link set "${ctName}" mtu 1405
  incus config device unset "${ctName}" eth0 mtu
  incus start "${ctName}"
  if ! incus exec "${ctName}" -- grep "1405" /sys/class/net/eth0/mtu ; then
    echo "mtu not inherited from parent"
    false
  fi

  #Spin up another container with multiple IPs.
  incus init testimage "${ctName}2"
  incus config device add "${ctName}2" eth0 nic \
    nictype=ipvlan \
    parent=${ctName} \
    ipv4.address="192.0.2.2${ipRand}, 192.0.2.3${ipRand}" \
    ipv6.address="2001:db8::2${ipRand}, 2001:db8::3${ipRand}"
  incus start "${ctName}2"

  # Check comms between containers.
  incus exec "${ctName}" -- ping -c2 -W5 "192.0.2.2${ipRand}"
  incus exec "${ctName}" -- ping -c2 -W5 "192.0.2.3${ipRand}"
  incus exec "${ctName}" -- ping6 -c2 -W5 "2001:db8::2${ipRand}"
  incus exec "${ctName}" -- ping6 -c2 -W5 "2001:db8::3${ipRand}"
  incus exec "${ctName}2" -- ping -c2 -W5 "192.0.2.1${ipRand}"
  incus exec "${ctName}2" -- ping6 -c2 -W5 "2001:db8::1${ipRand}"
  incus stop -f "${ctName}2"

  # Check IPVLAN on top of VLAN parent with custom routing tables.
  incus stop -f "${ctName}"
  incus config device set "${ctName}" eth0 vlan 1234
  incus config device set "${ctName}" eth0 ipv4.host_table=100
  incus config device set "${ctName}" eth0 ipv6.host_table=101

  # Check gateway settings don't accept IPs in default l3s mode.
  ! incus config device set "${ctName}" eth0 ipv4.gateway=192.0.2.254
  ! incus config device set "${ctName}" eth0 ipv6.gateway=2001:db8::FFFF

  incus start "${ctName}"

  # Check VLAN interface created
  if ! grep "1" "/sys/class/net/${ctName}.1234/carrier" ; then
    echo "vlan interface not created"
    false
  fi

  # Check static routes added to custom routing table
  ip -4 route show table 100 | grep "192.0.2.1${ipRand}"
  ip -6 route show table 101 | grep "2001:db8::1${ipRand}"

  # Check volatile cleanup on stop.
  incus stop -f "${ctName}"
  if incus config show "${ctName}" | grep volatile.eth0 | grep -v volatile.eth0.hwaddr | grep -v volatile.eth0.name ; then
    echo "unexpected volatile key remains"
    false
  fi

  # Check parent device is still up.
  if ! grep "1" "/sys/class/net/${ctName}/carrier" ; then
    echo "parent is down"
    false
  fi

  # Check static routes are removed from custom routing table
  ! ip -4 route show table 100 | grep "192.0.2.1${ipRand}"
  ! ip -6 route show table 101 | grep "2001:db8::1${ipRand}"

  # Check ipvlan l2 mode with mixture of singular and CIDR IPs, and gateway IPs.
  incus config device remove "${ctName}" eth0
  incus config device add "${ctName}" eth0 nic \
    nictype=ipvlan \
    mode=l2 \
    parent=${ctName} \
    ipv4.address="192.0.2.1${ipRand},192.0.2.2${ipRand}/32" \
    ipv6.address="2001:db8::1${ipRand},2001:db8::2${ipRand}/128" \
    ipv4.gateway=192.0.2.254 \
    ipv6.gateway=2001:db8::FFFF \
    mtu=1400
  incus start "${ctName}"

  incus config device remove "${ctName}2" eth0
  incus config device add "${ctName}2" eth0 nic \
    nictype=ipvlan \
    parent=${ctName} \
    ipv4.address="192.0.2.3${ipRand}" \
    ipv6.address="2001:db8::3${ipRand}" \
    mtu=1400
  incus start "${ctName}2"

  # Add an internally configured address (only possible in l2 mode).
  incus exec "${ctName}2" -- ip -4 addr add "192.0.2.4${ipRand}/32" dev eth0
  incus exec "${ctName}2" -- ip -6 addr add "2001:db8::4${ipRand}/128" dev eth0
  wait_for_dad "${ctName}2" eth0

  # Check comms between containers.
  incus exec "${ctName}" -- ping -c2 -W5 "192.0.2.3${ipRand}"
  incus exec "${ctName}" -- ping -c2 -W5 "192.0.2.4${ipRand}"
  incus exec "${ctName}" -- ping6 -c2 -W5 "2001:db8::3${ipRand}"
  incus exec "${ctName}" -- ping6 -c2 -W5 "2001:db8::4${ipRand}"
  incus exec "${ctName}2" -- ping -c2 -W5 "192.0.2.1${ipRand}"
  incus exec "${ctName}2" -- ping -c2 -W5 "192.0.2.2${ipRand}"
  incus exec "${ctName}2" -- ping6 -c2 -W5 "2001:db8::1${ipRand}"
  incus exec "${ctName}2" -- ping6 -c2 -W5 "2001:db8::2${ipRand}"

  incus stop -f "${ctName}"
  incus stop -f "${ctName}2"

  # Check we haven't left any NICS lying around.
  endNicCount=$(find /sys/class/net | wc -l)
  if [ "$startNicCount" != "$endNicCount" ]; then
    echo "leftover NICS detected"
    false
  fi

  # Cleanup ipvlan checks
  incus delete "${ctName}" -f
  incus delete "${ctName}2" -f
  ip link delete "${ctName}" type dummy
}
