#!/bin/bash
###############################################################################
#                                                                             #
# IPFire.org - A linux based firewall                                         #
# Copyright (C) 2010  Michael Tremer & Christian Schmidt                      #
#                                                                             #
# 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.                                #
#                                                                             #
# You should have received a copy of the GNU General Public License           #
# along with this program.  If not, see <http://www.gnu.org/licenses/>.       #
#                                                                             #
###############################################################################

# Parse the command line
while [ $# -gt 0 ]; do
	case "${1}" in
		-d|--debug)
			DEBUG=1
			;;
		*)
			action=${1}
			;;
	esac
	shift
	[ -n "${action}" ] && break
done

. /usr/lib/network/functions

function cli_config() {
	if cli_help_requested $@; then
		cli_show_man network-config
		exit ${EXIT_OK}
	fi

	if [ -n "${1}" ]; then
		config_set $@
		network_config_write
	else
		network_config_print
	fi
}

function cli_device() {
	if cli_help_requested $@; then
		cli_show_man network-device
		exit ${EXIT_OK}
	fi

	local device=${1}
	local action=${2}
	shift 2

	if ! isset device; then
		cli_show_man network-device
		return ${EXIT_ERROR}
	fi

	assert device_exists ${device}

	case "${action}" in
		discover)
			cli_device_discover ${device} $@
			;;
		status)
			cli_device_status ${device}
			;;
		unlock)
			cli_device_serial_unlock ${device} $@
			;;
		*)
			cli_show_man network-device
			;;
	esac

	return ${EXIT_OK}
}

function cli_device_status() {
	local device=${1}
	assert device_exists ${device}

	# Disable debugging output here.
	local log_disable_stdout=${LOG_DISABLE_STDOUT}
	LOG_DISABLE_STDOUT="true"

	# Save the type of the device for later.
	local type=$(device_get_type ${device})

	cli_headline 1 "Device status: ${device}"
	cli_print_fmt1 1 "Name"		"${device}"

	# Handle serial devices.
	if [ "${type}" = "serial" ]; then
		cli_device_status_serial ${device}
		return $?
	fi

	# Print the device status.
	device_is_up ${device} &>/dev/null
	local status=$?

	case "${status}" in
		${EXIT_TRUE})
			status="${CLR_GREEN_B}UP${CLR_RESET}"
			;;
		${EXIT_FALSE})
			status="${CLR_RED_B}DOWN${CLR_RESET}"
			;;
	esac

	cli_print_fmt1 1 "Status"	"${status}"
	cli_print_fmt1 1 "Type"		"${type}"
	cli_print_fmt1 1 "Address"	"$(device_get_address ${device})"
	cli_space

	# Print the link speed for ethernet devices.
	if device_is_up ${device} &>/dev/null; then
		case "${type}" in
			ethernet)
				cli_print_fmt1 1 "Link" \
					"$(device_get_speed ${device}) MBit/s $(device_get_duplex ${device}) duplex"
				;;
		esac
	fi

	cli_print_fmt1 1 "MTU"		"$(device_get_mtu ${device})"
	cli_space

	# Print device statistics.
	cli_device_stats 2 ${device}

	# Print some more information.
	device_has_carrier ${device} &>/dev/null
	cli_print_fmt1 1 "Has carrier?"	"$(cli_print_bool $?)"

	device_is_promisc ${device} &>/dev/null
	cli_print_fmt1 1 "Promisc"	"$(cli_print_bool $?)"
	cli_space

	# Print all vlan devices.
	local vlans=$(device_get_vlans ${device})
	if [ -n "${vlans}" ]; then
		cli_headline 2 "VLAN devices"

		local vlan
		for vlan in ${vlans}; do
			cli_print 2 "* %-6s - %s" "${vlan}" "$(device_get_address ${vlan})"
		done
		cli_space
	fi

	# Reset the logging level.
	LOG_DISABLE_STDOUT=${log_disable_stdout}
}

function cli_device_status_serial() {
	local device=${1}
	assert device_is_serial ${device}

	serial_is_locked ${device} &>/dev/null
	local locked=$?

	cli_print_fmt1 1 "Locked" "$(cli_print_bool ${locked})"
	cli_space

	# Cannot go on when the device is locked.
	[ ${locked} -eq ${EXIT_TRUE} ] && return ${EXIT_OK}

	cli_print_fmt1 1 "Manufacturer" \
		"$(modem_get_manufacturer ${device})"
	cli_print_fmt1 1 "Model" \
		"$(modem_get_model ${device})"
	cli_print_fmt1 1 "Software version" \
		"$(modem_get_software_version ${device})"

	if modem_is_mobile ${device}; then
		cli_print_fmt1 1 "IMEI" \
			"$(modem_get_device_imei ${device})"
		cli_space

		cli_headline 2 "Network status"
		modem_sim_status ${device} &>/dev/null
		local sim_status_code=$?

		local sim_status="unknown"
		case "${sim_status_code}" in
			${EXIT_SIM_READY})
				sim_status="SIM ready"
				;;
			${EXIT_SIM_PIN})
				sim_status="PIN locked"
				;;
			${EXIT_SIM_PUK})
				sim_status="PUK locked"
				;;
		esac
		cli_print_fmt1 2 "SIM status" "${sim_status}"

		if [ ${sim_status_code} -eq ${EXIT_SIM_READY} ]; then
			cli_print_fmt1 2 "IMSI" \
				"$(modem_get_sim_imsi ${device})"
			cli_print_fmt1 2 "Operator" \
				"$(modem_get_network_operator ${device})"
			cli_print_fmt1 2 "Mode" \
				"$(modem_get_network_mode ${device})"
			cli_print_fmt1 2 "Signal quality" \
				"$(modem_get_signal_quality ${device}) dBm"

			local ber=$(modem_get_bit_error_rate ${device})
			isset ber || ber="unknown"
			cli_print_fmt1 2 "Bit Error Rate" "${ber}"
		fi
	fi
	cli_space
}

function cli_device_discover() {
	local device=${1}
	shift

	local device_type=$(device_get_type ${device})
	if [ "${device_type}" != "real" ]; then
		return ${EXIT_OK}
	fi

	local raw

	while [ $# -gt 0 ]; do
		case "${1}" in
			--raw)
				raw=1
				;;
		esac
		shift
	done

	local up
	device_is_up ${device} && up=1
	device_set_up ${device}

	enabled raw || echo "${device}"

	local hook
	local out
	local ret
	for hook in $(hook_zone_get_all); do
		out=$(hook_zone_exec ${hook} discover ${device})
		ret=$?

		[ ${ret} -eq ${DISCOVER_NOT_SUPPORTED} ] && continue

		if enabled raw; then
			case "${ret}" in
				${DISCOVER_OK})
					echo "${hook}: OK"
					local line
					while read line; do
						echo "${hook}: ${line}"
					done <<<"${out}"
					;;

				${DISCOVER_ERROR})
					echo "${hook}: FAILED"
					;;
			esac
		else
			case "${ret}" in
				${DISCOVER_OK})
					echo "  ${hook} was successful."
					local line
					while read line; do
						echo "  ${line}"
					done <<<"${out}"
					;;

				${DISCOVER_ERROR})
					echo "  ${hook} failed."
					;;
			esac
		fi
	done

	echo # New line

	[ "${up}" = "1" ] || device_set_down ${device}
}

function cli_device_serial_unlock() {
	if cli_help_requested $@; then
		cli_show_man network-device
		exit ${EXIT_OK}
	fi

	local device=${1}
	assert isset device

	if ! device_is_serial ${device}; then
		error "${device} is not a serial device."
		error "Unlocking is only supported for serial devices."
		exit ${EXIT_ERROR}
	fi

	# Read the current state of the SIM card.
	modem_sim_status ${device} &>/dev/null
	local sim_status_code=$?

	# If the SIM card is already unlocked, we don't need to do anything.
	if [ ${sim_status_code} -eq ${EXIT_SIM_READY} ]; then
		print "The SIM card is already unlocked."
		exit ${EXIT_OK}

	# If the SIM card is in an unknown state, we cannot do anything.
	elif [ ${sim_status_code} -eq ${EXIT_SIM_UNKNOWN} ]; then
		error "The SIM card is in an unknown state."
		exit ${EXIT_ERROR}
	fi

	# Ask for the code.
	local code=${2}
	local require_new_pin="false"
	local new_pin

	while ! isinteger code; do
		local message
		case "${sim_status_code}" in
			${EXIT_SIM_PIN})
				message="Please enter PIN:"
				;;
			${EXIT_SIM_PUK})
				message="Please enter PUK:"
				require_new_pin="true"
				;;
		esac
		assert isset message

		echo -n "${message} "
		read -s code
		echo # Print newline.

		if enabled require_new_pin; then
			local i new_pin2
			for i in 0 1; do
				case "${i}" in
					0)
						message="Please enter a new PIN code:"
						;;
					1)
						message="Please confirm the new PIN code:"
						;;
				esac

				echo -n "${message} "
				read -s new_pin2
				echo # Print newline.

				if [ -n "${new_pin}" ]; then
					if [ "${new_pin}" != "${new_pin2}" ]; then
						error "The entered PIN codes did not match."
						exit ${EXIT_ERROR}
					fi
				else
					new_pin=${new_pin2}
				fi
			done
		fi
	done

	# Trying to unlock the SIM card.
	modem_sim_unlock ${device} ${code} ${new_pin}

	exit $?
}

function cli_hostname() {
	if cli_help_requested $@; then
		cli_show_man network
		exit ${EXIT_OK}
	fi

	local hostname=${1}

	if [ -n "${hostname}" ]; then
		config_hostname ${hostname}
		log INFO "Hostname was set to '${hostname}'."
		log INFO "Changes do only take affect after reboot."
		exit ${EXIT_OK}
	fi

	echo "$(config_hostname)"
	exit ${EXIT_OK}
}

function cli_port() {
	if cli_help_requested $@; then
		cli_show_man network-port
		exit ${EXIT_OK}
	fi

	local action
	local port

	if port_exists ${1}; then
		port=${1}
		action=${2}
		shift 2

		# Action aliases
		case "${action}" in
			start)
				action="up"
				;;
			stop)
				action="down"
				;;
			show)
				action="status"
				;;
		esac

		case "${action}" in
			edit|up|down|status)
				port_${action} ${port} $@
				;;
			*)
				error "Unrecognized argument: ${action}"
				exit ${EXIT_ERROR}
				;;
		esac
	else
		action=${1}
		shift

		case "${action}" in
			create|destroy)
				port_${action} $@
				;;
			*)
				error "Unrecognized argument: ${action}"
				exit ${EXIT_ERROR}
				;;
		esac
	fi
}

function cli_zone() {
	if cli_help_requested $@; then
		cli_show_man network-zone
		exit ${EXIT_OK}
	fi

	local action
	local zone

	if zone_name_is_valid ${1}; then
		zone=${1}
		action=${2}
		shift 2

		# Action aliases
		case "${action}" in
			start)
				action="up"
				;;
			stop)
				action="down"
				;;
			show)
				action="status"
				;;
		esac

		case "${action}" in
			config|down|edit|port|status|up)
				zone_${action} ${zone} $@
				;;
			*)
				error "Unrecognized argument: ${action}"
				cli_show_man network-zone
				exit ${EXIT_ERROR}
				;;
		esac
	else
		action=${1}
		shift

		case "${action}" in
			create)
				zone_${action} $@
				;;
			remove)
				cli_zone_remove $@
				;;
			list-hooks)
				cli_list_hooks zone $@
				;;
			""|*)
				if [ -n "${action}" ]; then
					error "Unrecognized argument: '${action}'"
					echo
				fi

				cli_show_man network-zone
				exit ${EXIT_ERROR}
				;;
		esac
	fi
}

# Removes a zone either immediately, if it is currently down,
# or adds a tag that the removal will be done when the zone
# is brought down the next time.
function cli_zone_remove() {
	if cli_help_requested $@; then
		cli_show_man network-zone
		exit ${EXIT_OK}
	fi

	local zone=${1}
	assert zone_exists ${zone}

	if zone_is_up ${zone}; then
		echo "Zone '${zone}' is up and will be removed when it goes down the next time."
		zone_remove ${zone}
	else
		echo "Removing zone '${zone}' now..."
		zone_remove_now ${zone}
	fi

	exit ${EXIT_OK}
}

function cli_list_hooks() {
	local type=${1}
	shift

	if cli_help_requested $@; then
		cli_show_man network-zone
		exit ${EXIT_OK}
	fi

	local hook_dir=$(hook_dir ${type})
	local hook

	for hook in ${hook_dir}/*; do
		hook=$(basename ${hook})
		if hook_exists ${type} ${hook}; then
			echo "${hook}"
		fi
	done | sort -u
}

function cli_route() {
	if cli_help_requested $@; then
		cli_show_man network-route
		exit ${EXIT_OK}
	fi

	local action=${1}
	shift

	case "${action}" in
		# Add a new route.
		add)
			route_add $@
			;;
		# Remove an existing route.
		remove)
			route_remove $@
			;;
		# List all routes.
		list)
			route_list $@
			return ${EXIT_OK}
			;;
		*)
			error "Unrecognized action: ${action}"
			cli_run_help network route

			exit ${EXIT_ERROR}
			;;
	esac

	# Applying all routes.
	route_apply

	exit ${EXIT_OK}
}

function cli_dhcpd() {
	local proto=${1}
	shift

	if cli_help_requested $@; then
		cli_show_man network-dhcp
		exit ${EXIT_OK}
	fi

	local action=${1}
	shift

	case "${action}" in
		edit)
			dhcpd_edit ${proto} $@
			;;
		start)
			dhcpd_start ${proto}
			;;
		stop)
			dhcpd_stop ${proto}
			;;
		restart|reload)
			dhcpd_reload ${proto}
			;;
		subnet)
			cli_dhcpd_subnet ${proto} $@
			;;
		show|"")
			cli_dhcpd_show ${proto} $@
			;;
		*)
			error "Unrecognized action: ${action}"
			cli_run_help network dhcpvN

			exit ${EXIT_ERROR}
			;;
	esac

	exit ${EXIT_OK}
}

function cli_dhcpd_show() {
	local proto=${1}
	assert isset proto

	local settings=$(dhcpd_settings ${proto})
	assert isset settings

	local ${settings}
	dhcpd_global_settings_read ${proto}

	cli_headline 1 "Dynamic Host Configuration Protocol Daemon for ${proto/ip/IP}"

	case "${proto}" in
		ipv6)
			cli_headline 2 "Lease times"
			if isinteger VALID_LIFETIME; then
				cli_print_fmt1 2 "Valid lifetime" "${VALID_LIFETIME}s"
			fi

			if isinteger PREFERRED_LIFETIME; then
				cli_print_fmt1 2 "Preferred lifetime" "${PREFERRED_LIFETIME}s"
			fi

			cli_space
			;;
		ipv4)
			cli_print_fmt1 1 "Authoritative" $(cli_print_enabled AUTHORITATIVE)
			cli_space

			cli_headline 2 "Lease times"
			cli_print_fmt1 2 "Default lease time" "${DEFAULT_LEASE_TIME}s"
			cli_print_fmt1 2 "Max. lease time" "${MAX_LEASE_TIME}s"

			if isset MIN_LEASE_TIME; then
				cli_print_fmt1 2 "Min. lease time" "${MIN_LEASE_TIME}s"
			fi

			cli_space
			;;
	esac

	# Read the options.
	local -A options
	dhcpd_global_options_read ${proto} ${subnet_id}

	# Print the options if any.
	if [ ${#options[*]} -gt 0 ]; then
		cli_headline 2 "Options"

		local option
		for option in $(dhcpd_options ${proto}); do
			[ -n "${options[${option}]}" ] || continue

			cli_print_fmt1 2 \
				"${option}" "${options[${option}]}"
		done
		cli_space
	fi

	# Subnets.
	local subnets=$(dhcpd_subnet_list ${proto})
	if [ -n "${subnets}" ]; then
		cli_headline 2 "Subnets"
		local subnet_id
		for subnet_id in ${subnets}; do
			cli_dhcpd_subnet_show ${proto} ${subnet_id} 2
		done
	fi
}

function cli_dhcpd_subnet() {
	local proto=${1}
	shift

	if cli_help_requested $@; then
		cli_show_man network-dhcp-subnet
		exit ${EXIT_OK}
	fi

	local action=${1}
	shift

	case "${action}" in
		new)
			dhcpd_subnet_new ${proto} $@
			;;
		remove)
			dhcpd_subnet_remove ${proto} $@
			;;
		[0-9]*)
			local subnet_id=${action}

			if ! dhcpd_subnet_exists ${proto} ${subnet_id}; then
				error "The given subnet with ID ${subnet_id} does not exist."
				return ${EXIT_ERROR}
			fi

			# Update the action.
			action=${1}
			shift

			case "${action}" in
				edit)
					dhcpd_subnet_edit ${proto} ${subnet_id} $@
					local ret=$?

					if [ ${ret} -eq ${EXIT_OK} ]; then
						dhcpd_reload ${proto}
					fi
					exit ${ret}
					;;
				range)
					cli_dhcpd_subnet_range ${proto} ${subnet_id} $@
					exit $?
					;;
				show)
					cli_dhcpd_subnet_show ${proto} ${subnet_id} $@
					exit $?
					;;
				options)
					cli_dhcpd_subnet_options ${proto} ${subnet_id} $@
					exit $?
					;;
				*)
					error "Unrecognized action: ${action}"
					cli_run_help network dhcpvN subnet
					exit ${EXIT_ERROR}
					;;
			esac
			;;
		show)
			local subnet_id
			for subnet_id in $(dhcpd_subnet_list ${proto}); do
				cli_dhcpd_subnet_show ${proto} ${subnet_id}
			done
			;;
		*)
			error "Unrecognized action: ${action}"
			cli_run_help network dhcpvN subnet

			exit ${EXIT_ERROR}
			;;
	esac

	exit ${EXIT_OK}
}

function cli_dhcpd_subnet_range() {
	local proto=${1}
	assert isset proto
	shift

	local subnet_id=${1}
	assert isset subnet_id
	shift

	local action=${1}
	shift

	case "${action}" in
		new)
			dhcpd_subnet_range_new ${proto} ${subnet_id} $@
			exit $?
			;;
		remove)
			dhcpd_subnet_range_remove ${proto} ${subnet_id} $@
			exit $?
			;;
		*)
			error "Unrecognized action: ${action}"
			cli_run_help network dhcpvN subnet range
			exit ${EXIT_ERROR}
			;;
	esac
}

function cli_dhcpd_subnet_show() {
	local proto=${1}
	assert isset proto

	local subnet_id=${2}
	assert isset subnet_id

	local level=${3}
	isset level || level=0

	local $(dhcpd_subnet_settings ${proto})

	# Read in configuration settings.
	dhcpd_subnet_read ${proto} ${subnet_id}

	cli_headline $(( ${level} + 1 )) \
		"DHCP${proto/ip/} subnet declaration #${subnet_id}"
	cli_print_fmt1 $(( ${level} + 1 )) \
		"Subnet" "${ADDRESS}/${PREFIX}"
	cli_space

	# Read the options.
	local -A options
	dhcpd_subnet_options_read ${proto} ${subnet_id}

	# Print the options if any.
	if [ ${#options[*]} -gt 0 ]; then
		cli_headline $(( ${level} + 2 )) "Options"

		local option
		for option in $(dhcpd_subnet_options ${proto}); do
			[ -n "${options[${option}]}" ] || continue

			cli_print_fmt1 $(( ${level} + 2 )) \
				"${option}" "${options[${option}]}"
		done
		cli_space
	fi

	# Ranges.
	cli_headline $(( ${level} + 2 )) "Ranges"

	local ranges=$(dhcpd_subnet_range_list ${proto} ${subnet_id})
	if isset ranges; then
		local range_id $(dhcpd_subnet_range_settings ${proto})
		for range_id in ${ranges}; do
			dhcpd_subnet_range_read ${proto} ${subnet_id} ${range_id}

			cli_print $(( ${level} + 2 )) \
				"#%d: %s - %s" ${range_id} ${START} ${END}
		done
	else
		cli_print $(( ${level} + 2 )) "No ranges have been defined."
	fi

	cli_space
}

function cli_dhcpd_options() {
	local proto=${1}
	assert isset proto
	shift

	local subnet_id=${1}
	assert isset subnet_id
	shift

	local valid_options=$(dhcpd_subnet_options ${proto})

	local key val
	while [ $# -gt 0 ]; do
		case "${1}" in
			*=*)
				key=$(cli_get_key ${1})
				val=$(cli_get_val ${1})

				dhcpd_subnet_option_set ${proto} ${subnet_id} ${key} ${val}
		esac
	done
}

function cli_start() {
	if cli_help_requested $@; then
		cli_show_man network
		exit ${EXIT_OK}
	fi

	local zones=$(zones_get $@)

	local zone
	for zone in ${zones}; do
		zone_start ${zone} &
	done

	wait # until everything is settled
}

function cli_stop() {
	if cli_help_requested $@; then
		cli_show_man network
		exit ${EXIT_OK}
	fi

	local zones=$(zones_get $@)

	local zone
	for zone in ${zones}; do
		zone_stop ${zone} &
	done

	wait # until everything is settled
}

function cli_restart() {
	if cli_help_requested $@; then
		cli_show_man network
		exit ${EXIT_OK}
	fi

	cli_stop $@

	# Give the system some time to calm down
	sleep ${TIMEOUT_RESTART}

	cli_start $@
}

function cli_status() {
	if cli_help_requested $@; then
		cli_show_man network
		exit ${EXIT_OK}
	fi

	# When dumping status information, the debug
	# mode clutters the console which is not what we want.
	# Logging on the console is disabled for a short time.
	local log_disable_stdout=${LOG_DISABLE_STDOUT}
	LOG_DISABLE_STDOUT="true"

	local zones=$(zones_get $@)

	local zone
	for zone in ${zones}; do
		zone_status ${zone}
	done

	# Reset logging.
	LOG_DISABLE_STDOUT=${log_disable_stdout}
}

function cli_reset() {
	if cli_help_requested $@; then
		cli_show_man network
		exit ${EXIT_OK}
	fi

	warning_log "Will reset the whole network configuration!!!"

	# Force mode is disabled by default
	local force=0

	while [ $# -gt 0 ]; do
		case "${1}" in
			--force|-f)
				force=1
				;;
		esac
		shift
	done

	# If we are not running in force mode, we ask the user if he does know
	# what he is doing.
	if ! enabled force; then
		if ! cli_yesno "Do you really want to reset the whole network configuration?"; then
			exit ${EXIT_ERROR}
		fi
	fi

	local zone
	for zone in $(zones_get --all); do
		zone_remove ${zone}
	done

	local port
	for port in $(ports_get --all); do
		port_remove ${port}
	done

	# Flush all DNS servers.
	dns_server_flush

	# Re-run the initialization functions
	init_run

	exit ${EXIT_OK}
}

# Help function: will show the default man page to the user.
# Optionally, there are two arguments taken, the type of hook
# and which hook should be shown.
function cli_help() {
	local type=${1}
	local what=${2}

	# Remove unknown types.
	if ! listmatch ${type} zone port config; then
		type=""
	fi

	# If no arguments were given, we will show the default page.
	if [ -z "${type}" ]; then
		cli_show_man network
		return ${EXIT_OK}
	fi

	if ! hook_exists ${type} ${what}; then
		error "Hook of type '${type}' and name '${what}' could not be found."
		exit "${EXIT_ERROR}"
	fi

	hook_exec ${type} ${what} help
}

function cli_dns() {
	if cli_help_requested $@; then
		cli_show_man network-dns
		exit ${EXIT_OK}
	fi

	# Get the command.
	local cmd=${1}; shift
	if [ -z "${cmd}" ]; then
		cli_show_man network-dns
		exit ${EXIT_ERROR}
	fi

	# Get the new server to process (if any).
	local server=${1}
	local priority=${2}

	case "${cmd}" in
		list)
			__dns_server_println "SERVER" "PRIORITY"
			dns_server_list
			exit ${EXIT_OK}
			;;
		add)
			log INFO "Adding new DNS server: ${server}"
			dns_server_add ${server} ${priority}
			;;
		remove)
			log INFO "Removing DNS server: ${server}"
			dns_server_remove ${server} ${priority}
			;;
		update)
			# Just run the update afterwards.
			;;
		*)
			error "No such command: ${cmd}"
			exit ${EXIT_ERROR}
	esac

	# Update the local DNS configuration after changes have been made.
	dns_generate_resolvconf
	radvd_update

	exit ${EXIT_OK}
}

# Process the given action
case "${action}" in
	init)
		init_run
		;;

	config|hostname|port|device|zone|start|stop|restart|status|reset|dns|route)
		cli_${action} $@
		;;

	# DHCP server configuration (automatically detects which protocol to use).
	dhcpv6|dhcpv4)
		cli_dhcpd ${action/dhcp/ip} $@
		;;

	""|help|--help|-h)
		cli_help $@
		;;

	*)
		error "Invalid command given: ${action}"
		cli_usage "network help"
		exit ${EXIT_CONF_ERROR}
		;;
esac

exit ${EXIT_OK}
