#!/bin/sh
# Copyright (C) BlueWave Projects and Services 2015-2026
#
#	This software is released under the GNU General Public License version 3 or any later version.
#	This program is free software: you can redistribute it and/or modify it under the terms of the GNU 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 General Public License for more details.
#
#	To obtain a copy of the GNU General Public License, see <https://www.gnu.org/licenses/>.
#
# mesh11sd daemon
#

last_version="6.2.0~be7a"
version="6.2.0"
checksum=$(sha256sum /usr/sbin/mesh11sd | awk '{printf "%s", $1}')
fixup1=0
failcount=0
network_restart=0

# initial tmpdir, use $log_mountpoint/mesh11sd once startup begins
tmpdir="/tmp/mesh11sd"
outputfile="$tmpdir/wifidetect"
mkdir -p "$tmpdir"

if [ "$1" = "daemon" ]; then
	mutestate=0
else
	mutestate=1
fi

mute=""

get_mesh_iflist () {
	iflist=""
	all_ifcs=$(iw dev | awk -F "Interface " '$2>0{printf "%s ", $2}')

	for iface in $all_ifcs; do
		iftype=$(iw dev $iface info 2> /dev/null | grep "type" | awk '{printf "%s", $2}')

		if [ ! -z "$iftype" ] && [ "$iftype" = "mesh" ]; then
			iflist="$iflist $iface"
		fi
	done
}

check_mesh_phantom () {
	local nodelist=$(iw dev $iface mpath dump | awk '{printf "%s,%s ", $1, $2}')
	local mnode=""
	local previous_mnode=""

	for mnode in $nodelist; do

		if [ -z $previous_mnode ] || [ "$previous_mnode" != "$mnode" ]; then
			local phantom=$(echo "$mnode" | awk -F"," '$2 == "00:00:00:00:00:00" {printf "%s", $1}')

			if [ ! -z "$phantom" ]; then
				debugtype="info"
				syslogmessage="Phantom meshnode detected [$phantom] - deleting"
				write_to_syslog
				iw dev $iface mpath del $phantom
			fi

			previous_mnode="$mnode"
		fi
	done
}

get_params () {
	params=$(iw dev $iface mesh_param dump 2> /dev/null)

	if [ "$?" -ne 0 ]; then
		buffer="$tmpdir/"$(date | sha256sum | awk '{printf "%s", $1}')
		param_list=$(iw dev $iface get mesh_param 2> /dev/null | grep " - " | awk -F" - " '{printf "%s ", $2}')
		params=""


		for param in $param_list; do
			paramval=$(iw dev $iface get mesh_param $param 2> /dev/null)
			if [ "$?" -eq 0 ]; then
				paramline="$param = $paramval"
				echo "$paramline" >> $buffer
			fi
		done

		if [ -f "$buffer" ]; then
			params=$(cat $buffer)
			rm "$buffer"
		fi
	fi
}

check_mesh_params () {

	for param in $params; do

		conf=$(uci get $uciname.$param 2> /dev/null)

		if [ -z "$conf" ]; then
			continue
		fi

		param_value=$(iw dev $iface get mesh_param $param 2> /dev/null | awk '{printf "%s", $1}')

		if [ -z "$param_value" ]; then
			debugtype="info"
			syslogmessage="Failed to get current value of $param, mesh interface not established."
			write_to_syslog
			continue
		fi

		if [ "$param_value" != "$conf" ]; then

			if [ "$param" = "mesh_rssi_threshold" ]; then
				if [ ! -f "$tmpdir/mesh_rssi_threshold" ]; then
					debugtype="info"
					syslogmessage="Old value:$param=$param_value, Setting new value:$param=$conf"
					write_to_syslog
					iwstatus=$(iw dev $iface set mesh_param $param "$conf" 2>&1)
					mesh_rssi_threshold=$conf
					echo "meshinterface=\"$iface\"" > "$tmpdir/meshinterface"
					all_nodes_rssi_update
				fi
			elif [ "$param" != "mesh_rssi_threshold" ]; then
				debugtype="info"
				syslogmessage="Old value:$param=$param_value, Setting new value:$param=$conf"
				write_to_syslog
				iwstatus=$(iw dev $iface set mesh_param $param "$conf" 2>&1)
			fi


			if [ ! -z "$iwstatus" ]; then
				debugtype="err"
				syslogmessage="$param: $iwstatus"
				write_to_syslog
			fi
		fi
	done
}

restart_mesh() {
	debugtype="debug"
	syslogmessage="restarting mesh"
	write_to_syslog
	set_txpower
	mesh_gate_state
	mobility_settings config
	wifi
	wait_for_mesh

	if [ -f "$tmpdir/devicemac" ]; then
		. $tmpdir/devicemac
		ip link set "$device" arp on
		ip link set "$device" multicast on
	fi

	check_mesh_mtu
	mobility_settings parameters

}

check_mesh_mtu() {
	if [ -f "$tmpdir/active_mesh_ifname" ]; then
		. $tmpdir/active_mesh_ifname

		vxlan_status=$(ip link show vxlan$tun_id &> /dev/null; echo $?)
		mesh_mtu=$(ip link show "$active_mesh_ifname" | grep -w "mtu" | awk -F "mtu " '{print $2}' | awk '{printf "%s", $1}')

		if [ -z "$mesh_mtu" ]; then
			return 0
		fi

		if [ "$vtun_enable" -eq 1 ] && [ "$vxlan_status" -eq 0 ] && [ "$mesh_mtu" -lt 1600 ]; then
			ip link set "$active_mesh_ifname" mtu 1600
		fi
	fi

}

get_current_setup() {
	debuglevel=$(uci get mesh11sd.setup.debuglevel 2> /dev/null | awk '{printf "%i", $1}')

	if [ -z "$debuglevel" ]; then
		debuglevel=1
	fi

	syslogmessage="Reading configuration"
	debugtype="notice"
	mute="$mutestate"
	write_to_syslog

	enabled=$(uci get mesh11sd.setup.enabled 2> /dev/null)

	if [ -z "$enabled" ]; then
		enabled=1
	fi

	syslogmessage="option enabled [ $enabled ]"
	debugtype="info"
	mute="$mutestate"
	write_to_syslog


	#####
	log_mountpoint=$(uci get mesh11sd.setup.log_mountpoint 2> /dev/null)

	if [ -z "$log_mountpoint" ]; then
		log_mountpoint="/tmp"
	fi

	mountcheck=$(df | grep -w  "$log_mountpoint")

	if [ -z "$mountcheck" ]; then
		log_mountpoint="/tmp"
	fi

	syslogmessage="option log_mountpoint [ $log_mountpoint ]"
	debugtype="info"
	mute="$mutestate"
	write_to_syslog

	tmpdir="$log_mountpoint/mesh11sd"
	mkdir -p "$tmpdir"

	#####
	max_log_entries=$(uci get mesh11sd.setup.max_log_entries 2> /dev/null)

	if [ -z "$max_log_entries" ]; then
		max_log_entries="500"
	fi

	syslogmessage="option max_log_entries [ $max_log_entries ]"
	debugtype="info"
	mute="$mutestate"
	write_to_syslog

	#####
	syslogmessage="option debuglevel [ $debuglevel ]"
	debugtype="info"
	mute="$mutestate"
	write_to_syslog

	#####
	checkinterval=$(uci get mesh11sd.setup.checkinterval 2> /dev/null)

	if [ -z "$checkinterval" ] || [ "$checkinterval" -lt 10 ]; then
		checkinterval=10
	fi

	syslogmessage="option checkinterval [ $checkinterval ]"
	debugtype="info"
	mute="$mutestate"
	write_to_syslog

	#####
	commit_all=$(uci get mesh11sd.setup.commit_all 2> /dev/null)

	if [ -z "$commit_all" ]; then
		commit_all=0
	fi

	syslogmessage="option commit_all [ $commit_all ]"
	debugtype="info"
	mute="$mutestate"
	write_to_syslog

	#####
	portal_detect=$(uci get mesh11sd.setup.portal_detect 2> /dev/null)

	if [ -z "$portal_detect" ] || [ "$portal_detect" -eq 2 ]; then
		portal_detect=1
	fi

	syslogmessage="option portal_detect [ $portal_detect ]"
	debugtype="info"
	mute="$mutestate"
	write_to_syslog

	#####
	portal_use_default_ipv4=$(uci get mesh11sd.setup.portal_use_default_ipv4 2> /dev/null)

	if [ -z "$portal_use_default_ipv4" ]; then
		portal_use_default_ipv4="0"
	fi

	syslogmessage="option portal_use_default_ipv4 [ $portal_use_default_ipv4 ]"
	debugtype="info"
	mute="$mutestate"
	write_to_syslog

	#####
	channel_tracking_checkinterval=$(uci get mesh11sd.setup.channel_tracking_checkinterval 2> /dev/null)

	if [ -z "$channel_tracking_checkinterval" ]; then
		channel_tracking_checkinterval="$(($checkinterval*8/3))"
	fi

	syslogmessage="option channel_tracking_checkinterval [ $channel_tracking_checkinterval ]"
	debugtype="info"
	mute="$mutestate"
	write_to_syslog

	#####
	portal_detect_threshold=$(uci get mesh11sd.setup.portal_detect_threshold 2> /dev/null)

	if [ -z "$portal_detect_threshold" ]; then
		portal_detect_threshold=5
	fi

	if [ "$portal_detect" -eq 0 ]; then
		portal_detect_threshold=0
	fi

	syslogmessage="option portal_detect_threshold [ $portal_detect_threshold ]"
	debugtype="info"
	mute="$mutestate"
	write_to_syslog

	#####
	get_mesh_path_cost

	#####
	interface_timeout=$(uci get mesh11sd.setup.interface_timeout 2> /dev/null)

	if [ -z "$interface_timeout" ] || [ "$interface_timeout" -lt 10 ]; then
		interface_timeout=10
	fi

	syslogmessage="option interface_timeout [ $interface_timeout ]"
	debugtype="info"
	mute="$mutestate"
	write_to_syslog

	#####
	auto_config=$(uci get mesh11sd.setup.auto_config 2> /dev/null)

	if [ -z "$auto_config" ]; then
		auto_config=0
	elif [ "$auto_config" -gt 2 ]; then
		auto_config=1
	fi

	syslogmessage="option auto_config [ $auto_config ]"
	debugtype="info"
	mute="$mutestate"
	write_to_syslog

	#####
	auto_mesh_id=$(uci get mesh11sd.setup.auto_mesh_id 2> /dev/null | awk '{printf "%s", $1}')

	if [ -z "$auto_mesh_id" ]; then
		auto_mesh_id="--__"
	fi

	auto_mesh_id=$(printf "$auto_mesh_id" | sha256sum | awk '{printf "%s", $1}' | tail -c55 | head -c30)

	syslogmessage="auto_mesh_id hash [ $auto_mesh_id ]"
	debugtype="info"
	mute="$mutestate"
	write_to_syslog

	#####
	auto_mesh_band=$(uci get mesh11sd.setup.auto_mesh_band 2> /dev/null | awk '{printf "%s", $1}')

	if [ -z "$auto_mesh_band" ]; then
		auto_mesh_band="2g40"
	fi

	bandlist="2g 5g 6g 60g"
	bandcheck=0

	for band in $bandlist; do

		if [ "$band" = "$auto_mesh_band" ]; then
			bandcheck=1
			break
		fi

	done

	if [ "$bandcheck" = 0 ]; then
		auto_mesh_band="2g40"
	fi

	syslogmessage="option auto_mesh_band [ $auto_mesh_band ]"
	debugtype="info"
	mute="$mutestate"
	write_to_syslog

	#####
	portal_channel=$(uci get mesh11sd.setup.portal_channel 2> /dev/null)

	if [ -z "$portal_channel" ]; then
		portal_channel="default"
	fi

	syslogmessage="option portal_channel [ $portal_channel ]"
	debugtype="info"
	mute="$mutestate"
	write_to_syslog

	#####
	mesh_phy_index=$(uci get mesh11sd.setup.mesh_phy_index 2> /dev/null)

	if [ ! -z "$mesh_phy_index" ]; then
		is_num=$(echo "$mesh_phy_index" | grep -E '^[0-9]+$' &>/dev/null; echo $?)
		if [ "$is_num" -ne 0 ]; then
			syslogmessage="option mesh_phy_index must be an integer - ignoring setup value of [ $mesh_phy_index ]"
			debugtype="info"
			mute="$mutestate"
			write_to_syslog
			mesh_phy_index=""
		else
			num_wiphys=$(iw list | grep -c "Wiphy")

			if [ "$mesh_phy_index" -ge "$num_wiphys" ]; then
				syslogmessage="option mesh_phy_index is invalid, phy does not exist - ignoring setup value of [ $mesh_phy_index ]"
				debugtype="info"
				mute="$mutestate"
				write_to_syslog
				mesh_phy_index=""
			fi
		fi

	fi

	syslogmessage="option mesh_phy_index [ $mesh_phy_index ]"
	debugtype="info"
	mute="$mutestate"
	write_to_syslog

	#####
	country=$(uci get mesh11sd.setup.country 2> /dev/null)

	if [ ! -z "$country" ]; then
		syslogmessage="option country [ $country ]"
		debugtype="info"
		mute="$mutestate"
		write_to_syslog
	fi

	#####
	auto_mesh_key=$(uci get mesh11sd.setup.auto_mesh_key 2> /dev/null | awk '{printf "%s", $1}')

	if [ -z "$auto_mesh_key" ]; then
		auto_mesh_key=$(echo "$auto_mesh_id""91b39a9b41e918e9bce1c8d5d7d3e071d6f1f8855a7c0214f687550177c5d5b8" | sha256sum | awk '{printf "%s", $1}')
	else
		auto_mesh_key=$(echo "$auto_mesh_id""$auto_mesh_key" | sha256sum | awk '{printf "%s", $1}')
	fi

	syslogmessage="option auto_mesh_key [ $auto_mesh_key ]"
	debugtype="info"
	mute="$mutestate"
	write_to_syslog

	#####
	auto_mesh_network=$(uci get mesh11sd.setup.auto_mesh_network 2> /dev/null)

	# Get the default lan mac address
	auto_mesh_network="lan"
	refresh_bridgemac
	. $tmpdir/devicemac


	if [ -z "$auto_mesh_network" ]; then
		auto_mesh_network="lan"

		if [ "$auto_config" -eq 0 ]; then
			syslogmessage="option auto_mesh_network is not configured, defaulting to [ $auto_mesh_network ]. Is this what you intended?"
			mute="$mutestate"
			debugtype="warn"
			write_to_syslog
		fi
	fi

	# Block wan and wan6 as valid network zones for mesh unless portal_detect - 3 (CPE mode)

	if [ "$portal_detect" -ne 3 ]; then

		if [ "$auto_mesh_network" = "wan" ] || [ "$auto_mesh_network" = "wan6" ]; then
			auto_mesh_network="lan"
		fi

	elif [ "$portal_detect" -eq 3 ] && [ "$auto_mesh_network" = "lan" ]; then
		auto_mesh_network="wan"
	fi

	zone_status=$(uci show firewall | grep -w "name='$auto_mesh_network'" &> /dev/null ; echo -n "$?")

	if [ "$zone_status" -eq 1 ]; then
		syslogmessage="network zone [ $auto_mesh_network ] does not exist"
		debugtype="warn"
		mute="$mutestate"
		write_to_syslog
		auto_mesh_network="lan"
	fi

	syslogmessage="option auto_mesh_network [ $auto_mesh_network ]"
	debugtype="info"
	mute="$mutestate"
	write_to_syslog

	#####
	mesh_basename=$(uci get mesh11sd.setup.mesh_basename 2> /dev/null)

	if [ -z "$mesh_basename" ]; then
		mesh_basename="m-11s-"
	else
		#remove non alphanumeric
		mesh_basename=$(echo "$mesh_basename" | sed 's/[^a-zA-Z0-9]//g')
		#get first 4 chars
		mesh_basename="m-${mesh_basename:0:4}-"
		#convert to lower case
		mesh_basename=$(printf $mesh_basename | tr '[A-Z]' '[a-z]')
	fi

	syslogmessage="option mesh_basename [ $mesh_basename ]"
	debugtype="info"
	mute="$mutestate"
	write_to_syslog

	#####
	mesh_gate_base_ssid=$(uci get mesh11sd.setup.mesh_gate_base_ssid 2> /dev/null)

	if [ "$ssid_suffix_enable" -eq 1 ]; then
		maxlen=22
	else
		maxlen=30
	fi

	mesh_gate_base_ssid=$(echo -n "$mesh_gate_base_ssid" | tr -d " " | head -c "$maxlen")

	if [ -z "$mesh_gate_base_ssid" ]; then
		config_ssid=$(cat /etc/config/wireless | grep -q -w "'OpenWrt'"; echo $?)

		if [ "$config_ssid" -eq 0 ]; then
			mesh_gate_base_ssid="MeshGate"
			mute="$mutestate"
			log_info "option mesh_gate_base_ssid [ Not Set ] Using default"
		fi
	else
		syslogmessage="option mesh_gate_base_ssid [ $mesh_gate_base_ssid ] "
		mute="$mutestate"
		log_info "option mesh_gate_base_ssid [ $mesh_gate_base_ssid ] "
	fi

	#####
	mesh_gate_encryption=$(uci get mesh11sd.setup.mesh_gate_encryption 2> /dev/null)
	package_list="wpad-mbedtls wpad-wolfssl wpad-openssl"
	check_package_list
	installed_wpad=$?

	if [ "$installed_wpad" -gt 0 ]; then
		package_list="wpad-mesh-mbedtls wpad-mesh-wolfssl wpad-mesh-openssl wpad-mbedtls wpad-wolfssl wpad-openssl"
		check_package_list
		installed_mesh_wpad=$?

		if [ "$installed_mesh_wpad" -gt 0 ]; then
			mesh_gate_encryption=0
		fi
	fi

	if [ -z "$mesh_gate_encryption" ]; then
		mesh_gate_encryption=4
	fi

	if [ "$mesh_gate_encryption" -eq 4 ] && [ "$installed_wpad" -gt 0 ]; then
		mesh_gate_encryption=0
	fi

	if [ "$mesh_gate_encryption" -le 4 ]; then
		syslogmessage="option mesh_gate_encryption [ $mesh_gate_encryption ]"
		debugtype="info"
		mute="$mutestate"
		write_to_syslog
	else
		syslogmessage="option mesh_gate_encryption must be 0 (none/owe_transition), 1 (sae, aka wpa3), 2 (sae-mixed, aka wpa2/wpa3), 3 (psk2, aka wpa2) or 4 (owe)"
		debugtype="err"
		mute="$mutestate"
		write_to_syslog
	fi

	#####
	mesh_gate_key=$(uci get mesh11sd.setup.mesh_gate_key 2> /dev/null)

	if [ -z "$mesh_gate_key" ]; then
		mesh_gate_key=""
	fi

	if [ "$mesh_gate_encryption" -eq 4 ]; then
		mesh_gate_key="owe_null_key"
	fi

	keylen=$((${#mesh_gate_key}))

	if [ "$keylen" -le 7 ] && [ "$keylen" -gt 0 ]; then
		syslogmessage="mesh_gate_key [ $mesh_gate_key ] is too short, it must be 8 or more characters in length"
		debugtype="err"
		mute="$mutestate"
		write_to_syslog
		mesh_gate_key=""
	else
		syslogmessage="option mesh_gate_key [ $mesh_gate_key ]"
		debugtype="info"
		mute="$mutestate"
		write_to_syslog
	fi

	#####
	ssid_suffix_enable=$(uci get mesh11sd.setup.ssid_suffix_enable 2> /dev/null)

	if [ -z "$ssid_suffix_enable" ]; then
		ssid_suffix_enable="1"
	fi

	syslogmessage="option ssid_suffix_enable [ $ssid_suffix_enable ]"
	debugtype="info"
	mute="$mutestate"
	write_to_syslog

	####
	# vxlan configs

	#####

	package_list="ip-full vxlan"
	check_package_list

	if [ "$ip_full" -eq 0 ] && [ "$vxlan" -eq 0 ]; then # we have the required packages for vxlan and it is enabled

		#####
		vtun_enable=$(uci get mesh11sd.setup.vtun_enable 2> /dev/null)

		if [ -z "$vtun_enable" ] || [ "$vtun_enable" -gt 1 ]; then
			vtun_enable=1
		fi

		syslogmessage="option vtun_enable [ $vtun_enable ]"
		debugtype="info"
		mute="$mutestate"
		write_to_syslog

		#####
		tun_id=$(uci get mesh11sd.setup.tun_id 2> /dev/null)

		if [ -z "$tun_id" ]; then
			tun_id="69"
		fi

		# Change any leading zero to 42

		if [ "${tun_id:0:1}" -eq 0 ]; then
			tun_id="42${tun_id:1:9}"
		fi

		syslogmessage="option tun_id [ $tun_id ]"
		debugtype="info"
		mute="$mutestate"
		write_to_syslog


		#####
		vtun_ip=$(uci get mesh11sd.setup.vtun_ip 2> /dev/null)
		vtun_ip_net=$(uci get network.vtunlan.ipaddr 2> /dev/null)

		if [ -z "$vtun_ip_net" ] && [ -z "$vtun_ip" ]; then

			if [ -z "$vtun_ip" ]; then
				vtun_ip=".1"
			fi

			is_ipv4addr_valid "$vtun_ip"

			if [ -z "$checkip" ]; then
				generate_ipv4_base "$auto_mesh_key"
				vtun_ip="$newipaddr"
			fi

		elif [ -z "$vtun_ip" ]; then
			vtun_ip="$vtun_ip_net"
		fi

		syslogmessage="option vtun_ip [ $vtun_ip ]"
		debugtype="info"
		mute="$mutestate"
		write_to_syslog

		#####
		vtun_mask=$(uci get mesh11sd.setup.vtun_mask 2> /dev/null)

		if [ -z "$vtun_mask" ]; then
			vtun_mask="255.255.255.0"
		fi

		is_ipv4mask_valid "$vtun_mask"

		if [ "$?" -gt 0 ]; then
			vtun_mask="255.255.255.0"
		fi

		syslogmessage="option vtun_mask [ $vtun_mask ]"
		debugtype="info"
		mute="$mutestate"
		write_to_syslog

		#####
		vtun_gate_encryption=$(uci get mesh11sd.setup.vtun_gate_encryption 2> /dev/null)

		if [ -z "$vtun_gate_encryption" ]; then
			vtun_gate_encryption=4
		fi

		if [ "$vtun_gate_encryption" -eq 4 ] && [ "$installed_wpad" -gt 0 ]; then
			vtun_gate_encryption=0
		fi

		if [ "$vtun_gate_encryption" -le 4 ]; then
			syslogmessage="option vtun_gate_encryption [ $vtun_gate_encryption ]"
			debugtype="info"
			mute="$mutestate"
			write_to_syslog
		else
			syslogmessage="option vtun_gate_encryption must be 0 (none/owe_transition), 1 (sae, aka wpa3), 2 (sae-mixed, aka wpa2/wpa3), 3 (psk2, aka wpa2) or 4 (owe)"
			debugtype="err"
			mute="$mutestate"
			write_to_syslog
		fi

		#####
		vtun_gate_key=$(uci get mesh11sd.setup.vtun_gate_key 2> /dev/null)

		if [ -z "$vtun_gate_key" ]; then
			vtun_gate_key=""
		fi

		if [ "$vtun_gate_encryption" -eq 4 ]; then
			vtun_gate_key="owe_null_key"
		fi

		keylen=$((${#vtun_gate_key}))

		if [ "$keylen" -le 7 ] && [ "$keylen" -gt 0 ]; then
			syslogmessage="vtun_gate_key [ $vtun_gate_key ] is too short, it must be 8 or more characters in length"
			debugtype="err"
			mute="$mutestate"
			write_to_syslog
			vtun_gate_key=""
		else
			syslogmessage="option vtun_gate_key [ $vtun_gate_key ]"
			debugtype="info"
			mute="$mutestate"
			write_to_syslog
		fi

		#####
		vtun_base_ssid=$(uci get mesh11sd.setup.vtun_base_ssid 2> /dev/null)

		if [ -z "$vtun_base_ssid" ]; then
			vtun_base_ssid="VTunnel"
		fi

		if [ "$ssid_suffix_enable" -eq 1 ]; then
			maxlen=22
		else
			maxlen=30
		fi

		vtun_base_ssid=$(echo -n "$vtun_base_ssid" | tr -d " " | head -c "$maxlen")
		syslogmessage="option vtun_base_ssid [ $vtun_base_ssid ]"
		debugtype="info"
		mute="$mutestate"
		write_to_syslog

		#####
		vtun_path_cost=$(uci get mesh11sd.setup.vtun_path_cost 2> /dev/null)

		if [ -z "$vtun_path_cost" ]; then
			vtun_path_cost="10"
		fi

		syslogmessage="option vtun_path_cost [ $vtun_path_cost ]"
		debugtype="info"
		mute="$mutestate"
		write_to_syslog

	else
		vtun_enable=0
		syslogmessage="option vtun_enable is disabled because required dependencies [ ip-full vxlan ] are not met"
		debugtype="info"
		mute="$mutestate"
		write_to_syslog
	fi

	#####
	mesh_gate_enable=$(uci get mesh11sd.setup.mesh_gate_enable 2> /dev/null)

	# Determines whether this node will be a gate
	#
	# Default: 1 (enable all mesh gate access points)
	#
	# Possible values:
	# 0 Disable all mesh gate access points.
	# 1 Enable all mesh gate access points.
	# 2 Enable ONLY access points on radios NOT shared with a mesh interface.
	#

	if [ -z "$mesh_gate_enable" ]; then
		mesh_gate_enable="1"
	fi

	if [ "$mesh_gate_enable" -eq 1 ] || [ "$mesh_gate_enable" -eq 2 ]; then
		ifdisable=0
	else
		ifdisable=1
	fi

	syslogmessage="option mesh_gate_enable [ $mesh_gate_enable ]"
	debugtype="info"
	mute="$mutestate"
	write_to_syslog

	#####
	# mesh_leechmode_enable
	# mesh_gate_only deprecated as a config option - use mesh_leechmode_enable instead as the config option
	#
	# Valid only with mesh_node_mobility_level = 0 and not on a portal node.
	mesh_gate_only=$(uci get mesh11sd.setup.mesh_leechmode_enable 2> /dev/null)

	if [ -z "$mesh_gate_only" ]; then
		mesh_gate_only=$(uci get mesh11sd.setup.mesh_gate_only 2> /dev/null)
	fi

	if [ -z "$mesh_gate_only" ]; then
		mesh_gate_only="0"
	fi

	if [ "$mesh_gate_only" -eq 1 ] && [ "$mesh_gate_enable" -eq 0 ]; then
		mesh_gate_only="0"
	fi

	syslogmessage="option mesh_leechmode_enable [ $mesh_gate_only ]"
	debugtype="info"
	mute="$mutestate"
	write_to_syslog

	#####

	txpower=$(uci get mesh11sd.setup.txpower 2> /dev/null)

	if [ ! -z "$txpower" ]; then
		syslogmessage="option txpower [ $txpower ]"
		debugtype="info"
		mute="$mutestate"
		write_to_syslog
	fi

	#####
	watchdog_nonvolatile_log=$(uci get mesh11sd.setup.watchdog_nonvolatile_log 2> /dev/null)

	if [ -z "$watchdog_nonvolatile_log" ]; then
		watchdog_nonvolatile_log="0"
	fi

	syslogmessage="option watchdog_nonvolatile_log [ $watchdog_nonvolatile_log ]"
	debugtype="info"
	mute="$mutestate"
	write_to_syslog

	if [ "$watchdog_nonvolatile_log" -gt 0 ]; then
		syslogmessage="******WARNING******: Irrepairable flash wear will occur and storage space will be used up. Please disable watchdog nonvolatile logging as soon as possible."
		debugtype="warn"
		mute="$mutestate"
		write_to_syslog
		syslogmessage="******WARNING******: Irrepairable flash wear will occur and storage space will be used up by watchdog debug log at /mesh11sd_log/watchdog.log"
		debugtype="warn"
		mute="$mutestate"
		write_to_syslog
		mkdir -p /mesh11sd_log
	fi

	#####
	mesh_path_stabilisation=$(uci get mesh11sd.setup.mesh_path_stabilisation 2> /dev/null)

	if [ -z "$mesh_path_stabilisation" ]; then
		mesh_path_stabilisation="0"
	fi

	syslogmessage="option mesh_path_stabilisation [ $mesh_path_stabilisation ]"
	debugtype="info"
	mute="$mutestate"
	write_to_syslog

	#####
	reactive_path_stabilisation_threshold=$(uci get mesh11sd.setup.reactive_path_stabilisation_threshold 2> /dev/null)

	if [ -z "$reactive_path_stabilisation_threshold" ] || [ "$reactive_path_stabilisation_threshold" -eq 0 ]; then
		reactive_path_stabilisation_threshold="5"
	fi

	syslogmessage="option reactive_path_stabilisation_threshold [ $reactive_path_stabilisation_threshold ]"
	debugtype="info"
	mute="$mutestate"
	write_to_syslog

	#####

	mesh_mac_forced_forwarding=$(uci get mesh11sd.setup.mesh_mac_forced_forwarding 2> /dev/null)

	if [ -z "$mesh_mac_forced_forwarding" ]; then
		mesh_mac_forced_forwarding="1"
	fi

	if [ "$mesh_mac_forced_forwarding" -ne 0 ]; then
		mesh_mac_forced_forwarding="1"
	fi

	syslogmessage="option mesh_mac_forced_forwarding [ $mesh_mac_forced_forwarding ]"
	debugtype="info"
	mute="$mutestate"
	write_to_syslog

	#####
	gateway_proxy_arp=$(uci get mesh11sd.setup.gateway_proxy_arp 2> /dev/null)

	if [ -z "$gateway_proxy_arp" ]; then
		gateway_proxy_arp="1"
	fi

	if [ "$gateway_proxy_arp" -ne 0 ]; then
		gateway_proxy_arp="1"
	fi

	syslogmessage="option gateway_proxy_arp [ $gateway_proxy_arp ]"
	debugtype="info"
	mute="$mutestate"
	write_to_syslog

	#####
	reboot_on_error=$(uci get mesh11sd.setup.reboot_on_error 2> /dev/null)

	if [ -z "$reboot_on_error" ]; then
		reboot_on_error="1"
	fi

	if [ "$reboot_on_error" -ne 0 ]; then
		reboot_on_error="1"
	fi

	syslogmessage="option reboot_on_error [ $reboot_on_error ]"
	debugtype="info"
	mute="$mutestate"
	write_to_syslog

	#####
	stop_on_error=$(uci get mesh11sd.setup.stop_on_error 2> /dev/null)

	if [ -z "$stop_on_error" ]; then
		stop_on_error="0"
	fi

	if [ "$stop_on_error" -ne 0 ]; then
		stop_on_error="1"
	fi

	syslogmessage="option stop_on_error [ $stop_on_error ]"
	debugtype="info"
	mute="$mutestate"
	write_to_syslog

	#####
	apmond_enable=$(uci get mesh11sd.setup.apmond_enable 2> /dev/null)

	if [ -z "$apmond_enable" ]; then
		apmond_enable="1"
	fi

	syslogmessage="option apmond_enable [ $apmond_enable ]"
	debugtype="info"
	mute="$mutestate"
	write_to_syslog

	#####
	apmond_cgi_dir=$(uci get mesh11sd.setup.apmond_cgi_dir 2> /dev/null)

	if [ -z "$apmond_cgi_dir" ]; then
		apmond_cgi_dir="/www/cgi-bin"
	fi

	strlen=$((${#apmon_cgi_dir}));
	lastchar=${apmond_cgi_dir:$strlen -1 :1}

	if [ "$lastchar" != "/" ]; then
		apmond_cgi_dir="$apmond_cgi_dir/"
	fi

	if [ ! -e "$apmond_cgi_dir" ]; then
		mkdir -p "$apmond_cgi_dir"
	fi

	syslogmessage="option apmond_cgi_dir [ $apmond_cgi_dir ]"
	debugtype="info"
	mute="$mutestate"
	write_to_syslog

	#####
	mesh_backhaul_led=$(uci get mesh11sd.setup.mesh_backhaul_led 2> /dev/null)

	# Enables the mesh backhaul heartbeat led indicator
	#	The led indicator will be on when the mesh interface is up, changing to the Linux heartbeat signal when peer nodes are connected
	# Default: auto
	#	By default, the power or system led will be used if present.
	#
	#	Other leds can be found listed in /sys/class/leds with the format "color:function"
	# Disable this option by setting its value to "none"
	#
	# Example, enable the "blue:run" led:
	#option mesh_backhaul_led 'blue:run'
	#
	# Example, disable the mesh backhaul led:
	#option mesh_backhaul_led 'none'


	if [ -z "$mesh_backhaul_led" ]; then
		mesh_backhaul_led="auto"
	fi

	syslogmessage="option mesh_backhaul_led [ $mesh_backhaul_led ]"
	debugtype="info"
	mute="$mutestate"
	write_to_syslog

	#####
	manage_opennds_startup=$(uci get mesh11sd.setup.manage_opennds_startup 2> /dev/null)

	if [ -z "$manage_opennds_startup" ]; then
		manage_opennds_startup="1"
	fi

	syslogmessage="option manage_opennds_startup [ $manage_opennds_startup ]"
	debugtype="info"
	mute="$mutestate"
	write_to_syslog

	#####
	use_default_beacon_interval=$(uci get mesh11sd.setup.use_default_beacon_interval 2> /dev/null)

	if [ -z "$use_default_beacon_interval" ] || [ "$use_default_beacon_interval" -lt 1 ]; then
		default_beacon_int_state="disabled"
		use_default_beacon_interval=0
	else
		default_beacon_int_state="enabled"
		use_default_beacon_interval=1
	fi
	syslogmessage="option use_default_beacon_interval is [ $default_beacon_int_state ]"
	debugtype="info"
	mute="$mutestate"
	write_to_syslog

	#####
	mesh_dtim_period=$(uci get mesh11sd.setup.mesh_dtim_period 2> /dev/null)

	if [ -z "$mesh_dtim_period" ]; then
		mesh_dtim_period="2"
	fi

	syslogmessage="option mesh_dtim_period [ $mesh_dtim_period ]"
	debugtype="info"
	mute="$mutestate"
	write_to_syslog

	#####
	mesh_node_mobility_level=$(uci get mesh11sd.setup.mesh_node_mobility_level 2> /dev/null)

	if [ -z "$mesh_node_mobility_level" ]; then
		mesh_node_mobility_level="1"
	fi

	syslogmessage="option mesh_node_mobility_level [ $mesh_node_mobility_level ]"
	debugtype="info"
	mute="$mutestate"
	write_to_syslog

	#####
	# CPE Mode
	#
	# Possible modes are prefix_delegation, relay and nat66
	#
	# Prefix Delegation works with all client devices, including Android,
	#	but the ISP needs to provide a prefix large enough to delegate a /64 subnet to every cpe mesh node.
	#	Exhausting available delegations is a danger.
	#
	# Relay does not require any prefix delegation, but some versions of Android devices will detect the relay and turn off the device's interface within ~60 seconds - because - Google.
	#
	# NAT66 will work with all types of client devices, including Android.
	# Nat66 is the default.
	#
	cpe_mode=$(uci get mesh11sd.setup.cpe_mode 2> /dev/null)

	if [ -z "$cpe_mode" ]; then
		cpe_mode="nat66"
	fi

	syslogmessage="option cpe_mode [ $cpe_mode ]"
	debugtype="info"
	mute="$mutestate"
	write_to_syslog

	#####
	apmon_verbose_debug_enable=$(uci get mesh11sd.setup.apmon_verbose_debug_enable 2> /dev/null)

	if [ -z "$apmon_verbose_debug_enable" ]; then
		apmon_verbose_debug_enable="0"
	else
		apmon_verbose_debug_enable="1"
	fi

	syslogmessage="option apmon_verbose_debug_enable [ $apmon_verbose_debug_enable ]"
	debugtype="info"
	mute="$mutestate"
	write_to_syslog

	#####
	odhcpd_log_level=$(uci get mesh11sd.setup.odhcpd_log_level 2> /dev/null)
	# Can be set from 0 to 7
	# 0 - Emergency
	# 1 - Alert
	# 2 - Critical
	# 3 - Error
	# 4 - Warning
	# 5 - Notice
	# 6 - Info
	# 7 - Debug
	#

	if [ -z "$odhcpd_log_level" ]; then
		odhcpd_log_level="3"
	fi

	syslogmessage="option odhcpd_log_level [ $odhcpd_log_level ]"
	debugtype="info"
	mute="$mutestate"
	write_to_syslog
}

check_config_params() {
	################
	# Set default parameters if not in config

	#####
	mesh_fwding=$(uci get mesh11sd.mesh_params.mesh_fwding 2> /dev/null)

	if [ -z "$mesh_fwding" ]; then
		mesh_fwding="1"
	fi

	if [ "$mesh_fwding" -eq 1 ] && [ "$mesh_gate_only" -eq 1 ]; then
		mesh_fwding="0"
	fi

	echo "set mesh11sd.mesh_params.mesh_fwding='$mesh_fwding'" | uci batch

	syslogmessage="option mesh_fwding [ $mesh_fwding ]"
	debugtype="info"
	write_to_syslog

	#####
	mesh_retry_timeout=$(uci get mesh11sd.mesh_params.mesh_retry_timeout 2> /dev/null)

	if [ -z "$mesh_retry_timeout" ]; then
		mesh_retry_timeout="255"
	fi

	echo "set mesh11sd.mesh_params.mesh_retry_timeout='$mesh_retry_timeout'" | uci batch

	syslogmessage="option mesh_retry_timeout [ $mesh_retry_timeout ]"
	debugtype="info"
	write_to_syslog

	#####
	mesh_confirm_timeout=$(uci get mesh11sd.mesh_params.mesh_confirm_timeout 2> /dev/null)

	if [ -z "$mesh_confirm_timeout" ]; then
		mesh_confirm_timeout="255"
	fi

	echo "set mesh11sd.mesh_params.mesh_confirm_timeout='$mesh_confirm_timeout'" | uci batch

	syslogmessage="option mesh_confirm_timeout [ $mesh_confirm_timeout ]"
	debugtype="info"
	write_to_syslog

	#####
	mesh_holding_timeout=$(uci get mesh11sd.mesh_params.mesh_holding_timeout 2> /dev/null)

	if [ -z "$mesh_holding_timeout" ]; then
		mesh_holding_timeout="255"
	fi

	echo "set mesh11sd.mesh_params.mesh_holding_timeout='$mesh_holding_timeout'" | uci batch

	syslogmessage="option mesh_holding_timeout [ $mesh_holding_timeout ]"
	debugtype="info"
	write_to_syslog

	#####
	mesh_rssi_threshold=$(uci get mesh11sd.mesh_params.mesh_rssi_threshold 2> /dev/null)

	if [ -z "$mesh_rssi_threshold" ] || [ "$mesh_rssi_threshold" -gt 0 ]; then
		mesh_rssi_threshold="-63"
		echo "set mesh11sd.mesh_params.mesh_rssi_threshold='$mesh_rssi_threshold'" | uci batch
	fi

	syslogmessage="option mesh_rssi_threshold [ $mesh_rssi_threshold ]"
	debugtype="info"
	write_to_syslog

	#####
	mesh_gate_announcements=$(uci get mesh11sd.mesh_params.mesh_gate_announcements 2> /dev/null)

	if [ -z "$mesh_gate_announcements" ]; then
		mesh_gate_announcements="1"
	fi

	if [ "$mesh_gate_announcements" -eq 1 ] && [ "$mesh_gate_enable" -eq 0 ]; then
		mesh_gate_announcements="0"
	fi

	if [ "$mesh_gate_only" -gt 0 ]; then
		mesh_gate_announcements="0"
	fi

	echo "set mesh11sd.mesh_params.mesh_gate_announcements='$mesh_gate_announcements'" | uci batch

	syslogmessage="option mesh_gate_announcements [ $mesh_gate_announcements ]"
	debugtype="info"
	write_to_syslog

	#####
	mesh_hwmp_rootmode=$(uci get mesh11sd.mesh_params.mesh_hwmp_rootmode 2> /dev/null)

	if [ -z "$mesh_hwmp_rootmode" ]; then
		mesh_hwmp_rootmode="0"
		echo "set mesh11sd.mesh_params.mesh_hwmp_rootmode='$mesh_hwmp_rootmode'" | uci batch
	fi

	syslogmessage="option mesh_hwmp_rootmode [ $mesh_hwmp_rootmode ]"
	debugtype="info"
	write_to_syslog

	#####

	mesh_hwmp_root_interval=$(uci get mesh11sd.mesh_params.mesh_hwmp_root_interval 2> /dev/null)

	if [ -z "$mesh_hwmp_root_interval" ]; then
		mesh_hwmp_root_interval="5000"
		echo "set mesh11sd.mesh_params.mesh_hwmp_root_interval='$mesh_hwmp_root_interval'" | uci batch
	fi

	syslogmessage="option mesh_hwmp_root_interval [ $mesh_hwmp_root_interval ]"
	debugtype="info"
	write_to_syslog

	#####

	mesh_hwmp_active_path_to_root_timeout=$(uci get mesh11sd.mesh_params.mesh_hwmp_active_path_to_root_timeout 2> /dev/null)

	if [ -z "$mesh_hwmp_active_path_to_root_timeout" ]; then
		mesh_hwmp_active_path_to_root_timeout="6000"
		echo "set mesh11sd.mesh_params.mesh_hwmp_active_path_to_root_timeout='$mesh_hwmp_active_path_to_root_timeout'" | uci batch
	fi

	syslogmessage="option mesh_hwmp_active_path_to_root_timeout [ $mesh_hwmp_active_path_to_root_timeout ]"
	debugtype="info"
	write_to_syslog

	#####

	mesh_max_peer_links=$(uci get mesh11sd.mesh_params.mesh_max_peer_links 2> /dev/null)

	if [ -z "$mesh_max_peer_links" ]; then
		mesh_max_peer_links="16"
		echo "set mesh11sd.mesh_params.mesh_max_peer_links='$mesh_max_peer_links'" | uci batch
	fi

	config_max_peer_links="$mesh_max_peer_links"
	syslogmessage="option mesh_max_peer_links [ $mesh_max_peer_links ]"
	debugtype="info"
	write_to_syslog

	#####

	mesh_plink_timeout=$(uci get mesh11sd.mesh_params.mesh_plink_timeout 2> /dev/null)

	if [ -z "$mesh_plink_timeout" ]; then
		mesh_plink_timeout="500"
		echo "set mesh11sd.mesh_params.mesh_plink_timeout='$mesh_plink_timeout'" | uci batch
	fi

	syslogmessage="option mesh_plink_timeout [ $mesh_plink_timeout ]"
	debugtype="info"
	write_to_syslog
}

get_mesh_path_cost() {
	mesh_path_cost=$(uci get mesh11sd.setup.mesh_path_cost 2> /dev/null)

	if [ -z "$mesh_path_cost" ]; then
		mesh_path_cost="65525"
	fi

	syslogmessage="option mesh_path_cost [ $mesh_path_cost ]"
	debugtype="info"
	write_to_syslog
	mute="$mutestate"
}

wait_for_interface() {
	local ifname="$1"
	local timeout=$interface_timeout

	for i in $(seq $timeout); do
		if [ $(ip link show $ifname 2> /dev/null | grep -c -w "state UP") -eq 1 ] || [ $(ip link show $ifname 2> /dev/null | grep -c -w "state UNKNOWN") -eq 1 ]; then
			ifstatus="up"
			break
		fi

		sleep 1

		if [ $i == $timeout ] ; then
			syslogmessage="$ifname is not up - giving up for now."
			debugtype="debug"
			write_to_syslog
			ifstatus="down"
		fi
	done
}

# Write debug message to mesh11sd's log
# $syslogmessage contains the string to log
# $debugtype contains the debug level string: debug, info, warn, notice, err, emerg.
write_log() {
	local logdir="$log_mountpoint/mesh11sd"
	local logfile="$logdir/mesh11sd_log.log"
	local awkcmd="awk ""'\$6==""\"$log_mountpoint\"""{print \$4}'"

	if [ ! -d "$logdir" ]; then
		mkdir -p "$logdir"
	fi

	if [ ! -z "$mountcheck" ]; then
		# Truncate the log file if max_log_entries is set

		log_entries=$(wc -l "$logfile" 2>/dev/null | awk '{printf "%d", $1}')

		if [ -z "$log_entries" ]; then
			log_entries="0"
		fi

		if [ "$log_entries" -gt "$max_log_entries" ]; then
			mv "$logfile" "$logfile.cut" 2>/dev/null
			tail -n "$max_log_entries" "$logfile.cut" 2>/dev/null >> "$logfile"
			rm "$logfile.cut" 2>/dev/null
		fi

		available=$(df | grep -w  "$log_mountpoint" | eval "$awkcmd")

		if [ "$log_mountpoint" = "$mountpoint" ]; then
			# Check the logfile is not too big
			min_freespace_to_log_ratio=10
			filesize=$(ls -s -1 $logfile | awk -F' ' '{print $1}')

			if [ $filesize -eq 0 ]; then
				filesize=1
			fi
			sizeratio=$(($available/$filesize))

			if [ $sizeratio -ge $min_freespace_to_log_ratio ]; then
				echo "$datetime""$loginfo" >> $logfile
			else
				echo "Log file too big, please archive contents and reduce max_log_entries" | logger -p "daemon.err" -s -t "mesh11sd[$mesh11sdpid]: "
			fi
		else
			if [ "$available" > 10 ];then
				datetime=$(date)
				datetime="$datetime "

				if [ ! -f "$logfile" ]; then
					echo "$datetime""New log file created" > $logfile
				fi

				echo "$datetime""daemon.$debugtype mesh11sd[$mesh11sdpid]: $logmessage" >> $logfile
			else
				echo "Log file too big, please archive contents and reduce max_log_entries" | logger -p "daemon.err" -s -t "mesh11sd[$mesh11sdpid]: "
			fi
		fi
	else
		echo "Log location is NOT a mountpoint - logs would fill storage space - logging disabled" | logger -p "daemon.err" -s -t "mesh11sd[$mesh11sdpid]: "
	fi
}

# Write debug message to syslog
# $syslogmessage contains the string to log
# $debugtype contains the debug level string: debug, info, warn, notice, err, emerg.
write_to_syslog() {

	if [ -z "$syslogmessage" ] || [ "$syslogmessage" = "$last_syslogmessage" ]; then
		return 0
	fi

	if [ -z "$mute" ]; then
		mute=$mutestate
	fi

	if [ "$mute" -eq 0 ]; then
		entrynum=$((entrynum + 1))

		if [ "$entrynum" -lt 0 ]; then
			entrynum=1
		fi

		syslogmessage="[$entrynum] $syslogmessage"

		if [ -z "$debuglevel" ]; then
			debuglevel=1
		fi

		if [ ! -z "$syslogmessage" ]; then

			case $debugtype in
				"emerg") debugnum=0; log_to_stderr="-s";;
				"err") debugnum=0; log_to_stderr="-s";;
				"notice") debugnum=1; log_to_stderr="";;
				"warn") debugnum=1; log_to_stderr="-s";;
				"info") debugnum=2; log_to_stderr="";;
				"debug") debugnum=3; log_to_stderr="";;
				*) debugnum=1; debugtype="notice"; log_to_stderr="";;
			esac

			if [ "$debuglevel" -ge "$debugnum" ]; then

				if [ -z "$log_mountpoint" ]; then
					logger -p "daemon.$debugtype" "$log_to_stderr" -t "mesh11sd[$mesh11sdpid]" "$syslogmessage"
					return 0
				fi

				if [ "$debugtype" = "emerg" ] || [ "$debugtype" = "err" ] || [ "$debugtype" = "notice" ]; then
					logger -p "daemon.$debugtype" "$log_to_stderr" -t "mesh11sd[$mesh11sdpid]" "$syslogmessage"
				fi

				logmessage="$syslogmessage"
				write_log
				last_syslogmessage="$syslogmessage"
			fi
		fi
	fi

	mute=$mutestate
	return 0
}

write_to_watchdog_nonvolatile_log() {

	if [ "$watchdog_nonvolatile_log" -gt 0 ]; then
		mkdir -p /mesh11sd_log
		echo "$(date) $syslogmessage" >> /mesh11sd_log/mesh11sd.log
	fi
}


mesh_gate_state() {

	if [ -f "$tmpdir/active_mesh_ifname" ]; then
		. "$tmpdir/active_mesh_ifname"
	fi

	aplist=$(uci show wireless | grep "mode='ap'" | awk -F "." '{printf "%s ", $2}')
	vxlist=$(uci show wireless | grep "vtunlan" | awk -F "." '{printf "%s ", $2}')
	meshdev=$(uci show wireless | grep "$active_mesh_ifname" | awk -F "." '{printf "%s.%s", $1, $2 }')
	mesh_device=$(echo "get $meshdev.device" | uci batch 2>/dev/null); echo "$mesh_device"

	for gate in $aplist; do
		gate_device=$(echo "get wireless.$gate.device" | uci batch 2>/dev/null)

		if [ "$mesh_gate_enable" -eq 1 ]; then
			ucibatch="set wireless.$gate.disabled='0'"
			echo "$ucibatch" | uci batch

		elif [ "$mesh_gate_enable" -eq 0 ]; then
			ucibatch="set wireless.$gate.disabled='1'"
			echo "$ucibatch" | uci batch

		elif [ "$mesh_gate_enable" -eq 2 ] && [ "$gate_device" = "$mesh_device" ]; then
			ucibatch="set wireless.$gate.disabled='1'"
			echo "$ucibatch" | uci batch

		elif [ "$mesh_gate_enable" -eq 2 ] && [ "$gate_device" != "$mesh_device" ]; then
			ucibatch="set wireless.$gate.disabled='0'"
			echo "$ucibatch" | uci batch
		fi
	done

	for gate in $vxlist; do
		is_vxradio=$(uci show wireless | grep "$gate" | grep -c "vtunlan")
		gate_device=$(echo "get wireless.$gate.device" | uci batch 2>/dev/null)

		if [ "$is_vxradio" -eq 1 ] && [ "$mesh_gate_enable" -eq 2 ] && [ "$gate_device" = "$mesh_device" ]; then
			ucibatch="set wireless.$gate.disabled='1'"
			echo "$ucibatch" | uci batch
		fi

	done
}

set_txpower() {

	if [ ! -z "$txpower" ]; then
		radiolist=$(uci show wireless | grep "wifi-device" | awk -F "=" '{printf "%s ", $1}')

		for radio in $radiolist; do
			config_state=$(echo "get $radio.txpower" | uci batch 2> /dev/null)

			if [ -z "$config_state" ]; then
				ucibatch="set $radio.txpower='$txpower'"
				echo "$ucibatch" | uci batch
			fi
		done
	fi
}


check_gate() {
	is_gate=$(iw dev | grep -w "type AP")

	if [ -z "$is_gate" ]; then
		uci set mesh11sd.mesh_params.mesh_connected_to_gate='0'
	else
		uci set mesh11sd.mesh_params.mesh_connected_to_gate='1'
	fi
}

check_channel() {

	if [ "$portal_detect" -eq 4 ]; then
		return 0
	fi

	# Can we see the portal?
	link_to_portal=0

	# We need to scan for working channel if we cannot see a portal
	# Check for portal every channel_tracking_checkinterval seconds

	timenow=$(date +%s)

	debugtype="debug"
	syslogmessage="checking for working channel...."
	write_to_syslog

	if [ -z $cctimestamp ]; then
		cctimestamp=$(date +%s)
	fi

	if [ "$check_channel_now" -eq 0 ]; then
		elapsed_time=$(($timenow - $cctimestamp))
		syslogmessage="Time since last channel tracking check [ $elapsed_time ], minimum checkinterval [ $mininterval ]"
	else
		elapsed_time=$(($timenow + $cctimestamp))
		syslogmessage="Checking working channel - immediate"
	fi

	mininterval=$channel_tracking_checkinterval

	debugtype="debug"
	write_to_syslog


	if [ "$mininterval" -le "$elapsed_time" ]; then
		ifaces=$(uci export wireless | grep "config wifi-iface" | awk -F"'" '$2 != "" {printf "%s ",$2}')

		for iface in $ifaces; do
			ucibatch="get wireless.$iface.mode"
			mode=$(echo "$ucibatch" | uci batch)

			if [ "$mode" = "mesh" ]; then
				network=$(echo "get wireless.$iface.network" | uci batch)
				device=$(echo "get network.$network.device" | uci batch)
				get_portal_ula

				manage_fallback_ula_route

				if [ "$link_to_portal" -eq 0 ]; then
					log_debug "Check Channel - portal detected"
					cctimestamp=""
					return 0
				else
					log_debug "Check Channel - portal not detected - Scanning..."
					scan_channel
					cctimestamp=""
					return 0
				fi

				continue
			fi
		done

		cctimestamp=""
	fi
}

manage_fallback_ula_route() {
	# ------------------------------------------------------------
	# Always assign deterministic ULA address to bridge on ALL peer nodes
	# + add portal-specific ::1 on MBP portal
	# + add sticky on-link route on peers in auto-detect modes
	# ------------------------------------------------------------

	if [ "$portal_detect" -ne 4 ] && [ "$detected_state" = "portal" ]; then
		log_debug "manage_fallback_ula_route - Node is a routed portal"
		return 0
	fi

	if [ -f "$tmpdir/devicemac" ]; then
		. "$tmpdir/devicemac"
	else
		log_warning "No $tmpdir/devicemac found → cannot determine bridge for ULA/route"
		device=""
		return 1
	fi

	# Compute shared ULA prefix deterministically
	set_ula_prefix "get"
	if [ -z "$ula_prefix" ]; then
		log_warning "Could not determine ULA prefix for address/route"
		return 1
	fi

	# Strip /48 to get clean 4-segment base (e.g. fd46:cfe5:34c5:ddd6)
	ula_base="${ula_prefix%%::*}"   # everything before the last ::
	log_debug "ula_base [ $ula_base ]"

	# --------------------------------------------------------------------
	# Part 1: Deterministic ULA address on bridge (ALL peer nodes)
	# --------------------------------------------------------------------
	if [ -n "$device" ] && [ -n "$devicemac" ] && [ "$portal_detect" -ne 4 ]; then

		# Generate EUI-64 from bridge MAC
		mac_no_colons="${devicemac//:/}"
		first_byte_hex=${mac_no_colons:0:2}
		first_byte_dec=$((0x$first_byte_hex ^ 0x02))
		first_byte=$(printf "%02x" "$first_byte_dec")
		eui64="${first_byte}${mac_no_colons:2:2}${mac_no_colons:4:2}fffe${mac_no_colons:6:2}${mac_no_colons:8:2}${mac_no_colons:10:2}"

		# Format EUI-64 with colons
		eui64_formatted="${eui64:0:4}:${eui64:4:4}:${eui64:8:4}:${eui64:12:4}"

		# Full address: 3-segment prefix : 1-segment subnet : EUI-64 /64
		ula_addr="$ula_base:$ula_subnet:$eui64_formatted/64"

		# Check if already present
		if ! ip -6 addr show dev "$device" | grep -q "$ula_addr"; then
			log_info "Adding deterministic ULA address $ula_addr to bridge $device"

			ip -6 addr add "$ula_addr" dev "$device" 2>/dev/null

			if [ $? -eq 0 ]; then
				log_notice "ULA address $ula_addr added to $device"
			else
				log_error "Failed to add ULA address $ula_addr to $device"
				log_debug "ip error: $(ip -6 addr add "$ula_addr" dev "$device" 2>&1)"
			fi
		fi
	fi

	# -------------------------------------------------------------------------------
	# Part 2: Add portal-specific ::1 ULA to bridge (only on MBP, Mesh Bridge Portal)
	# -------------------------------------------------------------------------------
	if [ "$detected_state" = "portal" ] && [ "$portal_detect" -eq 4 ] && [ -n "$device" ]; then

		# Portal ULA = prefix:subnet::1 /64
		portal_ula_addr="$ula_base:$ula_subnet::1/64"

		# Check if already present
		if ! ip -6 addr show dev "$device" | grep -q "$portal_ula_addr"; then
			log_info "MBP portal mode → adding portal ULA $portal_ula_addr to bridge $device"

			ip -6 addr add "$portal_ula_addr" dev "$device" 2>/dev/null

			if [ $? -eq 0 ]; then
				log_notice "Portal ULA $portal_ula_addr added to $device"
			else
				log_error "Failed to add portal ULA $portal_ula_addr to $device"
				log_debug "ip error: $(ip -6 addr add "$portal_ula_addr" dev "$device" 2>&1)"
			fi
		fi
	fi

	# --------------------------------------------------------------------
	# Part 3: Sticky on-link route (only on peers in auto-detect modes)
	# --------------------------------------------------------------------
	if [ "$detected_state" = "peer" ] && \
	   [ "$portal_detect" -ne 0 ] && [ "$portal_detect" -ne 4 ] && \
	   [ -n "$device" ]; then

		if ! ip -6 route show "$ula_base:$ula_subnet::/64" 2>/dev/null | grep -q "dev[[:space:]]\+$device.*proto static.*metric 512.*onlink"; then
			log_info "Adding sticky fallback on-link route $ula_base:$ula_subnet::/64 dev $device (peer auto-detect mode)"

			ip_error=$(ip -6 route add "$ula_base:$ula_subnet::/64" dev "$device" onlink metric 512 proto static 2>&1)

			if [ $? -eq 0 ]; then
				log_notice "Sticky fallback on-link route added on $device"
			else
				log_error "Failed to add fallback route on $device"
				log_debug "ip error: $ip_error"
			fi
		fi
	fi

	# --------------------------------------------------------------------
	# Cleanup: only for route when no longer a peer or bridge state lost
	# --------------------------------------------------------------------
	if [ "$detected_state" != "peer" ] || [ -z "$device" ] || [ ! -f "$tmpdir/devicemac" ]; then
		if [ -n "$ula_prefix" ] && [ -n "$device" ] && \
		   ip -6 route show "$ula_base:$ula_subnet::/64" 2>/dev/null | grep -q "dev[[:space:]]\+$device.*proto static"; then
			log_info "Removing fallback route $ula_base:$ula_subnet::/64 on $device (no longer required)"
			ip -6 route del "$ula_base:$ula_subnet::/64" dev "$device" 2>/dev/null
		fi
	fi

	return 0
}

scan_channel() {
	debugtype="debug"
	syslogmessage="Entering scan channel...."
	write_to_syslog

	if [ "$link_to_portal" -gt 0 ]; then
		# No link to portal. Maybe we are on the wrong channel, so scan for meshid to get channel
		meshconfigs=$(uci show wireless 2> /dev/null | grep "mode='mesh'" | awk -F ".mode='mesh'" '{printf "%s ", $1}')

		if [ ! -z "$meshconfigs" ]; then

			for meshconfig in $meshconfigs; do
				ifname=$(uci get $meshconfig.ifname)
				our_channel=$(iw dev $ifname info 2> /dev/null | grep -w "channel" | awk -F " " '{printf "%s", $2}')

				if [ -z "$our_channel" ]; then
					continue
				fi

				log_info "Current channel [ $our_channel ]"
				mesh_id=$(uci get $meshconfig.mesh_id)
				scanned_channels=$(iw dev $ifname scan 2> /dev/null | grep -F -w -B 20 "MESH ID: $mesh_id" | awk -F "primary channel: " '$2 != "" {printf "%s ",$2}')

				if [ ! -z "$our_channel" ] && [ "$our_channel" -le 14 ]; then
					band="2g"
				else
					band=""
				fi

				if [ ! -z "$our_channel" ] && [ ! -z "$scanned_channels" ]; then

					for channel in $scanned_channels; do

						if [ "$channel" -gt 14 ] && [ "$band" = "2g" ]; then
							continue
						fi

						if [ "$channel" -eq "$our_channel" ]; then
							continue
						else
							device=$(uci get $meshconfig.device)
							echo "set wireless.$device.channel='$channel'" | uci batch

							debugtype="notice"
							syslogmessage="Tracking [ $meshconfig, $device, $ifname ] from channel [ $our_channel ] to [ $channel ]"
							write_to_syslog
							restart_mesh
							cctimestamp=""
							return 0
						fi
					done
				fi

				ucibatch=""
			done

		fi
	fi
}

get_portal_state() {
	local muteorg=$mute
	log_debug "In get_portal_state. firstloop=$firstloop..."

	if [ "$firstloop" -eq 1 ]; then
		return 0
	fi

	if [ "$portal_detect" -eq 1 ] || [ "$portal_detect" -eq 3 ] || [ "$portal_detect" -eq 4 ] || [ "$portal_detect" -eq 5 ]; then
		default_gw=""

		if [ "$firstloop" -eq 0 ]; then
			#Check if portal - try to get the upstream default ipv4 gateway

			if [ "$portal_detect" -eq 4 ]; then
				up_zone=$(echo "get network.lan.device" | uci batch | awk '{printf "%s", $1}')
			else
				up_zone=$(echo "get network.wan.device" | uci batch | awk '{printf "%s", $1}')
			fi

			mute=1
			wait_for_interface "$up_zone"
			mute=0

			if [ "$ifstatus" = "up" ]; then
				default_gw=$(ip route | grep "default via" | grep -w "dev $up_zone")

				if [ -z "$default_gw" ]; then
					dhcpdevice="$up_zone"
					dhcp4_renew
					default_gw=$(ip route | grep "default via" | grep -w "dev $up_zone")
				fi
			fi

			if [ ! -z "$default_gw" ]; then
				# This is a portal
				is_portal="detected"
				debugtype="debug"
				syslogmessage="Node has an upstream ipv4 gateway"
				mute=$muteorg
				write_to_syslog
				detected_state="portal"
			else
				# Try to find a portal on the backhaul
				is_portal=""
				local retries=4

				if [ "$portal_detect" -eq 4 ]; then
					retries=1
				fi

				br_zone=$(echo "get network.$auto_mesh_network.device" | uci batch | awk '{printf "%s", $1}')

				for retry in $(seq $retries); do

					if [ -z "$default_gw" ]; then
						debugtype="debug"
						syslogmessage="Portal upstream link check  [$br_zone] - iteration [ $((retry -1)) ] ...."
						mute=$muteorg
						write_to_syslog
						wait_for_interface "$br_zone"

						if [ "$ifstatus" = "up" ]; then
							default_gw=$(ip route | grep "default via" | grep -w "dev $br_zone")

							if [ -z "$default_gw" ]; then
								get_portal_ula
								scan_channel

								dhcpdevice="$br_zone"
								dhcp4_renew
								default_gw=$(ip route | grep "default via" | grep -w "dev $br_zone")
							fi
						fi

						if [ ! -z "$default_gw" ]; then
							# Portal detected
							is_portal="backhaul"
							debugtype="debug"
							syslogmessage="Portal detected on backhaul"
							mute=$muteorg
							write_to_syslog
							detected_state="peer"
							break
						fi
					fi
				done
			fi
		fi

	elif [ "$portal_detect" -eq 3 ]; then
		# 3 - force CPE peer mode
		detected_state="peer"
		uci set dhcp.@dnsmasq[0].authoritative='0'

	elif [ "$portal_detect" -eq 0 ]; then
		# 0 - force portal mode
		is_portal="forced"
		detected_state="portal"
		default_gw=$(ip route | grep "default via")
	else
		# Default to peer
		detected_state="peer"
	fi

	# Log the default gateway, empty if we don't have one
	debugtype="debug"
	syslogmessage="default_gw=$default_gw...."
	mute=$muteorg
	write_to_syslog

	# If is_portal is empty, portal detect has failed

	if [ ! -z "$is_portal" ]; then
		gw_ip=$(echo "$default_gw" | awk -F" " 'NR==1 {printf "%s", $3}')
		gwstatus=$(ip neigh | grep -w "$gw_ip" | awk 'NR==1 {printf "%s", $NF}')

		if [ -z "$gwstatus" ]; then
			is_portal=""
		elif [ "$gwstatus" = "REACHABLE" ] || [ "$gwstatus" = "DELAY" ] || [ "$gwstatus" = "STALE" ]; then
			is_portal=$gwstatus
		elif [ "$gwstatus" = "PROBE" ] || [ "$gwstatus" = "INCOMPLETE" ] || [ "$gwstatus" = "FAILED" ]; then
			is_portal=""
		fi
	fi

	if [ "$portal_detect" -eq 5 ]; then
		detected_state="peer"
	elif [ "$portal_detect" -eq 4 ]; then
		detected_state="portal"
	fi

	if [ "$portal_detect" -eq 3 ]; then
		# CPE mode required
		detected_state="peer"
		debugtype="debug"
		syslogmessage="CPE mode..."
		mute=$muteorg
		write_to_syslog
	fi

	echo "is_portal=\"$is_portal\"; detected_state=\"$detected_state\"" > $tmpdir/is_portal
}

get_portal_type() {
	. $tmpdir/is_portal
	. $tmpdir/devicemac

	if [ "$detected_state" = "portal" ]; then

		if [ "$portal_detect" -eq 4 ]; then
			portal_type="MBP"
		else
			portal_type="MRP"
		fi

		log_info "Portal [ $portal_type@$devicemac ]"
		return 0
	fi

	get_portal_ula

	if [ "$link_to_portal" -gt 0 ]; then
		log_info "portal not accessible"
		portal_type="UNKNOWN"
		log_warning "Portal [ $portal_type ]"
		return 1
	fi

	portal_mac=$(ip -6 neigh | grep "$portal_ula" | awk '{print $5}')
	portal_ipv4=$(ip -4 neigh | grep "$device" | grep "$portal_mac" | awk '{printf "%s", $1}')
	mrp_default_ipv4_gw=$(ip -4 route | grep -m 1 "default via $portal_ipv4 dev $device")

	if [ -z "$mrp_default_ipv4_gw" ]; then
		portal_type="MBP"
	else
		portal_type="MRP"
	fi

	log_info "Portal [ $portal_type@$portal_mac ]"
	echo "portal_type=\"$portal_type\"; portal_mac=\"$portal_mac\"; portal_ipv4=\"$portal_ipv4\"; mrp_default_ipv4_gw=\"$mrp_default_ipv4_gw\"" > "$tmpdir/portal_type"
	return 0
}

portal_watchdog() {

	if [ "$portal_detect_threshold" -eq 0 ]; then
		log_info "Portal Watchdog is disabled"
		return 0
	fi

	# Set auto_detect state
	case $portal_detect in
		"0") auto_detect="0";;
		"1") auto_detect="1";;
		"3") auto_detect="1";;
		"4") auto_detect="0";;
		"5") auto_detect="0";;
		*) auto_detect="0";;
	esac

	if [ "$auto_detect" -eq 0 ]; then
		log_debug "Portal auto_detect [ $auto_detect ]"
		return 0
	fi

	if [ -f "$tmpdir/is_portal" ]; then
		. $tmpdir/is_portal
	fi

	if [ "$detected_state" != "portal" ] && [ "$auto_detect" -eq 1 ]; then
		# do portal detect with watchdog:

		# Wait until we reach the threshold before we start checking for the portal
		if [ "$portal_detect_threshold" -gt "$failcount" ]; then
			log_debug "Portal Watchdog countdown [ $(($portal_detect_threshold - $failcount)) ] "
			failcount=$(($failcount + 1))
			return 0
		fi

		log_debug "Portal Watchdog - checking status ...."

		get_portal_ula

		if [ "$link_to_portal" -eq 0 ]; then
			log_debug "Portal Watchdog - portal detected ...."
			failcount=0
			network_restart=0
			is_portal="detected"
			detected_state="peer"
			return 0
		fi

		# Do a channel Scan for our mesh_id
		check_channel_now=1
		check_channel
		check_channel_now=0

		if [ "$link_to_portal" -eq 0 ]; then
			# link to portal found
			log_debug "Portal Watchdog - portal found ...."
			failcount=0
			network_restart=0
			is_portal="detected"
			detected_state="peer"
			return 0
		fi

		# If we got here, we cannot see the portal, so start kicking things...
		if [ "$link_to_portal" -ne 0 ] && [ "$network_restart" -eq 0 ]; then
			# Try a dhcp4 renew - this sends a layer 2 broadcast
			dhcpdevice=$(echo "get network.$auto_mesh_network.device" | uci batch | awk '{printf "%s", $1}')
			mute=$muteorg
			log_debug "Link to portal fail, attempting to (re)establish via device [ $dhcpdevice ], iteration [ $failcount ]"
			dhcp4_renew
			failcount=0
			network_restart=1
			return 0
		fi

		if [ "$link_to_portal" -ne 0 ] && [ "$network_restart" -eq 1 ]; then
			# Still no portal, try dhcp4 renew again and make a non-volatile log if enabled
			dhcpdevice=$(echo "get network.$auto_mesh_network.device" | uci batch | awk '{printf "%s", $1}')
			debugtype="err"
			syslogmessage="portal detect threshold reached, watchdog is monitoring"
			mute=$muteorg
			write_to_syslog
			write_to_watchdog_nonvolatile_log
			network_restart=2
			failcount=0
			dhcp4_renew
			return 0
		fi

		if [ "$link_to_portal" -ne 0 ] && [ "$network_restart" -eq 2 ]; then
			# Do a network restart this time, followed by a dhcp4 renew
			dhcpdevice=$(echo "get network.$auto_mesh_network.device" | uci batch | awk '{printf "%s", $1}')
			debugtype="notice"
			syslogmessage="portal detect threshold exceeded, watchdog is restarting the network..."
			mute=$muteorg
			write_to_syslog
			write_to_watchdog_nonvolatile_log
			network_restart=3
			/sbin/service network restart
			wait_for_mesh
			dhcp4_renew

		if [ "$link_to_portal" -ne 0 ] && [ "$network_restart" -eq 3 ]; then
			# restarting the network did not work so:
			debugtype="err"

			if [ "$stop_on_error" -gt 0 ]; then
				syslogmessage="portal detect threshold exceeded, restarting network did not work... disabling mesh management, changing to mesh status mode"
				enabled=0
				mute=$muteorg
				write_to_syslog
				write_to_watchdog_nonvolatile_log
			elif [ "$reboot_on_error" -gt 0 ]; then
				syslogmessage="portal detect threshold exceeded, restarting network did not work... rebooting node"
				mute=$muteorg
				write_to_syslog
				write_to_watchdog_nonvolatile_log
				reboot
			else
				#Reset the portal detect threshold. Keep trying to find a portal...
				failcount=0
				network_restart=0
			fi
		fi

	else
		failcount=0
		network_restart=0
	fi
fi

if [ "$detected_state" = "portal" ]; then
		ipv4statusdev=$(ip -4 addr show "$device" 2> /dev/null | grep -q "inet"; echo $?)

		if [ "$vtun_enable" -eq 1 ]; then
			ipv4statustun=$(ip -4 addr show "br-tun$tun_id" 2> /dev/null | grep -q "inet"; echo $?)
		else
			ipv4statustun=0
		fi

		if [ "$ipv4statusdev" -eq 1 ] || [ "$ipv4statustun" -eq 1 ]; then
			/sbin/service network restart
		fi
	fi
}

restore_ipv4() {
	. $tmpdir/devicemac
	config_ipaddr=$(uci get network.$auto_mesh_network.ipaddr)
	active_ipaddr=$(ip addr show dev $device | grep -w "$config_ipaddr" | awk '{printf "%s", $2}' | awk -F"/" '{printf "%s", $1}')

	if [ "$config_ipaddr" != "$active_ipaddr" ]; then
		config_netmask=$(uci get network.$auto_mesh_network.netmask)
		cdir_netmask=$(echo "$config_netmask" | awk -F"." '{for(i=1; i<=NF; ++i) {mask+=8-log(2**8-$i)/log(2)} {printf "/%.0f", mask}}')
		cdir_ip="$config_ipaddr""$cdir_netmask"

		eval $(echo "$config_ipaddr" | awk -F"." '{printf "i1=%s i2=%s i3=%s i4=%s", $1 ,$2, $3, $4}')
		eval $(echo "$config_netmask" | awk -F"." '{printf "m1=%s m2=%s m3=%s m4=%s", $1 ,$2, $3, $4}')
		network_address="$(( $i1 & $m1 )).$(( $i2 & $m2 )).$(( $i3 & $m3 )).$(( $i4 & $m4 ))"
		eval $(echo "$network_address" | awk -F"." '{printf "n1=%s n2=%s n3=%s n4=%s", $1 ,$2, $3, $4}')
		broadcast_mask="$(( 255 ^ $m1 )).$(( 255 ^ $m2 )).$(( 255 ^ $m3 )).$(( 255 ^ $m4 ))"
		eval $(echo "$broadcast_mask" | awk -F"." '{printf "b1=%s b2=%s b3=%s b4=%s", $1 ,$2, $3, $4}')
		broadcast_address="$(( $n1 ^ $b1 )).$(( $n2 ^ $b2 )).$(( $n3 ^ $b3 )).$(( $n4 ^ $b4 ))"

		debugtype="debug"
		syslogmessage=" addr add \"$cdir_ip\" broadcast \"$broadcast_address\" dev \"$device\""
		write_to_syslog

		ip addr add "$cdir_ip" broadcast "$broadcast_address" dev "$device"
		ip link set "$device" arp on
		ip link set "$device" multicast on
	fi
}

dhcp4_renew() {
	hostname=$(uci get system.@system[0].hostname 2>/dev/null)
	udhcpc -p /var/run/udhcpc-renew-$dhcpdevice.pid -f -q -n -t0 -A 0 -i $dhcpdevice -x hostname:$hostname &
	sleep 6

	if [ -f "/var/run/udhcpc-renew-$dhcpdevice.pid" ]; then
		debugtype="debug"
		syslogmessage="Killing dhcp client-renew as it failed on [ $dhcpdevice ] server not found"
		write_to_syslog

		kill $(cat /var/run/udhcpc-renew-$dhcpdevice.pid)
	else
		/sbin/service dnsmasq reload
	fi
}

check_dns_server() {
	default_gw=$(ip route | grep "default via" | grep -w "dev $br_$auto_mesh_network")

	if [ ! -z "$default_gw" ]; then
		gw_ip=$(echo "$default_gw" | awk -F" " '{printf "%s", $3}')

		if [ ! -z "$gw_ip" ]; then
			dns_server_config=$(uci get dhcp.@dnsmasq[0].server 2> /dev/null | grep -q "$gw_ip"; echo $?)

			if [ "$dns_server_config" -eq 1 ] && [ "$commit_all" -eq 0 ]; then
				echo "add_list dhcp.@dnsmasq[0].server='$gw_ip'" | uci batch
				uci set dhcp.@dnsmasq[0].authoritative='0'
				uci set dhcp.@dnsmasq[0].rebind_protection='0'
				/sbin/service dnsmasq reload
			fi
		fi
	fi
}

check_portal() {
	log_debug "Checking portal status"
	is_portal=""

	if [ "$enabled" -eq 1 ]; then
		get_portal_state
		. $tmpdir/devicemac

		if [ "$portal_detect" -eq 3 ]; then
			log_debug "Checking CPE status"
			wait_for_mesh
			vxlan_status=$(ip link show vxlan$tun_id &> /dev/null; echo $?)
			log_debug "vxlan_status [ $vxlan_status ]"

			if [ "$vxlan_status" -eq 1 ]; then
				log_debug "MBP - checking vtunnel"
				check_vtunnel
			fi

			return 0
		fi

		if [ "$portal_detect" -eq 4 ]; then
			log_debug "Checking MBP (Mesh Bridge Portal) status"
			wait_for_mesh
			vxlan_status=$(ip link show vxlan$tun_id &> /dev/null; echo $?)
			log_debug "vxlan_status [ $vxlan_status ]"

			if [ "$vxlan_status" -eq 1 ]; then
				log_debug "MBP - checking vtunnel"
				check_vtunnel
			fi

			return 0
		fi

		if [ "$detected_state" != "portal" ]; then
			# This IS NOT a layer 3 mesh portal
			log_debug "This IS NOT a layer 3 mesh portal"

			if [ "$detected_state" = "peer" ] && [ "$last_state" = "initial" ]; then
				last_state="portal"
			fi

			if [ "$detected_state" = "peer" ] && [ "$last_state" = "portal" ]; then
				# We have become a peer
				last_state="peer"
				log_debug "This meshnode is NOT a portal"

				if [ -f "/var/run/udhcpc-$device.pid" ]; then
					kill $(cat /var/run/udhcpc-$device.pid)
				fi

				ip route flush default
				hostname=$(uci get system.@system[0].hostname 2>/dev/null)

				vxlan_status=$(ip link show vxlan$tun_id &> /dev/null; echo $?)

				if [ "$vxlan_status" -eq 0 ]; then
					echo "set network.vtunlan.proto='dhcp'" | uci batch
				fi

				echo "set network.$auto_mesh_network.proto='dhcp'" | uci batch

				uci set dhcp.@dnsmasq[0].authoritative='0'
				echo "set dhcp.$auto_mesh_network.ignore='1'" | uci batch
				echo "set dhcp.$auto_mesh_network.ra_default='2'" | uci batch
				echo "set dhcp.$auto_mesh_network.dhcpv6='disabled'" | uci batch
				echo "set dhcp.$auto_mesh_network.dhcpv4='disabled'" | uci batch
				echo "set dhcp.$auto_mesh_network.ra='disabled'" | uci batch
				echo "del_list dhcp.$auto_mesh_network.ra_flags='managed-config'" | uci batch 2> /dev/null
				echo "del_list dhcp.$auto_mesh_network.ra_flags='other-config'" | uci batch 2> /dev/null
				echo "add_list dhcp.$auto_mesh_network.ra_flags='none'" | uci batch
				echo "del_list dhcp.$auto_mesh_network.ra_dns='$portal_ula'" | uci batch 2> /dev/null
				uci set dhcp.@dnsmasq[0].rebind_protection='0'

				uci set dhcp.@dnsmasq[0].logfacility='-'
				uci set dhcp.@dnsmasq[0].quietdhcp='1'

				vxlan_status=$(ip link show vxlan$tun_id &> /dev/null; echo $?)

				if [ "$vxlan_status" -eq 0 ]; then
					uci set dhcp.vtunlan.dhcpv4='disabled'
					uci set dhcp.vtunlan.dhcpv6='disabled'
					uci set dhcp.vtunlan.ra='disabled'
					uci set dhcp.vtunlan.ignore='1'
					echo "del_list dhcp.vtunlan.ra_flags='managed-config'" | uci batch 2> /dev/null
					echo "del_list dhcp.vtunlan.ra_flags='other-config'" | uci batch 2> /dev/null
					echo "add_list dhcp.vtunlan.ra_flags='none'" | uci batch
					echo "del_list dhcp.vtunlan.ra_dns='$portal_ula_vtun'" | uci batch 2> /dev/null
					uci set dhcp.@dnsmasq[0].rebind_protection='0'
				fi


				if [ "$mesh_gate_only" -eq 0 ]; then

					if [ "$portal_detect" -eq 0 ]; then
						non_portal_mesh_hwmp_rootmode="4"
					else
						non_portal_mesh_hwmp_rootmode="2"
					fi

				else
					non_portal_mesh_hwmp_rootmode="0"
				fi

				set_ula_prefix "revert"
				/sbin/service network reload

				/sbin/service dnsmasq restart
				/sbin/service odhcpd restart

				local retries=5

				for retry in $(seq $retries); do
					default_gw=$(ip route | grep "default via")

					if [ -z "$default_gw" ]; then
						log_debug "Mesh startup watchdog - iteration [ $retry ] ...."
						wait_for_mesh
						log_debug "Mesh interface is [ $ifstatus ] ...."

						if [ "$ifstatus" = "up" ]; then
							break
						fi

						sleep 2
						continue
					else
						gw_ip=$(echo "$default_gw" | awk -F" " '{printf "%s", $3}')
						break
					fi
				done

				check_dns_server
				/sbin/service dnsmasq reload
				/sbin/service system restart
				echo 2 > /proc/sys/net/ipv6/conf/$device/accept_ra
				# Just become a peer so stop opennds if it is installed and enabled
				manage_opennds stop
			fi

			check_dns_server
			check_channel

		else
			# This IS a layer 3 mesh portal
			log_debug "This IS a layer 3 mesh portal"
			uci set mesh11sd.mesh_params.mesh_connected_to_as='1'

			if [ -f "/var/run/udhcpc-$device.pid" ]; then
				kill $(cat /var/run/udhcpc-$device.pid)
			fi

			if [ "$detected_state" = "portal" ] && [ "$last_state" = "initial" ]; then
				last_state="peer"
			fi

			if [ "$detected_state" = "portal" ] && [ "$last_state" = "peer" ]; then

				if [ "$leechmode" -eq 1 ]; then
					leechmode=0
					echo "leechmode=$leechmode" > "$tmpdir/leechmode"
				fi

				last_state="portal"
				debugtype="debug"
				syslogmessage="This meshnode is an upstream portal"
				write_to_syslog

				vxlan_status=$(ip link show vxlan$tun_id &> /dev/null; echo $?)


				if [ "$vxlan_status" -eq 0 ]; then
					echo "set network.vtunlan.proto='static'" | uci batch
				fi

				echo "set network.$auto_mesh_network.proto='static'" | uci batch

				set_ula_prefix "set"
				/sbin/service network reload

				uci set dhcp.@dnsmasq[0].authoritative='1'
				uci set dhcp.@dnsmasq[0].logfacility='-'
				uci set dhcp.@dnsmasq[0].quietdhcp='1'
				echo "del_list dhcp.@dnsmasq[0].server='$config_ipaddr'" | uci batch
				echo "set dhcp.$auto_mesh_network.ignore='0'" | uci batch
				echo "set dhcp.$auto_mesh_network.ra_default='2'" | uci batch
				echo "set dhcp.$auto_mesh_network.dhcpv6='server'" | uci batch
				echo "set dhcp.$auto_mesh_network.dhcpv4='server'" | uci batch
				echo "set dhcp.$auto_mesh_network.ra='server'" | uci batch
				echo "del_list dhcp.$auto_mesh_network.ra_flags='none'" | uci batch 2> /dev/null
				echo "del_list dhcp.$auto_mesh_network.ra_flags='managed-config'" | uci batch 2> /dev/null
				echo "del_list dhcp.$auto_mesh_network.ra_flags='other-config'" | uci batch 2> /dev/null
				echo "add_list dhcp.$auto_mesh_network.ra_flags='managed-config'" | uci batch 2> /dev/null
				echo "add_list dhcp.$auto_mesh_network.ra_flags='other-config'" | uci batch 2> /dev/null
				echo "del_list dhcp.$auto_mesh_network.ra_dns=''" | uci batch 2> /dev/null
				echo "add_list dhcp.$auto_mesh_network.ra_dns='$portal_ula'" | uci batch 2> /dev/null
				uci set dhcp.@dnsmasq[0].rebind_protection='1'

				if [ "$vxlan_status" -eq 0 ]; then
					uci set dhcp.vtunlan.dhcpv4='server'
					uci set dhcp.vtunlan.dhcpv6='server'
					uci set dhcp.vtunlan.ra='server'
					uci set dhcp.vtunlan.ignore='0'
					echo "del_list dhcp.vtunlan.ra_flags='none'" | uci batch 2> /dev/null
					echo "del_list dhcp.vtunlan.ra_flags='managed-config'" | uci batch 2> /dev/null
					echo "del_list dhcp.vtunlan.ra_flags='other-config'" | uci batch 2> /dev/null
					echo "add_list dhcp.vtunlan.ra_flags='managed-config'" | uci batch 2> /dev/null
					echo "add_list dhcp.vtunlan.ra_flags='other-config'" | uci batch 2> /dev/null
					echo "add_list dhcp.vtunlan.ra_dns='$portal_ula_vtun'" | uci batch 2> /dev/null
					uci set dhcp.@dnsmasq[0].rebind_protection='1'

				fi

				if [ "$mesh_gate_only" -eq 0 ]; then
					mesh_hwmp_rootmode="4"
				else
					mesh_hwmp_rootmode="0"
				fi

				if [ "$portal_detect" -ne 4 ]; then
					restore_ipv4
				fi

				/sbin/service dnsmasq restart
				/sbin/service odhcpd restart
				/sbin/service system restart

				if [ "$daemon_startup" -eq 1 ]; then
					daemon_startup=0
				fi

				echo 0 > /proc/sys/net/ipv6/conf/$device/accept_ra

				if [ "$portal_detect" -ne 4 ]; then
					# Just become a portal so start opennds if it is installed and enabled
					manage_opennds start
				fi
			fi
		fi

		portal_watchdog
	fi
}

wait_for_nodes() {
	local tics=5

	echo -n " Building node list * "

	ccpy_setup

	for tic in $(seq $tics); do
		ndp_scan_peer
		echo -n "* "
	done

	sleep 1
	echo
}

convert_to_la() {
	local mac_to_convert="$1"

	eval $(echo "$mac_to_convert" | awk -F":" '{printf "p1=%s p2=%s p3=%s p4=%s p5=%s p6=%s", $1 ,$2, $3, $4, $5, $6}')

	la_check=$(printf '%x\n' "$(( 0x2 & 0x$p1 ))")

	if [ "$la_check" -eq 0 ]; then
		octet=$(printf '%x\n' "$(( 0x2 | 0x$p1 ))")
	else
		octet="$p1"
	fi

	local len=${#octet}
	if [ "$len" -eq 1 ]; then
		octet="0$octet"
	fi

	mac_la="$octet:$p2:$p3:$p4:$p5:$p6"
}

convert_from_la() {
	local mac_to_convert="$1"

	eval $(echo "$mac_to_convert" | awk -F":" '{printf "p1=%s p2=%s p3=%s p4=%s p5=%s p6=%s", $1 ,$2, $3, $4, $5, $6}')

	la_check=$(printf '%x\n' "$(( 0x2 & 0x$p1 ))")

	if [ "$la_check" -eq 2 ]; then
		octet=$(printf '%x\n' "$(( 0x2 ^ 0x$p1 ))")
	else
		octet="$p1"
	fi

	local len=${#octet}
	if [ "$len" -eq 1 ]; then
		octet="0$octet"
	fi

	mac_from_la="$octet:$p2:$p3:$p4:$p5:$p6"
}

make_indexed_mac() {
	local mac_to_convert="$1"
	local index="$2"
	eval $(echo "$mac_to_convert" | awk -F":" '{printf "p1=%s p2=%s p3=%s p4=%s p5=%s p6=%s", $1 ,$2, $3, $4, $5, $6}')
	indexed_octet=$(printf '%x\n' "$(( 0x$index + 0x$p4 ))")
	local len=${#indexed_octet}
	indexed_octet=${indexed_octet:$(($len-2)):2}

	if [ "$len" -eq 1 ]; then
		indexed_octet="0$indexed_octet"
	fi

	mac_indexed="$p1:$p2:$p3:$indexed_octet:$p5:$p6"
}


wait_for_mesh() {
	fixup1=1
	get_mesh_iflist

	local retries=2

	for iface in $iflist; do

		for retry in $(seq $retries); do
			wait_for_interface "$iface"

			if [ "$ifstatus" = "down" ]; then
				sleep 1
				continue
			fi
		done

		if [ "$ifstatus" = "down" ] && [ "$fixup1" -eq 0 ] && [ "$commit_all" -eq 0 ]; then
			fixup1=1
			debugtype="err"
			syslogmessage="First attempt at fixup: Mesh interface $iface failed - Possible wireless driver bug...."
			write_to_syslog

			. $tmpdir/devicemac
			convert_to_la "$devicemac"

			uci_section_name=$(uci show wireless | grep "ifname='$iface'" | awk -F "." '{printf "%s", $2}')
			radio=$(uci show wireless | grep "$uci_section_name" | grep "device=" | awk -F "'" '{printf "%s", $2}')
			wifiifaces=$(uci show wireless | grep "device='$radio'" | awk -F "." '{printf "%s ", $2}')

			for wifiiface in $wifiifaces; do

				if [ "$wifiiface" = "$uci_section_name" ]; then
					continue
				else
					debugtype="notice"
					syslogmessage="Attempting fixup: Setting locally administered mac address"
					write_to_syslog
					sleep 1
					echo "set wireless.$wifiiface.macaddr='$mac_la'" | uci batch
					wifi
				fi
			done

		elif [ "$ifstatus" = "down" ] && [ "$fixup1" -eq 1 ]; then
			fixup1=2
			debugtype="err"
			syslogmessage="Mesh interface $iface failed - Possible wireless driver bug.... Attempting fixup: "
			write_to_syslog
			uci_section_name=$(uci show wireless | grep "ifname='$iface'" | awk -F "." '{printf "%s", $2}')
			radio=$(uci show wireless | grep "$uci_section_name" | grep "device=" | awk -F "'" '{printf "%s", $2}')
			wifiifaces=$(uci show wireless | grep "device='$radio'" | awk -F "." '{printf "%s ", $2}')

			for wifiiface in $wifiifaces; do

				if [ "$wifiiface" = "$uci_section_name" ]; then
					continue
				else
					debugtype="notice"
					syslogmessage="Attempting fixup: Disabling mesh gate for $wifiiface"
					write_to_syslog
					sleep 1
					echo "set wireless.$wifiiface.disabled='1'" | uci batch
					wifi
				fi
			done
		fi

		###########
		# Process htmode if needed
		#uci_section_name=$(uci show wireless | grep "ifname='$iface'" | awk -F "." '{printf "%s", $2}')
		#radio=$(uci show wireless | grep "$uci_section_name" | grep "device=" | awk -F "'" '{printf "%s", $2}')

		#htmode_config=$(echo "get wireless.$radio.htmode" | uci batch)
		#echo "iface=$iface! uci_section_name=$uci_section_name! radio=$radio! wifiifaces=$wifiifaces! htmode_config=$htmode_config!"
		###########
	done

	# Reload odhcpd to refresh RA timers
	/sbin/service odhcpd reload
}

all_nodes_rssi_update() {

	if [ "$auto_config" -gt 0 ]; then

		if [ -f "$tmpdir/meshinterface" ]; then

			if [ ! -f "$tmpdir/mesh_rssi_threshold" ]; then
				debugtype="debug"
				syslogmessage="Disconnecting nodes"
				write_to_syslog

				rm "$tmpdir/meshinterface"

				get_wiphys

				if [ ! -z "$wiphys" ]; then

					for wiphy in $wiphys; do
						mesh_capable=$(iw phy $wiphy info | grep -A 15 "Supported interface modes:" | grep -w "mesh point")

						if [ -z "$mesh_capable" ]; then
							continue
						fi

						phyindex=$(echo "$wiphy" | awk -F "phy" '{printf "%s", $2}')
						phyindex=$(printf "%x" $phyindex)

						echo "set wireless.m11s$phyindex.mesh_rssi_threshold='$mesh_rssi_threshold'" | uci batch
					done
				fi

				restart_mesh
			fi
		fi
	fi
}

get_peers() {
	get_mesh_iflist

	for miface in $iflist; do
		wait_for_interface "$miface"

		if [ "$ifstatus" = "down" ]; then
			continue
		fi

		peers=$(iw dev $miface mpath dump | grep -c -w $miface)
		macrouting=$(iw dev $miface mpath dump | awk -F" " 'NR>1 {printf "%s/%s ", $1, $2}')

		for peer in $macrouting; do
			peermac=$(echo "$peer" | awk -F"/" '{printf "%s", $1}')
			peerlist="$peerlist ""$peermac"
			mute=$mutestate
			convert_from_la "$peermac"
			peerlist="$peerlist ""$mac_from_la"
		done
	done
}

ccpy_setup() {
	get_peers

	meshconfigs=$(uci show wireless 2> /dev/null | grep "mode='mesh'" | awk -F ".mode='mesh'" '{printf "%s ", $1}')
	connectlist=""

	if [ ! -z "$meshconfigs" ]; then
		for meshconfig in $meshconfigs; do
			networks=$(uci get $meshconfig.network)

			for network in $networks; do
				device=$(uci get network.$network.device)

				if [ "$network" != "$network_previous" ]; then
					connectlist="$connectlist ""$(ip -6 neigh | grep "fe80::" | grep "$device" | grep "lladdr" | awk '{printf "%s%s%s,%s ", $1, "%", $3, $5}')"
				fi

				network_previous=$network
			done

			# Stop after the first mesh network - there should only be one but more might have been added in error
			break
		done
	fi
}

ndp_scan_peer() {
	# NDP - Send linklocal multicast ip6-allrouters
	if [ -f "$tmpdir/devicemac" ]; then
		. $tmpdir/devicemac
		response=$(ping -q -c 8 "ff02::2%""$device" &>/dev/null &)
	fi
}

get_wiphys() {
	# get list of physical wireless interfaces and reverse the list order
	wiphys=$(iw list | grep -w "Wiphy" | awk -F "Wiphy " '{printf "%s ", $2}' | awk '{ for (i = NF; i > 0; i = i - 1) printf("%s ", $i) }')
	mkdir -p "$tmpdir/radios"

	for wiphy in $wiphys; do
		radioindex=${wiphy:3:10}
		band=$(echo "get wireless.radio$radioindex.band" | uci batch)
		mesh_capable=$(iw phy "$wiphy" info | grep -w -q  "mesh point"; echo -n "$?")
		total_ifaces=$(iw phy $wiphy info | grep -w "total <=" | awk -F"," '{print $1}' | awk '{print $3}')

		echo "mesh_capable=$mesh_capable; total_ifaces=$total_ifaces; radioindex=$radioindex; band=$band" > "$tmpdir/radios/radio$radioindex"

		if [ $mesh_capable -gt 0 ]; then
			debugtype="info"
			syslogmessage="WARNING - phy [ $wiphy ] does not support mesh interfaces."
			write_to_syslog
		fi

		if [ -z "$total_ifaces" ] || [ $total_ifaces -lt 5 ]; then

			if [ "$vtun_gate_encryption" -eq 0 ] && [ "$mesh_gate_encryption" -eq 0 ]; then
				debugtype="info"
				syslogmessage="WARNING - phy [ $wiphy ] only supports [ $total_ifaces ] interfaces - possibly some or maybe all configured wireless APs will be missing."
				write_to_syslog
			fi
		fi
	done
}

refresh_bridgemac() {
	# get the device mac and write it to $tmpdir/devicemac
	device=$(uci get network.$auto_mesh_network.device)
	configured_mac=$(uci get network.$auto_mesh_network.macaddr 2>/dev/null)

	if [ -z "$device" ]; then
		device="br-lan"
	fi

	wait_for_interface "$device"
	devicemac=$(ip link | grep -A 1 "$device:" | grep -w "link/ether" | awk '{printf "%s", $2}')
	echo "device=\"$device\" ; devicemac=\"$devicemac\"" > $tmpdir/devicemac
}

auto_config() {
	debugtype="debug"
	syslogmessage="Entering auto config...."
	write_to_syslog
	autoconfig=0

	# Set the default region to DFS-ETSI (safer than DFS-UNSET for a mesh)
	# Code EU is not recognised in current OpenWrt releases, so use first alphabetically valid code ie Andorra
	dfs_etsi="AD"
	meshifenabled=0

	# Are we using ath10k drivers?
	is_installed "kmod-ath10k-ct"

	if [ "$exitcode" -eq 0 ]; then
		debugtype="err"
		syslogmessage="Unable to autoconfigure. Please install ath10k NON ct drivers for mesh support"
		write_to_syslog
		exit 1
	fi

	if [ "$autoconfig" -eq 0 ]; then
		# Set the odhcpd loglevel
		echo "set dhcp.odhcpd.loglevel='$odhcpd_log_level'" | uci batch

		# mute dnsmasq logging
		uci set dhcp.@dnsmasq[0].logfacility='-'
		uci set dhcp.@dnsmasq[0].quietdhcp='1'


		# Add vxlan wireless interface(s)
		package_list="ip-full vxlan"
		check_package_list

		if [ "$ip_full" -eq 0 ] && [ "$vxlan" -eq 0 ] && [ "$vtun_enable" -eq 1 ]; then
			# we have the required packages for vxlan
			aplist=$(uci show wireless | grep "mode='ap'" | awk -F "." '{printf "%s ", $2}')

			for ap in $aplist; do
				radio_index=$(echo "show wireless.$ap" | uci batch 2> /dev/null | grep "device=" | awk -F "'" '{printf "%s", $2}' | awk -F "radio" '{printf "%s", $2}')

				echo "set wireless.vxradio$radio_index=wifi-iface" | uci batch
				echo "set wireless.vxradio$radio_index.device='radio$radio_index'" | uci batch
				echo "set wireless.vxradio$radio_index.network='vtunlan'" | uci batch
				echo "set wireless.vxradio$radio_index.mode='ap'" | uci batch
				echo "set wireless.vxradio$radio_index.ifname='vxradio$radio_index'" | uci batch
				echo "set wireless.vxradio$radio_index.ssid='$vtun_base_ssid'" | uci batch
				echo "set wireless.vxradio$radio_index.encryption='none'" | uci batch
				echo "set wireless.vxradio$radio_index.disabled='$ifdisable'" | uci batch
			done
		fi

		# do we have a mesh supporting wpad?
		package_list="wpad-mbedtls wpad-wolfssl wpad-openssl wpad-mesh-mbedtls wpad-mesh-wolfssl wpad-mesh-openssl"
		check_package_list

		if [ "$?" -eq 0 ]; then

			refresh_bridgemac
			. $tmpdir/devicemac
			get_wiphys

			if [ ! -z "$wiphys" ]; then
				for wiphy in $wiphys; do
					mesh_capable=$(iw phy $wiphy info | grep -A 15 "Supported interface modes:" | grep -w "mesh point")

					if [ -z "$mesh_capable" ]; then
						continue
					fi

					# Prerequisites met so we can autoconfigure
					phyindex=$(echo "$wiphy" | awk -F "phy" '{printf "%s", $2}')

					phyindex=$(printf "%x" $phyindex)

					if [ ! -z "$mesh_phy_index" ] && [ "$phyindex" != "$mesh_phy_index" ]; then
						syslogmessage="option mesh_phy_index [ $mesh_phy_index ] does not match phyindex [ $phyindex ] - skipping this phy"
						debugtype="info"
						mute="$mutestate"
						write_to_syslog
						continue
					fi

					mesh_ifname="$mesh_basename""$phyindex"


					echo "set wireless.m11s$phyindex=wifi-iface" | uci batch
					echo "set wireless.m11s$phyindex.device='radio$phyindex'" | uci batch
					echo "set wireless.m11s$phyindex.mode='mesh'" | uci batch
					echo "set wireless.m11s$phyindex.encryption='sae'" | uci batch
					echo "set wireless.m11s$phyindex.mesh_id='$auto_mesh_id'" | uci batch
					echo "set wireless.m11s$phyindex.key='$auto_mesh_key'" | uci batch
					echo "set wireless.m11s$phyindex.network='$auto_mesh_network'" | uci batch
					echo "set wireless.m11s$phyindex.ifname='$mesh_ifname'" | uci batch
					echo "set wireless.m11s$phyindex.mesh_rssi_threshold='$mesh_rssi_threshold'" | uci batch
					echo "set wireless.m11s$phyindex.dtim_period='$mesh_dtim_period'" | uci batch
					echo "set wireless.radio$phyindex.disabled='0'" | uci batch
					echo "set wireless.radio$phyindex.beacon_int='$mesh_beacon_interval'" | uci batch
					echo "set wireless.radio$phyindex.log_level='3'" | uci batch

					ucicountry=$(uci get wireless.radio$phyindex.country 2> /dev/null | awk '{printf "%s", $1}')

					if [ -z "$country" ] && [ -z "$ucicountry" ]; then
						echo "set wireless.radio$phyindex.country='$dfs_etsi'" | uci batch
						syslogmessage="WARNING - country code - interoperability with other mesh nodes would be compromised or fail altogether if not set, so setting to safe default...."
						debugtype="warn"
						write_to_syslog
						syslogmessage="Country code defaulting to [ DFS-ETSI ]...."
						debugtype="warn"
						write_to_syslog
						country="$dfs_etsi"
					fi

					if [ ! -z "$country" ]; then
						echo "set wireless.radio$phyindex.country='$country'" | uci batch
					fi

					band=$(uci get wireless.radio$phyindex.band 2> /dev/null | awk '{printf "%s", $1}')

					if [ "$auto_mesh_band" = "2g40" ]; then
						mesh_band="2g"
					else
						mesh_band=$auto_mesh_band
					fi

					if [ "$band" = "$mesh_band" ] && [ "$meshifenabled" -eq 0 ]; then
						echo "set wireless.m11s$phyindex.disabled='0'" | uci batch
						meshifenabled=1

						if [ ! -f "$tmpdir/active_mesh_ifname" ]; then
							echo "active_mesh_ifname=\"$mesh_basename$phyindex\"" > "$tmpdir/active_mesh_ifname"
						fi
					else
						echo "set wireless.m11s$phyindex.disabled='1'" | uci batch
					fi

					if [ "$band" = "2g" ]; then
						get_portal_state

						if [ "$auto_mesh_band" = "2g40" ]; then
							originalhtmode=$(echo "get wireless.radio$phyindex.htmode" | uci batch)
							he_mode=$(echo "$originalhtmode" | grep -q "HE"; echo -n "$?")

							if [ "$he_mode" -eq 0 ]; then
								htmode="HE40"
							else
								htmode="HT40"
							fi

							if [ ! -z "$country" ]; then
								echo "set wireless.radio$phyindex.noscan='1'" | uci batch
								echo "set wireless.radio$phyindex.htmode='$htmode'" | uci batch
							else
								echo "set wireless.radio$phyindex.noscan='0'" | uci batch
								echo "set wireless.radio$phyindex.htmode='$originalhtmode'" | uci batch
							fi

						fi

						if [ "$portal_channel" = "auto" ] && [ "$band" = "2g" ]; then

							for i in $(seq 9 1 64); do
								#Generate a channel, choosing start point of sequence away from end eg "9"
								chan=$((0x$(printf "$devicemac" | sha256sum | awk '{printf "%s", $1}' | tail -c$i | head -c1)))

								#Check for max
								if [ "$chan" -gt 13 ]; then
									continue
								fi

								#Check for min
								if [ "$chan" -le 0 ]; then
									continue
								fi

								echo "set wireless.radio$phyindex.channel='$chan'" | uci batch
								break
							done

						elif [ "$portal_channel" != "auto" ] && [ "$portal_channel" != "default" ] && [ "$band" = "2g" ]; then
							chan="$portal_channel"

							#Check for max
							if [ "$chan" -gt 13 ]; then
								chan=13
							fi

							#Check for min
							if [ "$chan" -le 0 ]; then
								chan=1
							fi

							echo "set wireless.radio$phyindex.channel='$chan'" | uci batch
						fi
					fi
				done

				# get list of access points
				aplist=$(uci show wireless 2> /dev/null | grep "='ap'" | awk -F "." '{printf "%s.%s ", $1, $2}')

				#Add ssid and suffix
				suffix=$(echo $devicemac | awk -F":" '{printf "%s%s", $5, $6}')

				ifindex=0

				for ap in $aplist; do
					radio=$(uci get "$ap.device" 2> /dev/null)
					radioindex=$(echo "$radio" | awk -F "radio" '{printf "%s", $2}')
					band=$(uci get "wireless.$radio.band" 2> /dev/null)
					ssid=$(uci get "$ap.ssid" 2> /dev/null | awk '{printf "%s", $1}')
					ifname=$(uci get "$ap.ifname" 2> /dev/null | awk '{printf "%s", $1}')

					if [ -z "$ifname" ]; then
						ifname="phy$radioindex-ap$ifindex"
						ifindex=$((ifindex+1))
						echo "set $ap.ifname='$ifname'" | uci batch
					fi

					if [ "$ssid" != "$vtun_base_ssid" ] && [ ! -z "$mesh_gate_base_ssid" ]; then
						ssid="$mesh_gate_base_ssid"
					fi

					if [ "$ssid_suffix_enable" -eq 1 ]; then
						echo "set $ap.ssid='$ssid-$band-$suffix'" | uci batch
					else
						echo "set $ap.ssid='$ssid'" | uci batch
					fi
				done

				echo "set system.@system[0].hostname='meshnode-$suffix'" | uci batch
				/sbin/service system restart

				#Configure gate encryption
				case $mesh_gate_encryption in
					0) encryption_type="none";;
					1) encryption_type="sae";;
					2) encryption_type="sae-mixed+aes";;
					3) encryption_type="psk2+aes";;
					4) encryption_type="owe";;
					*) encryption_type="none";;
				esac

				#Configure vxradio encryption
				case $vtun_gate_encryption in
					0) vx_encryption_type="none";;
					1) vx_encryption_type="sae";;
					2) vx_encryption_type="sae-mixed+aes";;
					3) vx_encryption_type="psk2+aes";;
					4) vx_encryption_type="owe";;
					*) vx_encryption_type="none";;
				esac

				if [ "$encryption_type" = "owe" ] || [ "$encryption_type" = "none" ] || [ "$vx_encryption_type" = "owe" ] || [ "$vx_encryption_type" = "none" ]; then

					if [ "$wpad_mbedtls" -eq 0 ] || [ "$wpad_wolfssl" -eq 0 ] || [ "$wpad_openssl" -eq 0 ]; then

						# We have a full version so owe is supported
						owe_able=1
						debugtype="notice"
						syslogmessage="full version of wpad is installed, owe supported"
						write_to_syslog
					else
						encryption_type="none"
						vx_encryption_type="none"
						owe_able=0
						debugtype="err"
						syslogmessage="owe not supported, install a full version of wpad"
						write_to_syslog
					fi
				fi

				for ap in $aplist; do
					radio=$(uci get "$ap.device" 2> /dev/null)
					. "$tmpdir/radios/$radio"

					is_vx=$(echo "$ap" | grep -c "vxradio")

					if [ "$is_vx" -eq 0 ]; then
						echo "set $ap.encryption='$encryption_type'" | uci batch
						echo "set $ap.key='$mesh_gate_key'" | uci batch

					elif [ "$is_vx" -eq 1 ]; then
						echo "set $ap.encryption='$vx_encryption_type'" | uci batch
						echo "set $ap.key='$vtun_gate_key'" | uci batch
					fi

					if [ "$encryption_type" = "none" ] || [ "$vx_encryption_type" = "none" ]; then

						if [ "$total_ifaces" -ge 5 ]; then
							# setup owe transition if wpad supports it

							if [ "$owe_able" -eq 1 ]; then
								owe_transition
							fi
						fi
					fi
				done

				debugtype="debug"
				syslogmessage="auto config complete...."
				write_to_syslog

				# set changed flag to signify a mesh restart is needed
				changed=1

			else
				debugtype="err"
				syslogmessage="Unable to autoconfigure. No physical wireless interfaces found"
				write_to_syslog
			fi
		else
			debugtype="err"
			syslogmessage="Unable to autoconfigure. Please install package wpad-mbedtls, wpad-wolfssl, wpad-openssl or an equivalent mesh only version"
			write_to_syslog
			exit 1
		fi

		# TODO If we want to call auto_config from cli then we can stop it happening more than once by uncommenting the following:
		#autoconfig=1
	fi
}

owe_transition() {
	local enc_type=$(uci get $ap.encryption)

	if [ "$enc_type" = "none" ]; then
		section_ap=$(uci show "$ap")
		section_list=$(echo "$section_ap" | awk '{printf "%s ", $1}')

		radio_index=$(echo "$section_ap" | grep "radio" | awk -F "'" '{printf "%s", $2}' | awk -F "radio" '{printf "%i", $2}')

		echo "set $ap.ifname='open$transition_count-$radio_index'" | uci batch
		echo "set $ap.owe_transition_ifname='owe$transition_count-$radio_index'" | uci batch

		for section_item in $section_list; do
			oweap="s/$ap/wireless.owe$transition_count$radio_index/g"
			uci_option=$(echo "$section_item" | sed "$oweap")
			echo "set $uci_option" | uci batch
		done

		echo "set wireless.owe$transition_count$radio_index.ifname='owe$transition_count-$radio_index'" | uci batch
		echo "set wireless.owe$transition_count$radio_index.hidden='1'" | uci batch
		echo "set wireless.owe$transition_count$radio_index.encryption='owe'" | uci batch
		transition_count=$(($transition_count + 1))
	fi
}

urlencode() {
	entitylist="
		s/%/%25/g
		s/\s/%20/g
		s/\"/%22/g
		s/-/%2D/g
		s/:/%3A/g
		s/>/%3E/g
		s/</%3C/g
		s/'/%27/g
		s/(/%28/g
		s/)/%29/g
		s/\`/%60/g
	"
	local buffer="$1"

	for entity in $entitylist; do
		urlencoded=$(echo "$buffer" | sed "$entity")
		buffer=$urlencoded
	done

	urlencoded=$(echo "$buffer" | awk '{ gsub(/\$/, "\\%24"); print }')
}

urldecode() {
	entitylist="
		s/%22/\"/g
		s/%2D/-/g
		s/%3A/:/g
		s/%3E/>/g
		s/%3C/</g
		s/%27/'/g
		s/%28/(/g
		s/%29/)/g
		s/%60/\`/g
		s/%25/%/g
	"
	local buffer="$1"

	for entity in $entitylist; do
		urldecoded=$(echo "$buffer" | sed "$entity")
		buffer=$urldecoded
	done

	buffer=$(echo "$buffer" | awk '{ gsub(/%24/, "$"); print }')
	urldecoded=$(echo "$buffer" | awk '{ gsub(/%20/, " "); print }')
}

sys_setup() {
	refresh_bridgemac
	# Set arp and ndp defaults
	echo 1 > /proc/sys/net/ipv4/conf/all/arp_accept
	echo 1 > /proc/sys/net/ipv4/conf/default/arp_accept
	echo 1 > /proc/sys/net/ipv4/conf/$device/arp_accept
	echo 2 > /proc/sys/net/ipv4/conf/all/arp_announce
	echo 2 > /proc/sys/net/ipv4/conf/default/arp_announce
	echo 2 > /proc/sys/net/ipv4/conf/$device/arp_announce
	echo 0 > /proc/sys/net/ipv4/conf/all/arp_filter
	echo 0 > /proc/sys/net/ipv4/conf/default/arp_filter
	echo 0 > /proc/sys/net/ipv4/conf/$device/arp_filter
	echo 0 > /proc/sys/net/ipv4/conf/all/arp_ignore
	echo 0 > /proc/sys/net/ipv4/conf/default/arp_ignore
	echo 0 > /proc/sys/net/ipv4/conf/$device/arp_ignore
	echo 1 > /proc/sys/net/ipv4/conf/all/arp_notify
	echo 1 > /proc/sys/net/ipv4/conf/default/arp_notify
	echo 1 > /proc/sys/net/ipv4/conf/$device/arp_notify
	echo 0 > /proc/sys/net/ipv4/conf/all/proxy_arp
	echo 0 > /proc/sys/net/ipv4/conf/default/proxy_arp
	echo 1 > /proc/sys/net/ipv4/conf/$device/proxy_arp
	echo 0 > /proc/sys/net/ipv4/conf/all/proxy_arp_pvlan
	echo 0 > /proc/sys/net/ipv4/conf/default/proxy_arp_pvlan
	echo 0 > /proc/sys/net/ipv4/conf/$device/proxy_arp_pvlan
	echo 1 > /proc/sys/net/ipv4/conf/all/bc_forwarding
	echo 1 > /proc/sys/net/ipv4/conf/default/bc_forwarding
	echo 1 > /proc/sys/net/ipv4/conf/$device/bc_forwarding

	echo 2 > /proc/sys/net/ipv6/conf/all/accept_ra
	echo 2 > /proc/sys/net/ipv6/conf/default/accept_ra
	echo 0 > /proc/sys/net/ipv6/conf/$device/accept_ra
	echo 1 > /proc/sys/net/ipv6/conf/all/forwarding
	echo 1 > /proc/sys/net/ipv6/conf/default/forwarding
	echo 1 > /proc/sys/net/ipv6/conf/$device/forwarding
	echo 1 > /proc/sys/net/ipv6/conf/all/proxy_ndp
	echo 1 > /proc/sys/net/ipv6/conf/default/proxy_ndp
	echo 1 > /proc/sys/net/ipv6/conf/$device/proxy_ndp
	echo 5 > /proc/sys/net/ipv6/conf/all/router_solicitations
	echo 5 > /proc/sys/net/ipv6/conf/default/router_solicitations
	echo 5 > /proc/sys/net/ipv6/conf/$device/router_solicitations
}

default_settings() {
	echo "del wireless.radio$phyindex.beacon_int" | uci batch 2>/dev/null
	echo "del mesh11sd.mesh_params.mesh_fwding" | uci batch 2>/dev/null
	echo "del mesh11sd.mesh_params.mesh_hwmp_rann_interval" | uci batch 2>/dev/null
	echo "del mesh11sd.mesh_params.mesh_hwmp_preq_min_interval" | uci batch 2>/dev/null
	echo "del mesh11sd.mesh_params.mesh_hwmp_active_path_timeout" | uci batch 2>/dev/null
	echo "del mesh11sd.mesh_params.mesh_hwmp_max_preq_retries" | uci batch 2>/dev/null
	echo "del mesh11sd.mesh_params.mesh_hwmp_rootmode" | uci batch 2>/dev/null
}

mobility_settings() {
	. $tmpdir/active_mesh_ifname
	stanza=$(uci show wireless | grep ".ifname='$active_mesh_ifname'" | awk -F "." '{printf "%s.%s", $1, $2}')
	radio=$(uci get "$stanza.device")
	phyindex="$(echo "$radio" | awk -F "radio" '{printf "%s", $2}')"
	phy="phy$phyindex"
	mesh_node_mobility_level=$(uci get mesh11sd.setup.mesh_node_mobility_level 2>/dev/null)

	if [ -z "$mesh_node_mobility_level" ]; then
		mesh_node_mobility_level=1
	fi

	case $mesh_node_mobility_level in
		0) default_settings; return 0;;
		1) rts=500; limit=1000; quantum=3000; aql_threshold=2000; beacon_int=50; dtim_period=2;\
			rootmode=3;\
			mesh_fwding=1;\
			mesh_hwmp_rann_interval=1953;\
			mesh_hwmp_preq_min_interval=586;\
			mesh_hwmp_active_path_timeout=1465;\
			mesh_max_peer_links=16;\
			mesh_rssi_threshold="-68";\
			mesh_hwmp_max_preq_retries=4;;
		2) rts=300; limit=750; quantum=4500; aql_threshold=1500; beacon_int=25; dtim_period=2;\
			rootmode=2;\
			mesh_fwding=1;\
			mesh_hwmp_rann_interval=1464;\
			mesh_hwmp_preq_min_interval=488;\
			mesh_hwmp_active_path_timeout=1074;\
			mesh_max_peer_links=32;\
			mesh_rssi_threshold="-74";\
			mesh_hwmp_max_preq_retries=3;;
		3) rts=200; limit=500; quantum=6000; aql_threshold=1000; beacon_int=20; dtim_period=1;\
			rootmode=0;\
			mesh_fwding=1;\
			mesh_hwmp_rann_interval=977;\
			mesh_hwmp_preq_min_interval=390;\
			mesh_hwmp_active_path_timeout=684;\
			mesh_max_peer_links=64;\
			mesh_rssi_threshold="-80";\
			mesh_hwmp_max_preq_retries=2;;
		4) rts=100; limit=250; quantum=9000; aql_threshold=500; beacon_int=15; dtim_period=1;\
			rootmode=0;\
			mesh_fwding=1;\
			mesh_hwmp_rann_interval=488;\
			mesh_hwmp_preq_min_interval=293;\
			mesh_hwmp_active_path_timeout=293;\
			mesh_max_peer_links=128;\
			mesh_rssi_threshold="-86";\
			mesh_hwmp_max_preq_retries=1;;
		*) return 0;;
	esac

	if [ ! -z "$use_default_beacon_interval" ] && [ "$use_default_beacon_interval"  -gt 0 ]; then
		beacon_int=100
		debugtype="info"
		syslogmessage="Default beacon interval requested [ $beacon_int ] Mesh mobility may be suboptimal."
		write_to_syslog
	fi

	case "$auto_mesh_band" in
		"2g")	mesh_band="2.4GHz" ;;
		"2g40")	mesh_band="2.4GHz" ;;
		"5g")	mesh_band="5GHz" ;;
		"6g")	mesh_band="6GHz" ;;
		"60g")	mesh_band="60GHz" ;;
	esac

	start_marker="\"$mesh_band\""
	end_marker="}"
	mesh_driver=$(eval $(echo "sed -n '/$start_marker/,/$end_marker/{/$start_marker\|$end_marker/!p}' /tmp/mesh11sd/wifidetect") | grep "\"Driver\"" | awk -F "\"" '{print $4}')

	if [ "$mesh_driver" = "ath11k" ]; then
		beacon_int=100
		debugtype="info"
		syslogmessage="Qualcomm ath11k detected, beacon interval override to [ $beacon_int ms ] (workaround for ath11k bug). Mesh mobility may be suboptimal."
		write_to_syslog
	fi

	echo "mesh_node_mobility_level=\"$mesh_node_mobility_level\"; rootmode=\"$rootmode\"; beacon_int=\"$beacon_int\"; dtim_period=\"$dtim_period\"; \
		rts=\"$rts\"; limit=\"$limit\"; quantum=\"$quantum\"; aql_threshold=\"$aql_threshold\"" > "$tmpdir/mesh_mobility"

	if [ -z "$1" ] || [ "$1" = "parameters" ]; then

		# RTS/CTS (Request to Send/Clear to Send) is a MAC-layer mechanism to reduce collisions, but in 802.11s,
		# it’s typically controlled at the PHY/MAC layer via general 802.11 parameters (e.g., rts_threshold) rather than HWMP-specific settings.
		# The mac80211 mesh stack does not provide a dedicated HWMP-level RTS/CTS toggle like mesh_hwmp_rts.
		iw phy $phy set rts $rts # Sets the frame size above which RTS/CTS is used.

		# TXQ settings
		iw phy $phy set txq limit $limit # Caps the number of packets in the queue.
		iw phy $phy set txq quantum $quantum # Sets the Deficit Round Robin (DRR) scheduler quantum (bytes), balancing fairness for mesh traffic.

		# AQL settings - Airtime Queue Limit in microseconds
		echo 1 > /sys/kernel/debug/ieee80211/$phy/aql_enable
		echo $aql_threshold > /sys/kernel/debug/ieee80211/$phy/aql_threshold
	fi

	if [ -z "$1" ] || [ "$1" = "config" ]; then

		if [ "$detected_state" = "portal" ] || [ "$portal_detect" -eq 0 ]; then
			# this is a portal node, so override hwmp_rootmode and mesh_fwding
			rootmode=4
			mesh_fwding=1
		fi

		# Beacon Interval - This parameter, defined in the 802.11 standard, sets the time interval (in Time Units, typically 1 TU = 1024 µs) between beacon transmissions by mesh stations (STAs).
		# Beacons carry information used to update link quality metrics, which feed into the Airtime Link Metric (ALM) calculation.
		# A shorter beacon interval increases the frequency of link quality updates, allowing the ALM to reflect changes in the mobile environment more quickly.
		# However, it also increases control overhead.
		echo "set wireless.radio$phyindex.beacon_int='$beacon_int'" | uci batch

		# DTIM (Discovery Timeout) Period: Total timeout for path discovery attempts.
		# Set as a number of Beacon Intervals before a path discovery attempts are repeated.
		# The default is 2 beacon intervals.
		echo "set wireless.m11s$phyindex.dtim_period='$dtim_period'" | uci batch

		# Mesh Parameter config options
		echo "set mesh11sd.mesh_params.mesh_fwding='$mesh_fwding'" | uci batch
		echo "set mesh11sd.mesh_params.mesh_hwmp_rann_interval='$mesh_hwmp_rann_interval'" | uci batch
		echo "set mesh11sd.mesh_params.mesh_hwmp_preq_min_interval='$mesh_hwmp_preq_min_interval'" | uci batch
		echo "set mesh11sd.mesh_params.mesh_hwmp_active_path_timeout='$mesh_hwmp_active_path_timeout'" | uci batch
		echo "set mesh11sd.mesh_params.mesh_max_peer_links='$mesh_max_peer_links'" | uci batch
		echo "set mesh11sd.mesh_params.mesh_hwmp_max_preq_retries='$mesh_hwmp_max_preq_retries'" | uci batch
		echo "set mesh11sd.mesh_params.mesh_hwmp_rootmode='$rootmode'" | uci batch
		echo "set mesh11sd.mesh_params.mesh_rssi_threshold='$mesh_rssi_threshold'" | uci batch
	fi
}

check_mesh_hwmp_rootmode() {
	log_debug "Checking mesh_hwmp_rootmode"
	current_mesh_hwmp_rootmode=$(uci get mesh11sd.mesh_params.mesh_hwmp_rootmode)

	if [ "$portal_detect" -eq 0 ] || [ "$portal_detect" -eq 4 ] || [ "$detected_state" = "portal" ]; then

		if [ "$current_mesh_hwmp_rootmode" -ne 4 ]; then
			uci set mesh11sd.mesh_params.mesh_hwmp_rootmode='4'
		fi
	fi
}

check_path_changes() {
	# get list of interfaces
	get_mesh_iflist

	if [ -f "$tmpdir/devicemac" ]; then
		. $tmpdir/devicemac
	fi

	if [ -f "$tmpdir/is_portal" ]; then
		. $tmpdir/is_portal
	fi


	for iface in $iflist; do
		wait_for_interface "$iface"

		if [ "$ifstatus" = "down" ]; then
			continue
		fi

		# get list of mesh parameters for this interface
		get_params

		if [ -z "$params" ]; then
			# this is not a mesh interface
			continue
		else
			macrouting=$(iw dev $iface mpath dump 2>/dev/null | awk -F" " 'NR>1 {printf "%s/%s/%s/%s/%s/%s/%s ", $1, $2, $5, $11, $12, $4, $10}')

			if [ ! -z "$macrouting" ]; then
				for peer in $macrouting; do
					peermac=$(echo "$peer" | awk -F"/" '{printf "%s", $1}')
					peermac_id=$(echo -n "$peermac" | sha256sum | awk '{printf "%s", $1}')

					next_hop=$(echo "$peer" | awk -F"/" '{printf "%s", $2}')

					hop_count=$(echo "$peer" | awk -F"/" '{printf "%s", $4}')

					path_change_count=$(echo "$peer" | awk -F"/" '{printf "%s", $5}')

					metric=$(echo "$peer" | awk -F"/" '{printf "%s", $3}')

					sequence_number=$(echo "$peer" | awk -F"/" '{printf "%s", $6}')

					flags_bitmask=$(echo "$peer" | awk -F"/" '{printf "%s", $7}')


					if [ -f "$tmpdir/$peermac_id" ]; then
						. $tmpdir/$peermac_id
					else
						previous_path_change_count=1
					fi

					if [ "$previous_sequence_number" -lt "$sequence_number" ]; then
						path_updates=$(($sequence_number - $previous_sequence_number))
						elapsed_time_millis=$((($(date +%s) - $previous_time) * 1000))
						hwmp_update_period=$(($elapsed_time_millis/$path_updates))

					fi

					if [ "$path_change_count" -ne "$previous_path_change_count" ]; then

						if [ "$hop_count" -gt 1 ] && [ "$path_change_count" -gt 1 ]; then

							is_connected=$(iw dev $iface station dump | grep -q "$peermac"; echo -n $?)

							if [ "$is_connected" -eq 0 ]; then
								debugtype="debug"
								syslogmessage="Station [ $peermac ] is [ $hop_count ] hops away and [ $path_change_count ] path_change(s) for station have been detected"
								write_to_syslog

								debugtype="info"
								syslogmessage="Path to [ $peermac ], changed, [ $hop_count ] hop(s) via [ $next_hop ], HWMP Sequence Lifetime [ $hwmp_update_period ] ms"
								write_to_syslog

								# Reactive path stabilisation:

								if [ "$mesh_path_stabilisation" -eq 1 ] && [ "$detected_state" != "portal" ] ; then

									debugtype="debug"
									syslogmessage="Station [ $peermac ] has an incrementing path change count, consider its location or adjust txpower and/or rssi_threshold ....."
									write_to_syslog
									syslogmessage=" ..... or, if not already enabled, consider enabling mesh_leechmode on this node"
									write_to_syslog

									mrstloopcount=$(eval echo "\$node$peermac_id")
									debugtype="debug"
									syslogmessage="mrstloopcount [ $mrstloopcount ]"
									write_to_syslog

									if [ -z "$mrstloopcount"]; then
										mrstloopcount=0
									fi

									if [ "$mrstloopcount" -ge "$reactive_path_stabilisation_threshold" ]; then
										debugtype="notice"
										syslogmessage="Stabilsing path to node [ $peermac ]"
										write_to_syslog
										iw dev $iface station del $peermac
										eval "node$peermac_id"="0"

										if [ "$mesh_node_mobility_level" -eq 0 ]; then
											leechmode=1
											echo "leechmode=$leechmode" > "$tmpdir/leechmode"
										fi
									else
										debugtype="notice"
										syslogmessage="Mesh Reactive Stabilisation Threshold counter [ $mrstloopcount/$reactive_path_stabilisation_threshold ]"
										write_to_syslog
										mrstloopcount="$((mrstloopcount + 1))"
										eval "node$peermac_id"="$mrstloopcount"
									fi
								fi
							fi

						elif [ "$hop_count" -eq 1 ] && [ "$path_change_count" -gt 1 ]; then
							debugtype="debug"
							syslogmessage="Station [ $peermac ] is an immediate neighbour, but has had [ $path_change_count ] path_change(s) detected"
							write_to_syslog
							debugtype="info"
							syslogmessage="Path to [ $peermac ], changed, [ $hop_count ] hop(s) via [ $next_hop ], HWMP Sequence Lifetime [ $hwmp_update_period ] ms"
							write_to_syslog

							if [ "$mesh_path_stabilisation" -eq 1 ]; then
								debugtype="debug"
								syslogmessage="Station [ $peermac ], consider its location or adjust txpower and/or rssi_threshold"
								write_to_syslog

								if [ "$detected_state" != "portal" ]; then
									syslogmessage=" ..... or, if not already enabled, consider enabling mesh_leechmode on this node"
									write_to_syslog
								fi
							fi
						fi

					else
						debugtype="info"
						syslogmessage="Path to [ $peermac ], stable, [ $hop_count ] hop(s) via [ $next_hop ], HWMP Sequence Lifetime [ $hwmp_update_period ] ms"
						write_to_syslog
					fi

					echo "previous_path_change_count=$path_change_count; previous_sequence_number=$sequence_number; previous_time=$(date +%s)" > $tmpdir/$peermac_id
				done
			fi
		fi
	done
}

block_bridge_loops() {
	rule_status=0
	refresh_bridgemac
	table_ruleset=$(nft -a list table bridge mesh11s 2> /dev/null)

	if [ "$?" -gt 0 ]; then
		# Create the table
		nft add table bridge mesh11s
		# Create the chain
		nft add chain bridge mesh11s mesh_PRE { type filter hook prerouting priority -350\; }
	fi

	local bridgelist=$(brctl show | awk 'NF==4 {printf "%s ", $1}')

	for device in $bridgelist; do
		local devicemac=$(ip link show dev "$device" | grep "link/ether" | awk '{printf "%s", $2}')
		local mac_changed=0
		local br_iface
		local br_ifaces=$(brctl showstp "$device" | grep -B 1 -w "port id" | grep "(" | awk '{printf "%s ", $1}')

		get_mesh_iflist

		br_mac_list=""

		for br_iface in $br_ifaces; do
			rulecount=$(nft -a list table bridge mesh11s 2> /dev/null | grep -w -c "$br_iface" | awk '{printf "%s", $1}')

			if [ "$rulecount" -lt 2 ]; then
				rule_status=$(nft add rule bridge mesh11s mesh_PRE meta iifname { "\"$br_iface\"" } ether saddr "\"$devicemac\"" counter drop 2> /dev/null; echo -n $?)
				rule_status=$(nft add rule bridge mesh11s mesh_PRE meta iifname { "\"$br_iface\"" } counter accept 2> /dev/null; echo -n $?)
			fi
		done
	done

	check_rule_status
	refresh_bridgemac

	# we do not and need not restart the mesh here
}

check_rule_status() {

	if [ "$rule_status" -gt 0 ]; then
		debugtype="info"
		syslogmessage="-------------------------------------------------------------------------------------------------------------------------"
		write_to_syslog
		debugtype="info"
		syslogmessage="******* WARNING ******* Unable to add nftables rule(s) - error code [ $rule_status ]. Is the kmod-nft-bridge package installed?"
		write_to_syslog
		debugtype="info"
		syslogmessage="******* WARNING ******* A severe mesh bridge-loop storm is likely to occur if any non-mesh backhaul segments are used!"
		write_to_syslog
		debugtype="info"
		syslogmessage="-------------------------------------------------------------------------------------------------------------------------"
		write_to_syslog
	fi
}

check_if_peer() {
	ispeer=1
	eval $(echo "$ndpmac" | awk -F":" '{printf "p1=%s p2=%s p3=%s p4=%s p5=%s p6=%s", $1 ,$2, $3, $4, $5, $6}')
	local ndpmacid="$p2-$p3-$p5-$p6"

	for peermac in $peerlist; do
		eval $(echo "$peermac" | awk -F":" '{printf "p1=%s p2=%s p3=%s p4=%s p5=%s p6=%s", $1 ,$2, $3, $4, $5, $6}')
		local peermacid="$p2-$p3-$p5-$p6"

		if [ "$peermacid" = "$ndpmacid" ]; then
			ispeer=0
			break
		fi
	done
}

get_kernel_version() {
	if [ -e "/proc/version" ]; then
		kmajor=$(awk '{print $3}' /proc/version | awk -F"." '{printf "%s", $1}')
		kminor=$(awk '{print $3}' /proc/version | awk -F"." '{printf "%s", $2}')
		kpatch=$(awk '{print $3}' /proc/version | awk -F"." '{printf "%s", $3}')
		debugtype="debug"
		syslogmessage="Kernel version: Major [ $kmajor ], Minor [ $kminor ], Patch [ $kpatch ]"
		write_to_syslog
	else
		kmajor=0
		kminor=0
		kpatch=0
		debugtype="warn"
		syslogmessage="WARNING: Unable to determine kernel version"
		write_to_syslog
	fi
}

probe_interface_mac() {
	# Create a wireless probe interface
	wifi down
	get_wiphys
	dsa_mesh_maclist=""

	for wiphy in $wiphys; do
		iw phy "$wiphy" interface add probe type mesh mesh_id "probe"
		# Get the probe mac address
		meshmac=$(iw dev | grep -A 5 "Interface probe" | grep -w "addr" | awk '{printf "%s", $2}')
		dsa_mesh_maclist="$dsa_mesh_maclist $meshmac"
		# Delete the probe interface
		iw dev "probe" del
	done

	debugtype="debug"
	syslogmessage="dsa_mesh_maclist [ $dsa_mesh_maclist ]"
	write_to_syslog
}

check_mesh_gate_only() { # AKA leechmode

	if [ ! -f "$tmpdir/is_portal" ]; then
		return
	else
		. $tmpdir/is_portal
	fi

	# No leechmode for a portal
	if [ "$portal_detect" -eq 0 ] || [ "$detected_state" = "portal" ]; then
		return 0
	fi

	if [ -e "$tmpdir/leechmode" ]; then
		. "$tmpdir/leechmode" # gets current leechmode status
		current_mgo_option="$leechmode"
	else
		current_mgo_option=$(uci get mesh11sd.setup.mesh_leechmode_enable 2> /dev/null | awk '{printf "%s", $1}') # gets config value
	fi

	if [ -z "$current_mgo_option" ]; then
		current_mgo_option="$mesh_gate_only"
	else
		if [ "$current_mgo_option" -gt 0 ]; then
			debugtype="debug"
			syslogmessage="mesh_leechmode status [ $current_mgo_option ]"
			write_to_syslog
		fi
	fi

	if [ "$current_mgo_option" -ne "$mesh_gate_only" ]; then
		#option has changed

		if [ "$mesh_node_mobility_level" -gt 1  ]; then
			rootmode=3
		else
			rootmode=0
		fi

		if [ "$current_mgo_option" -eq 1 ]; then
			echo "set mesh11sd.mesh_params.mesh_fwding='0'" | uci batch
			echo "set mesh11sd.mesh_params.mesh_gate_announcements='0'" | uci batch
			echo "set mesh11sd.mesh_params.mesh_max_peer_links='2'" | uci batch

		elif [ "$current_mgo_option" -eq 0 ]; then
			echo "set mesh11sd.mesh_params.mesh_fwding='1'" | uci batch
			echo "set mesh11sd.mesh_params.mesh_max_peer_links='$config_max_peer_links'" | uci batch

			current_ctg_option=$(uci get mesh11sd.mesh_params.mesh_connected_to_gate 2> /dev/null | awk '{printf "%s", $1}')

			if [ "$current_ctg_option" -eq 1 ]; then
				echo "set mesh11sd.mesh_params.mesh_gate_announcements='1'" | uci batch
			fi
		fi

		mesh_gate_only=$current_mgo_option

	elif [ "$current_mgo_option" -eq 1 ]; then
		echo "set mesh11sd.mesh_params.mesh_fwding='0'" | uci batch
		echo "set mesh11sd.mesh_params.mesh_gate_announcements='0'" | uci batch
		echo "set mesh11sd.mesh_params.mesh_max_peer_links='2'" | uci batch
		leechmode=1
		echo "leechmode=$leechmode" > "$tmpdir/leechmode"

	elif [ "$current_mgo_option" -eq 0 ]; then
		leechmode=0
		echo "leechmode=$leechmode" > "$tmpdir/leechmode"

		if [ -e "$tmpdir/mesh_mobility" ]; then
			. "$tmpdir/mesh_mobility"
		else
			rootmode="$mesh_hwmp_rootmode"
		fi

		current_rootmode=$(uci get mesh11sd.mesh_params.mesh_hwmp_rootmode 2>/dev/null)

		echo "set mesh11sd.mesh_params.mesh_max_peer_links='$config_max_peer_links'" | uci batch
	fi
}

is_installed() {
	# Check if a package is installed
	package="$1"
	is_opkg=$(type "opkg" &> /dev/null; echo $?)
	is_apk=$(type "apk" &> /dev/null; echo $?)

	if [ -z "$package" ]; then
		exitcode=2
	elif [ "$is_opkg" -eq 0 ]; then
		matches=$(opkg list-installed | grep -w "$package"" ")
		urlencode "$matches"; encmatches="$urlencoded"
		urlencode "$package"; encpackage="$urlencoded"
		exitcode=$(echo "$encmatches" | grep -w -q "$encpackage"; echo $?)
	elif [ "$is_apk" -eq 0 ]; then
		exitcode=$(apk list -I "$package" 2>/dev/null  | grep -w -q "$package"; echo $?)
	else
		exitcode=127
	fi
}

check_package_list() {
	# Expects $package_list, a space separated list of package names to check
	# eg package_list="wpad-mbedtls wpad-wolfssl wpad-openssl"
	# returns 0 if any of the listed packages is installed
	# sets a variable-name equal to the package name (with hyphens translated to underscores) with value 0 if installed or 1 if not

	if [ -z "$package_list" ]; then
		return 127
	fi

	retval=1

	for package in $package_list; do
		# translate any hyphens to underscores
		pkg=$(echo "$package" | sed s/-/_/g)
		is_installed "$package"
		eval $pkg=$exitcode

		if [ "$exitcode" -eq 0 ]; then
			retval=0
		fi
	done

	return $retval
}

create_vxlan_if() {

	if [ "$portal_detect" -eq 3 ]; then
		device="br-wan"
	fi

	debugtype="debug"
	syslogmessage="create_vxlan_if [ vxlan$tun_id ], address [ $vxmac_indexed ], link_local [ $link_local_addr ], group [ ff02::$tun_id ], device [ $device ]"
	write_to_syslog

	if [ ! -f "$tmpdir/active_mesh_ifname" ]; then
		. $tmpdir/active_mesh_ifname
	fi

	ip -6 link add "vxlan$tun_id" \
		address "$vxmac_indexed" \
		type vxlan \
		id "$tun_id" \
		dstport 4789 \
		local "$link_local_addr" \
		group "ff02::$tun_id" \
		dev "$device" \
		ttl 5

	ip link set "vxlan$tun_id" up

	local retries=16

	for retry in $(seq $retries); do
		wait_for_interface "vxlan$tun_id"
		vxlanstate="%ifstatus"

		if [ "$ifstatus" = "down" ]; then
			ip link set "vxlan$tun_id" up
			sleep 1
			continue
		elif [ "$ifstatus" = "up" ]; then
			ip link set "$active_mesh_ifname" mtu 1600
			ip link set "$device" mtu 1600
			ip link set "vxlan$tun_id" mtu 1500
			break
		fi
	done

	# Set default dhcp settings
	if [ "$portal_detect" -ne 4 ]; then
		uci set dhcp.vtunlan.dhcpv4='server'
		uci set dhcp.vtunlan.dhcpv6='server'
		uci set dhcp.vtunlan.ra='server'
		uci set dhcp.vtunlan.ignore='0'
		echo "del_list dhcp.vtunlan.ra_flags='none'" | uci batch 2> /dev/null
		echo "del_list dhcp.vtunlan.ra_flags='managed-config'" | uci batch 2> /dev/null
		echo "del_list dhcp.vtunlan.ra_flags='other-config'" | uci batch 2> /dev/null
		echo "add_list dhcp.vtunlan.ra_flags='managed-config'" | uci batch 2> /dev/null
		echo "add_list dhcp.vtunlan.ra_flags='other-config'" | uci batch 2> /dev/null
		echo "add_list dhcp.vtunlan.ra_dns='$portal_ula_vtun'" | uci batch 2> /dev/null
		uci set dhcp.@dnsmasq[0].rebind_protection='1'
	else
		uci set dhcp.vtunlan.dhcpv4='disabled'
		uci set dhcp.vtunlan.dhcpv6='disabled'
		uci set dhcp.vtunlan.ra='disabled'
		uci set dhcp.vtunlan.ignore='1'

		echo "del_list dhcp.vtunlan.ra_flags='managed-config'" | uci batch 2> /dev/null
		echo "del_list dhcp.vtunlan.ra_flags='other-config'" | uci batch 2> /dev/null
		echo "del_list dhcp.vtunlan.ra_flags='none'" | uci batch 2> /dev/null
		echo "add_list dhcp.vtunlan.ra_flags='none'" | uci batch 2> /dev/null
	fi

	. $tmpdir/devicemac
}

check_vtunnel() {
	# Check if vxlan tunnel is required and set it up

	if [ ! -f "$tmpdir/active_mesh_ifname" ]; then
		return
	fi

	if [ ! -f "$tmpdir/is_portal" ]; then
		return
	else
		. $tmpdir/is_portal
	fi

	if [ "$vtun_enable" -eq 1 ]; then
		vxlan_status=$(ip link show vxlan$tun_id &> /dev/null; echo $?)

		if [ "$vxlan_status" -eq 0 ]; then
			vtunnel="configured"
		else
			vtunnel=""
			debugtype="debug"
			syslogmessage="check_vtunnel. Vxlan Status [ $vxlan_status ]"
			write_to_syslog
		fi

		if [ "$vxlan_status" -eq 1 ] && [ "$commit_all" -eq 0 ]; then
			package_list="ip-full vxlan"
			check_package_list

			if [ "$ip_full" -eq 0 ] && [ "$vxlan" -eq 0 ] && [ -z "$vtunnel" ] && [ -z "$vxlan_if" ]; then
				# we have ip-full and vxlan packages
				debugtype="debug"
				syslogmessage="vxtunnel configuration"
				write_to_syslog

				. $tmpdir/active_mesh_ifname
				. $tmpdir/devicemac

				vtunlan_dhcp="$(uci get dhcp.vtunlan)"

				if [ -z "$vtunlan_dhcp" ]; then
					uci set dhcp.vtunlan=dhcp
					uci set dhcp.vtunlan.interface='vtunlan'
					uci set dhcp.vtunlan.start='100'
					uci set dhcp.vtunlan.limit='150'
					uci set dhcp.vtunlan.leasetime='12h'
					uci set dhcp.vtunlan.ra_slaac='1'

					if [ "$commit_all" -eq 0 ]; then
						echo "del_list dhcp.vtunlan.ra_flags='managed-config'" | uci batch 2> /dev/null
						echo "del_list dhcp.vtunlan.ra_flags='other-config'" | uci batch 2> /dev/null
						echo "add_list dhcp.vtunlan.ra_flags='managed-config'" | uci batch 2> /dev/null
						echo "add_list dhcp.vtunlan.ra_flags='other-config'" | uci batch 2> /dev/null
					fi

					uci set dhcp.vtunlan.ignore='1'
					uci set dhcp.vtunlan.ra_default='2'

					uci set firewall.vtunlan='zone'
					uci set firewall.vtunlan.name='vtunlan'
					uci set firewall.vtunlan.network='vtunlan'
					uci set firewall.vtunlan.input='ACCEPT'
					uci set firewall.vtunlan.output='ACCEPT'
					uci set firewall.vtunlan.forward='ACCEPT'

					uci set firewall.dhcptun$tun_id=rule
					uci set firewall.dhcptun$tun_id.name='VTUN-DHCP-Renew'
					uci set firewall.dhcptun$tun_id.src='vtunlan'
					uci set firewall.dhcptun$tun_id.proto='udp'
					uci set firewall.dhcptun$tun_id.dest_port='67'
					uci set firewall.dhcptun$tun_id.target='ACCEPT'
					uci set firewall.dhcptun$tun_id.family='ipv4'

					uci set firewall.dnstun$tun_id=rule
					uci set firewall.dnstun$tun_id.name='VTUN-Allow-DNS'
					uci set firewall.dnstun$tun_id.src='vtunlan'
					uci set firewall.dnstun$tun_id.proto='udp tcp'
					uci set firewall.dnstun$tun_id.dest_port='53'
					uci set firewall.dnstun$tun_id.target='ACCEPT'
					uci set firewall.dnstun$tun_id.family='ipv4'

					uci set firewall.vxlan=rule
					uci set firewall.vxlan.name='Allow-vxlan'
					uci set firewall.vxlan.src='lan'
					uci set firewall.vxlan.proto='udp'
					uci set firewall.vxlan.dest_port='4789'
					uci set firewall.vxlan.target='ACCEPT'
					uci set firewall.vxlan_fwd=forwarding
					uci set firewall.vxlan_fwd.src='vtunlan'
					uci set firewall.vxlan_fwd.dest='wan'

					vtunnel="configured"
					/sbin/service network reload
					/sbin/service firewall restart
					convert_to_la "$devicemac"
					index=$(($index + 1))
					make_indexed_mac "$mac_la" "$index"
					vxmac_indexed="$mac_indexed"
					link_local_addr=$(ip addr show "$device" | grep "inet6 fe80" | awk '{printf "%s", $2}' | awk -F "/" '{printf "%s", $1}')

					if [ "$portal_detect" -eq 0 ]; then
						# this is a forced portal
						uci set dhcp.vtunlan.dhcpv4='server'
						uci set dhcp.vtunlan.dhcpv6='server'
						uci set dhcp.vtunlan.ra='server'
						uci set dhcp.vtunlan.ignore='0'
						echo "del_list dhcp.vtunlan.ra_flags='none'" | uci batch
						echo "del_list dhcp.vtunlan.ra_flags='managed-config'" | uci batch 2> /dev/null
						echo "del_list dhcp.vtunlan.ra_flags='other-config'" | uci batch 2> /dev/null
						echo "add_list dhcp.vtunlan.ra_flags='managed-config'" | uci batch 2> /dev/null
						echo "add_list dhcp.vtunlan.ra_flags='other-config'" | uci batch 2> /dev/null
						echo "add_list dhcp.vtunlan.ra_dns='$portal_ula_vtun'" | uci batch 2> /dev/null
						uci set dhcp.@dnsmasq[0].rebind_protection='1'
					fi
				fi
			fi

			. $tmpdir/devicemac

			if [ "$portal_detect" -eq 0 ]; then
				proto="static"
			elif [ "$portal_detect" -eq 1 ] && [ "$detected_state" = "portal" ]; then
				proto="static"
			else
				proto="dhcp"
			fi

			# Create the vtunnel bridge
			uci set network.vtunnel=device
			echo "set network.vtunnel.name='br-tun$tun_id'" | uci batch
			uci set network.vtunnel.type='bridge'
			uci set network.vtunnel.bridge_empty='1'
			echo "del_list network.vtunnel.ports='vxlan$tun_id'" | uci batch 2>/dev/null
			echo "add_list network.vtunnel.ports='vxlan$tun_id'" | uci batch
			uci set network.vtunlan=interface
			echo "set network.vtunlan.device='br-tun$tun_id'" | uci batch
			echo "set network.vtunlan.ipaddr='$vtun_ip'" | uci batch
			echo "set network.vtunlan.netmask='$vtun_mask'" | uci batch
			echo "set network.vtunlan.proto='$proto'" | uci batch
			uci set network.vtunlan.ip6assign='64'
			echo "set network.vtunlan.ip6hint='$ula_vxsubnet'" | uci batch
			echo "set network.vtunlan.ip6privacy='0'" | uci batch
			echo "set dhcp.vtunlan.ra_lifetime='3600'" | uci batch
			echo "set dhcp.vtunlan.ra_mininterval='20'" | uci batch
			echo "set dhcp.vtunlan.ra_maxinterval='60'" | uci batch

			if [ "$portal_detect" -eq 4 ] || [ "$portal_detect" -eq 5 ]; then
				# Disable wan and add the port that was wan to the vtunnel bridge
				echo "set network.wan.disabled='1'" | uci batch
				echo "set network.wan6.disabled='1'" | uci batch
				wanif=$(uci get network.wan.device)
				echo "del_list network.vtunnel.ports='$wanif'" | uci batch 2>/dev/null
				echo "add_list network.vtunnel.ports='$wanif'" | uci batch
			fi

			wifi down
			/sbin/service firewall restart
			/sbin/service network restart
			wait_for_interface "$device"
			vtunnel="configured"

			convert_to_la "$devicemac"
			index=$(($index + 1))
			make_indexed_mac "$mac_la" "$index"
			vxmac_indexed="$mac_indexed"
			link_local_addr="$(ip addr show "$device" | grep "inet6 fe80" | awk '{printf "%s", $2}' | awk -F "/" '{printf "%s", $1}')"

			if [ -z "$link_local_addr" ]; then
				return 1
			fi

			create_vxlan_if
			vxlan_if="created"

			if [ "$portal_detect" -ne 4 ] && [ "$commit_all" -eq 0 ]; then
				device="br-tun$tun_id"
				network_zone="vtunlan"
			else
				network_zone="$auto_mesh_network"
			fi

			echo 1 > /proc/sys/net/ipv4/conf/$device/proxy_arp
			/sbin/service dnsmasq reload

			vxiflist=$(uci show wireless | grep "network='$network_zone'" | awk -F "." '{printf "%s.%s ", $1, $2}')

			for vxif in $vxiflist; do
				vxifname=$(echo "get $vxif.ifname" | uci batch | awk '{printf "%s", $1}')
				brctl addif "$device" "$vxifname"
			done

		elif [ "$vxlan_status" -eq 1 ] && [ "$commit_all" -eq 1 ]; then
			. $tmpdir/devicemac
			convert_to_la "$devicemac"
			index=$(($index + 1))
			make_indexed_mac "$mac_la" "$index"
			vxmac_indexed="$mac_indexed"
			link_local_addr=$(ip addr show "$device" | grep "inet6 fe80" | awk '{printf "%s", $2}' | awk -F "/" '{printf "%s", $1}')

			if [ -z "$link_local_addr"]; then
				return 1
			fi

			create_vxlan_if
			vxlan_if="created"

			device="br-tun$tun_id"
			echo 1 > /proc/sys/net/ipv4/conf/$device/proxy_arp

			vxiflist=$(uci show wireless | grep "network='vtunlan'" | awk -F "." '{printf "%s.%s ", $1, $2}')

			for vxif in $vxiflist; do
				vxifname=$(echo "get $vxif.ifname" | uci batch | awk '{printf "%s", $1}')
				brctl addif "$device" "$vxifname"
			done

		fi

		if [ "$vxlan_status" -eq 0 ] && [ "$detected_state" != "portal" ]; then

			if [ "$portal_detect" -eq 1 ] || [ "$portal_detect" -eq 3 ] || [ "$portal_detect" -eq 4 ] || [ "$portal_detect" -eq 5 ]; then

				dhcpserver=$(uci get dhcp.vtunlan.dhcpv4)

				if [ "$dhcpserver" = "server" ]; then
					uci set dhcp.vtunlan.dhcpv4='disabled'
					uci set dhcp.vtunlan.dhcpv6='disabled'
					uci set dhcp.vtunlan.ra='disabled'
					uci set dhcp.vtunlan.ignore='1'
					echo "del_list dhcp.vtunlan.ra_flags='managed-config'" | uci batch 2> /dev/null
					echo "del_list dhcp.vtunlan.ra_flags='other-config'" | uci batch 2> /dev/null
					echo "add_list dhcp.vtunlan.ra_flags='none'" | uci batch
					echo "del_list dhcp.vtunlan.ra_dns='$portal_ula_vtun'" | uci batch 2> /dev/null
					uci set dhcp.@dnsmasq[0].rebind_protection='0'
					/sbin/service dnsmasq restart
					/sbin/service odhcpd restart
				fi
			fi
		fi

		device="br-tun$tun_id"
		set_iface_path_cost "$vtun_path_cost" "$device" "vxlan$tun_id"
		. $tmpdir/devicemac
	fi
}

check_dadfailed() {

	for br_if in "br-$auto_mesh_network br-tun$tun_id"; do
		dadfailed_list=$(ip addr show "$br_if" | grep "dadfailed" | awk '{printf "%s ", $2}')

		if [ ! -z "$dadfailed_list" ]; then
			mute=0
			debugtype="debug"
			syslogmessage="Fixing Global ULA Prefix DADfail (Duplicate Address Detection) failures: [ $dadfailed_list ] @ [ $br_if ]"
			write_to_syslog

			for dadfail in $dadfailed_list; do

				mute=0
				debugtype="debug"
				syslogmessage="Removing [ $dadfail ] dev [ $br_if ]"
				write_to_syslog

				ip addr del "$dadfail" dev "$br_if"
			done
		fi
	done
}

is_ipv4addr_valid() {
	checkip=$(echo -n "$1" | \
		awk '/^([0-9]{1,3}[.])([0-9]{1,3}[.]){2}([0-9]{1,3})$/{printf "%s", $1}' | \
		awk -F "." '($1+0<255) && ($2+0<255) && ($3+0<255) && ($4+0<255) {printf "%i.%i.%i.%i", $1, $2, $3, $4}')

	if [ "$1" = "$checkip" ]; then
		return 0
	else
		return 1
	fi
}

is_ipv4mask_valid() {
	checkmask=$(echo -n "$1" | \
		awk '/^([0-9]{1,3}[.])([0-9]{1,3}[.]){2}([0-9]{1,3})$/{printf "%s", $1}' | \
		awk -F "." '($1+0<256) && ($2+0<256) && ($3+0<256) && ($4+0<255) {printf "%i.%i.%i.%i", $1, $2, $3, $4}')

	if [ "$1" = "$checkmask" ]; then
		return 0
	else
		return 1
	fi
}

set_ula_prefix() {

	if [ -z "$1" ]; then
		return 1
	fi

	current_prefix="$(uci get network.globals.ula_prefix)"
	preconf_ula_prefix=$(cat /etc/config/network | grep "ula_prefix" | awk -F "'" '{printf "%s", $2}')

	if [ "$1" = "revert" ]; then
		ula_prefix=$preconf_ula_prefix

	elif [ "$1" = "set" ] || [ "$1" = "get" ]; then
		first=${auto_mesh_id:9:2}
		second=${auto_mesh_id:11:4}
		third=${auto_mesh_id:15:4}
		ula_subnet=${auto_mesh_id:20:4}           # ← fourth segment from hash becomes lan subnet/ip6hint value for odhcpd
		ula_vxsubnet=${auto_mesh_id:25:4}           # ← fourth segment from hash becomes vxlan subnet/ip6hint value for odhcpd
		ula_prefix="fd$first:$second:$third::/48"   # ← trailing ::
	else
		return 1
	fi

	if [ -z "$current_prefix" ] || [ "$current_prefix" != "$ula_prefix" ]; then

		if [ "$1" != "get" ]; then

			if [ "$portal_detect" -eq 1 ]; then

				if [ -f "$tmpdir/is_portal" ]; then
					. $tmpdir/is_portal
				fi

				if [ "$detected_state" = "peer" ]; then
					return 0
				fi
			fi

			if [ "$portal_detect" -eq 3 ] || [ "$portal_detect" -eq 5 ]; then
				return 0
			fi

			echo "set network.globals.ula_prefix='$ula_prefix'" | uci batch
			echo "set network.$auto_mesh_network.ip6hint='$ula_subnet'" | uci batch
		fi
	fi
}

get_portal_ula() {
	# Calculates portal ula from common mesh id etc and ping it 
	# If failed, tries vxlan tunnel
	# returns $portal_ula if found and $pingstatus (>0 if failed)
	set_ula_prefix "get"

	# Get base prefix without /48 (now 4 segments, no trailing ::)
	ula_base=$(echo "$ula_prefix" | awk -F"::/" '{print $1}')

	# Portal ULA = prefix + ::1 (standard gateway address)
	portal_ula="$ula_base:$ula_subnet::1"

	# VXLAN tunnel variant: insert :$ula_vxsubnet: after the prefix (before ::)
	portal_ula_vtun="$ula_base:$ula_vxsubnet::1"

	pingstatus=$(ping -q -W 10 -c 1 "$portal_ula" &>/dev/null ; echo $?)

	if [ "$pingstatus" -eq 1 ] && [ "$vtun_enable" -eq 1 ]; then
		# VXLAN tunnel variant: insert :$ula_vxsubnet: after the prefix (before ::)
		pingstatus=$(ping -q -W 1 -c 1 "$portal_ula_vtun" &>/dev/null ; echo $?)

		if [ "$pingstatus" -eq 0 ]; then
			portal_ula="$portal_ula_vtun"
		fi
	fi

	link_to_portal=$pingstatus
}

str_to_hex() {
	local input="$1"
	local i=0
	local char=""
	local hex=""
	local hexstr=""
	local inputlen=$((${#input}))

	while true; do
		char=${input:$i:1}

		if [ -z "$char" ]; then
			break
		fi

		hex=$(printf "%02x" "'$char")
		hexstr="$hexstr$hex"
		i=$((i+1))

		if [ "$i" -ge "$inputlen" ]; then
			break
		fi
	done

	echo -n "$hexstr" > "$tmpdir/apmond/hexencoded"
}

hex_to_str() {
	local input="$1"
	local i=0
	local hex=""
	local hexstr=""
	local inputlen=$((${#input}))

	is_hex "$input"
	retval=$?

	if [ "$retval" -ne 0 ]; then
		str=""
		return 1
	fi

	while true; do
		hex="\x${input:$i:2}"
		hexstr="$hexstr$hex"
		i=$((i+2))

		if [ "$i" -ge "$inputlen" ]; then
			break
		fi
	done

	str=$(printf "$hexstr")
}

is_hex() {
	local input="$1"
	# Check if input is empty

	if [ -z "$input" ]; then
		return 1
	fi

	# Check if input contains only valid hex characters (0-9, a-f, A-F)
	echo "$input" | grep -E '^[0-9a-fA-F]+$' >/dev/null

	if [ $? -ne 0 ]; then
		return 1
	fi
}

write_node_data() {
	node_id="$1"

	if [ -z "$node_id" ]; then
		mute=0
		log_warning "Empty node node hostname - rejecting attempted apmond server access"
		return 1
	fi

	data_raw="$2"

	node_type=$(echo "$node_id" | awk -F ";" '{printf "%s", $1}')

	if [ -z "$node_type" ]; then
		mute=0
		log_warning "Empty node type - rejecting attempted apmond server access"
		return 1
	fi

	# Check the user agent is valid for apmond
	if [ "$node_type" = "apmond" ]; then
		node_hostname=$(echo "$node_id" | awk -F ";" '{printf "%s", $2}')

		if [ -z "$node_hostname" ]; then
			mute=0
			log_warning "Empty node node hostname - rejecting attempted apmond server access"
			return 1
		fi

		if [ "${node_hostname:0:9}" = "meshnode-" ]; then
			node_mac=$(echo "$node_id" | awk -F ";" '{printf "%s", $3}' | awk -F ":" 'NF==6 {printf "%s%s%s%s%s%s%s", $1, $2, $3, $4, $5, $6}')
			chunkstatus=$(echo "$node_id" | awk -F ";" '{printf "%s", $4}')
			strindex=$(echo "$node_id" | awk -F ";" '{printf "%s", $5}')
		else
			mute=0
			log_warning "Bad node node hostname - rejecting attempted apmond server access"
			return 1
		fi

		if [ -z "$node_mac" ] || [ -z "$chunkstatus" ] || [ -z "$strindex" ]; then
			mute=0
			log_warning "Invalid parameters - rejecting attempted apmond server access"
			return 1
		fi
	else
		mute=0
		log_warning "Invalid node type - rejecting attempted apmond server access"
		return 1
	fi

	maxchunksize=16 # 16 is max size for hextest
	strindex=0
	chunkstr=""
	data_raw_len=$((${#data_raw}))
	data_checked=""
	cumulative_transfer=""

	if [ "$data_raw_len" -eq 0 ]; then
		return 0
	fi

	while true; do
		# get a chunk
		chunk=${data_raw:$strindex:$maxchunksize}
		hexchunksize=$(printf %x $((${#chunk})))
		chunksize=$((${#chunk}))

		if [ "$apmon_verbose_debug_enable" -eq 1 ]; then
			mute=0
			debugtype="debug"
			syslogmessage="apmond - collected chunked data [ $chunk ], type [$chunkstatus], from [$node_hostname] id [ $node_mac ]"
			write_to_syslog
		fi


		if [ -z "$chunk" ]; then
			mute=0
			debugtype="debug"
			syslogmessage="apmond - end of data reached [$node_hostname] id [ $node_mac ], [ "$strindex" ] bytes received"
			write_to_syslog
			break
		fi

		hextest=$(printf "%d" "0x$chunk" &>/dev/null; echo -n $?)

		if [ "$hextest" -eq 0 ]; then
			cumulative_transfer="$cumulative_transfer""$chunk"
			# Increment the pointer
			strindex=$((strindex+$maxchunksize))

			continue
		else
			status="$hextest_invalid_data"
			retval=127
			printf "$status, code [ $retval ]"
			return $retval
		fi
	done

	if [ ! -z "$node_mac" ]; then

		if [ "$node_type" = "apmond" ]; then

			if [ "$chunkstatus" = "begin" ]; then
				echo -n "$node_id $data_raw" > "$tmpdir/$node_type/ap/$node_mac"
				status="*begin"
				echo -n "$status"
				retval=0
			elif [ "$chunkstatus" = "chunk" ]; then
				echo -n "$data_raw" >> "$tmpdir/$node_type/ap/$node_mac"
				status="*chunk"
				echo -n "$status"
				retval=0
			elif [ "$chunkstatus" = "end" ]; then
				echo " $node_id" >> "$tmpdir/$node_type/ap/$node_mac"
				debugtype="info"
				mute=0
				syslogmessage="apmond data received from node [$node_hostname] id [ $node_mac ]"
				write_to_syslog
				status="*end"
				echo -n "$status"
				retval=0
			fi
		else
			status="nack"
			retval=1
		fi
	else
		status="nack"
		retval=127
	fi

	if [ "$retval" -gt 0 ]; then
		debugtype="warn"
		syslogmessage="WARNING! Invalid remote data format - rejecting..."
		mute=0
		write_to_syslog
	fi

	if [ "$status" = "nack" ]; then
		echo -n "ERROR 418: Invalid HTCPCP, Code [ $retval ] "
	fi

	return $retval
}

setup_apmon_cgi() {
	apmon_cgi_status=127

	if [ -e "$apmond_cgi_dir" ]; then
		cgiuhttp="$apmond_cgi_dir""apmon.cgi"
		cgitmp="$tmpdir/apmon.cgi"

		if [ -f "$cgiuhttp" ]; then
			apmon_cgi_status=$(apmon_cgi "$cgitmp"; echo -n $?)
			cgitmphash=$(sha256sum "$cgitmp" | awk '{printf "%s", $1}')
			cgihash=$(sha256sum "$cgiuhttp" | awk '{printf "%s", $1}')

			if [ "$cgitmphash" != "$cgihash" ]; then
				cp "$cgitmp" "$cgiuhttp"
			fi
		else
			apmon_cgi_status=$(apmon_cgi "$cgiuhttp"; echo -n $?)
		fi
	fi

	return $apmon_cgi_status
}

apmon_cgi() {
	local cgi=$1

	if [ -z "$cgi" ]; then
		return 127
	fi

	echo "#!/bin/sh" > "$cgi"
	echo "printf \"%s\n%s\n\n\" \"Content-type: text/html\"" >> "$cgi"
	echo "node_id=\"\$HTTP_USER_AGENT\"" >> "$cgi"
	echo "querystring=\"\$QUERY_STRING\"" >> "$cgi"
	echo "status=\$(mesh11sd write_node_data \"\$node_id\" \"\$querystring\")" >> "$cgi"
	echo "echo \"\$status\"" >> "$cgi"
	chmod 755 "$cgi"
	mkdir -p "$tmpdir/apmond"
	mkdir -p "$tmpdir/apmond/ap"
	return $?
}

restore_luci_cgi() {
	luci_cgi_status=127

	if [ -e "$apmond_cgi_dir" ]; then
		cgiuhttp="$apmond_cgi_dir""luci"
		cgioriginal="$apmond_cgi_dir""luci.original"

		if [ -f "$cgioriginal" ]; then
			cgiorghash=$(sha256sum "$cgioriginal" | awk '{printf "%s", $1}')
			cgihash=$(sha256sum "$cgiuhttp" | awk '{printf "%s", $1}')

			if [ "$cgiorghash" != "$cgihash" ]; then
				cp "$cgioriginal" "$cgiuhttp"
				chmod 755 "$cgiuhttp"
				luci_cgi_status=0
			else
				luci_cgi_status=1
			fi
		fi

	fi

	return $luci_cgi_status

}

replace_luci_cgi() {
	luci_cgi_status=127

	if [ -e "$apmond_cgi_dir" ]; then
		cgiuhttp="$apmond_cgi_dir""luci"
		cgitmp="$tmpdir/luci"

		if [ ! -f "$cgiuhttp.original" ]; then
			mv "$cgiuhttp" "$cgiuhttp.original"
			chmod 444 "$cgiuhttp.original"
		fi

		if [ -f "$cgiuhttp" ]; then
			luci_cgi_status=$(luci_cgi "$cgitmp"; echo -n $?)
			cgitmphash=$(sha256sum "$cgitmp" | awk '{printf "%s", $1}')
			cgihash=$(sha256sum "$cgiuhttp" | awk '{printf "%s", $1}')

			if [ "$cgitmphash" != "$cgihash" ]; then
				cp "$cgitmp" "$cgiuhttp"
			fi
		else
			luci_cgi_status=$(luci_cgi "$cgiuhttp"; echo -n $?)
		fi
	fi

	return $luci_cgi_status
}

luci_cgi() {
	local cgi=$1

	if [ -z "$cgi" ]; then
		return 127
	fi

	echo "#!/bin/sh" > "$cgi"
	echo "printf \"%s\n%s\n\n\" \"Content-type: text/html\"" >> "$cgi"
	echo -e "status=\$(mesh11sd -v)" >> "$cgi"
	echo -e "hostname=\$(uci get system.@system[0].hostname)" >> "$cgi"
	echo -e "portal_ula=\$(mesh11sd get_portal_ula)" >> "$cgi"
	echo -e "active_nodecount=\$(mesh11sd active_nodecount)" >> "$cgi"
	echo -e "current_time=\$(date)" >> "$cgi"
	echo -e "host=\"\$HTTP_HOST\"" >> "$cgi"
	echo -e "echo -e \"<!DOCTYPE html>\"" >> "$cgi"
	echo -e "echo -e \"<html>\"" >> "$cgi"
	echo -e "echo -e \"<head>\"" >> "$cgi"
	echo -e "echo -e \"<meta http-equiv=\"Cache-Control\" content=\"no-cache, no-store, must-revalidate\">\"" >> "$cgi"
	echo -e "echo -e \"<meta http-equiv=\\\"Pragma\\\" content=\\\"no-cache\\\">\"" >> "$cgi"
	echo -e "echo -e \"<meta http-equiv=\\\"Expires\\\" content=\\\"0\\\">\"" >> "$cgi"
	echo -e "echo -e \"<meta charset=\\\"utf-8\\\">\"" >> "$cgi"
	echo -e "echo -e \"<meta name=\\\"viewport\\\" content=\\\"width=device-width, initial-scale=1.0\\\">\"" >> "$cgi"
	echo -e "echo -e \"<title>\$hostname</title>\"" >> "$cgi"
	echo -e "echo -e \"<style>\"" >> "$cgi"
	echo -e "echo -e \"  body { font-family: Arial, sans-serif; background: #f8f9fa; color: #212529; margin: 20px; }\"" >> "$cgi"
	echo -e "echo -e \"  .warning { background: #fff3cd; color: #856404; border: 1px solid #ffeeba; padding: 15px; border-radius: 6px; margin: 20px 0; }\"" >> "$cgi"
	echo -e "echo -e \"  .warning strong { color: #c82333; }\"" >> "$cgi"
	echo -e "echo -e \"  hr { border: 0; height: 1px; background: #dee2e6; margin: 20px 0; }\"" >> "$cgi"
	echo -e "echo -e \"  @media (prefers-color-scheme: dark) { body { background: #212529; color: #dee2e6; } .warning { background: #664d03; color: #fff3cd; border-color: #856404; } }\"" >> "$cgi"
	echo -e "echo -e \"</style>\"" >> "$cgi"
	echo -e "echo -e \"</head>\"" >> "$cgi"
	echo -e "echo -e \"<body>\"" >> "$cgi"
	echo -e "echo \"<h2 style=\\\"margin-top: 0;\\\">Mesh11sd Node Status</h2><hr>\"" >> "$cgi"
	echo -e "echo \"<b>Hostname:</b> [\$hostname] <br><br> \"" >> "$cgi"
	echo -e "echo \"<b>Host ULA:</b> \$host <br><br> \"" >> "$cgi"
	echo -e "echo \"<b>Portal ULA:</b> [\$portal_ula] <br><br>\"" >> "$cgi"
	echo -e "echo \"<b>Active Node Count:</b> [\$active_nodecount] <br><br>\"" >> "$cgi"
	echo -e "echo \"<b>System Time:</b> [\$current_time] <br><br>\"" >> "$cgi"
	echo -e "echo \"<hr>\"" >> "$cgi"

	# WARNING BLOCK - placed at the end, before version
	echo -e "echo \"<div class=\\\"warning\\\">\"" >> "$cgi"
	echo -e "echo \"<strong>WARNING: LuCI is currently DISABLED</strong><br>\"" >> "$cgi"
	echo -e "echo \"LuCI does not understand Mesh11sd's autonomous dynamic Auto Configuration.<br><br>\"" >> "$cgi"
	echo -e "echo \"At your own risk, Mesh11sd's configuration changes can be persisted to flash/disk storage,<br>thus re-enabling LuCI.<br><br>\"" >> "$cgi"
	echo -e "echo \"Generally this is safe to do if the location and functionality of your mesh nodes<br>is unlikely to change from the current state.<br>This process can be reverted at any time.<br><br>\"" >> "$cgi"
	echo -e "echo \"<strong>Be warned</strong> that LuCI may well omit or incorrectly display many aspects of the network configuration.<br><br>\"" >> "$cgi"
	echo -e "echo \"See the documentation at:<br>\"" >> "$cgi"
	echo -e "echo \"<a href=\\\"https://github.com/openNDS/mesh11sd?tab=readme-ov-file#13-command-line-interface\\\">https://github.com/openNDS/mesh11sd?tab=readme-ov-file#13-command-line-interface</a>\"" >> "$cgi"
	echo -e "echo \"</div>\"" >> "$cgi"

	# Version
	echo -e "echo \"<br><small>\$status<br>$checksum</small><br><hr>\"" >> "$cgi"
	echo -e "echo \"</body>\"" >> "$cgi"
	echo -e "echo \"</html>\"" >> "$cgi"

	chmod 755 "$cgi"
}
luci_add_custom() {

	if [ "$auto_config" -ne 0 ]; then

		uci del luci.mesh11sd_typecode 2> /dev/null
		uci set luci.mesh11sd_typecode=command
		uci set luci.mesh11sd_typecode.name='Show Node Type code for this node'
		uci set luci.mesh11sd_typecode.command='mesh11sd get_node_type_code'

		uci del luci.mesh11sd_status 2> /dev/null
		uci set luci.mesh11sd_status=command
		uci set luci.mesh11sd_status.name='Status of This Meshnode'
		uci set luci.mesh11sd_status.command='mesh11sd status'

		uci del luci.mesh11sd_nodecount 2> /dev/null
		uci set luci.mesh11sd_nodecount=command
		uci set luci.mesh11sd_nodecount.name='Current Active Node Count'
		uci set luci.mesh11sd_nodecount.command='mesh11sd active_nodecount'

		uci del luci.mesh11sd_stations 2> /dev/null
		uci set luci.mesh11sd_stations=command
		uci set luci.mesh11sd_stations.name='MeshNodes 1 Hop Away'
		uci set luci.mesh11sd_stations.command='mesh11sd stations'

		uci del luci.mesh11sd_connect 2> /dev/null
		uci set luci.mesh11sd_connect=command
		uci set luci.mesh11sd_connect.name='MeshNode List'
		uci set luci.mesh11sd_connect.command='mesh11sd connect'

		uci del luci.mesh11sd_portal_ap_data 2> /dev/null
		uci set luci.mesh11sd_portal_ap_data=command
		uci set luci.mesh11sd_portal_ap_data.name='Show ALL Mesh Gate (AP) Data (only portal nodes collect data)'
		uci set luci.mesh11sd_portal_ap_data.command='mesh11sd show_ap_data all'

		uci del luci.mesh11sd_ap_data 2> /dev/null
		uci set luci.mesh11sd_ap_data=command
		uci set luci.mesh11sd_ap_data.name='Show Data For This Mesh Gate (AP)'
		uci set luci.mesh11sd_ap_data.command='mesh11sd get_ap_data'

		uci del luci.mesh11sd_get_portal_ula 2> /dev/null
		uci set luci.mesh11sd_get_portal_ula=command
		uci set luci.mesh11sd_get_portal_ula.name='Mesh Portal ULA'
		uci set luci.mesh11sd_get_portal_ula.command='mesh11sd get_portal_ula'

		uci del luci.mesh11sd_verbose_log 2> /dev/null
		uci set luci.mesh11sd_verbose_log=command
		uci set luci.mesh11sd_verbose_log.name='Enable verbose Mesh11sd logging'
		uci set luci.mesh11sd_verbose_log.command='mesh11sd debuglevel 3'

		uci del luci.mesh11sd_normal_log 2> /dev/null
		uci set luci.mesh11sd_normal_log=command
		uci set luci.mesh11sd_normal_log.name='Restore notification only Mesh11sd logging'
		uci set luci.mesh11sd_normal_log.command='mesh11sd debuglevel 1'

		uci del luci.mesh11sd_read_log 2> /dev/null
		uci set luci.mesh11sd_read_log=command
		uci set luci.mesh11sd_read_log.name='Read Mesh11sd Log'
		uci set luci.mesh11sd_read_log.command='mesh11sd read_log'

		uci del luci.mesh11sd_wifidetect 2> /dev/null
		uci set luci.mesh11sd_wifidetect=command
		uci set luci.mesh11sd_wifidetect.name='Detect Wireless Chipset Capabilities'
		uci set luci.mesh11sd_wifidetect.command='mesh11sd wifi_chipset_detect'

	fi
}

send_ap_data() {

	if [ "$apmond_enable" -eq 0 ]; then
		retcode=1
		return "$retcode"
	fi

	. $tmpdir/devicemac
	. /etc/factory_mac

	if [ "$portal_detect" -eq 3 ]; then
		ap_macaddr="$factory_mac"
	else
		ap_macaddr="$devicemac"
	fi

	if [ -z "$ap_macaddr" ]; then
		retcode=1
		return "$retcode"
	fi

	maxchunksize=2048
	strindex=0
	chunkstr=""
	hostname=$(uci get system.@system[0].hostname 2> /dev/null | awk '{printf "%s", $1}')
	str_data=$(get_ap_data)
	str_to_hex "$str_data"
	hexencoded=$(cat "$tmpdir/apmond/hexencoded")
	hexencoded_len=$((${#hexencoded}))

	# If no data, Spider to force NDP update
	if [ "$hexencoded_len" -eq 0 ]; then
		spider="-s"
	else
		spider=""
	fi

	mute=0
	debugtype="debug"
	syslogmessage="apmond sending chunked data"
	write_to_syslog

	get_portal_ula

	while true; do
		# get a chunk
		chunk=${hexencoded:$strindex:$maxchunksize}
		chunksize=$((${#chunk}))
		hexchunksize=$(printf %x $((${#chunk})))

		if [ -z "$chunk" ]; then

			if [ "$spider" = "-s" ]; then
				chunkstatus="spider"
			fi

			send_status=$(wget $spider -6 -O - -U "apmond;$hostname;$ap_macaddr;$chunkstatus;$strindex" --no-check-certificate https://[$portal_ula]/cgi-bin/apmon.cgi)
			retcode=$?
			send_status=$(echo "$send_status" | awk '{printf "%s", $1}')
			mute=0
			debugtype="debug"
			syslogmessage="apmond - send_ap_data - chunkstatus [ $chunkstatus ], chunksize [ $chunksize ], return code [ $retcode ]"
			write_to_syslog

			if [ "$apmon_verbose_debug_enable" -eq 1 ]; then
				debugtype="debug"
				syslogmessage="apmond - send_status [ $send_status ]"
				write_to_syslog
			fi

			break

		elif [ "$strindex" -eq 0 ]; then
			chunkstatus="begin"
		else
			chunkstatus="chunk"
		fi

		send_status=$(wget -6 -O - -U "apmond;$hostname;$ap_macaddr;$chunkstatus;$strindex" --no-check-certificate https://[$portal_ula]/cgi-bin/apmon.cgi?"$chunk")
		retcode=$?
		send_status=$(echo "$send_status" | awk '{printf "%s", $1}')
		mute=0
		debugtype="debug"
		syslogmessage="apmond - send_ap_data - chunkstatus [ $chunkstatus ], chunksize [ $chunksize ], return code [ $retcode ], send_status [ $send_status ], sent [ $strindex ] "
		write_to_syslog

		if [ "$apmon_verbose_debug_enable" -eq 1 ]; then
			debugtype="debug"
			syslogmessage="apmond - send_command [ wget -6 -O - -U \"apmond;$hostname;$ap_macaddr;$chunkstatus;$strindex\" --no-check-certificate https://[$portal_ula]/cgi-bin/apmon.cgi?\"$chunk\" ]"
			write_to_syslog
			syslogmessage="apmond - send_status [ $send_status ]"
			write_to_syslog
		fi

		if [ "$send_status" = "nack" ]; then
			break
		fi

		# Increment the pointer
		strindex=$((strindex+$maxchunksize))
	done

	mute=0
	debugtype="debug"

	if [ "$send_status" = "nack" ]; then
		syslogmessage="apmond return code [ $retcode ], send_status [ $send_status ], data rejected by portal"
		write_to_syslog
	fi

	return "$retcode"
}

usi(){
	ubus call system "$1" |
	sed 's/\t/  /g' |
	awk '{
		out = ""
		in_string = 0
		for (i = 1; i <= length($0); i++) {
			c = substr($0, i, 1)

			if (c == "\"") {
				in_string = !in_string
				out = out c
				continue
			}

			if (in_string) {
				out = out c
				continue
			}

			if (c ~ /[0-9tf]/) {
				match(substr($0, i), /^[0-9]+|[tf][a-z]+/)
				val = substr($0, i, RLENGTH)
				if (val ~ /^(true|false|null|[0-9]+)$/) {
					out = out "\"" val "\""
					i += RLENGTH - 1
					continue
				}
			}

			out = out c
		}
		print out
	}'
}

get_system_stats_json() {
	# Calculate all values first for clean, readable JSON output
	uptime_sec=$(cut -d' ' -f1 < /proc/uptime | cut -d. -f1)

	load1=$(cut -d' ' -f1 < /proc/loadavg)
	load5=$(cut -d' ' -f2 < /proc/loadavg)
	load15=$(cut -d' ' -f3 < /proc/loadavg)

	if grep -q MemAvailable /proc/meminfo; then
		free_ram=$(awk '/MemAvailable/ {print $2}' /proc/meminfo)
	else
		free_ram=$(awk '/MemFree/ {print $2}' /proc/meminfo)
	fi

	free_flash=$(df /overlay 2>/dev/null | tail -1 | awk '{print $4}')
	[ -z "$free_flash" ] && free_flash=$(df / | tail -1 | awk '{print $4}')

	# CPU temperature
	cpu_temp="not available"
	for zone in /sys/class/thermal/thermal_zone*; do
		[ -e "$zone" ] || continue
		type=$(cat "$zone/type" 2>/dev/null)
		case "$type" in
			*cpu*|*CPU*|*core*|*thermal*)
				temp_raw=$(cat "$zone/temp" 2>/dev/null)
				if [ -n "$temp_raw" ] && [ "$temp_raw" -eq "$temp_raw" ] 2>/dev/null; then
					cpu_temp=$(awk "BEGIN {printf \"%.1f\", $temp_raw / 1000}")
					break
				fi
				;;
		esac
	done

	# CPU frequency
	cpu_freq="none scaling"
	if [ -e /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq ]; then
		freq_khz=$(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq 2>/dev/null)
		if [ -n "$freq_khz" ] && [ "$freq_khz" -eq "$freq_khz" ] 2>/dev/null; then
			cpu_freq=$(awk "BEGIN {printf \"%d\", $freq_khz / 1000}")
		fi
	fi

	if [ -f "/etc/factory_mac" ]; then
		. /etc/factory_mac
	else
		factory_mac="unknown"
	fi

	get_node_type_code

	# Output JSON
	echo "    \"node_status\": {"
	echo "      \"mesh11sd_version\": \"$version\","
	echo "      \"mesh11sd_checksum\": \"$checksum\","
	echo "      \"node_type\": \"$node_type_code\","
	echo "      \"factory_mac\": \"$factory_mac\","
	echo "      \"mesh_bridge_mac\": \"$devicemac\","
	echo "      \"uptime_seconds\": $uptime_sec,"
	echo "      \"load_average_1min\": \"$load1\","
	echo "      \"load_average_5min\": \"$load5\","
	echo "      \"load_average_15min\": \"$load15\","
	echo "      \"free_ram_kb\": $free_ram,"
	echo "      \"free_flash_kb\": $free_flash,"
	echo "      \"cpu_temperature_c\": \"$cpu_temp\","
	echo "      \"cpu_frequency_mhz\": \"$cpu_freq\""
	echo "    },"
}

usi_full() {
	node="node@$devicemac"
	system="system@$devicemac"
	status="status@$devicemac"

	detect=$(cat "$tmpdir/wifidetect")
	strlen="$((${#detect}))"
	detect=${detect:0:strlen-12}
	echo "$detect"
	echo "    },"
	get_system_stats_json
	#usi info  | sed 's/^.'// | sed 's/^/     /' | sed -e '${/^$/d;}' -e '$d'   # same for info
}

get_ap_data() {
	aplist=$(uci show wireless | grep "mode='ap'" | awk -F "." '{printf "%s ", $2}')
	first_ap=1
	sta_flag=0
	clients="clients@$devicemac"

	local timeout=4

	# create a lock file
	for i in $(seq $timeout); do
		if [ ! -e $tmpdir/apmond/clients.loc ]; then
			touch $tmpdir/apmond/clients.loc
			break
		else
			sleep 1
		fi
	done

	# abort if unable to lock
	if [ "$i" -eq $timeout ]; then
		return 0
	fi

	# Output the clients at node_mac line for this node
	printf '    "%s": {\n' $clients > $tmpdir/apmond/clients

	# Scan around the aps on this node
	for ap in $aplist; do
		first_sta=1
		ap_ifname=$(uci get wireless."$ap".ifname)
		ap_ssid=$(uci get wireless."$ap".ssid)
		iw dev "$ap_ifname" station dump 2>/dev/null > $tmpdir/stations

		# if no stations, skip to next ap
		if [ ! -s "$tmpdir/stations" ]; then
			continue
		else
			sta_flag=1 # flag that we have some stations
		fi

		# Scan around stations on this ap
		cat /tmp/mesh11sd/stations | while read line; do

			if [ -z "$line" ]; then
				continue
			fi

			is_station_id=$(echo "$line" | grep "Station")

			if [ ! -z "$is_station_id" ]; then

				if [ "$first_sta" -eq 1 ]; then
					echo -e "      \"$ap_ssid@$ap_ifname\": {" >> $tmpdir/apmond/clients # this is the first sta on the list for this ap
				fi

				echo "$line" | awk '{printf "        \"%s\": {", $2}' >> $tmpdir/apmond/clients; echo >> $tmpdir/apmond/clients
			else
				ap_param_str=$(echo "$line" | tr -d \\t | tr -s " " "_" | awk -F ":" '{printf "\"%s\": \"%s\"", $1, $2}'; echo)
				ap_param=$(echo "$ap_param_str" | awk -F "\"" '{printf "%s", $2}')

				case $ap_param in
					"rx_bytes") echo	"          $ap_param_str," >> $tmpdir/apmond/clients;;
					"tx_bytes") echo	"          $ap_param_str," >> $tmpdir/apmond/clients;;
					"tx_retries") echo	"          $ap_param_str," >> $tmpdir/apmond/clients;;
					"signal_avg") echo	"          $ap_param_str," >> $tmpdir/apmond/clients;;
					"tx_bitrate") echo	"          $ap_param_str," >> $tmpdir/apmond/clients;;
					"rx_bitrate") echo	"          $ap_param_str," >> $tmpdir/apmond/clients;;
					"avg_ack_signal") echo	"          $ap_param_str," >> $tmpdir/apmond/clients;;
					"connected_time") echo	"          $ap_param_str," >> $tmpdir/apmond/clients;;
					"current_time") \
						ap_param_str=$(echo -n "$ap_param_str" | sed s/current_time/timestamp/g);\
						echo "          $ap_param_str," >> $tmpdir/apmond/clients;\
						ms_to_dt "$ap_param_str"
						echo "          \"date_time\":\"$date_time\"" >> $tmpdir/apmond/clients;\
						echo "        }," >> $tmpdir/apmond/clients\
					;;
				esac
			fi

			first_sta=0 # finished with the first sta
		done

		first_ap=0 # finished with the first ap
	done

	if [ "$sta_flag" -eq 1 ]; then
		sed -i '$ s/.$//' "$tmpdir/apmond/clients"
		echo -e "      }\n    }\n  }\n}" >> $tmpdir/apmond/clients
		usi_full
		cat $tmpdir/apmond/clients
	fi

	# remove the lock file
	rm $tmpdir/apmond/clients.loc
}

is_default() {
	configs="mesh11sd wireless dhcp network firewall system"

	for config in $configs; do

		if [ -f "/etc/config/$config""_mesh11sd_default" ]; then
			currenthash=$(sha256sum "/etc/config/$config" | awk '{printf "%s", $1}')
			defaulthash=$(sha256sum "/etc/config/$config""_mesh11sd_default" | awk '{printf "%s", $1}')

			if [ "$currenthash" = "$defaulthash" ]; then
				is_default="$is_default $config"
			fi

		else
			return 127
		fi

	done

	if [ "$configs" = "$is_default" ]; then
		all_default=1
	else
		all_default=0
	fi
}

set_led() {

	if [ "$mesh_backhaul_led" = "none" ]; then
		return 0
	fi

	if [ -z "$1" ]; then
		return 127
	fi

	led_action="$1"
	leds=$(ls "/sys/class/leds")

	#initialise leds
	if [ "$led_action" = "init" ]; then

		for led in $leds; do
			echo "default-on" > "/sys/class/leds/$led/trigger"
		done

		sleep 1

		for led in $leds; do
			echo "none" > "/sys/class/leds/$led/trigger"
		done

		return 0
	fi


	if [ "$led_action" = "off" ] || [ "$led_action" = "none" ]; then
		led_action="none"
	elif [ "$led_action" = "on" ] || [ "$led_action" = "default-on" ]; then
		led_action="default-on"
	elif [ "$led_action" = "beat" ] || [ "$led_action" = "heartbeat" ]; then
		led_action="heartbeat"
	fi

	# Check if backhaul led is specified in config
	if [ "$mesh_backhaul_led" != "auto" ] && [ -e "/sys/class/leds/$mesh_backhaul_led/" ]; then
		echo "$led_action" > "/sys/class/leds/$mesh_backhaul_led/trigger"
		return 0
	fi

	# Backhaul led is set (default) to auto - try to find a suitable led
	for led in $leds; do
		last_param=$(echo "$led" | awk -F":" '{printf "%s", $NF}')

		# Most common leds
		if [ "$last_param" = "mesh" ] || [ "$last_param" = "power" ] || [ "$last_param" = "pwr" ] || [ "$last_param" = "system" ] || [ "$last_param" = "status" ] || 	[ "$last_param" = "run" ]; then
			echo "$led_action" > "/sys/class/leds/$led/trigger"
			break

		# Fallback to wlan
		elif [ "$last_param" = "wlan" ]; then
			echo "$led_action" > "/sys/class/leds/$led/trigger"
			break

		# Some known edge cases
		elif [ "$led" = "green-2" ] || [ "$led" = "pwr" ] || [ "$led" = "system" ] || [ "$led" = "status" ] || [ "$led" = "run" ]; then
			echo "$led_action" > "/sys/class/leds/$led/trigger"
			break

		fi
	done
}

check_mesh_active_led() {

	if [ ! -f "$tmpdir/active_mesh_ifname" ]; then
		return
	fi

	. $tmpdir/active_mesh_ifname
	active_peers=$(iw dev $active_mesh_ifname mpath dump | grep -c -w $active_mesh_ifname)

	if [ "$active_peers" -gt 0 ]; then

		if [ -z "$heartbeatflag" ] || [ "$heartbeatflag" -eq 0 ]; then
			heartbeatflag=1
			set_led heartbeat
		fi
	else
		heartbeatflag=0
		set_led on
	fi
}

ms_to_dt() {
	local time_stamp="$1"
	local ms=$(echo "$time_stamp" | awk -F "\"" '{printf "%s", $4}')
	local secs=$(echo "$ms" | head -c 10)
	date_time="$(date -d "@$secs")"
	# translate any spaces to underscores
	date_time=$(echo ${date_time// /_})
}

set_iface_path_cost() {
	local path_cost="$1"
	local device="$2"
	local iface="$3"
	local other_path_cost=$(( 65535 - $path_cost))
	local current_path_cost=""

	if [ "$path_cost" -gt 0 ] && [ "$path_cost" -lt 65535 ]; then
		# Make sure STP is on
		stpstatus=$(cat "/sys/class/net/$device/bridge/stp_state")

		if [ -z "$stpstatus" ] || [ "$stpstatus" -eq 0 ]; then
			echo "1" > "/sys/class/net/$device/bridge/stp_state"
		fi

		br_ifaces=$(brctl showstp "$device" | grep -B 1 -w "port id" | grep "(" | awk '{printf "%s ", $1}')

		for br_iface in $br_ifaces; do
			current_path_cost=$(cat "/sys/class/net/$br_iface/brport/path_cost")

			if [ "$br_iface" = "$iface" ]; then

				if [ "$current_path_cost" -ne "$path_cost" ]; then
					echo "$path_cost" > "/sys/class/net/$br_iface/brport/path_cost"
				fi

				continue
			fi

			if [ "$current_path_cost" -ne "$other_path_cost" ]; then
				echo "$other_path_cost" > "/sys/class/net/$br_iface/brport/path_cost"
			fi
		done

	elif [ "$path_cost" -eq 0 ]; then
		echo "0" > "/sys/class/net/$device/bridge/stp_state"
	fi
}

generate_ipv4_base() {
	local seed="$1"

	for i in $(seq 9 1 64); do
		#choose start point of sequence away from end eg "9"
		cpipsubnet=$((0x$(printf "$seed" | sha256sum | awk '{printf "%s", $1}' | tail -c$i | head -c2)))

		#Check for max
		if [ $cpipsubnet -ge 255 ];then
			continue
		fi

		#Skip 168, 167, 1, 0
		skiplist="168 167 8 1 0"

		for skip in $skiplist; do

			if [ $cpipsubnet -eq $skip ];then
				cpipsubnet=0
				break
			fi
		done

		if [ $cpipsubnet -eq 0 ];then
			continue
		fi

		newipaddr="192.168.$cpipsubnet.1"

		# is newipaddr configured elsewhere?
		is_used=$(uci show network | grep -c "$newipaddr")

		if [ "$is_used" -gt 0 ]; then
			continue
		fi

		break
	done

	syslogmessage="Validated ipv4 address [ $newipaddr ] using seed value [ $seed ]"
	debugtype="debug"
	write_to_syslog
}

manage_opennds() {

	if [ "$manage_opennds_startup" -eq 1 ]; then
		# Is opennnds installed?
		is_installed "opennds"
		opennds_installed=$exitcode

		if [ "$opennds_installed" -eq 0 ]; then
			local action=$1
			# action  can be stop, start, restart

			if [ ! -z "$action" ]; then

				if [ "$action" = "stop" ]; then
					opennds_enabled=$(/sbin/service | grep "opennds" | grep "enabled" &> /dev/null ; echo $?)
					opennds_running=$(/sbin/service opennds info | grep -A2 "instance1" | grep "running" | grep "true" &> /dev/null ; echo $?)

					if [ "$opennds_enabled" -eq 0 ]; then
						syslogmessage="Disabling auto-startup of opennds"
						debugtype="debug"
						write_to_syslog
						/sbin/service opennds disable
					fi

					if [ "$opennds_running" -eq 0 ]; then
						syslogmessage="The opennds daemon is running, stopping it"
						debugtype="debug"
						write_to_syslog
						/sbin/service opennds stop
					fi
				fi

				if [ "$action" = "start" ] || [ "$action" = "restart" ]; then
						syslogmessage="Restarting the opennds daemon"
						debugtype="debug"
						write_to_syslog
						/sbin/service opennds restart
				fi
			fi
		fi
	fi
}

get_valid_channels() {
	valid_channels=$(iw list | grep "0 MHz \[" | grep -v "disabled" | grep -v "radar detection" | awk '{printf "%s ", $4}' | tr -d "[" | tr -d "]")
}

get_valid_channels_by_phyindex() {
	valid_channels=$(iw phy phy$phyindex channels | grep "*" | awk -F "[" '{printf "%s\n", $2'} | awk -F "]" '$2==" " {printf "%s%s", $1, $2}')
}


# Set and commit a single change, passed in $uci_set_string
commit_single_change() {
	local changes=$(uci changes mesh11sd)
	local changelist=$(echo "$changes" | awk '{printf "%s ", $1}')
	uci revert mesh11sd
	echo "$uci_set_string" | uci batch
	uci commit mesh11sd

	# Restore any uncommitted changes
	for change in $changelist; do
		echo "set $change" | uci batch
	done
}

# Debug shim for wifi-chipset-detect integration
write_debug() {
	echo > /dev/null
}

wifi_chipset_detect() {
	# Attempt to get the label mac
	ifaces="wan eth1 eth0"

	for iface in $ifaces; do
		labelmac=$(cat /sys/class/net/$iface/address 2>/dev/null)

		if [ -z "$labelmac" ]; then
			continue
		else
			write_debug "labelmac"
			labelmac="@$labelmac"
			break
		fi
	done

	. /etc/openwrt_release 2>/dev/null
	echo "{" > "$outputfile"
	echo "  \"System$labelmac\": {" >> "$outputfile"
	echo "    \"Distribution\": \"${DISTRIB_ID:-unknown}\"," >> "$outputfile"
	echo "    \"Release\": \"${DISTRIB_RELEASE:-unknown}\"," >> "$outputfile"
	echo "    \"Revision\": \"${DISTRIB_REVISION:-unknown}\"," >> "$outputfile"
	echo "    \"Target\": \"${DISTRIB_TARGET:-unknown}\"," >> "$outputfile"
	echo "    \"Architecture\": \"${DISTRIB_ARCH:-unknown}\"," >> "$outputfile"
	echo "    \"Description\": \"${DISTRIB_DESCRIPTION:-unknown}\"," >> "$outputfile"

	if [ -f /etc/board.json ]; then
		device=$(grep -m1 '"name":' /etc/board.json | sed 's/.*"name": *"\([^"]*\)".*/\1/' 2>/dev/null)
		if [ -n "$device" ] && ! echo "$device" | grep -Eqw "(WAN|LAN|ACT|LINK|DSL|WLAN|POWER|USB|WPS)"; then
			echo "    \"Device\": \"$device\"," >> "$outputfile"
		fi
	elif [ -f /tmp/sysinfo/model ]; then
		echo "    \"Device\": \"$(cat /tmp/sysinfo/model)\"," >> "$outputfile"
	elif [ -f /tmp/sysinfo/board_name ]; then
		echo "    \"Device\": \"$(cat /tmp/sysinfo/board_name)\"," >> "$outputfile"
	else
		echo "    \"Device\": \"unknown\"," >> "$outputfile"
	fi

	write_debug "device"

	echo "    \"phy\": {" >> "$outputfile"

	for dir in /sys/class/ieee80211/phy* /sys/class/ieee80211/wl*; do
		[ -d "$dir" ] || continue
		name="${dir##*/}"
		phyname="$name"
		write_debug "phyname"
		driver=$(awk -F= '/^DRIVER=/ {print $2; exit}' "$dir/device/uevent" 2>/dev/null || echo "n/a")
		default_driver="$driver"
		write_debug "default_driver"
		chipset=""

		# Band and 802.11 standards detection - robust for Wi-Fi 6E/7 MediaTek?
		phy_info=$(iw phy "$name" info 2>/dev/null)
		bands=""
		standards=""

		detected_something=0

		for band_num in 1 2 3 4; do
			if echo "$phy_info" | grep -q "Band ${band_num}:"; then
				next_band=$((band_num + 1))
				band_info=$(echo "$phy_info" | sed -n "/Band ${band_num}:/,/Band ${next_band}:/p")
				if echo "$band_info" | grep -q "Band ${next_band}:"; then
					band_info=$(echo "$band_info" | sed '$d')
				fi

				# Explicit frequencies (primary method)
				if echo "$band_info" | grep -qE "[* ]+24[0-9]{2}(\.0)? MHz"; then
					echo "$bands" | grep -qw "2.4GHz" || bands="${bands:+$bands }2.4GHz"
				fi
				if echo "$band_info" | grep -qE "[* ]+5[0-9]{3}(\.0)? MHz"; then
					echo "$bands" | grep -qw "5GHz" || bands="${bands:+$bands }5GHz"
				fi
				if echo "$band_info" | grep -qE "[* ]+[6-7][0-9]{3}(\.0)? MHz"; then
					echo "$bands" | grep -qw "6GHz" || bands="${bands:+$bands }6GHz"
				fi

				# Standards (strict)
				if echo "$band_info" | grep -qE "HT20|HT40|^\s*HT Capabilities"; then
					echo "$standards" | grep -qw "n" || standards="${standards:+$standards }n"
					detected_something=1
				fi
				if echo "$band_info" | grep -qE "^\s*VHT Capabilities| VHT RX MCS| VHT TX MCS|80MHz|160MHz"; then
					echo "$standards" | grep -qw "ac" || standards="${standards:+$standards }ac"
					detected_something=1
				fi
				if echo "$band_info" | grep -qE "HE Capabilities|HE MAC Capabilities|HE PHY Capabilities|^\s*HE Iftypes"; then
					echo "$standards" | grep -qw "ax" || standards="${standards:+$standards }ax"
					detected_something=1
				fi
				if echo "$band_info" | grep -qE "EHT Capabilities|EHT MAC|EHT PHY"; then
					echo "$standards" | grep -qw "be" || standards="${standards:+$standards }be"
					detected_something=1
				fi
			fi
		done

		per_band_detection_bands="$bands"
		write_debug "per_band_detection_bands"
		per_band_detection_standards="$standards"
		write_debug "per_band_detection_standards"

		# Improved fallback: prioritize explicit frequencies, infer conservatively
		has_2ghz=0; has_5ghz=0; has_6ghz=0
		echo "$bands" | grep -qw "2.4GHz" && has_2ghz=1
		echo "$bands" | grep -qw "5GHz" && has_5ghz=1
		echo "$bands" | grep -qw "6GHz" && has_6ghz=1

		# Explicit frequencies anywhere (robust even if not in Band sections)
		if ! [ "$has_6ghz" = 1 ] && echo "$phy_info" | grep -qE "[* ]+[6-7][0-9]{3}(\.0)? MHz"; then
			bands="${bands:+$bands }6GHz"
		fi

		# Inference only for true tri-band Wi-Fi 7 (EHT = be standard)
		if echo "$standards" | grep -qw "be" && [ "$has_2ghz" = 1 ]; then
			# Wi-Fi 7 almost always tri-band if 2.4GHz present
			[ "$has_5ghz" = 0 ] && bands="${bands:+$bands }5GHz"
			[ "$has_6ghz" = 0 ] && bands="${bands:+$bands }6GHz"
		fi

		# Gentle 5GHz inference for Wi-Fi 6 if wide channels mentioned
		if [ "$has_5ghz" = 0 ] && echo "$standards" | grep -qw "ax" && \
		   echo "$phy_info" | grep -qiE "HE.*(80MHz|160MHz|80+80)"; then
			bands="${bands:+$bands }5GHz"
		fi

		# Fallback standards ONLY if still empty
		if [ -z "$standards" ]; then
			if echo "$phy_info" | grep -qE "HT20|HT40|HT Capabilities"; then standards="n"; fi
			if echo "$phy_info" | grep -qE "VHT Capabilities|VHT RX MCS|VHT TX MCS"; then
				standards="${standards:+$standards }ac"
			fi
			if echo "$phy_info" | grep -qE "HE Capabilities|HE MAC Capabilities|HE PHY Capabilities|HE Iftypes"; then
				standards="${standards:+$standards }ax"
			fi
			if echo "$phy_info" | grep -qE "EHT Capabilities|EHT MAC|EHT PHY"; then
				standards="${standards:+$standards }be"
			fi

			fallback_standards="$standards"
			write_debug "fallback_standards"
		fi

		# Final standards: deduplicated union of per-band and fallback detection
		standards=$(echo "$per_band_detection_standards $standards" | tr ' ' '\n' | sort -u | tr '\n' ' ' | sed 's/^ //;s/ $//')
		[ -z "$standards" ] && standards="legacy"

		# Filter out invalid "ac" on pure 2.4 GHz PHYs (keep for dual-band)
		if echo "$bands" | grep -q "2.4GHz" && ! echo "$bands" | grep -q "5GHz"; then
			standards=$(echo "$standards" | sed 's/ac//g' | sed 's/  */ /g' | sed 's/^ //;s/ $//')
		fi

		# Final defaults
		[ -z "$bands" ] && bands="unknown"
		[ -z "$standards" ] && standards="legacy"

		write_debug "bands"
		write_debug "standards"

		# Derive friendly Wi-Fi generation
		wifigen="Legacy"
		if echo "$standards" | grep -qw "be"; then
			wifigen="Wi-Fi 7"
		elif echo "$standards" | grep -qw "ax"; then
			if echo "$bands" | grep -qw "6GHz"; then
				wifigen="Wi-Fi 6E"
			else
				wifigen="Wi-Fi 6"
			fi
		elif echo "$standards" | grep -qw "ac"; then
			wifigen="Wi-Fi 5"
		elif echo "$standards" | grep -qw "n"; then
			wifigen="Wi-Fi 4"
		fi

		write_debug "wifigen"

		# MIMO / Max spatial streams detection
		mimo="unknown"
		max_nss=0

		# Helper to update max NSS
		update_max_nss() {
			local nss="$1"
			[ -n "$nss" ] && [ "$nss" -gt "$max_nss" ] && max_nss="$nss"
		}

		# HT (802.11n) - MCS max index to NSS
		ht_mcs=$(echo "$phy_info" | grep "HT TX/RX MCS rate indexes supported" | head -1 | sed -n 's/.*: 0-\([0-9]\+\).*/\1/p')
		if [ -n "$ht_mcs" ]; then
			if [ "$ht_mcs" -le 7 ]; then update_max_nss 1
			elif [ "$ht_mcs" -le 15 ]; then update_max_nss 2
			elif [ "$ht_mcs" -le 23 ]; then update_max_nss 3
			elif [ "$ht_mcs" -le 31 ]; then update_max_nss 4
			elif [ "$ht_mcs" -le 39 ]; then update_max_nss 5
			elif [ "$ht_mcs" -le 47 ]; then update_max_nss 6
			elif [ "$ht_mcs" -le 55 ]; then update_max_nss 7
			elif [ "$ht_mcs" -le 63 ]; then update_max_nss 8
			fi
		fi

		# VHT / HE / EHT - highest supported streams (only match "MCS" lines, skip "not supported")
		for section in "VHT RX MCS" "HE RX MCS" "EHT RX MCS"; do
			if echo "$phy_info" | grep -q "$section"; then
				nss_list=$(echo "$phy_info" | grep -A 10 "$section" | \
						   grep -E "[1-8] streams: MCS" | \
						   sed 's/.*\([1-8]\) streams: MCS.*/\1/' | \
						   sort -nr | head -1)
				update_max_nss "$nss_list"
			fi
		done

		# Fallback: antenna bits if nothing else
		if [ "$max_nss" -eq 0 ] && echo "$phy_info" | grep -q "Available Antennas: TX 0x"; then
			ant_hex=$(echo "$phy_info" | grep "Available Antennas: TX 0x" | sed 's/.*TX 0x\([0-9a-fA-F]\+\).*/\1/' | tr 'A-F' 'a-f')
			ant_count=$(printf "%d" "0x$ant_hex" | awk '{popcount=0; n=$1; while(n>0){popcount += n%2; n=int(n/2)} print popcount}')
			update_max_nss "$ant_count"
		fi

		# Final MIMO string
		if [ "$max_nss" -ge 1 ]; then
			mimo="${max_nss}x${max_nss}"
		fi

		# Max channel width detection + ChannelWidthCap flag
		max_width="unknown"
		channel_width_cap=false

		# Normalize driver name
		driver_norm=$(echo "$driver" | tr -d '[:space:]' | tr 'A-Z' 'a-z')

		# 1. Practical cap for known over-reporting drivers (mt7615e family reports 160 MHz but unstable)
		if echo "$driver_norm" | grep -qE "mt7615e|mt7613|mt7663|mt76.*"; then
			if echo "$bands" | grep -q "5GHz"; then
				max_width="80 MHz"
				channel_width_cap=true
			fi
		fi

		# 2. If no cap was applied above, use normal detection
		if [ "$max_width" = "unknown" ]; then
			# Primary: radar detect widths — robust extraction
			radar_line=$(echo "$phy_info" | grep -i "radar detect widths:" | head -1)

			if [ -n "$radar_line" ]; then
				# Extract the highest supported width, including Wi-Fi 7 320 MHz
				largest=$(echo "$radar_line" | grep -oE "320|240|160|80\+80|80|40|20" | sort -nr | head -1)
				case "$largest" in
					320)     max_width="320 MHz" ;;
					240)     max_width="240 MHz" ;;
					160)     max_width="160 MHz" ;;
					"80+80") max_width="80+80 MHz" ;;
					80)      max_width="80 MHz" ;;
					40)      max_width="40 MHz" ;;
					20)      max_width="20 MHz" ;;
				esac
			fi
			# Fallback: explicit capability mentions (for when radar line is missing)
			if [ "$max_width" = "unknown" ]; then
				if echo "$phy_info" | grep -qiE "320 MHz|EHT.*320"; then
					max_width="320 MHz"
				elif echo "$phy_info" | grep -qiE "160 MHz|HE.*160|EHT.*160|VHT.*160"; then
					max_width="160 MHz"
				elif echo "$phy_info" | grep -qiE "80\+80|80 MHz|HE.*80|VHT.*80"; then
					max_width="80 MHz"
				elif echo "$phy_info" | grep -qiE "HT40|40 MHz"; then
					max_width="40 MHz"
				else
					max_width="20 MHz"
				fi
			fi

			# Wi-Fi 7 EHT 320 MHz detection (radar line may be conservative)
			if echo "$standards" | grep -q "be" && echo "$phy_info" | grep -qi "320MHz"; then
				max_width="320 MHz"
			fi

		fi

		# 3. Safety cap: limit pure 2.4 GHz PHYs to 40 MHz max
		if echo "$bands" | grep -q "2.4GHz" && ! echo "$bands" | grep -qE "5GHz|6GHz"; then
			if [ "$max_width" != "20 MHz" ] && [ "$max_width" != "40 MHz" ]; then
				max_width="40 MHz"
			fi
		fi

		# Last resort default
		[ "$max_width" = "unknown" ] && max_width="20 MHz"

		# Extended Wi-Fi features from "Supported extended features"
		txqs=false
		airtime_fairness=false
		aql=false

		extended_features=$(echo "$phy_info" | grep -A 20 "Supported extended features:" | grep -E "\[.*\]:.*")

		if echo "$extended_features" | grep -q "\[ TXQS \]"; then
			txqs=true
		fi

		if echo "$extended_features" | grep -q "\[ AIRTIME_FAIRNESS \]"; then
			airtime_fairness=true
		fi

		if echo "$extended_features" | grep -q "\[ AQL \]"; then
			aql=true
		fi

		# AQL runtime support check (ath11k exposes via debugfs even if not in "extended features")
		aql_runtime_support=false

		# Check if debugfs path for this phy exists and has aql_enable file
		debugfs_path="/sys/kernel/debug/ieee80211/$name"
		if [ -d "$debugfs_path" ] && [ -f "$debugfs_path/aql_enable" ]; then
			aql_runtime_support=true
		fi

		# Antenna count (TX/RX from mask)
		antennas="unknown"
		ant_line=$(echo "$phy_info" | grep "Available Antennas: TX 0x" | head -1)
		if [ -n "$ant_line" ]; then
			tx_mask=$(echo "$ant_line" | sed -n 's/.*TX 0x\([0-9a-fA-F]\+\).*/\1/p' | tr 'A-F' 'a-f')
			rx_mask=$(echo "$ant_line" | sed -n 's/.*RX 0x\([0-9a-fA-F]\+\).*/\1/p' | tr 'A-F' 'a-f')

			bitcount() { 
				local hex="$1"
				printf "%d" "0x$hex" 2>/dev/null | awk '{c=0; n=$1; while(n>0){c+=n%2; n=int(n/2)} print c}' || echo 0
			}

			tx_count=$(bitcount "$tx_mask")
			rx_count=$(bitcount "$rx_mask")

			if [ "$tx_count" -gt 0 ] && [ "$rx_count" -gt 0 ]; then
				antennas="${tx_count}x${rx_count}"
			fi
		fi

		# Fallback: if antenna mask missing (common on rt2800, some mt76), use MIMO value
		if [ "$antennas" = "unknown" ] && [ "$mimo" != "unknown" ]; then
			antennas="$mimo"
		fi

		# Theoretical max throughput per PHY (rough, Mbps) - reference only
		theoretical_mbps="unknown"

		# Base rate per stream at 40 MHz (realistic, no marketing inflation)
		base_per_stream=0
		if echo "$standards" | grep -q "be"; then
			base_per_stream=360    # Wi-Fi 7 EHT @ 40 MHz per stream (realistic, ~20% over Wi-Fi 6)
		elif echo "$standards" | grep -q "ax"; then
			base_per_stream=300    # Wi-Fi 6 HE @ 40 MHz per stream
		elif echo "$standards" | grep -q "ac"; then
			base_per_stream=217    # Wi-Fi 5 VHT @ 40 MHz per stream (~433 @ 80 MHz scaled down)
		elif echo "$standards" | grep -q "n"; then
			base_per_stream=150    # Wi-Fi 4 HT @ 40 MHz per stream
		fi

		# Width multiplier scaled by 100 (relative to 40 MHz = 1x)
		width_mult_scaled=100
		case "$max_width" in
			"320 MHz") width_mult_scaled=800 ;;
			"240 MHz") width_mult_scaled=600 ;;
			"160 MHz") width_mult_scaled=400 ;;
			"80 MHz")  width_mult_scaled=200 ;;
			"40 MHz")  width_mult_scaled=100 ;;
			"20 MHz")  width_mult_scaled=50 ;;
			*)         width_mult_scaled=100 ;;
		esac

		# Streams from MIMO (default 1)
		streams=1
		if echo "$mimo" | grep -qE "^[1-8]x[1-8]$"; then
			streams=$(echo "$mimo" | cut -d'x' -f1)
			[ "$streams" -eq "$streams" ] 2>/dev/null || streams=1
		fi

		# Calculate per-PHY theoretical throughput
		if [ "$base_per_stream" -gt 0 ]; then
			temp=$(( base_per_stream * streams * width_mult_scaled ))
			theoretical_mbps=$(( temp / 100 ))
		fi

		# AggregatedMaxMbps for Wi-Fi 7 MLO (realistic sum of per-band estimates)
		aggregated_mbps="unknown"
		if echo "$standards" | grep -q "be"; then
			# Per-band per-stream rates (consistent scaling from 40 MHz base)
			rate_24_per_stream=360   # 2.4 GHz @ 40 MHz
			rate_5_per_stream=1440   # 5 GHz @ 160 MHz (4x)
			rate_6_per_stream=2880   # 6 GHz @ 320 MHz (8x)

			total_agg=0
			if echo "$bands" | grep -q "2.4GHz"; then total_agg=$(( total_agg + rate_24_per_stream * streams )); fi
			if echo "$bands" | grep -q "5GHz"; then total_agg=$(( total_agg + rate_5_per_stream * streams )); fi
			if echo "$bands" | grep -q "6GHz"; then total_agg=$(( total_agg + rate_6_per_stream * streams )); fi

			if [ "$total_agg" -gt 0 ]; then
				aggregated_mbps="$total_agg"
			fi
		fi

		# 802.11s mesh support detection
		mesh="no"
		if echo "$phy_info" | grep -q "mesh point"; then
			mesh="yes"
		fi

		write_debug "mesh"

		chipset=$(cat "$dir/device/label" "$dir/device/name" \
		              "$dir/mac80211/label" "$dir/mac80211/name" 2>/dev/null | head -n1 | xargs)

		default_chipset_detect="$chipset"
		write_debug "default_chipset_detect"

		# Broadcom brcmfmac
		if [ -z "$chipset" ] && echo "$driver" | grep -q brcmfmac; then
			modalias=$(tr '\0' '\n' < "$dir/device/modalias" 2>/dev/null)
			broadcom_modalias="$modalias"
			write_debug "broadcom_modalias"
			fw=$(ls "$dir"/device/firmware/*/brcmfmac* 2>/dev/null | head -n1 | xargs basename 2>/dev/null || true)
			broadcom_fw="$fw"
			write_debug "broadcom_fw"

			case "$modalias:$fw" in
				*bcm4329*|*bcm4330*) chipset="Broadcom BCM4329/BCM4330 (Wi-Fi 4)" ;;
				*bcm43430*)          chipset="Broadcom BCM43430 (Wi-Fi 5)" ;;
				*bcm43455*)          chipset="Broadcom BCM43455 (Wi-Fi 5)" ;;
				*bcm4356*)           chipset="Broadcom BCM4356 (Wi-Fi 5)" ;;
				*bcm4378*)           chipset="Broadcom BCM4378 (Wi-Fi 6)" ;;
				*)                   chipset="Broadcom brcmfmac (Wi-Fi)" ;;
			esac

			broadcom_chipset="$chipset"
			write_debug "broadcom_chipset"
		fi

		# MediaTek Filogic / mt79xx family – now includes MT7996 Wi-Fi 7
		if [ -z "$chipset" ] && [ -f "$dir/device/modalias" ]; then
			modalias=$(tr '\0' '\n' < "$dir/device/modalias" 2>/dev/null)

			if echo "$modalias" | grep -iq 'mt79'; then
				filogic_modalias="$modalias"
				write_debug "filogic_modalias"

				if echo "$modalias" | grep -iq '7996'; then chipset="MediaTek MT7996 (Wi-Fi 7)"
				elif echo "$modalias" | grep -iq '798'; then chipset="MediaTek Filogic 830/810/880 (MT798x integrated WiFi)"
				elif echo "$modalias" | grep -iq '7916'; then chipset="MediaTek MT7916 (Wi-Fi 6E)"
				elif echo "$modalias" | grep -iq '7915'; then chipset="MediaTek MT7915E (Wi-Fi 6)"
				elif echo "$modalias" | grep -iq '792'; then chipset="MediaTek MT792x"
				fi
				filogic_chipset="$chipset"
				write_debug "filogic_chipset"
			fi

			if [ -z "$chipset" ] && [ "$driver" = "mt7996e" ]; then
				chipset="MediaTek MT7996 (Wi-Fi 7)"
				filogic_chipset="$chipset"
				write_debug "filogic_chipset"
			fi
		fi

		# MediaTek PCIe + USB
		if [ -z "$chipset" ] && echo "$driver" | grep -q -E 'mt7603e|mt76x2e|mt7915e|mt7615e|mt76x0u'; then
			mediatek_pcie_usb_driver="$driver"
			write_debug "mediatek_pcie_usb_driver"
			case "$driver" in
				mt7915e) chipset="MediaTek MT7915E (Wi-Fi 6)" ;;
				mt7615e) chipset="MediaTek MT7615E (Wi-Fi 5)" ;;
				mt7603e) chipset="MediaTek MT7603E (2.4 GHz)" ;;
				mt76x2e) chipset="MediaTek MT76x2E (5 GHz)" ;;
				mt76x0u) chipset="MediaTek MT7610U (Wi-Fi 5 USB) (mt76x0u)" ;;
			esac

			mediatek_pcie_usb_chipset="$chipset"
			write_debug "mediatek_pcie_usb_chipset"
		fi

		# MediaTek SoCs with mt76_wmac
		if [ -z "$chipset" ] && echo "$driver" | grep -q mt76_wmac; then
			mediatek_socs_with_mt76_wmac_driver="$driver"
			write_debug "mediatek_socs_with_mt76_wmac_driver"
			compat=$(grep -h '^OF_COMPATIBLE_' "$dir"/hwmon/hwmon*/uevent "$dir"/hwmon*/uevent 2>/dev/null | head -n1 | cut -d= -f2)
			mediatek_socs_with_mt76_wmac_compat="$compat"
			write_debug "mediatek_socs_with_mt76_wmac_compat"

			case "$compat" in
				*"mt7628"*) chipset="MediaTek MT7628AN (integrated 2.4 GHz)" ;;
				*"mt7621"*) chipset="MediaTek MT7621AN/AT (integrated 2.4 GHz)" ;;
				*"mt7622"*) chipset="MediaTek MT7622 (integrated 2.4/5 GHz)" ;;
				*)          chipset="" ;;
			esac

			if [ -z "$chipset" ]; then
				modalias=$(cat "$dir/device/modalias" 2>/dev/null)
				mediatek_socs_with_mt76_wmac_modalias="$modalias"
				write_debug "mediatek_socs_with_mt76_wmac_modalias"

				case "$modalias" in
					*"mt7628"*) chipset="MediaTek MT7628AN (integrated 2.4 GHz)" ;;
					*"mt7621"*) chipset="MediaTek MT7621AN/AT (integrated 2.4 GHz)" ;;
					*"mt7622"*) chipset="MediaTek MT7622 (integrated 2.4/5 GHz)" ;;
					*)          chipset="Legacy MediaTek mt76_wmac SoC" ;;
				esac
			fi

			mediatek_socs_with_mt76_wmac_chipset="$chipset"
			write_debug "mediatek_socs_with_mt76_wmac_chipset"
		fi

		# MediaTek Filogic 820/830 integrated (mt7622-wmac – eg. E8450 etc.)
		if [ -z "$chipset" ] && echo "$driver" | grep -q mt7622-wmac; then
			chipset="MediaTek MT7622 (integrated 2.4 GHz)"

			filogic_integrated_mt7622_wmac_chipset="$chipset"
			write_debug "filogic_integrated_mt7622_wmac_chipset"
		fi

		# MediaTek Ralink rt7620/rt2880
		if [ -z "$chipset" ] && [ "$driver" = "rt2800_wmac" ]; then
			chipset="MediaTek Ralink Embedded MT7620-RT2880 series(Wi-Fi 4)"
			ralink_rt7620_rt2880_chipset="$chipset"
			write_debug ralink_rt7620_rt2880_chipset
		fi

		# MediaTek Ralink rt2800pci / rt2800usb
		if [ -z "$chipset" ] && echo "$driver" | grep -q -E 'rt2800pci|rt2800usb'; then
			chipset="MediaTek Ralink RT5370/RT3070 (2.4 GHz, Wi-Fi 4)"
			ralink_rt2800pci_usb_chipset="$chipset"
			write_debug "ralink_rt2800pci_usb_chipset"
		fi

		# Realtek rtl8192cu
		if [ -z "$chipset" ] && echo "$driver" | grep -q rtl8192cu; then
			chipset="Realtek RTL8192CU (2.4/5 GHz, Wi-Fi 4)"
			realtek_rtl8192cu_chipset="$chipset"
			write_debug "realtek_rtl8192cu_chipset"
		fi

		# Marvell mwlwifi mwifiex_sdio
		if [ -z "$chipset" ] && echo "$driver" | grep -q -E 'mwlwifi|mwifiex_sdio'; then
			compat=$(grep -Eh 'PCI_ID|SDIO_ID' "$dir"/device/uevent 2>/dev/null | cut -d= -f2)
			marvell_mwlwifi_mwifiex_sdio_compat="$compat"
			write_debug "marvell_mwlwifi_mwifiex_sdio_compat"
			case "$compat" in
				"11AB:2A55") chipset="Marvell 88W8864 (2.4/5 GHz, Wi-Fi 5)" ;;
				"11AB:2B40") chipset="Marvell 88W8964 (2.4/5 GHz, Wi-Fi 5)" ;;
				"11AB:2B38") chipset="Marvell 88W8897 (2.4/5 GHz, Wi-Fi 5)" ;;
				"02DF:9135") chipset="Marvell 88W8887 (2.4/5 GHz, Wi-Fi 5)" ;;
				*) chipset="unknown Marvell Wi-Fi" ;;
			esac

			marvell_mwlwifi_mwifiex_sdio_chipset="$chipset"
			write_debug "marvell_mwlwifi_mwifiex_sdio_chipset"
		fi

		# Qualcomm Atheros – ath9k + ath10k + ath11k + ath12k (Wi-Fi 7)
		if [ -z "$chipset" ] && echo "$driver" | grep -q -E 'ath9k|ath10k|ath11k|ath12k'; then
			compat=$(grep -h '^OF_COMPATIBLE_' "$dir"/hwmon/hwmon*/uevent "$dir"/hwmon*/uevent 2>/dev/null | head -n1 | cut -d= -f2)

			if [ -z "$compat" ]; then
				compat=$(grep -h '^OF_COMPATIBLE_' "$dir"/device/uevent 2>/dev/null | head -n1 | cut -d= -f2)
			fi

			qualcomm_atheros_ath9k_ath10k_ath11k_compat="$compat"
			write_debug "qualcomm_atheros_ath9k_ath10k_ath11k_compat"

			modalias=$(cat "$dir/device/modalias" 2>/dev/null)
			qualcomm_atheros_ath9k_ath10k_ath11k_modalias="$modalias"
			write_debug "qualcomm_atheros_ath9k_ath10k_ath11k_modalias"

			case "$driver" in
				ath9k)
					case "$compat" in
						*"qca,qca9530-wmac"*) chipset="Qualcomm Atheros AR953x series (2.4 GHz, Wi-Fi 4)" ;;
						*)                    chipset="" ;;
					esac

					if [ -z "$chipset" ]; then
						case "$modalias" in
							*"953"*) chipset="Qualcomm Atheros QCA9531/QCA9533 (integrated 2.4 GHz)" ;;
							*"956"*) chipset="Qualcomm Atheros QCA9561/QCA9563 (integrated 2.4 GHz)" ;;
							*"9556"*) chipset="Qualcomm Atheros QCA9556 (integrated 2.4 GHz)" ;;
							*"9557"*) chipset="Qualcomm Atheros QCA9557 (integrated 2.4 GHz)" ;;
							*"9558"*) chipset="Qualcomm Atheros QCA9558 (integrated 2.4 GHz)" ;;
							*)        chipset="" ;;
						esac
					fi

					if [ -z "$chipset" ]; then
						chipset="Qualcomm Atheros AR93xx/AR92xx/AR5416 series"
					fi
				;;

				ath10k_ahb) chipset="Qualcomm Atheros QCA9886 (AHB)" ;;
				ath10k_pci) chipset="Qualcomm Atheros QCA9984/QCA988x/QCA986x series (PCI)" ;;
				ath11k)
					case "$compat" in
						*"ipq5018-wifi"*) chipset="Qualcomm IPQ5018 integrated 2.4 GHz" ;;
						*"qcn6122"*)      chipset="Qualcomm QCN6122 (Wi-Fi 6E)" ;;
						*"qcn9074"*)      chipset="Qualcomm QCN9074" ;;
						*"ipq6018-wifi"*) chipset="Qualcomm QCN9074" ;;
						*"ipq807"*)       chipset="Qualcomm QCN9074/QCN5024" ;;
						*)                chipset="Qualcomm QCN9074/QCN5024" ;;
					esac
					;;
				ath12k_pci|ath12k)
					chipset="Qualcomm FastConnect 7800 (WCN785x Wi-Fi 7)"
					;;
			esac

			qualcomm_atheros_ath9k_ath10k_ath11k_ath12k_chipset="$chipset"
			write_debug "qualcomm_atheros_ath9k_ath10k_ath11k_ath12k_chipset [ $chipset ]"
		fi

		[ -z "$chipset" ] && chipset="unknown"
		echo "      \"$name\": {" >> "$outputfile"
		echo "        \"Chipset\": \"$chipset\"," >> "$outputfile"
		echo "        \"Bands\": [" >> "$outputfile"

		for band in $bands; do
			echo "          \"$band\"," >> "$outputfile"
		done

		sed -i '$ s/.$//' "$outputfile"
		echo "        ]," >> "$outputfile"

		echo "        \"Standards\": [" >> "$outputfile"

		for standard in $standards; do
			echo "          \"802.11$standard\"," >> "$outputfile"
		done

		sed -i '$ s/.$//' "$outputfile"
		echo "        ]," >> "$outputfile"

		if [ "$mesh" = "yes" ]; then
			echo "        \"MeshPoint\": \"802.11s\"," >> "$outputfile"
		else
			echo "        \"MeshPoint\": \"N/A\"," >> "$outputfile"
		fi

		echo "        \"Wi-FiGeneration\": \"$wifigen\"," >> "$outputfile"
		echo "        \"MIMO\": \"$mimo\"," >> "$outputfile"
		echo "        \"MaxChannelWidth\": \"$max_width\"," >> "$outputfile"
		echo "        \"ChannelWidthCap\": $channel_width_cap," >> "$outputfile"
		echo "        \"Antennas\": \"$antennas\"," >> "$outputfile"
		echo "        \"TheoreticalMaxMbps\": \"$theoretical_mbps\"," >> "$outputfile"

		if echo "$standards" | grep -q "be"; then
			echo "        \"Aggregated_MLO_MaxMbps\": \"$aggregated_mbps\"," >> "$outputfile"
		fi

		echo "        \"TXQS\": $txqs," >> "$outputfile"
		echo "        \"AIRTIME_FAIRNESS\": $airtime_fairness," >> "$outputfile"
		echo "        \"AQL_Extended\": $aql," >> "$outputfile"
		echo "        \"AQL_Runtime\": $aql_runtime_support," >> "$outputfile"
		echo "        \"Driver\": \"$driver\"" >> "$outputfile"
		echo "      }," >> "$outputfile"
	done

	sed -i '$ s/.$//' "$outputfile"
	echo "    }" >> "$outputfile"
	echo "  }" >> "$outputfile"
	echo "}" >> "$outputfile"
}

fix_dropbear() {
	local fixed=$(grep -q -w "STOP=10" /etc/init.d/dropbear; echo $?)

	if [ "$fixed" -eq 1 ]; then
		sed -i '/^[[:space:]]*STOP=/c\STOP=10' /etc/init.d/dropbear
		/sbin/service dropbear restart
	fi
}

# Wrappers for write_to_syslog
log_warning() {
	syslogmessage="$1"
	debugtype="warn"
	write_to_syslog
}

log_error() {
	syslogmessage="$1"
	debugtype="err"
	write_to_syslog
}

log_notice() {
	syslogmessage="$1"
	debugtype="notice"
	write_to_syslog
}

log_info() {
	syslogmessage="$1"
	debugtype="info"
	write_to_syslog
}

log_debug() {
	syslogmessage="$1"
	debugtype="debug"
	write_to_syslog
}

get_node_type_code() {

	if [ -f "$tmpdir/is_portal" ]; then
		. $tmpdir/is_portal
	else
		return 1
	fi

	case "$portal_detect" in
		0) node_type_code="MRP" ;;	# Mesh Routed Portal
		1) 

			case "$detected_state" in
				"portal")	node_type_code="MRP" ;;
				"peer")		node_type_code="MPE" ;;	# Mesh PEer
				*)		node_type_code="N/A" ;;
			esac
		;;
		3) node_type_code="CPE" ;;	# Customer (or Client) PEer
		4) node_type_code="MBP" ;;	# Mesh Bridge Portal
		5) node_type_code="TPE" ;;	# Tunnel PEer
		*) node_type_code="N/A" ;;	# Not Available
	esac

	return 0
}

##############
# Start point
##############

entrynum=0

if [ -f "/var/run/mesh11sd.pid" ]; then
	mesh11sdpid=$(cat /var/run/mesh11sd.pid)
else
	mesh11sdpid=$(pgrep -f "/bin/sh /usr/sbin/mesh11sd")
fi

get_current_setup 2> /dev/null

if [ -z "$1" ] || [ "$1" = "-h" ] || [ $1 = "--help" ] || [ $1 = "help" ]; then
	echo "
  Usage: mesh11sd [option] [argument...]]

  Option: -h --help help
  Returns: This help information

  Option: -v --version version
  Returns: The mesh11sd version

  Option: debuglevel
  Argument: 0 - silent, 1 - notice, 2 - info, 3 - debug
  Returns: The mesh11sd debug level

  Option: enable
  Returns: \"1\" and exit code 0 if successful, exit code 1 if was already enabled

  Option: disable
  Returns: \"0\" and exit code 0 if successful, exit code 1 if was already disabled

  Option: auto_config
  Configure auto config mode
  Usage:
    mesh11sd auto_config test | enable | disable
      Takes immediate effect, any current connection is likely to be lost, requiring re-connection on possibly a different ipv4 address 
      test - turn on auto config test mode. A reboot will revert the auto configuration.
      enable - enable auto config mode.
      disable - disable auto config mode.

  Option: status
  Returns: the mesh status in json format

  Option: connect
  Connect a remote terminal session on a remote meshnode
  Usage: mesh11sd connect [remote_meshnode_macaddress]
    If the remote meshnode mac address is omitted, a list of meshnode mac addresses available for connection is listed

  Option: copy
  Copy a file to $tmpdir/ on a remote meshnode
  Usage: mesh11sd copy [remote_meshnode_macaddress] [path_of_source_file]
    If the remote meshnode mac address is null, or both arguments are omitted, a list of meshnode mac addresses available for copy is listed

  Option: txpower
  Change the mesh transmit power
  Usage: mesh11sd txpower [+|-]
    where \"+\" increments by 3dBm and \"-\" decrements by 3dBm
    Takes effect immediately

  Option: stations
  List all mesh peer stations directly connected to this mesh peer station (one hop)
  Usage: mesh11sd stations

  Option: mesh_rssi_threshold
  Change the mesh rssi threshold
  Usage: mesh11sd mesh_rssi_threshold [+|-] [force]
    where \"+\" increments by 3dBm and \"-\" decrements by 3dBm
    Takes effect immediately on NEW connections
    The keyword \"force\" forces the new threshold on all connected peers
    Warning - \"force\" will briefly remove this node from the mesh network, taking a few seconds to rejoin

  Option: mesh_leechmode
  Usage: mesh11sd mesh_leechmode [enable/disable]
  If enabled, the mesh node connects to the mesh backhaul but does not forward packets or contribute to the mesh.

  Option: mesh_node_mobility_level
  Usage: mesh11sd mesh_node_mobility_level [level]
  Sets the mesh node mobility level
    Supported levels are 0, 1, 2, 3 and 4
    Level 0 - not recommended for normal use - node must be stationary and carefully positioned
    Level 1 - Enables mesh_hwmp_rts for on air collision avoidance, enables transmit queue and aql_threshold to minimise latency, enables rapid path convergence
    Level 1 supports inter node relative velocities up to 1.5 metres per second
    Levels 2 to 4 support progressively higher relative inter node velocities at the expense of a larger and larger backhaul overhead

  Option: commit_changes
  Usage: mesh11sd commit_changes commit
    Commits changes to mesh_leechmode, txpower and rssi_threshold to non volatile configuration (make permanent)

  Option: commit_all
  Usage: mesh11sd commit_all commit
    Writes all changes made by auto_config to configuration file
    Active only when auto_config is enabled

  Option: revert_all
  Usage: mesh11sd revert_all revert
    Reverts all changes made by commit_all
    Active only when auto_config is enabled

  Option: force_ipv4_download
  Usage: mesh11sd force_ipv4_download
    Forces opkg or apk to use ipv4 for its downloads

  Option: download_revert_to_default
  Usage: mesh11sd download_revert_to_default
    Reverts opkg or apk to default for its downloads

  Option: dhcp4_renew
  Usage: mesh11sd dhcp4_renew
    Renews the current dhcp4 lease

  Option: is_installed
  Usage: mesh11sd is_installed [packagename]
    Checks if \"packagename\" is installed
    Returns the installation status of the package and exit code 0 if installed

  Option: get_portal_ula
  Usage: mesh11sd get_portal_ula
    Gets the portal unique local ipv6 address (ula)

  Option: set_ula_prefix
  Usage: mesh11sd set_ula_prefix [get|set|revert]
    get - gets the current ula prefix
    set - sets the ula prefix for the mesh backhaul based on the mesh id
    revert - reverts a previously set ula prefix

  Option: str_to_hex
  Usage: mesh11sd str_to_hex [access_point_data_string]
    Run length hex encodes access_point_data from apmond format

  Option: hex_to_str
  Usage: mesh11sd hex_to_str [hex encoded_access_point_data_string]
    Decode run length hex encoded access_point_data to apmond format

  Option: is_hex
  Usage: mesh11sd is_hex [string]
    Check if string contains only valid hex characters (0-9, a-f, A-F)
    Return code 0 if string is valid hex, 1 otherwise.

  Option: is_ipv4addr_valid [string]
    Check if string contains a single ipv4 address
    Return code 0 if string is valid ipv4 address, 1 otherwise.

  Option: write_node_data
  Usage: mesh11sd write_node_data [node_id] [querystring]
    Called from apmond cgi script. Writes node data received by web server

  Option: send_ap_data
  Usage: mesh11sd send_ap_data
    Sends hex encoded apmon data to portal/apmond web server

  Option: get_ap_data
  Usage: mesh11sd get_ap_data
    Get json format access point data for this node

  Option: show_ap_data
  Usage: mesh11sd show_ap_data [node_id | all]
    Shows json format access point data for requested node | all nodes
    Valid on portal/apmond node

  Option: country
  Usage: mesh11sd country [country_code]
    Shows the regulatory domain and country code if the optional country code is not supplied.
    Sets the regulatory domain for the country code if it is supplied.
    A restart or reboot is required for changes to take effect.

  Option: write_to_syslog
  Usage: mesh11sd write_to_syslog [string to log] [debuglevel (debug, info, warn, notice, err or emerg)]
    Writes to the syslog at the specified debuglevel

  Option: write_log
  Usage: mesh11sd write_log [string to log] [debuglevel (debug, info, warn, notice, err or emerg)]
    Writes to the mesh11sd log at the specified debuglevel

  Option: read_log
  Usage: mesh11sd read_log [ -f ]
    Displays the mesh11sd log
      If -f is specified, the existing log will be deleted and all subsequent logs will be followed on the display

  Option: get_valid_channels
  Usage: mesh11sd get_valid_channels
    Returns a list of valid wireless channels for the current country code setting
    Note: DFS channels are not suitable for use in a mesh backhaul so are excluded.

  For further documentation, see: https://github.com/openNDS/mesh11sd#readme

"

elif [ "$1" = "-v" ] || [ $1 = "--version" ] || [ $1 = "version" ]; then
	echo "mesh11sd version $version"

elif [ "$1" = "debuglevel" ]; then

	if [ -z "$2" ];then
		echo "$debuglevel"
	else

		if [ "$2" -ge 0 ] && [ "$2" -le 3 ]; then
			ucibatch="set mesh11sd.setup.debuglevel='$2'"
			echo "$ucibatch" | uci batch
			echo "$2"
		else
			echo "Invalid debuglevel requested"
		fi
	fi

elif [ "$1" = "enable" ]; then

	if [ "$enabled" -eq 1 ]; then
		echo "1"
		exit 1
	else
		ucibatch="set mesh11sd.setup.enabled='1'"
		echo "$ucibatch" | uci batch
		echo "1"
		exit 0
	fi

elif [ "$1" = "disable" ]; then

	if [ "$enabled" -eq 0 ]; then
		echo "0"
		exit 1
	else
		ucibatch="set mesh11sd.setup.enabled='0'"
		echo "$ucibatch" | uci batch
		echo "0"
		exit 0
	fi

elif [ "$1" = "auto_config" ]; then

	if [ -z "$2" ]; then
		echo
		echo "Usage:"
		echo "mesh11sd auto_config test - turn on auto config test mode "
		echo "mesh11sd auto_config enable - configure auto config mode "
		echo "mesh11sd auto_config disable - disable auto config mode "
		echo
		exit 1
	fi

	if [ "$auto_config" -ne 0 ] && [ "$2" = "test" ];  then
		echo "auto_config is already enabled"
		exit 0
	fi

	if [ "$auto_config" -ne 0 ] && [ "$2" = "enable" ];  then
		echo "auto_config is already enabled"
		exit 0
	fi

	if [ "$auto_config" -eq 0 ] && [ "$2" = "disable" ];  then
		echo "auto_config is already disabled"
		exit 0
	fi

	if [ -e "$tmpdir/auto_config_test" ] && [ "$auto_config" -eq 0 ] && [ "$2" != "enable" ]; then
		echo "Auto config is already running in test mode!"
		exit 1
	fi

	if [ "$2" = "test" ]; then

		if [ -e "$tmpdir/auto_config_test" ]; then
			echo "Auto config test mode is already running!"
			exit 1
		fi

		echo
		echo "This will test autoconfiguration."
		echo "Rebooting will revert the test autoconfiguration."
		echo
		echo "    Do you want to continue? [yes/no] "
		echo

		read reply

		if [ "$reply" = "yes" ]; then
			touch "$tmpdir/auto_config_test"
		fi

		exit 0

	elif [ "$2" = "enable" ]; then
		echo
		echo "This will enable and start autoconfiguration."
		echo "Autoconfiguration will start immediately and again on every restart."
		echo
		echo "    Do you want to continue? [yes/no] "
		echo

		read reply

		if [ "$reply" = "yes" ]; then
			touch "$tmpdir/auto_config_test"
			changes=$(uci changes mesh11sd)
			changelist=$(echo "$changes" | awk '{printf "%s ", $1}')

			uci revert mesh11sd
			uci set mesh11sd.setup.auto_config='1'
			uci commit mesh11sd

			for change in $changes; do
				echo "set $change" | uci batch
			done
		fi

	elif [ "$2" = "disable" ]; then
		echo
		echo "This will disable and stop autoconfiguration."
		echo "You must restart the device for this to take effect."
		echo
		echo "    Do you want to continue? [yes/no] "
		echo

		read reply

		if [ "$reply" = "yes" ]; then

			if [ -e "$tmpdir/auto_config_test" ]; then
				rm "$tmpdir/auto_config_test 2> /dev/null"
			fi

			changes=$(uci changes mesh11sd)
			changelist=$(echo "$changes" | awk '{printf "%s ", $1}')

			uci revert mesh11sd
			uci set mesh11sd.setup.auto_config='0'
			uci commit mesh11sd

			for change in $changes; do
				echo "set $change" | uci batch
			done
		fi

		exit 0
	else
		echo "Invalid option...."
		exit 1
	fi

elif [ "$1" = "status" ]; then
	# get list of interfaces
	get_mesh_iflist

	if [ -f "$tmpdir/devicemac" ]; then
		. $tmpdir/devicemac
	fi

	procd_status=$(/etc/init.d/mesh11sd status)
	get_node_type_code

	echo "{"
	echo "  \"setup\":{"
	echo "    \"version\":\"$version\","
	echo "    \"checksum\":\"$checksum\","
	echo "    \"enabled\":\"$enabled\","
	echo "    \"procd_status\":\"$procd_status\","
	echo "    \"node_type\":\"$node_type_code\","
	echo "    \"portal_detect\":\"$portal_detect\","
	echo "    \"portal_detect_threshold\":\"$portal_detect_threshold\","
	echo "    \"portal_channel\":\"$portal_channel\","
	echo "    \"portal_use_default_ipv4\":\"$portal_use_default_ipv4\","
	echo "    \"channel_tracking_checkinterval\":\"$channel_tracking_checkinterval\","
	echo "    \"mesh_mac_forced_forwarding\":\"$mesh_mac_forced_forwarding\","
	echo "    \"gateway_proxy_arp\":\"$gateway_proxy_arp\","
	echo "    \"reboot_on_error\":\"$reboot_on_error\","
	echo "    \"stop_on_error\":\"$stop_on_error\","
	echo "    \"watchdog_nonvolatile_log\":\"$watchdog_nonvolatile_log\","
	echo "    \"mesh_basename\":\"$mesh_basename\","
	echo "    \"auto_config\":\"$auto_config\","
	echo "    \"auto_mesh_network\":\"$auto_mesh_network\","
	echo "    \"auto_mesh_band\":\"$auto_mesh_band\","

	if [ ! -z "$mesh_phy_index" ]; then
		echo "    \"mesh_phy_index\":\"$mesh_phy_index\","
	fi

	echo "    \"auto_mesh_id\":\"$auto_mesh_id\","
	echo "    \"mesh_gate_enable\":\"$mesh_gate_enable\","

	if [ -e "$tmpdir/leechmode" ]; then
		. "$tmpdir/leechmode"
	else
		leechmode="$mesh_gate_only"
	fi

	echo "    \"mesh_leechmode_enable\":\"$leechmode\","

	echo "    \"mesh_node_mobility_level\":\"$mesh_node_mobility_level\","

	if [ -e "$tmpdir/mesh_mobility" ]; then
		. "$tmpdir/mesh_mobility"
		echo "    \"use_default_beacon_interval\":\"$use_default_beacon_interval\","
		echo "    \"mesh_beacon_interval\":\"$beacon_int\","
		echo "    \"mesh_dtim_period\":\"$dtim_period\","
	fi

	echo "    \"mesh_gate_encryption\":\"$mesh_gate_encryption\","
	echo "    \"mesh_backhaul_led\":\"$mesh_backhaul_led\","

	# check if opennds is installed:
	type opennds &>/dev/null
	opennds_installed=$?

	if [ "$opennds_installed" -eq 0 ]; then
		echo "    \"manage_opennds_startup\":\"$manage_opennds_startup\","
	fi

	# vxlan configs
	package_list="ip-full vxlan"
	check_package_list
	echo "    \"vtun_enable\":\"$vtun_enable\","

	if [ "$ip_full" -eq 0 ] && [ "$vxlan" -eq 0 ]; then # we have the required packages for vxlan
		echo "    \"tun_id\":\"$tun_id\","
		echo "    \"vtun_ip\":\"$vtun_ip\","
		echo "    \"vtun_mask\":\"$vtun_mask\","
		echo "    \"vtun_gate_encryption\":\"$vtun_gate_encryption\","
		echo "    \"vtun_base_ssid\":\"$vtun_base_ssid\","
		echo "    \"vtun_path_cost\":\"$vtun_path_cost\","
	fi


	# get list of interfaces
	get_mesh_iflist

	for iface in $iflist; do
		wait_for_interface "$iface"

		if [ "$ifstatus" = "down" ]; then
			continue
		fi

		txpower=$(iwinfo | grep -A 7 "$iface" | grep -w "Tx-Power:" | awk '{printf "%s", $2}')
		break
	done


	echo "    \"txpower\":\"$txpower\","
	echo "    \"mesh_path_cost\":\"$mesh_path_cost\","
	echo "    \"mesh_path_stabilisation\":\"$mesh_path_stabilisation\","
	echo "    \"reactive_path_stabilisation_threshold\":\"$reactive_path_stabilisation_threshold\","
	echo "    \"checkinterval\":\"$checkinterval\","
	echo "    \"interface_timeout\":\"$interface_timeout\","
	echo "    \"ssid_suffix_enable\":\"$ssid_suffix_enable\","
	echo "    \"apmond_enable\":\"$apmond_enable\","
	echo "    \"apmond_cgi_dir\":\"$apmond_cgi_dir\","
	echo "    \"apmon_verbose_debug_enable\":\"$apmon_verbose_debug_enable\","
	echo "    \"odhcpd_log_level\":\"$odhcpd_log_level\","

	if [ "$portal_detect" -eq 3 ]; then
		echo "    \"cpe_mode\":\"$cpe_mode\","
	fi

	echo "    \"debuglevel\":\"$debuglevel\""
	echo "  },"
	echo "  \"mesh_interfaces\":{"

	for iface in $iflist; do
		wait_for_interface "$iface"

		if [ "$ifstatus" = "down" ]; then
			continue
		fi

		# get list of mesh parameters for this interface
		get_params

		if [ -z "$params" ]; then
			# this is not a mesh interface
			continue
		else
			params=$(echo "$params" | awk -F" " '{printf "      \"%s\":\"%s\",\n", $1, $3}')
			echo "    \"$iface\":{"
			echo "$params"

			meshconfig=$(uci show wireless | grep "ifname='$iface'" | awk -F ".ifname='$iface'" '{printf "%s", $1}')

			if [ -z "$meshconfig" ]; then
				echo "      \"interface\":\"ERROR: mesh interface is unnamed in wireless config\""
				echo "      \"error\":\" interface must be named to see further status information\""
				echo "    }"
			else
				device=$(uci get $meshconfig.device 2> /dev/null)
				channel=$(uci show wireless | grep "$device.channel" | awk -F "'" '{printf "%s", $2}')
				meshid=$(uci get $meshconfig.mesh_id 2> /dev/null)

				echo "      \"mesh_id\":\"$meshid\","

				if [ -e "$tmpdir/mesh_mobility" ]; then
					. "$tmpdir/mesh_mobility"
					echo "      \"mesh_rts_threshold\":\"$rts\","
					echo "      \"mesh_queue_limit\":\"$limit\","
					echo "      \"mesh_ddr_scheduler_quantum\":\"$quantum\","
					echo "      \"mesh_airtime_queue_limit\":\"$aql_threshold\","
				fi


				echo "      \"device\":\"$device\","
				echo "      \"channel\":\"$channel\","

				tx_packets=$(devstatus $iface 2>/dev/null | grep "tx_packets" | awk -F": " '{print $2}' | tr -d ",")
				echo "      \"tx_packets\":\"$tx_packets\","

				tx_bytes=$(devstatus $iface 2>/dev/null | grep "tx_bytes" | awk -F": " '{print $2}' | tr -d ",")
				echo "      \"tx_bytes\":\"$tx_bytes\","

				rx_packets=$(devstatus $iface 2>/dev/null | grep "rx_packets" | awk -F": " '{print $2}' | tr -d ",")
				echo "      \"rx_packets\":\"$rx_packets\","

				rx_bytes=$(devstatus $iface 2>/dev/null | grep "rx_bytes" | awk -F": " '{print $2}' | tr -d ",")
				echo "      \"rx_bytes\":\"$rx_bytes\","

				peernum=0
				peers=$(iw dev $iface mpath dump 2>/dev/null | grep -c -w $iface)
				macrouting=$(iw dev $iface mpath dump 2>/dev/null | awk -F" " 'NR>1 {printf "%s/%s/%s/%s/%s/%s/%s ", $1, $2, $5, $11, $12, $4, $10}')
				peer_macs="$devicemac ""$(iw dev $iface mpath dump 2>/dev/null | awk -F" " 'NR>1 {printf "%s ", $1}')"

				echo "      \"this_node\":\"$devicemac\","
				echo "      \"active_peers\":\"$peers\","
				echo "      \"peers\":{"

				for peer in $macrouting; do
					peernum=$((peernum+1))
					peermac=$(echo "$peer" | awk -F"/" '{printf "%s", $1}')
					echo "        \"$peermac\":{"

					next_hop=$(echo "$peer" | awk -F"/" '{printf "%s", $2}')
					echo "          \"next_hop\":\"$next_hop\","

					hop_count=$(echo "$peer" | awk -F"/" '{printf "%s", $4}')
					echo "          \"hop_count\":\"$hop_count\","

					path_change_count=$(echo "$peer" | awk -F"/" '{printf "%s", $5}')
					echo "          \"path_change_count\":\"$path_change_count\","

					metric=$(echo "$peer" | awk -F"/" '{printf "%s", $3}')
					echo "          \"metric\":\"$metric\","

					sequence_number=$(echo "$peer" | awk -F"/" '{printf "%s", $6}')
					echo "          \"sequence_number\":\"$sequence_number\","

					flags_bitmask=$(echo "$peer" | awk -F"/" '{printf "%s", $7}')
					echo "          \"hwmp_flags_bitmask\":\"$flags_bitmask\""

					if [ "$peers" = "$peernum" ]; then
						echo "        }"
					else
						echo "        },"
					fi
				done
				echo "      },"

				ap_ifaces=$(iw dev | grep -B 7 "type AP"| grep "Interface" | awk '{printf "%s ", $2}')

				for ap_iface in $ap_ifaces; do
					ap_sta=$(iw dev $ap_iface station dump | grep "Station" | awk  '{printf "%s ", $2}')
					ap_stas="$ap_stas""$ap_sta"
				done

				for ap_sta in $ap_stas; do
					starouting="$starouting""$ap_sta/$devicemac "
				done

				stanum=0
				stations=0
				starouting="$starouting"$(iw dev $iface mpp dump | awk -F" " 'NR>1 {printf "%s/%s ", $1, $2}')
				stas=$(iw dev $iface mpp dump | awk -F" " 'NR>1 {printf "%s ", $1}')
				stas="$ap_stas""$stas"

				for sta in $stas; do
					stations=$((stations+1))
				done

				echo "      \"active_stations\":\"$stations\","
				echo "      \"stations\":{"

				for staroute in $starouting; do
					sta="${staroute%/*}"
					inpeermacs=$(echo "$peer_macs" | grep "$sta")

					stamac=$(echo "$sta" | awk -F"/" '{printf "%s", $1}')
					proxy_node=$(echo "$sta" | awk -F"/" '{printf "%s", $2}')
					proxy_node="${staroute##*/}"

					stamacid=$(printf "%s" "$stamac" | awk -F ":" '{printf "%s%s%s%s%s", $2, $3, $4, $5, $6}')
					proxy_nodeid=$(printf "%s" "$proxy_node" | awk -F ":" '{printf "%s%s%s%s%s", $2, $3, $4, $5, $6}')
					stanum=$((stanum+1))
					echo "        \"$stamac\":{"
					echo "          \"proxy_node\":\"$proxy_node\""

					if [ "$stations" = "$stanum" ]; then
						echo "        }"
					else
						echo "        },"
					fi

				done

				echo "      }"
				echo "    }"

			fi
		fi
	done

	echo "  },"

	echo "  \"layer2_connections\":{"

	devicelist=$(uci show network | grep "type='bridge'" | awk -F "." '{printf "%s.%s ", $1, $2}')
	devicecount=$( echo "$devicelist" | awk '{printf "%s", NF}')

	for devicesection in $devicelist; do
		devicecount=$((devicecount - 1))
		bridge=$(uci get "$devicesection".name)
		echo "    \"$bridge\":{"
		bridgeportlist=$(brctl showstp "$bridge" 2>/dev/null | grep " (" | grep ")" | awk '{printf "interface=%s;port=%s ", $1, $2}'| tr -d "(" | tr -d ")")
		firstmac=1

		for bridgeport in $bridgeportlist; do
			eval "$bridgeport"
			allmacs=$(brctl showmacs "$bridge" | awk -v bridge="$bridge" '$3=="no" {printf "%s,%s,%s ", bridge, $1, $2}')

			for clientmac in $allmacs; do
				cmac=$(echo "$clientmac" | awk -v port="$port" -F "," '$2==port {printf "%s", $3}')

				if [ ! -z "$cmac" ]; then

					if [ "$firstmac" -eq 0 ]; then
						echo ","
					else
						firstmac=0
					fi

					echo -n "      \"$cmac\":\"$interface\""
				fi
			done
		done

		if [ -z "$allmacs" ]; then
			echo -n -e "    }"
		else
			echo -n -e "\n    }"
		fi

		if [ "$devicecount" -eq 0 ]; then
			echo
		else
			echo ","
		fi
	done

	echo "  }"
	echo "}"

elif [ "$1" = "connect" ]; then
	mute=$mutestate
	firstloop=0

	if [ -f "$tmpdir/is_portal" ]; then
		. $tmpdir/is_portal
	fi

	if [ -f "$tmpdir/devicemac" ]; then
		. $tmpdir/devicemac
	fi

	if [ "$auto_config" -eq 0 ] || [ -z "$detected_state" ]; then
		echo "======================================================================"
		echo "Unable to connect -  enable auto_config to connect to other meshnodes"
		echo "======================================================================"
		exit 1
	fi

	nodeaddr=$(printf "%s" "$2" | tr "-" ":" | tr "[A-Z]" "[a-z]")

	echo "==========================================================================="

	echo " Connect a remote terminal session on a remote meshnode"
	echo "    Usage: mesh11sd connect [remote_meshnode_macaddress]"
	echo
	wait_for_nodes
	echo

	if [ -z "$nodeaddr" ]; then
		echo " If the node you are looking for is not in the list - re-run this command."
		echo "===================================================================================================================="
		echo " The following meshnodes are available for remote connection:"
	fi

	if [ ! -z "$peerlist" ]; then
		# peerlist is a list of mac addresses of peers, both la and non la

		get_portal_ula

		for connectto in $connectlist; do
			linklocal="${connectto%,*}"
			ndpmac="${connectto##*,}"
			nodeid=$(echo "$ndpmac" | awk -F ":" '{printf "%s%s", $5. $6}')

			check_if_peer

			if [ "$ispeer" -ne 0 ]; then
				continue
			fi

			ndpmacf=$(printf "%s" "$ndpmac" | tr ":" "-")
			ip6addr=$(echo "$linklocal" | awk -F "%" '{printf "%s", $1}')

			if [ -z "$nodeaddr" ]; then
				# Generate EUI-64 from target peer's MAC (ndpmac)
				mac_no_colons="${ndpmac//:/}"
				first_byte_hex=${mac_no_colons:0:2}
				first_byte_dec=$((0x$first_byte_hex ^ 0x02))
				first_byte=$(printf "%02x" "$first_byte_dec")
				eui64="${first_byte}${mac_no_colons:2:2}${mac_no_colons:4:2}fffe${mac_no_colons:6:2}${mac_no_colons:8:2}${mac_no_colons:10:2}"
				eui64_formatted="${eui64:0:4}:${eui64:4:4}:${eui64:8:4}:${eui64:12:4}"

				# Construct final clean 8-segment ULA for this peer
				node_ula="$ula_base:$ula_subnet:$eui64_formatted"

				pingreturn=$(ping6 -q -c 1 -W 1 -I $device $ip6addr &>/dev/null; echo $?)

				if [ "$pingreturn" -eq 0 ]; then
					if [ -n "$node_ula" ]; then
						echo "$ndpmacf [ ipaddress: $ip6addr] [ web_ui: https://[$node_ula] ]"
					else
						echo "$ndpmacf [ ipaddress: $ip6addr]"
					fi
				fi

			elif [ ! -z "$nodeaddr" ]; then

				if [ "$nodeaddr" = "$ndpmac" ]; then
					echo
					echo "Trying to connect to meshnode \"$ndpmacf\"....."
					ssh "root@$linklocal"
					echo
					echo "Disconnected from meshnode \"$ndpmacf\""
					echo
					break
				fi
			fi
		done
	fi

	echo "===================================================================================================================="
	echo

	exit 0

elif [ "$1" = "copy" ]; then

	if [ "$auto_config" -eq 0 ]; then
		echo "================================================================"
		echo "Unable to copy -  enable auto_config to copy to other meshnodes"
		echo "================================================================"
		exit 1
	fi
	nodeaddr=$(printf "%s" "$2" | tr "-" ":" | tr "[A-Z]" "[a-z]")
	filepath=$3
	filename=$(echo "$filepath" | awk -F "/" '{printf "%s", $NF}')

	echo "==========================================================================="

	echo " Copy a file to $tmpdir/ on a remote meshnode"
	echo "    Usage: mesh11sd copy [remote_meshnode_macaddress] [path_of_source_file]"
	echo
	wait_for_nodes
	echo

	if [ -z "$nodeaddr" ]; then
		echo " If the node you are looking for is not in the list - re-run this command."
		echo "==========================================================================="
		echo " The following meshnodes are available for remote copy:"

	fi

	if [ ! -z "$peerlist" ]; then

		for connectto in $connectlist; do
			linklocal="${connectto%,*}"
			ndpmac="${connectto##*,}"

			check_if_peer

			if [ "$ispeer" -ne 0 ]; then
				continue
			fi

			ndpmacf=$(printf "%s" "$ndpmac" | tr ":" "-")
			ip6addr=$(echo "$linklocal" | awk -F "%" '{printf "%s", $1}')

			if [ -z "$nodeaddr" ]; then
				echo "	$ndpmacf	[ ip address: $ip6addr ]"
			elif [ ! -z "$nodeaddr" ]; then

				if [ "$nodeaddr" = "$ndpmac" ] && [ ! -z "$filepath" ]; then
					echo
					echo "Trying to copy [ $tmpdir/$filename ] to meshnode [ $ndpmacf ]....."
					scp "$filepath" "root@[$linklocal]:$tmpdir/$filename"
					echo
					echo "Disconnected from meshnode \"$ndpmacf\""
					echo
					break
				fi
			fi
		done
	fi

	echo "==========================================================================="
	echo

	exit 0

elif [ "$1" = "txpower" ]; then
	# get list of interfaces
	get_mesh_iflist
	new_current_power=""

	for iface in $iflist; do
		wait_for_interface "$iface"

		if [ "$ifstatus" = "down" ]; then
			continue
		fi

		current_power=$(iwinfo | grep -A 7 "$iface" | grep -w "Tx-Power:" | awk '{printf "%s", $2}')
		break
	done

	echo "==========================================================================="

	echo "Change the mesh transmit power"

	if [ -z "$2" ]; then
		echo "    Current TX power [ $current_power ] dBm "
		echo "    Usage: mesh11sd txpower [+|-] "
		echo "    where \"+\" increments by 3dBm and \"-\" decrements by 3dBm"

	elif [ "$2" = "+" ]; then
		echo "    Previous TX power [ $current_power ] dBm "
		new_power=$(($current_power + 3))
		iw dev $iface set txpower fixed "$(($new_power * 100))"
		new_current_power=$(iwinfo | grep -A 7 "$iface" | grep -w "Tx-Power:" | awk '{printf "%s", $2}')
		echo
		echo "    New TX power - [ $new_current_power ] dBm "

	elif [ "$2" = "-" ]; then
		echo "    Previous TX power [ $current_power ] dBm "
		new_power=$(($current_power - 3))
		iw dev $iface set txpower fixed "$(($new_power * 100))"
		new_current_power=$(iwinfo | grep -A 7 "$iface" | grep -w "Tx-Power:" | awk '{printf "%s", $2}')
		echo
		echo "    New TX power - [ $new_current_power ] dBm "
	fi

	if [ ! -z "$new_current_power" ]; then
		echo "set mesh11sd.setup.txpower='$new_current_power'" | uci batch
	fi

	echo "==========================================================================="
	echo
	exit 0

elif [ "$1" = "stations" ]; then
	# get list of interfaces
	get_mesh_iflist

	echo "==========================================================================="

	echo "Mesh backhaul stations connected to this node:"

	for iface in $iflist; do
		wait_for_interface "$iface"

		if [ "$ifstatus" = "down" ]; then
			continue
		fi

		iw dev $iface station dump
	done

	echo "==========================================================================="
	echo
	exit 0

elif [ "$1" = "mesh_leechmode" ]; then

	if [ -z "$2" ]; then
		echo "Usage: mesh11sd mesh_leechmode [enable/disable]"
		outputstring="Option mesh_leechmode_enable current value [ $mesh_gate_only ]"
	elif [ "$2" = "enable" ]; then
		mesh_gate_only=1
		echo "set mesh11sd.setup.mesh_leechmode_enable='$mesh_gate_only'" | uci batch
		outputstring="Option mesh_leechmode_enable set to [ $mesh_gate_only ]"
	elif [ "$2" = "disable" ]; then
		mesh_gate_only=0
		echo "set mesh11sd.setup.mesh_leechmode_enable='$mesh_gate_only'" | uci batch
		outputstring="Option mesh_leechmode_enable set to [ $mesh_gate_only ]"
	else
		outputstring="Invalid option [ $2 ]"
	fi

	echo "==========================================================================="

	echo "$outputstring"

	echo "==========================================================================="
	echo
	exit 0

elif [ "$1" = "mesh_node_mobility_level" ]; then
	warning_message="Warning, if you continue, the mesh will be restarted and you may loose your current terminal connection and have to reopen it"

	if [ -z "$2" ]; then
		echo "Usage: mesh11sd mesh_node_mobility_level <level>"
		echo "    where level can be 0, 1, 2, 3, 4"
		echo "    0 is for static nodes, 1 for nodes with low relative velocity"
		echo "    and 2, 3, 4 for consecutively higher relative velocity"
		echo "    level 1 corresponds to a relative velocity in the region of 1 metre per second"
		echo "    level 4 corresponds to a relative velocity in the region of 5 metres per second"
		echo "    These are approximate values of achievable velative velocity"
		echo
		echo "$warning_message"
		echo

		if [ -e "$tmpdir/mesh_mobility" ]; then
			. "$tmpdir/mesh_mobility"
			echo "Current level is [ $mesh_node_mobility_level ]"
			echo
		fi
	elif [ "$2" -ge 0 ] && [ $2 -le 4 ]; then
		echo
		echo "$warning_message"
		echo
		echo "    Do you want to continue? [yes/no] "
		read reply

		if [ ! -z "$reply" ] && [ "$reply" = "yes" ]; then
			echo "set mesh11sd.setup.mesh_node_mobility_level='$2'" | uci batch
			echo
			echo "Activating change.... mesh_node_mobility_level [ $2 ]"
			echo "Note: Change can be reverted by restarting mesh11sd or rebooting"
			echo "Use the \"mesh11sd commit_changes\" command to make the change non-volatile"
			sleep 2

			restart_mesh
		else
			echo "Aborting..."
		fi
	fi

	exit 0

elif [ "$1" = "commit_changes" ]; then

	rssi_change_flag=0
	txpower_change_flag=0
	mesh_gate_only_change_flag=0
	debuglevel_change_flag=0
	mesh_node_mobility_level_change_flag=0

	changes=$(uci changes mesh11sd)
	changelist=$(echo "$changes" | awk '{printf "%s ", $1}')

	debuglevel_change=$(echo "$changes" | grep "debuglevel" | tail -n 1)
	mesh_rssi_threshold_change=$(echo "$changes" | grep "mesh_rssi_threshold" | tail -n 1)
	txpower_change=$(echo "$changes" | grep "txpower" | tail -n 1)
	mesh_gate_only_change=$(echo "$changes" | grep "mesh_leechmode_enable" | tail -n 1)
	mesh_node_mobility_level_change=$(echo "$changes" | grep "mesh_node_mobility_level" | tail -n 1)

	echo "==========================================================================="

	echo "Commit changes, making them non volatile on restart/reboot"

	if [ -z "$2" ] || [ "$2" != "commit" ]; then
		echo "    Usage: mesh11sd commit_changes commit "
		echo "    Writes changes to configuration file "
	elif [ "$2" = "commit" ]; then
		uci revert mesh11sd

		if [ ! -z "$debuglevel_change" ]; then
			echo "set $debuglevel_change" | uci batch
			debuglevel_change_flag=1
		fi

		if [ ! -z "$mesh_rssi_threshold_change" ]; then
			echo "set $mesh_rssi_threshold_change" | uci batch
			rssi_change_flag=1
		fi

		if [ ! -z "$txpower_change" ]; then
			echo "set $txpower_change" | uci batch
			txpower_change_flag=1
		fi

		if [ ! -z "$mesh_gate_only_change" ]; then
			echo "set $mesh_gate_only_change" | uci batch
			mesh_gate_only_change_flag=1
		fi

		if [ ! -z "$mesh_node_mobility_level_change" ]; then
			echo "set $mesh_node_mobility_level_change" | uci batch
			mesh_node_mobility_level_change_flag=1
		fi

		uci commit mesh11sd

		for change in $changelist; do
			is_debuglevel=$(echo "$change" | grep -q "debuglevel"; echo -n $?)
			is_txpower=$(echo "$change" | grep -q "txpower"; echo -n $?)
			is_mesh_rssi_threshold=$(echo "$change" | grep -q "mesh_rssi_threshold"; echo -n $?)
			is_mesh_gate_only=$(echo "$change" | grep -q "mesh_gate_only"; echo -n $?)

			if [ "$is_txpower" -eq 0 ] || [ "$is_mesh_rssi_threshold" -eq 0 ] || [ "$is_mesh_gate_only" -eq 0 ]; then
				continue
			fi

			echo "set $change" | uci batch

		done

		if [ "$debuglevel_change_flag" -eq 1 ]; then
			echo "Debuglevel change committed"
		fi

		if [ "$txpower_change_flag" -eq 1 ]; then
			echo "TX power change committed"
		fi

		if [ "$rssi_change_flag" -eq 1 ]; then
			echo "Mesh RSSI change committed"
		fi

		if [ "$mesh_gate_only_change_flag" -eq 1 ]; then
			echo "Mesh Leechmode change committed"
		fi

		if [ "$mesh_node_mobility_level_change_flag" -eq 1 ]; then
			echo "Mesh Mobility Level change committed"
		fi

		if [ "$debuglevel_change_flag" -eq 0 ] && [ "$rssi_change_flag" -eq 0 ] && [ "$txpower_change_flag" -eq 0 ] && [ "$mesh_gate_only_change_flag" -eq 0 ]; then
			echo "No changes found"
		fi

	fi


	echo "==========================================================================="
	echo
	exit 0

elif [ "$1" = "commit_all" ]; then

	if [ -z "$3" ] || [ "$3" != "silent" ]; then
		output="verbose"
	else
		output="silent"
	fi

	if [ "$auto_config" -eq 0 ]; then

		if [ "$output" = "verbose" ]; then
			echo "Auto config is not enabled - aborting..."
		fi

		exit 1
	fi

	commit_all_status=$(echo "get mesh11sd.setup.commit_all" | uci batch 2>/dev/null)

	if [ -z "$commit_all_status" ]; then
		commit_all_status=0
	fi

	if [ "$commit_all_status" -eq 1 ]; then

		if [ "$output" = "verbose" ]; then
			echo "A commit_all command has already been run. Use revert_all to revert."
		fi

		exit 1
	fi

	if [ "$output" = "verbose" ]; then
		echo "=========================================================================================================="
		echo "Commit ***ALL*** changes, including auto_configuration changes, making them non volatile on restart/reboot"

		if [ -z "$2" ] || [ "$2" != "commit" ]; then
			echo "    Usage: mesh11sd commit_all commit "
			echo "    Writes changes to configuration file "
			echo "    Active only when auto_config is enabled "

		elif [ "$2" = "commit" ] && [ "$auto_config" -ne 0 ]; then

			echo "    Dynamic auto_configure options will be written to non volatile storage and LuCi UI will be enabled..... "
			echo "    This is for advanced users, continue at your own risk."
			echo "    This process can be reverted with the \"revert_all\" command."
			echo
			echo "    Do you want to continue? [yes/no] "

			read reply
		fi
	fi

	if [ "$reply" = "yes" ] || [ "$output" = "silent" ]; then
		configs="mesh11sd wireless dhcp network firewall system"
		echo "set mesh11sd.setup.commit_all='1'" | uci batch
		echo "set mesh11sd.setup.portal_use_default_ipv4='1'" | uci batch

		for config in $configs; do
			uci commit "$config"
		done

		restore_luci_cgi

	elif [ "$reply" != "yes" ] && [ "$output" = "verbose" ]; then
		echo "Aborting commit_all"
	fi

	if [ "$output" = "verbose" ]; then
		echo "=========================================================================================================="
		echo
	fi

	luci_add_custom

	exit 0

elif [ "$1" = "revert_all" ]; then

	if [ "$auto_config" -eq 0 ]; then
		echo "Auto config is not enabled - aborting..."
		exit 1
	fi

	echo "=========================================================================================================="

	echo "Revert ***ALL*** changes previously committed by a commit_all command"

	if [ -z "$2" ] || [ "$2" != "revert" ]; then
		echo "    Usage: mesh11sd revert_all revert "
		echo "    Reverts previously committed changes "
		echo "    Active only when auto_config is enabled "
		echo "    Connection will be lost if you proceed as node will restart..... "

	elif [ "$2" = "revert" ] && [ "$auto_config" -ne 0 ]; then

		echo "    Connection will be lost if you proceed as node will restart..... "
		echo "    Do you want to continue? [yes/no] "

		read reply

		if [ "$reply" = "yes" ]; then
			configs="mesh11sd wireless dhcp network firewall system"

			for config in $configs; do

				if [ -f "/etc/config/$config""_mesh11sd_default" ]; then
					cp "/etc/config/$config""_mesh11sd_default" "/etc/config/$config"
				fi

			done

			replace_luci_cgi
			echo "Revert complete, auto configuration will rebuild - restarting......"

			tics=5

			for tic in $(seq $tics); do
				sleep 1
				echo -n "* "
			done

			echo
			echo "=========================================================================================================="
			echo
			echo "Rebooting..."
			sync  # Flush disks
			reboot
			kill -9 $PPID
		else
			echo "Aborting revert_all"
		fi
	fi

	echo "=========================================================================================================="
	echo
	exit 0

elif [ "$1" = "mesh_rssi_threshold" ]; then
	# get list of interfaces
	get_mesh_iflist

	for iface in $iflist; do
		wait_for_interface "$iface"

		if [ "$ifstatus" = "down" ]; then
			continue
		fi

		current_rssi=$(iw dev $iface get mesh_param mesh_rssi_threshold | awk '{printf "%s", $1}')
		break
	done

	echo "==========================================================================="

	echo "Change the mesh rssi threshold"

	if [ -z "$2" ]; then
		echo "    Current threshold for new connections [ $current_rssi ] dBm "
		echo "    Usage: mesh11sd mesh_rssi_threshold [+|-] [force]"
		echo "    where \"+\" increments by 3dBm and \"-\" decrements by 3dBm"
		echo "    The keyword \"force\" forces the new threshold on all peers"

	elif [ "$2" = "+" ]; then
		new_rssi=$(($current_rssi + 3))
		echo "    Previous threshold [ $current_rssi ] dBm "

		if [ -z "$3" ]; then
			touch "$tmpdir/mesh_rssi_threshold"
			iw dev $iface set mesh_param mesh_rssi_threshold $new_rssi
			echo "set mesh11sd.mesh_params.mesh_rssi_threshold='$new_rssi'" | uci batch
			new_current_rssi=$(iw dev $iface get mesh_param mesh_rssi_threshold | awk '{printf "%s", $1}')

		elif [ "$3" = "force" ]; then
			echo "set mesh11sd.mesh_params.mesh_rssi_threshold='$new_rssi'" | uci batch
			echo
			echo "    Forcing new threshold on all peers..."

			if [ -f "$tmpdir/mesh_rssi_threshold" ]; then
				rm "$tmpdir/mesh_rssi_threshold"
			fi

			new_current_rssi=$new_rssi
		fi

		echo
		echo "    New threshold - [ $new_current_rssi ] dBm "

	elif [ "$2" = "-" ]; then
		new_rssi=$(($current_rssi - 3))
		echo "    Previous threshold [ $current_rssi ] dBm "

		if [ -z "$3" ]; then
			touch "$tmpdir/mesh_rssi_threshold"
			iw dev $iface set mesh_param mesh_rssi_threshold $new_rssi
			echo "set mesh11sd.mesh_params.mesh_rssi_threshold='$new_rssi'" | uci batch
			new_current_rssi=$(iw dev $iface get mesh_param mesh_rssi_threshold | awk '{printf "%s", $1}')

		elif [ "$3" = "force" ]; then
			echo "set mesh11sd.mesh_params.mesh_rssi_threshold='$new_rssi'" | uci batch
			echo
			echo "    Forcing new threshold on all peers..."

			if [ -f "$tmpdir/mesh_rssi_threshold" ]; then
				rm "$tmpdir/mesh_rssi_threshold"
			fi

			new_current_rssi=$new_rssi
		fi

		echo
		echo "    New threshold - [ $new_current_rssi ] dBm "
	fi

	echo "==========================================================================="
	echo
	exit 0

elif [ "$1" = "country" ]; then
	current_country=$(iw reg get | grep "country")

	if [ -z "$2" ]; then
		echo "$current_country"
		exit 0
	fi

	if [ -z "$commit_all" ] || [ "$commit_all" -eq 0 ]; then
		echo "Changing regulatory domain from [ $current_country ] to [ $2 ]"
		echo "A Restart or Reboot is required for this to take effect"
		uci_set_string="set mesh11sd.setup.country='$2'"
		commit_single_change
	else
		echo "Unable to change regulatory domain. Auto config is committed - edit wireless config to change country settings."
		exit 1
	fi

	exit 0

elif [ "$1" = "force_ipv4_download" ]; then

	if ! grep "#!/bin/sh" /usr/bin/wget; then
		mv /usr/bin/wget /usr/bin/wget.bak
		echo -e "#!/bin/sh\n/usr/bin/wget.bak -4 \"\$@\"" > /usr/bin/wget && chmod +x /usr/bin/wget
		exit 0
	fi

	exit 127

elif [ "$1" = "download_revert_to_default" ]; then

	if grep "#!/bin/sh" /usr/bin/wget; then
		rm /usr/bin/wget
		mv /usr/bin/wget.bak /usr/bin/wget
		exit 0
	fi

	exit 127

elif [ "$1" = "dhcp4_renew" ]; then
	dhcpdevice=$(echo "get network.$auto_mesh_network.device" | uci batch | awk '{printf "%s", $1}')
	dhcp4_renew
	exit 0

elif [ "$1" = "get_portal_ula" ]; then
	get_portal_ula
	echo "$portal_ula"
	exit 0

elif [ "$1" = "get_portal_type" ]; then

	if [ -f "$tmpdir/portal_type" ]; then
		. "$tmpdir/portal_type"
		echo "portal_type=\"$portal_type\"; portal_mac=\"$portal_mac\"; portal_ipv4=\"$portal_ipv4\"; mrp_default_ipv4_gw=\"$mrp_default_ipv4_gw\""
	else
		echo "UNKNOWN"
	fi

	exit 0

elif [ "$1" = "set_ula_prefix" ]; then
	set_ula_prefix "$2"
	retval=$?
	echo -n "$ula_prefix"
	exit $retval

elif [ "$1" = "str_to_hex" ]; then
	str_to_hex "$2"

	if [ -f "$tmpdir/apmond/hexencoded" ]; then
		cat "$tmpdir/apmond/hexencoded"
	fi

	exit 0

elif [ "$1" = "hex_to_str" ]; then
	hex_to_str "$2"
	echo -n "$str"
	exit 0

elif [ "$1" = "is_hex" ]; then
	is_hex "$2"
	exit $?

elif [ "$1" = "is_ipv4addr_valid" ]; then
	is_ipv4addr_valid "$2"
	retval=$?
	echo -n "$checkip"
	exit $retval

elif [ "$1" = "is_installed" ]; then
	is_installed "$2"

	case $exitcode in
		0) echo "Package [ $2 ] is installed";;
		1) echo "Package [ $2 ] is NOT installed";;
		2) echo "Package NOT specified";;
		127) echo "Error: Package manager cannot be identified";;
		*) echo "Unexpected Error.....";;
	esac

	exit $exitcode

elif [ "$1" = "write_node_data" ]; then
	write_node_data "$2" "$3"
	exit 0

elif [ "$1" = "send_ap_data" ]; then
	send_ap_data
	exit $retcode

elif [ "$1" = "get_ap_data" ]; then
	get_ap_data
	exit 0

elif [ "$1" = "show_ap_data" ]; then

	if [ "$auto_config" -eq 0 ] && [ ! -e "$tmpdir/auto_config_test" ]; then
		echo "================================================================================"
		echo "Unable to collect access point data -  enable auto_config for this functionality"
		echo "================================================================================"
		exit 1
	fi

	data_nodes=$(ls "$tmpdir/apmond/ap/" | awk '{printf "%s ", $1}')

	if [ -z "$data_nodes" ]; then
		exit 0
	fi

	if [ "$2" = "all" ]; then
		all_first=1

		for data_node in $data_nodes; do
			hex_to_str "$(awk '{printf "%s", $2}'  "$tmpdir/apmond/ap/$data_node" 2>/dev/null)"

			if [ ! -z "$str" ]; then
				str=$(echo -n "$str" | tail -n +2 | head -n -2)

				if [ "$all_first" -eq 1 ]; then
					echo "{"
					echo -n "$str"
					all_first=0
				else
					echo -e -n "\n  },\n$str"
				fi
			fi

		done

		echo -e "\n  }\n}"
	fi

	if [ -z "$2" ]; then
		echo "===================================================================================================================="

		echo " Show access point usage data"
		echo "    Usage: mesh11sd show_ap_data [ap_mac_id]"
		echo "       or: mesh11sd show_ap_data all"
		echo

		if [ ! -z "$data_nodes" ]; then
			echo " If the node you are looking for is not in the list - it probably has not had any connections yet - try again later."
			echo "===================================================================================================================="
			echo " The following access points have sent usage data:"

			for data_node in $data_nodes; do
				echo "===================================================================================================================="
				echo
				echo "Access point ID (ap_mac_id)	$data_node"
			done

			echo "===================================================================================================================="
		else
			echo " If the node you are looking for is not shown - it probably has not had any connections yet - try again later."
			echo "===================================================================================================================="
		fi

		exit 1

	elif [ "$2" != "all" ]; then
		hex_to_str "$(awk '{printf "%s", $2}'  "$tmpdir/apmond/ap/$2" 2>/dev/null)"

		if [ ! -z "$str" ]; then
			echo "$str"
		fi
	fi

	exit 0

elif [ "$1" = "write_to_syslog" ]; then
	# Write debug message to syslog
	# $2 contains the string to log
	# $3 contains the debug level string: debug, info, warn, notice, err, emerg.

	if [ -z "$2" ]; then
		exit 1
	else
		syslogmessage="$2"
		debugtype="$3"
		write_to_syslog
	fi

	exit 0

elif [ "$1" = "write_log" ]; then
	# Write debug message to mesh11sd log
	# $2 contains the string to log
	# $3 contains the debug level string: debug, info, warn, notice, err, emerg.

	if [ -z "$2" ]; then
		exit 1
	else
		logmessage="$2"
		debugtype="$3"
		write_log
	fi

	exit 0

elif [ "$1" = "read_log" ]; then
	#Read the mesh11sd log
	# $2 contains the follow flag

	if [ ! -e "$log_mountpoint/mesh11sd/mesh11sd_log.log" ]; then
		exit 1
	fi

	if [ -z "$2" ]; then
		cat "$log_mountpoint/mesh11sd/mesh11sd_log.log"
	elif [ "$2" = "-F" ] || [ "$2" = "-f" ]; then
		cat "$log_mountpoint/mesh11sd/mesh11sd_log.log"
		rm "$log_mountpoint/mesh11sd/mesh11sd_log.log"
		touch "$log_mountpoint/mesh11sd/mesh11sd_log.log"
		tail -F "$log_mountpoint/mesh11sd/mesh11sd_log.log" 2>/dev/null
	fi

	exit 0

elif [ "$1" = "get_valid_channels" ]; then
	get_valid_channels
	echo "$valid_channels"
	exit 0

elif [ "$1" = "wifi_chipset_detect" ]; then
	wifi_chipset_detect
	cat "$outputfile"
	exit 0

elif [ "$1" = "active_nodecount" ]; then

	if [ -f "$tmpdir/active_mesh_ifname" ]; then
		. "$tmpdir/active_mesh_ifname"
		active_nodecount=$(($(iw dev "$active_mesh_ifname" mpath dump | wc -l) -1))
		echo -n "$active_nodecount"
	else
		echo -n "0"
	fi

	exit 0

elif [ "$1" = "get_node_type_code" ]; then
	get_node_type_code
	exitcode=$?
	echo -n "$node_type_code"
	exit $exitcode

elif [ "$1" = "daemon" ]; then
	# Detect what we are running on - Write $tmpdir/wifidetect file with os, device wireless chipset and driver info
	# vars Sdriver, $chipset labelmac and others are set
	outputfile="/$tmpdir/wifidetect"
	wifi_chipset_detect
	installver="legacy"

	if [ -f "/etc/factory_mac" ]; then
		. /etc/factory_mac

		if [ "$installver" != "$version" ]; then
			rm "/etc/factory_mac"
		fi
	fi

	strlen="$((${#labelmac}))"
	factory_mac=${labelmac:1:strlen}

	if [ ! -f "/etc/factory_mac" ]; then
		echo "factory_mac=\"$factory_mac\"; installver=\"$version\"" > /etc/factory_mac
	fi

	# Fix dropbear - make sure reboot terminates ssh session before closing wireless
	fix_dropbear

	debugtype="notice"
	syslogmessage="mesh11sd is in startup"
	write_to_syslog

	#Default flags
	startup=4 # bit 2
	statusmode=2 # bit 1
	enabled=1 #bit 0

	# Initial conditions
	link_to_portal=1
	statusmode=0
	changed=0
	firstloop=1 # Set if we have just started
	daemon_startup=1 # For future watchdog tests
	autoconfig=0
	autostartup=0
	macfixup=0 # Used if we need to fix the mesh interface mac address
	mute=0 # Mute some debug messages in function calls
	previous_path_change_count=1
	index=0
	transition_count=0
	vtunnel=""
	apmondloopcount=0
	apmondloopstart=6
	setroute=0
	check_channel_now=0

	#loop counter for Mesh Reactive Stabilsation Threshold:
	mrstloopcount=0

	refresh_bridgemac

	/sbin/service odhcpd stop
	/sbin/service network reload

	set_led init

	set_led on

	get_kernel_version

	# Ensure debugfs is mounted
	mount -t debugfs debugfs /sys/kernel/debug 2>/dev/null

	cpe_conf=0

	if [ -f "$tmpdir/active_mesh_ifname" ]; then
		rm "$tmpdir/active_mesh_ifname"
	fi

	# unconditionally try to stop openNDS
	manage_opennds stop

	if [ -e "$tmpdir/auto_config_test" ] && [ "$auto_config" -eq 0 ]; then
		autostartup=1
	elif [ "$auto_config" -gt 0 ]; then
		autostartup=1
	fi

	if [ "$autostartup" -eq 1 ]; then

		if [ "$commit_all" -eq 0 ]; then 
			# redirect luci web ui
			replace_luci_cgi
		fi

		# We do not know the current state so reinitialise everything
		debugtype="info"
		syslogmessage="Initialising state for auto_config"
		write_to_syslog

		configs="mesh11sd wireless dhcp network firewall system"

		for config in $configs; do
			uci revert "$config"

			# Copy the default config if we have not already done so
			if [ ! -f "/etc/config/$config""_mesh11sd_default" ]; then
				cp "/etc/config/$config" "/etc/config/$config""_mesh11sd_default"
			fi

		done

		# Set ula prefix for forced portal modes
		if [ "$portal_detect" -eq 0 ] || [ "$portal_detect" -eq 4 ]; then
			set_ula_prefix "set"
			/sbin/service network reload
		fi

		# Force a restart of ethernet ports - some built in switch chips do not initialise on reboot, only on cold start
		ports=$(cat /etc/config/network_mesh11sd_default | grep "list ports" | awk -F "'" '{printf "%s ", $2}' )
		wanport=$(uci get network_mesh11sd_default.wan.device)
		ports="$ports""$wanport"

		for port in $ports; do
			ip link set $port down && sleep 1 && ip link set $port up
		done

		# Set default dhcp settings for lan
		if [ "$portal_detect" -ne 4 ]; then
			uci set dhcp.$auto_mesh_network.dhcpv4='server'
			uci set dhcp.$auto_mesh_network.dhcpv6='server'
			uci set dhcp.$auto_mesh_network.ra='server'
			uci set dhcp.$auto_mesh_network.ignore='0'
			echo "del_list dhcp.$auto_mesh_network.ra_flags='none'" | uci batch 2> /dev/null
			echo "del_list dhcp.$auto_mesh_network.ra_flags='managed-config'" | uci batch 2> /dev/null
			echo "del_list dhcp.$auto_mesh_network.ra_flags='other-config'" | uci batch 2> /dev/null
			echo "add_list dhcp.$auto_mesh_network.ra_flags='managed-config'" | uci batch 2> /dev/null
			echo "add_list dhcp.$auto_mesh_network.ra_flags='other-config'" | uci batch 2> /dev/null
			echo "del_list dhcp.$auto_mesh_network.ra_dns=''" | uci batch 2> /dev/null
			echo "add_list dhcp.$auto_mesh_network.ra_dns='$portal_ula'" | uci batch 2> /dev/null
			echo "set network.$auto_mesh_network.proto='static'" | uci batch

			uci set dhcp.@dnsmasq[0].rebind_protection='1'
		else
			uci set dhcp.$auto_mesh_network.dhcpv4='disabled'
			uci set dhcp.$auto_mesh_network.dhcpv6='disabled'
			uci set dhcp.$auto_mesh_network.ra='disabled'
			uci set dhcp.$auto_mesh_network.ignore='1'
	
			echo "del_list dhcp.$auto_mesh_network.ra_flags='managed-config'" | uci batch 2> /dev/null
			echo "del_list dhcp.$auto_mesh_network.ra_flags='other-config'" | uci batch 2> /dev/null
			echo "del_list dhcp.$auto_mesh_network.ra_flags='none'" | uci batch 2> /dev/null
			echo "add_list dhcp.$auto_mesh_network.ra_flags='none'" | uci batch 2> /dev/null
			echo "set network.$auto_mesh_network.proto='dhcp'" | uci batch
		fi


		# Do we have a previously commited auto_config? If so get the active_mesh_ifname.
		meshifs=$(uci show wireless | grep "mode='mesh'" | awk -F "." '{printf "%s.%s ", $1, $2}')

		if [ ! -z "$meshifs" ]; then

			for meshif in $meshifs; do
				disab=$(uci get $meshif.disabled)

				if [ "$disab" -eq 0 ]; then
					active_mesh_ifname=$(uci get $meshif.ifname)
					echo "active_mesh_ifname=\"$active_mesh_ifname\"" > "$tmpdir/active_mesh_ifname"
					break
				fi
			done
		fi

		if [ "$portal_detect" -ne 3 ]; then
			echo "set network.$auto_mesh_network.ip6assign='64'" | uci batch
		fi

		if [ "$portal_use_default_ipv4" -eq 0 ]; then

			if [ "$portal_detect" -eq 3 ]; then
				# Set the cpe gateway ipv4 address based on mac address
				. $tmpdir/devicemac

				seedvalue="$devicemac"
			else
				seedvalue="$auto_mesh_id"
			fi

			generate_ipv4_base "$seedvalue"

			cpe_mask="255.255.255.0"

			echo "set network.lan.ipaddr='$newipaddr'" | uci batch
			echo "set network.lan.netmask='$cpe_mask'" | uci batch
			echo "set network.lan.ip6assign='64'" | uci batch
			echo "set network.wan6.reqprefix='auto'" | uci batch
			echo "set network.wan6.reqaddress='try'" | uci batch
		fi

		echo "set network.lan.ip6privacy='0'" | uci batch
		echo "set dhcp.lan.ra_lifetime='3600'" | uci batch
		echo "set dhcp.lan.ra_mininterval='20'" | uci batch
		echo "set dhcp.lan.ra_maxinterval='60'" | uci batch

		# Before restarting the network, check if CPE mode is requested
		if [ "$portal_detect" -eq 3 ] && [ "$commit_all" -eq 0 ]; then
			. $tmpdir/devicemac
			convert_to_la "$devicemac"
			#CPE mode, so setup network config accordingly
			wanif=$(uci get network.wan.device)
			wan6if=$(uci get network.wan6.device)

			if [ -e /etc/factory_mac ]; then
				. /etc/factory_mac
			fi

			if [ -z "$factory_mac" ]; then
				bridge_mac="$devicemac"
			else
				bridge_mac="$factory_mac"
			fi

			uci set network.cpewan=device
			uci set network.cpewan.name='br-wan'
			uci set network.cpewan.type='bridge'
			echo "add_list network.cpewan.ports='$wanif'" | uci batch

			uci set network.wan.device='br-wan'
			uci set network.wan6.device='br-wan'

			# Refresh the ipaddr
			setipaddr=$(uci get network.lan.ipaddr)

			debugtype="notice"
			syslogmessage="lan ip address set to [ $setipaddr ]"
			write_to_syslog

			zones=7

			for zoneindex in $(seq 0 1 $zones); do
				zone=$(echo "get firewall.@zone[$zoneindex].name" | uci batch)

				if [ "$zone" = "wan" ]; then
					wanzoneindex=$zoneindex
					break
				else
					wanzoneindex=$((zones + 1))
				fi
			done

			if [ "$wanzoneindex" -le "$zones" ]; then
				echo "set firewall.@zone[$wanzoneindex].input='ACCEPT'" | uci batch
			else
				debugtype="err"
				syslogmessage="Unable to find wan zone"
				write_to_syslog
			fi

			suffix=$(echo $bridge_mac | awk -F":" '{printf "%s%s", $5, $6}')

			if [ "$cpe_mode" = "relay" ]; then
				## Relay Mode - May fail with some versions of Android, Android turns off its interface after ~60 seconds - because Google
				echo "set network.wan.hostname='meshnode-$suffix'" | uci batch

				echo "set network.wan6.device='br-wan'" | uci batch
				echo "set network.wan6.proto='dhcpv6'" | uci batch
				echo "set network.wan6.reqaddress='try'" | uci batch
				echo "set network.wan6.reqprefix='no'" | uci batch
				echo "set network.wan6.hostname='meshnode-$suffix'" | uci batch

				echo "set dhcp.wan6=dhcp" | uci batch
				echo "set dhcp.wan6.master='1'" | uci batch
				echo "set dhcp.wan6.ra='relay'" | uci batch
				echo "set dhcp.wan6.dhcpv6='relay'" | uci batch
				echo "set dhcp.wan6.ndp='relay'" | uci batch

				echo "set dhcp.lan.ra='relay'" | uci batch
				echo "set dhcp.lan.dhcpv6='relay'" | uci batch
				echo "set dhcp.lan.ndp='relay'" | uci batch
				echo "set dhcp.lan.ra_slaac='1'" | uci batch
				echo "set dhcp.lan.ra_default='2'" | uci batch
				echo "set dhcp.lan.ra_lifetime='3600'" | uci batch
				echo "set dhcp.lan.ra_mininterval='20'" | uci batch
				echo "set dhcp.lan.ra_maxinterval='60'" | uci batch

			elif [ "$cpe_mode" = "prefix_delegation" ]; then
				# Prefix delegation mode - compatible with everything but available prefixes may get exhausted
				echo "set network.wan.hostname='meshnode-$suffix'" | uci batch

				echo "set network.wan6.device='br-wan'" | uci batch
				echo "set network.wan6.proto='dhcpv6'" | uci batch
				echo "set network.wan6.reqaddress='try'" | uci batch
				echo "set network.wan6.reqprefix='64'" | uci batch
				echo "set network.wan6.delegate='1'" | uci batch
				echo "set network.wan6.hostname='meshnode-$suffix'" | uci batch

				echo "set network.lan.ipassign='64'" | uci batch

				echo "set dhcp.lan.dhcpv6='server'" | uci batch
				echo "set dhcp.lan.ra='server'" | uci batch
				echo "set dhcp.lan.ra_slaac='1'" | uci batch
				echo "set dhcp.lan.ra_default='2'" | uci batch
				echo "set dhcp.lan.ra_lifetime='3600'" | uci batch
				echo "set dhcp.lan.ra_mininterval='20'" | uci batch
				echo "set dhcp.lan.ra_maxinterval='60'" | uci batch

				if [ "$wanzoneindex" -le "$zones" ]; then
					echo "set firewall.@zone[$wanzoneindex].masq6='0'" | uci batch
				else
					debugtype="err"
					syslogmessage="Unable to find wan zone, bad or incompatible config"
					write_to_syslog
				fi

			elif [ "$cpe_mode" = "nat66" ]; then
				# NAT 66 mode - compatible with everything and is therefore the default

				if [ "$wanzoneindex" -le "$zones" ]; then
					echo "set firewall.@zone[$wanzoneindex].masq6='1'" | uci batch
				else
					debugtype="err"
					syslogmessage="Unable to find wan zone, bad or incompatible config - NAT66 disabled"
					write_to_syslog
				fi

				echo "set network.wan.hostname='meshnode-$suffix'" | uci batch

				echo "set network.wan6.device='br-wan'" | uci batch
				echo "set network.wan6.proto='dhcpv6'" | uci batch
				echo "set network.wan6.reqaddress='try'" | uci batch
				echo "set network.wan6.reqprefix='no'" | uci batch
				echo "set network.wan6.delegate='0'" | uci batch
				echo "set network.wan6.hostname='meshnode-$suffix'" | uci batch

				echo "set dhcp.lan.dhcpv6='server'" | uci batch
				echo "set dhcp.lan.ra='server'" | uci batch
				echo "set dhcp.lan.ra_slaac='1'" | uci batch
				echo "set dhcp.lan.ra_default='2'" | uci batch
				echo "set dhcp.lan.ra_lifetime='3600'" | uci batch
				echo "set dhcp.lan.ra_mininterval='20'" | uci batch
				echo "set dhcp.lan.ra_maxinterval='60'" | uci batch

			else
				debugtype="err"
				syslogmessage="Invalid cpe_mode - ipv6 probably not working"
				write_to_syslog
			fi
		fi

		br_tun=$(brctl show | grep "br-tun" | awk '{printf "%s", $1}')

		if [ ! -z "$br_tun" ]; then
			ip link delete dev "$br_tun"
		fi

		/sbin/service network restart
		/sbin/service dnsmasq restart
		/sbin/service firewall restart
		sleep 5

		probe_interface_mac
	else
		restore_luci_cgi
	fi

	if [ -f "$tmpdir/mesh_rssi_threshold" ]; then
		rm "$tmpdir/mesh_rssi_threshold"
	fi

	if [ -f "$tmpdir/dhcp6probe" ]; then
		mv "$tmpdir/dhcp6probe" "$tmpdir/dhcp6probe.prev"
	fi

	# Clear the dhcp authoritative flag
	uci delete dhcp.@dnsmasq[0].authoritative 2>/dev/null

	# default state
	last_state="initial"

	# Initial Mode Flag
	mode=$(($startup + $statusmode + $enabled ))
	lastmode=0
	ucinamecheck=0

	check_config_params

	sys_setup

	setup_apmon_cgi

	refresh_bridgemac

	luci_add_custom

	get_portal_ula

	# Start the main loop
	while true; do
		meshindex=0
		meshconfigs=""
		debuglevel=$(uci get mesh11sd.setup.debuglevel 2> /dev/null)

		if [ "$enabled" = 1 ]; then
			#get list of mesh configs
			meshconfigs=$(uci show wireless 2> /dev/null | grep "mode='mesh'" | awk -F ".mode='mesh'" '{printf "%s ", $1}')

			if [ -z "$meshconfigs" ]; then
				# no mesh configuration yet
				debugtype="info"
				syslogmessage="No mesh interfaces detected yet.... Attempting auto configure"
				write_to_syslog

				############# Autoconfig

				if [ "$auto_config" -ne 0 ] || [ -e "$tmpdir/auto_config_test" ]; then
					auto_config
				else
					debugtype="err"
					syslogmessage="auto_config is disabled. Please configure a mesh interface or enable auto_config...."
					write_to_syslog
					sleep $checkinterval
				fi

				########################
			fi

			# Is the list of meshconfigs still empty?
			if [ -z "$meshconfigs" ]; then
				meshconfigs=$(uci show wireless 2> /dev/null | grep "mode='mesh'" | awk -F ".mode='mesh'" '{printf "%s ", $1}')
			fi

			if [ ! -z "$meshconfigs" ]; then
				# we have an existing mesh config

				for meshconfig in $meshconfigs; do

					if [ $meshindex -gt 15 ]; then
						# Max number of mesh interfaces exceeded - abort
						debugtype="warn"
						syslogmessage="Maximum number of mesh interfaces exceeded"
						write_to_syslog
						break
					fi

					meshindex=$(($meshindex+1))
				done

				# set mesh mac address for this meshconfig
				refresh_bridgemac
				. $tmpdir/devicemac
				get_wiphys

				#### The mesh interface MUST have a unique mac address ie not duplicated from another interface.
				# We will set it ip as a locally administered version of the bridge mac for facilitating connect/copy functionality
				# If manually configured, or after commit_all and changes have been made, let the user figure it out

				if [ "$macfixup" -eq 0 ] && [ "$auto_config" -gt 0 ] && [ "$commit_all" -eq 0 ]; then

					for wiphy in $wiphys; do
						ucimeshconfig=$(uci get $meshconfig.macaddr 2> /dev/null)
						convert_to_la "$devicemac"

						if [ -z "$ucimeshconfig" ] && [ "$commit_all" -eq 0 ]; then
							# No mac address fixup, so add one
							phyindex=$(echo "$wiphy" | awk -F "phy" '{printf "%s", $2}')
							phyindex=$(printf "%x" $phyindex)
							mesh_ifname="$mesh_basename""$phyindex"
							index=$(($index + 1))
							make_indexed_mac "$mac_la" "$index"
							echo "set wireless.m11s$phyindex.macaddr='$mac_indexed'" | uci batch
							debugtype="notice"
							syslogmessage="Setting mac address of mesh interface $mesh_ifname to [ $mac_indexed ]"
							write_to_syslog
							changed=1
						fi
					done

					# do the same for ap interfaces, indexing the la mac
					aplist=$(uci show wireless | grep "mode='ap'" | awk -F "." '{printf "%s ", $2}')

					convert_to_la "$devicemac"

					for ap in $aplist; do

						uciapconfig=$(uci get wireless.$ap.macaddr 2> /dev/null)

						if [ -z "$uciapconfig" ] && [ "$commit_all" -eq 0 ]; then
							index=$(($index + 1))
							make_indexed_mac "$mac_la" "$index"
							echo "set wireless.$ap.macaddr=$mac_indexed" | uci batch
						fi
					done

					macfixup=1
				fi
			fi

			if [ "$firstloop" -eq 1 ] || [ "$changed" -eq 1 ]; then
				firstloop=0
				changed=0
				set_led on

				touch "$tmpdir/meshinterface"
				all_nodes_rssi_update
			fi
			# get a list of interfaces
			get_mesh_iflist

			for iface in $iflist; do
				wait_for_interface "$iface"

				if [ "$ifstatus" = "down" ]; then
					debugtype="notice"
					syslogmessage="interface $iface is $ifstatus"
					write_to_syslog

					continue
				fi

				# get list of mesh parameters for this interface
				get_params

				if [ -z "$params" ]; then
					# this is not a mesh interface
					continue
				else
					# Check if this interface has a uci ifname
					uciname=$(uci show wireless | grep "ifname='$iface'" | awk -F "." '{printf "wireless.%s", $2}')

					if [ -z "$uciname" ]; then
						# Error - No interface name in config, auto config would have added one
						if [ "$ucinamecheck" -eq 0 ]; then
							ucinamecheck=1
							uciname=$iface
							debugtype="err"
							syslogmessage="ERROR: Mesh interface name not set in wireless config - using autogenerated name [ $iface ]"
							write_to_syslog
						fi
					fi
				fi


				# Set stp path cost
				set_iface_path_cost "$mesh_path_cost" "$device" "$iface"

				#Override wireless config and/or set parameters to those found in mesh11sd config
				uciname="mesh11sd.mesh_params"
				check_mesh_params
				check_mesh_phantom
			done

			if [ "$auto_config" -ne 0 ] || [ -e "$tmpdir/auto_config_test" ]; then

				if [ "$portal_detect" -ne 0 ] || [ "$portal_detect" -ne 4 ] || [ "$portal_detect" -ne 5 ]; then
					check_portal

				elif [ "$portal_detect" -eq 0 ] || [ "$portal_detect" -eq 4 ]; then

					if [ "$commit_all" -eq 0 ]; then
						# portal_detect is not enabled - force portal mode:
						authoritative=$(uci get dhcp.@dnsmasq[0].authoritative 2>/dev/null | awk '{printf "%d", $1}')

						if [ -z "$authoritative" ]; then
							authoritative=0
						fi

						uci set mesh11sd.mesh_params.mesh_connected_to_as='1'

						debugtype="notice"
						syslogmessage="Portal detection is disabled, forcing portal mode"
						write_to_syslog

						if [ -f "/var/run/udhcpc-$device.pid" ]; then
							kill $(cat /var/run/udhcpc-$device.pid)
						fi

						if [ "$portal_detect" -eq 0 ]; then
							echo "set dhcp.$auto_mesh_network.dhcpv4='server'" | uci batch
							echo "set dhcp.$auto_mesh_network.dhcpv6='server'" | uci batch
							echo "set dhcp.$auto_mesh_network.ignore='0'" | uci batch
							restore_ipv4

							uci set dhcp.@dnsmasq[0].authoritative='1'
							echo "set dhcp.$auto_mesh_network.ra_default='2'" | uci batch
							echo "set dhcp.$auto_mesh_network.ra='server'" | uci batch
							uci set dhcp.@dnsmasq[0].logfacility='-'
							uci set dhcp.@dnsmasq[0].quietdhcp='1'

							if [ "$vtunnel" = "configured" ]; then
								uci set dhcp.vtunlan.dhcpv4='server'
								uci set dhcp.vtunlan.dhcpv6='server'
								uci set dhcp.vtunlan.ra='server'
								uci set dhcp.vtunlan.ignore='0'
							fi

						else
							echo "set dhcp.$auto_mesh_network.dhcpv4='disabled'" | uci batch
							echo "set dhcp.$auto_mesh_network.dhcpv6='disabled'" | uci batch
							echo "set dhcp.$auto_mesh_network.ignore='1'" | uci batch
							restore_ipv4

							uci set dhcp.@dnsmasq[0].authoritative='1'
							echo "set dhcp.$auto_mesh_network.ra_default='2'" | uci batch
							echo "set dhcp.$auto_mesh_network.ra='disabled'" | uci batch
							uci set dhcp.@dnsmasq[0].logfacility='-'
							uci set dhcp.@dnsmasq[0].quietdhcp='1'

							if [ "$vtunnel" = "configured" ]; then
								uci set dhcp.vtunlan.dhcpv4='disabled'
								uci set dhcp.vtunlan.dhcpv6='disabled'
								uci set dhcp.vtunlan.ra='disabled'
								uci set dhcp.vtunlan.ignore='1'
								echo "del_list dhcp.vtunlan.ra_flags='managed-config'" | uci batch 2> /dev/null
								echo "del_list dhcp.vtunlan.ra_flags='other-config'" | uci batch 2> /dev/null
								echo "add_list dhcp.vtunlan.ra_flags='none'" | uci batch
								echo "del_list dhcp.vtunlan.ra_dns='$portal_ula_vtun'" | uci batch 2> /dev/null
								uci set dhcp.@dnsmasq[0].rebind_protection='0'
							fi

						fi

						if [ "$mesh_gate_only" -eq 0 ]; then
							mesh_hwmp_rootmode="4"
						else
							mesh_hwmp_rootmode="0"
						fi

						/sbin/service dnsmasq restart
						/sbin/service odhcpd restart
						restart_mesh
					fi

				elif [ "$portal_detect" -eq 3 ] && [ "$commit_all" -eq 0 ]; then

					if [ "$cpe_conf" -eq 0 ]; then

						# portal_detect is set to CPE mode - this is a router where the wan interface is the mesh backhaul:
						authoritative=$(uci get dhcp.@dnsmasq[0].authoritative 2>/dev/null | awk '{printf "%d", $1}')

						if [ -z "$authoritative" ]; then
							authoritative=0
						fi

						# To other meshnodes, this looks like a peer
						uci set mesh11sd.mesh_params.mesh_connected_to_as='0'

						if [ -z "$authoritative" ] || [ "$authoritative" -eq 0 ]; then
							debugtype="notice"
							syslogmessage="CPE mode"
							write_to_syslog
						fi

						if [ "$mesh_gate_only" -eq 0 ]; then
							non_portal_mesh_hwmp_rootmode="2"
						else
							non_portal_mesh_hwmp_rootmode="0"
						fi

						echo "set dhcp.lan.ignore='0'" | uci batch
						echo "set dhcp.lan.ra_default='2'" | uci batch
						echo "set dhcp.lan.dhcpv6='server'" | uci batch
						echo "set dhcp.lan.ra='server'" | uci batch
						uci set dhcp.@dnsmasq[0].logfacility='-'
						uci set dhcp.@dnsmasq[0].quietdhcp='1'

						/sbin/service dnsmasq restart
						/sbin/service odhcpd restart
						restart_mesh
						manage_opennds restart
						cpe_conf=1
					fi

				elif [ "$portal_detect" -eq 0 ]; then
					/sbin/service dnsmasq restart
					/sbin/service odhcpd restart

					if [ -z "$opennds_conf" ] || [ "$opennds_conf" -eq 0 ]; then
						opennds_conf=1
						manage_opennds restart
					fi
				fi

				if [ "$mesh_mac_forced_forwarding" -eq 1 ] && [ -f "$tmpdir/active_mesh_ifname" ]; then
					. $tmpdir/active_mesh_ifname
					echo 1 > /proc/sys/net/ipv4/conf/$active_mesh_ifname/proxy_arp_pvlan
				fi

				if [ "$gateway_proxy_arp" -eq 0 ]; then
					. $tmpdir/devicemac
					echo 0 > /proc/sys/net/ipv4/conf/$device/proxy_arp
				fi
			fi

			# check if the vtunnel is needed and if it is, is it configured
			check_vtunnel
			check_gate
			check_mesh_gate_only
			block_bridge_loops
			check_dadfailed
			check_path_changes

			if [ "$portal_detect" -eq 3 ]; then
				check_dns_server
				check_channel_now=0
				check_channel
			fi

			apmondloopcount=$((apmondloopcount + 1))

			if [ "$apmondloopcount" -ge "$apmondloopstart" ]; then
				send_ap_data
				apmondloopcount=0
				apmondloopstart=3
			fi
		fi

		if [ -e "$tmpdir/auto_config_test" ] && [ "$auto_config" -eq 0 ]; then
			debugtype="warn"
			syslogmessage="Auto Config is in TEST mode"
			write_to_syslog
		fi

		check_mesh_mtu
		check_mesh_active_led # sets heartbeatflag = 1 when mesh is active
		check_mesh_hwmp_rootmode

		if [ "$heartbeatflag" -eq 1 ] && [ "$commit_all" -eq 0 ] && [ "$auto_config" -eq 2 ]; then
			/usr/sbin/mesh11sd commit_all commit silent &
			commit_all=1
		fi

		if [ "$portal_detect" -eq 4 ] && [ "$link_to_portal" -eq 1 ]; then
			get_portal_ula

			if [ "$link_to_portal" -eq 1 ]; then
				/sbin/service network reload
			fi
		fi

		if [ "$portal_detect" -eq 4 ] || [ "$portal_detect" -eq 5 ]; then
			manage_fallback_ula_route
		fi

		accept_ra=$(cat /proc/sys/net/ipv6/conf/$device/accept_ra)

		if [ "$detected_state" = "portal" ] && [ "$portal_detect" -ne 4 ] && [ "$accept_ra" -ne 0 ]; then
			echo 0 > /proc/sys/net/ipv6/conf/$device/accept_ra
		elif [ "$portal_detect" -eq 4 ] && [ "$accept_ra" -ne 2 ]; then
			echo 2 > /proc/sys/net/ipv6/conf/$device/accept_ra
		elif [ "$detected_state" = "peer" ] && [ "$accept_ra" -ne 2 ]; then
			echo 2 > /proc/sys/net/ipv6/conf/$device/accept_ra
		fi

		get_portal_type

		if [ "$portal_type" = "MBP" ] && [ ! -z "$portal_mac" ] && [ ! -z "$portal_ipv4" ] && [ ! -z "$mrp_default_ipv4_gw" ]; then
			/sbin/service network restart
		fi

		log_info "Checkinterval [$checkinterval]"

		sleep $checkinterval
	done
	exit 0
else
	echo "Unrecognised command - For help, try mesh11sd --help "
fi
