#!/bin/sh

PATH="/usr/lib/alterator-net-functions:$PATH"

etcnet_iface_dir=/etc/net/ifaces
etcnet_default_iface_dir="$etcnet_iface_dir/default"
resolvconf_rdelim='[[:space:]]\+'
resolvconf_wdelim=' '

. alterator-hw-functions
. shell-config
. shell-var

### common helpers

next_iface()
{
	local name="$1";shift
	local i="${1:-0}"
	local path="$etcnet_iface_dir/$name"
	while true; do
		[ -d "$path$i" ] || { echo "$name$i" && break; }
		i=$(($i + 1))
	done
}

### common iface options

write_iface_option()
{
    shell_config_set "$1/options" "$2" "$3"
}

write_iface_addr()
{
    echo "$2" >"$1/ipv4address"
}

write_iface_addresses()
{
	local addresses_file="$1/ipv4address"
	>"$addresses_file"
	for i in $2;do
	    echo "$i" >>"$addresses_file"
	done
}

read_iface_option()
{
    # shell_config_get always returns 0,
    # so we can't merge commands with || :(

    local ret="$(shell_config_get "$1/options" "$2")"
    if [ -z "$ret" ]; then
      local type="$(shell_config_get "$1/options" "TYPE")"
      [ -z "$type" ] ||
        ret="$(shell_config_get "$etcnet_default_iface_dir/options-$type" "$2")"
    fi
    if [ -z "$ret" ]; then
      ret="$(shell_config_get "$etcnet_default_iface_dir/options" "$2")"
    fi
    echo "$ret"
}

read_iface_addr()
{
    [ ! -s "$1/ipv4address" ] || grep -m 1 '^[0-9]' "$1/ipv4address"
}

read_iface_addresses()
{
    local retval
    local v=
    [ ! -s "$1/ipv4address" ] || v=$(grep '^[0-9]' "$1"/ipv4address| tr '\n' ' ')
    shell_var_trim retval "$v"
    echo "$retval"
}

__default_gw_re='^[[:space:]]*default[[:space:]]\+via[[:space:]]\([0-9.]\+\).*'

read_iface_default_gw()
{
    [ ! -s "$1/ipv4route" ] ||
	sed -n -e "s/$__default_gw_re/\1/p" \
	       -e 't l1' \
	       -e 'b' \
	       -e ': l1' \
	       -e 'q' \
	       "$1/ipv4route"
}

write_iface_default_gw()
{
    [ ! -s "$1/ipv4route" ] ||
	sed "/$__default_gw_re/ d" -i "$1/ipv4route"
    [ -z "$2" ] ||
	printf 'default via %s\n' "$2" >>"$1/ipv4route"
}

read_iface_search()
{
    local retval
    local v="$(shell_config_get "$1/resolv.conf" search "$resolvconf_rdelim"| tr '\n' ' ')"
    shell_var_trim retval "$v"
    echo "$retval"
}

write_iface_search()
{
	local resolvconf_file="$1/resolv.conf"

	shell_config_del "$resolvconf_file" search "$resolvconf_rdelim"
	[ -z "$2" ] || shell_config_set "$resolvconf_file" search "$2" "$resolvconf_rdelim" "$resolvconf_wdelim"

	[ -s "$resolvconf_file" ] || rm -f -- "$resolvconf_file"
}

read_iface_dns()
{
    local retval
    local v="$(shell_config_get "$1/resolv.conf" nameserver "$resolvconf_rdelim"| tr '\n' ' ')"
    shell_var_trim retval "$v"
    echo "$retval"
}

write_iface_dns()
{
	local resolvconf_file="$1/resolv.conf"

	shell_config_del "$resolvconf_file" nameserver "$resolvconf_rdelim"

	local IFS=' '
	for i in $2;do
	    printf 'nameserver %s\n' "$i" >>"$resolvconf_file"
	done

	[ -s "$resolvconf_file" ] || rm -f -- "$resolvconf_file"
}

### string ppp options

write_ppp_option()
{
    shell_config_set "$1/pppoptions" "$2" "$3" '[[:space:]]\+' ' '
    chmod o-rw "$name/pppoptions"
}

read_ppp_option()
{
    shell_config_get "$1/pppoptions" "$2" '[[:space:]]\+'
}

### boolean ppp options

read_ppp_option1()
{
    local file="$1";shift
    local name1="$1";shift
    local name2="$1";shift
    local defvalue="${1:-no}";shift ||:

    if grep -qs "^$name1\$" "$file/pppoptions";then
	echo 'yes'
    elif grep -qs "^$name2\$" "$file/pppoptions"; then
	echo 'no'
    else
	echo "$defvalue"
    fi
}

write_ppp_option1()
{
    local file="$1";shift
    local name1="$1";shift
    local name2="$1";shift
    local value="$1";shift

    [ -f "$file/pppoptions" ] || touch "$file/pppoptions"

    sed \
	-e "/^$name1/d" \
	-e "/^$name2/d " \
	-i "$file/pppoptions"

    if [ "$value" -eq 0 ]; then
	echo "$name1" >> "$file/pppoptions"
    else
	echo "$name2" >> "$file/pppoptions"
    fi
}

next_ppp()
{
    # not to interfere with dialup ppp0
    next_iface ppp 1
}

### list various interface types

list_ppp()
{
    local t="${1:-}"
    for i in `find "$etcnet_iface_dir" -type d`; do
	[ "$(read_iface_option "$i" TYPE)" != "ppp" ] ||
	[ -n "$t" -a "$(read_iface_option "$i" PPPTYPE)" != "$t" ] ||
	    echo "${i##*/}" 2>/dev/null
    done
}

list_eth()
{
    netdev_list|
	while read iface; do
	    netdev_is_eth "$iface" || continue
	    netdev_is_real "$iface" || continue

	    local b="$(netdev_find_bridge "$iface")"
	    echo "${b:-$iface}" 2>/dev/null
	done
}

list_iface()
{
    netdev_list|
	while read iface; do
	    [ "$iface" != "lo" ] || continue
	    [ -z "$(netdev_find_bridge "$iface")" ] || continue
	    local tf="/sys/class/net/$iface/type"
	    [ ! -f "$tf" ] || [ "$(cat "$tf")" != 801 ] || continue
	    echo "$iface" 2>/dev/null
	done
}

list_static_iface()
{
    for i in `find "$etcnet_iface_dir" -type d`; do
	local name="${i##*/}"
	[ "$name" != "lo" -a "$name" != "default" -a "$name" != "unknown" ] || continue
	[ "$(read_iface_option $i BOOTPROTO)" = "static" -a -s "$i/ipv4address" ] || continue
	echo "${i##*/}"
    done
}

list_network()
{
    for i in `find "$etcnet_iface_dir" -type d`; do
	local name="${i##*/}"
	[ "$name" != "lo" -a "$name" != "default" -a "$name" != "unknown" ] || continue

	local proto="$(read_iface_option $i BOOTPROTO)"
	local addr=
	if [ "$proto" = "static" ];then
	    addr="$(read_iface_addresses $i)"
	else
	    addr="$(/sbin/ip a s "$name" 2>/dev/null|sed -n 's,[[:space:]]\+inet[[:space:]]\+\([.0-9/]\+\).*,\1,p')"
	fi
	[ -z "$addr" ] || netname "$addr" | cut -f1
    done
}

### start/stop interfaces

iface_up()
{
    local iface="$1";shift
    local n="${1:-0}";shift

    env -i PATH="$PATH" HOME="$HOME" TMPDIR="$TMPDIR" /sbin/ifup "$iface" >/dev/null
    for i in $(seq 0 "$n"); do
	netdev_is_up "$iface" && return 0
	[ "$i" != "$n" ] || sleep 1
    done
    return 1
}

iface_down()
{
    local iface="$1";shift
    local n="${1:-0}";shift

    env -i PATH="$PATH" HOME="$HOME" TMPDIR="$TMPDIR" /sbin/ifdown "$iface" >/dev/null
    for i in $(seq 0 "$n"); do
	netdev_is_up "$iface" || return 0
	[ "$i" != "$n" ] || sleep 1
    done
    return 1
}

### ipv4address calculations

ipv4addr_to_hex()
{
	if [ -n "$1" ];then
		local i=0
		local tmp="$(echo "$1" | tr '.' ' ')"
		for b in $tmp;do
			[ "$b" -ge 0 -a "$b" -le 255 ] || break
			i=$(($i+1))
		done
		[ "$i" -eq 4 ] && printf '0x%02x%02x%02x%02x' $tmp
	fi
}

ipv4addr_is_in_subnet()
{
	local ip="$1";shift
	local net="$1";shift

	local hex_addr="$(ipv4addr_to_hex "$ip")"
	local hex_net="$(ipv4addr_to_hex "${net%%/*}")"
	local prefix="${net##*/}"
	local p=0xffffffff
	local hex_mask="$(( $p - ($p >> $prefix) ))"
	[ "$(( $hex_net & $hex_mask ))" -eq "$(( $hex_addr & $hex_mask ))" ]
}

ipv4addr_mask_to_prefix()
{
	local hex_mask="$(ipv4addr_to_hex "$1")";shift

	if [ -n "$hex_mask" ];then
		local p=$(( ~$hex_mask & 0xffffffff ))
		local i=0 prefix=
		while [ "$p" -ne 0 ];do
			p=$(($p >> 1 & 0xffffffff))
			i=$(($i + 1))
		done
		prefix=$((32 - $i))
		[ "$prefix" -ge 0 -a "$prefix" -le 32 ] && echo "$prefix"
	fi
}

ipv4addr_prefix_to_mask()
{
	local len=$1;shift

	[ "$len" -ge 0 -a "$len" -le 32 ] || return

	local position=$((0xFFFFFFFF))
	local mask=$(( $position - ($position >> $len) ))

	printf '%s.%s.%s.%s\n' \
	    "$(($mask >> 24 & 0xff))" \
	    "$(($mask >> 16 & 0xff))" \
	    "$(($mask >> 8 & 0xff))" \
	    "$(($mask & 0xff))"
}
