#!/bin/bash -efu

. /.initrd/initenv
. /etc/init.d/functions
. shell-ip-address
. network-sh-functions

lower_before_char()
{
	local part="${2%%$1*}"
	if [ "$part" != "$2" ]; then
		printf '%s%s%s\n' "${part,,}" "$1" "${2#*$1}"
	else
		printf '%s\n' "$2"
	fi
}

ip_to_var()
{
	local i v="$1:"
	set --

	while [ -n "$v" ]; do
		if [ -z "${v##\[*:*:*\]:*}" ]; then
			# handle IPv6 address
			i="${v%%\]:*}"
			i="${i##\[}"

			set -- "$@" "$i"
			v=${v#\[$i\]:}
		else
			set -- "$@" "${v%%:*}"
			v=${v#*:}
		fi
	done

	ipaddr='' srv='' gw='' netmask='' hostname='' interface='' autoconf='' macaddr='' mtu='' dns1='' dns2=''

	if [ $# -eq 0 ]; then
		autoconf="error"
		return 0
	fi

	# format: ip=<autoconf>

	if [ $# -eq 1 ]; then
		autoconf="$1"
		return 0
	fi

	# format: ip=<interface>:<autoconf>[:[<mtu>][:<macaddr>]]

	case "${2-}" in
		on|any|dhcp|auto|dhcp4|auto4|dhcp6|auto6)
			interface="$1"
			autoconf="$2"
			shift 2
			[ $# -eq 0 ] ||
				{ mtu="$1"; shift; }
			[ $# -ne 6 ] ||
				{ macaddr="$1:$2:$3:$4:$5:$6"; shift 6; }
			[ $# -eq 0 ] ||
				fatal "syntax error: macaddr must contain 6 components"
			[ -n "$interface" ] ||
				fatal "interface name must be non-empty"
			return 0
			;;
	esac

	# format: ip=<ipaddr>:[<peer>]:<gw>:<netmask>:<hostname>:<interface>:<autoconf>[:[<mtu>][:<macaddr>]]

	ipaddr="$1" srv="$2" gw="$3" netmask="$4" hostname="$5" interface="$6" autoconf="$7"
	shift 7

	[ -n "$interface" ] ||
		fatal "interface name must be non-empty"

	[ $# -gt 0 ] ||
		return 0

	if [ -z "$1" ] || [ -n "${1##*[!0-9]*}" ]; then
		mtu="$1"; shift
		[ $# -ne 6 ] ||
			{ macaddr="$1:$2:$3:$4:$5:$6"; shift 6; }
		[ $# -eq 0 ] ||
			fatal "syntax error: macaddr must contain 6 components"
		return 0
	fi

	# format: ip=<ipaddr>:[<peer>]:<gw>:<netmask>:<hostname>:<interface>:<autoconf>[:[<dns1>][:<dns2>]]

	dns1="$1"; shift
	[ $# -eq 0 ] || dns2="$1"
}

route_to_var()
{
	local i v="$1:"
	set --
	while [ -n "$v" ]; do
		if [ "${v#\[*:*:*\]:}" != "$v" ]; then
			# handle IPv6 address
			i="${v%%\]:*}"
			i="${i##\[}"
			set -- "$@" "$i"
			v=${v#\[$i\]:}
		else
			set -- "$@" "${v%%:*}"
			v=${v#*:}
		fi
	done

	# format: route=<net>/<netmask>:<gateway>:<interface>

	route_mask='' route_gw='' interface=''

	case "$#" in
		3)
			route_mask="$1"
			route_gw="$2"
			interface="$3"
			;;
		*)
			fatal "syntax error: route must contain 3 components"
			;;
	esac
}

ifname_to_var()
{
	local IFS=:

	# format: ifname=<interface>:<macaddr>
	set :$1

	interface='' macaddr=''

	case "$#" in
		7)
			interface="${1#:}"
			macaddr="$2:$3:$4:$5:$6:$7"
			;;
		*)
			fatal "syntax error: ifname must contain 7 components"
			;;
	esac
}

get_version()
{
	local i="${2-}"
	case "$1" in
	*.*.*.*) i=4 ;;
	*:*:*)   i=6 ;;
	esac
	ip_version=$i
}

configure()
{
	if [ "${NAMESERVER:-0}" != 0 ]; then
		printf '# cmdline nameservers start\n' > /etc/resolv.conf
		i=0
		while [ $i -lt ${NAMESERVER:-0} ]; do
			eval "OPTS=\"\$NAMESERVER$i\""
			OPTS="${OPTS#[}"
			OPTS="${OPTS%]}"
			printf 'nameserver %s\n' "$OPTS"
			i=$(($i+1))
		done >> /etc/resolv.conf
		printf '# cmdline nameservers end\n' >> /etc/resolv.conf
	fi

	i=0
	while [ $i -lt ${IFNAME:-0} ]; do
		eval "ifname_to_var \"\$IFNAME$i\""

		# udev requires MAC addresses to be lower case
		macaddr="$(lower_before_char '=' "$macaddr")"

		printf 'SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="%s", ATTR{dev_id}=="0x0", ATTR{type}=="1", NAME="%s"\n' \
			"$macaddr" "$interface" >> /etc/udev/rules.d/60-persistent-net.rules

		i=$(($i+1))
	done

	i=0
	while [ $i -lt ${IP:-0} ]; do
		eval "ip_to_var \"\$IP$i\""

		dir=
		[ "${#interface}" != 0 ] &&
			dir="$net_confdir/ifaces/$interface" ||
			dir="$net_confdir/default"

		rm -rf -- "$dir"
		mkdir -p -- "$dir"

		case "$autoconf" in
			none|off|static)
				;;
			on|any|dhcp)
				:> "$dir/ipv4dhcp"
				:> "$dir/ipv6dhcp"
				;;
			dhcp4)
				:> "$dir/ipv4dhcp"
				;;
			dhcp6)
				:> "$dir/ipv6dhcp"
				;;
			*)
				fatal "unknown autoconf value: $autoconf"
				;;
		esac

		:> "$dir/options"
		:> "$dir/iplink"

		[ "${#macaddr}" = 0 ] || [ "${#interface}" = 0 ] ||
			printf "set address %s\n" "$(lower_before_char '=' "$macaddr")" >> "$dir/iplink"

		[ -z "$mtu" ] ||
			printf 'set mtu %s\n' "$mtu" >> "$dir/iplink"

		[ -s "$dir/iplink" ] ||
			rm -f -- "$dir/iplink"

		[ -z "$hostname" ] ||
			printf '%s\n' "$hostname" > "$dir/hostname"

		mask=
		if [ -n "$netmask" ]; then
			[ -z "${netmask##*[!0-9]*}" ] ||
				mask="$netmask"
			[ -n "${netmask##*.*.*.*}" ] ||
				mask=$(ipv4_mask2prefix "$netmask") ||
				fatal "unable to convert prefix to mask: $netmask"
		fi

		if [ -n "$ipaddr" ]; then
			get_version "$ipaddr" 4
			printf '%s\n' "$ipaddr${mask:+/$mask}${srv:+ peer $srv}" > "$dir/ipv${ip_version}address"
		fi

		if [ -n "$mask" ] && [ -n "$gw" ]; then
			get_version "$gw" 4
			printf 'default via %s\n' "$gw" > "$dir/ipv${ip_version}route"
		fi

		[ -z "$dns1" ] && [ -z "$dns2" ] ||
			printf 'nameserver %s\n' $dns1 $dns2 >> "$dir/resolv.conf"

		i=$(($i+1))
	done

	i=0
	while [ $i -lt ${ROUTE:-0} ]; do
		eval "route_to_var \"\$ROUTE$i\""

		dir="$net_confdir/ifaces/$interface"
		mkdir -p -- "$dir"

		get_version "$route_gw" 4
		printf '%s\n' "$route_mask${route_gw:+ via $route_gw}" >> "$dir/ipv${ip_version}route"

		i=$(($i+1))
	done
}

[ "$IP" = 0 ] && [ "$ROUTE" = 0 ] && [ "$NAMESERVER" = 0 ] && [ "$IFNAME" = 0 ] ||
	action_shell 'Generating network configuration:' configure
