#!/bin/sh
#
# Manage IP address configuration.
#
# The IP address configuration exists in 3 places:
#
#  - The persistent configuration is stored in network.conf on
#    the configuration partition of the SD card.
#    This configuration is activated on boot.
#
#    The syntax of this file is as follows:
#
#      MODE=dhcp / MODE=static
#      IPADDR=172.16.3.4
#      NETMASK=255.255.0.0
#      GATEWAY=172.16.0.1
#
#  - The active configuration, which determines the actual IP address,
#    is stored in /var/lib/puzzlefw/cfg/network_active.conf in
#    the RAM filesystem. It has the same format as described above.
#
#    The same information is also present in /etc/network/interfaces.
#

. /opt/puzzlefw/lib/functions.sh

# Load the persistent or active configuration.
#   $1 = file name to read
load_network_config() {

    if [ ! -f ${CONFIG_DIR}/${1} ]; then
        echo "ERROR: File not found ${CONFIG_DIR}/${1}" >&2
        exit 1
    fi

    . ${CONFIG_DIR}/${1}
}

# Write configuration to file.
#   $1 = file name to write
write_network_config() {

    mkdir -p ${CONFIG_DIR}

    cat >${CONFIG_DIR}/${1} <<EOF
MODE=${MODE}
IPADDR=${IPADDR}
NETMASK=${NETMASK}
GATEWAY=${GATEWAY}
EOF
}

# Write /etc/network/interfaces
write_network_interfaces() {

    IF_METHOD="dhcp"
    IF_ADDRESS=""
    IF_NETMASK=""
    IF_GATEWAY=""

    if [ "$MODE" = "static" ]; then
        IF_METHOD="static"
        IF_ADDRESS="    address ${IPADDR}"
	IF_NETMASK="    netmask ${NETMASK}"
        if [ -n "$GATEWAY" ]; then
            IF_GATEWAY="    gateway ${GATEWAY}"
        fi
    fi

    echo "Writing /etc/network/interfaces ..."
    cat >/etc/network/interfaces.new <<EOF

auto lo
iface lo inet loopback

auto eth0
iface eth0 inet $IF_METHOD
$IF_ADDRESS
$IF_NETMASK
$IF_GATEWAY
EOF

    mv /etc/network/interfaces.new /etc/network/interfaces
}

# Initialize configuration from SD card.
ipcfg_init() {

    echo "Activating saved IP address configuration ..."

    # Lock to avoid conflicting changes.
    lock_config || exit 1

    # Persistent configuration file has already been copied from SD card.
    # Copy persistent configuration to active configuration.
    cp -a ${CONFIG_DIR}/network.conf ${CONFIG_DIR}/network_active.conf

    # Load persistent configuration.
    load_network_config network.conf

    # Write /etc/network/interfaces
    write_network_interfaces
}

# Show loaded network configuration.
show_network_config() {
    echo "    Mode:       $MODE"
    if [ "$MODE" = "static" ]; then
        echo "    IP address: $IPADDR"
        echo "    Netmask:    $NETMASK"
        echo "    Gateway:    $GATEWAY"
    fi
}

# Display configuration.
ipcfg_show() {

    load_network_config network_active.conf
    echo "Active IP address configuration:"
    show_network_config

    load_network_config network.conf
    echo "Saved IP address configuration:"
    show_network_config
}

# Check that parameter is a well-formed IPv4 address.
check_ipaddr() {
    IFS="." read a b c d <<EOF
$1
EOF
    for i in "$a" "$b" "$c" "$d" ; do
        if ! [ "$i" -ge 0 -a "$i" -le 255 ]; then
            echo "ERROR: Invalid IP address '$1'" >&2
            exit 1
        fi
    done
}

# Check that parameter is a valid IPv4 netmask.
check_netmask() {
    IFS="." read a b c d <<EOF
$1
EOF
    netmask_force_zero=0
    for i in "$a" "$b" "$c" "$d" ; do
        if [ "$netmask_force_zero" = "1" -a "$i" != "0" ]; then
            echo "ERROR: Invalid netmask '$1'" >&2
            exit 1
        fi
        if [ "$i" != "255" ]; then
            netmask_force_zero=1
            netmask_ok=0
            for k in 254 252 248 240 224 192 128 0 ; do
                if [ "$i" = "$k" ]; then
                    netmask_ok=1
                fi
            done
	    if [ "$netmask_ok" != "1" ]; then
                echo "ERROR: Invalid netmask '$1'" >&2
                exit 1
            fi
        fi
    done
}

# Parse IP address options.
parse_options() {

    while [ -n "$1" ]; do

        case "$1" in

          --mode)
            if [ "$2" != "dhcp" -a "$2" != "static" ]; then
                echo "ERROR: Unknown mode '$2'" >&2
                exit 1
            fi
            MODE="$2"
            ;;

          --ipaddr)
            check_ipaddr "$2"
            IPADDR="$2"
            ;;

          --netmask)
            check_netmask "$2"
            NETMASK="$2"
            ;;

          --gateway)
            [ -z "$2" ] || check_ipaddr "$2"
            GATEWAY="$2"
            ;;

          *)
            echo "ERROR: Unknown option '$1'" >&2
            exit 1
        esac

        shift 2
    done

    if [ -z "$MODE" ]; then
        echo "ERROR: Missing mode" >&2
        exit 1
    fi

    if [ "$MODE" = "static" ]; then
        if [ -z "$IPADDR" ]; then
            echo "ERROR: Missing IP address" >&2
            exit 1
        fi
        if [ -z "$NETMASK" ]; then
            echo "ERROR: Missing netmask" >&2
            exit 1
        fi
    else
        IPADDR=""
	NETMASK=""
	GATEWAY=""
    fi
}

# Change active IP address configuration.
ipcfg_config() {

    # Lock to avoid conflicting changes.
    lock_config || exit 1

    echo "Changing active IP address configuration ..."

    write_network_config network_active.conf.new
    mv ${CONFIG_DIR}/network_active.conf.new ${CONFIG_DIR}/network_active.conf

    echo "Shutting down eth0 ..."
    /sbin/ifdown eth0
    sleep 2

    write_network_interfaces

    echo "Starting eth0 ..."
    /sbin/ifup eth0 || exit 1
}

# Change the persistent IP address configuration on the SD card.
ipcfg_save() {

    # Lock to avoid conflicting changes.
    lock_config || exit 1

    echo "Changing saved IP address configuration ..."

    write_network_config network.conf.new || exit 1
    sync_config network.conf || exit 1
}

case "$1" in
  init)
    ipcfg_init
    ;;
  show)
    ipcfg_show
    ;;
  config)
    shift
    parse_options "$@"
    ipcfg_config
    ;;
  save)
    shift
    parse_options "$@"
    ipcfg_save
    ;;
  *)
    script="${0##*/}"
    cat <<EOF
Usage: $script {init|show|config|save|restart}

Manage IP address configuration.

  $script init
    Initialize IP address from saved configuration.
    This command is used during boot and should not be invoked manually.

  $script show
    Display active and saved IP address configuration.

  $script config {options}
    Change active IP address configuration.
    The new configuration is not written to the SD card.

    options:
      --mode dhcp           Enable configuration via DHCP.
      --mode static         Enable static IP address configuration.
      --ipaddr n.n.n.n      Specify static IPv4 address.
      --netmask n.n.n.n     Specify netmask.
      --gateway n.n.n.n     Specify gateway address, or "" to disable gateway.

  $script save {options}
    Change the saved IP address configuration on the SD card.
    Options are the same as for command 'config'.

EOF
    exit 1
    ;;
esac

exit $?