#!/bin/bash
#
# script: create_network_ini
#
# Copyright 2005-2025, Lime Technology
# Copyright 2012-2025, Bergware International.
#
# create initial network.ini file on system start
# create system welcome message
# update files on DHCP events 'BOUND[6] IPV4LL EXPIRE'
# update services listening interfaces / addresses
# exclude non-existing interfaces from config

[[ (-z $reason && -z $1) || (-n $reason && ! "BOUND6 IPV4LL EXPIRE" =~ $reason) ]] && exit 0

INI=/var/local/emhttp/network.ini.new
CFG=/boot/config/network.cfg
SYS=/sys/class/net

declare -A VLANID USE_DHCP IPADDR NETMASK GATEWAY METRIC USE_DHCP6 IPADDR6 NETMASK6 GATEWAY6 PRIVACY6 METRIC6 DESCRIPTION PROTOCOL

# run & log functions
. /etc/rc.d/rc.runlog

mask(){
  [[ -z $1 ]] && return
  # convert prefix to netmask
  set -- $((5-($1/8))) 255 255 255 255 $(((255<<(8-($1%8)))&255)) 0 0 0
  [[ $1 -gt 1 ]] && shift $1 || shift
  echo $1.$2.$3.$4
}

dns() {
  [[ $1 == 4 ]] && ADDR='(\d{1,3}\.){3}\d+' || ADDR='([0-9a-fA-F]{1,4}::?){1,7}[0-9a-fA-F]*'
  grep -Po "^nameserver \K$ADDR" /etc/resolv.conf
}

if [[ -s $CFG ]]; then
  # import existing settings
  . <(fromdos <$CFG)
else
  # import default settings
  IPADDR=
  NETMASK=
  GATEWAY=
  USE_DHCP=yes
  DHCP_KEEPRESOLV=no
  BONDING=yes
  BRIDGING=yes
fi
# prepare empty file
echo -n >$INI

# loop thru all defined interfaces (=1 in case of legacy)
for ((i=0; i<${SYSNICS:-1}; i++)); do
  IFACE=${IFNAME[$i]:-eth$i}
  ETH=${IFACE/#bond/eth}
  ETH=${ETH/#br/eth}
  # don't store when non-existing
  [[ -e $SYS/$ETH ]] || continue
  echo "[$ETH]" >>$INI
  if [[ $i -eq 0 ]]; then
    # process legacy settings
    [[ $BRIDGING == yes ]] && BRNICS=eth0
    [[ $BONDING == yes ]] && BRNICS=bond0
    [[ $BONDING == yes ]] && IFACE=bond0
    [[ $BRIDGING == yes ]] && IFACE=br0
    [[ $BONDING == yes ]] && BONDNICS=${BONDNICS:-eth0 eth1 eth2 eth3}
    if [[ ${USE_DHCP:-yes} == yes ]]; then
      # force DNS setting to automatic if not set
      DHCP_KEEPRESOLV=${DHCP_KEEPRESOLV:-no}
    else
      # force DNS setting to static
      DHCP_KEEPRESOLV=yes
    fi
    if [[ ${USE_DHCP6:-yes} == yes ]]; then
      # force DNS6 setting to automatic if not set
      DHCP6_KEEPRESOLV=${DHCP6_KEEPRESOLV:-no}
    else
      # force DNS6 setting to static
      DHCP6_KEEPRESOLV=yes
    fi
    echo "DHCP_KEEPRESOLV=\"$DHCP_KEEPRESOLV\"" >>$INI
    if [[ $DHCP_KEEPRESOLV == no ]]; then
      # dhcp assigned DNSv4 servers
      DNS4=$(dns 4)
      x=1
      for SERVER in $DNS4; do
        echo "DNS_SERVER$x=\"$SERVER\"" >>$INI
        ((x++))
      done
    else
      # static assigned DNSv4 servers
      echo "DNS_SERVER1=\"$DNS_SERVER1\"" >>$INI
      echo "DNS_SERVER2=\"$DNS_SERVER2\"" >>$INI
      echo "DNS_SERVER3=\"$DNS_SERVER3\"" >>$INI
    fi
    echo "DHCP6_KEEPRESOLV=\"$DHCP6_KEEPRESOLV\"" >>$INI
    if [[ $DHCP6_KEEPRESOLV == no ]]; then
      # dhcp assigned DNSv6 servers
      DNS6=$(dns 6)
      x=1
      for SERVER6 in $DNS6; do
        echo "DNS6_SERVER$x=\"$SERVER6\"" >>$INI
        ((x++))
      done
    else
      # static assigned DNSv6 servers
      echo "DNS6_SERVER1=\"$DNS6_SERVER1\"" >>$INI
      echo "DNS6_SERVER2=\"$DNS6_SERVER2\"" >>$INI
      echo "DNS6_SERVER3=\"$DNS6_SERVER3\"" >>$INI
    fi
  fi
  [[ -n ${BONDNICS[$i]} ]] && echo "BONDING=\"yes\"" >>$INI || echo "BONDING=\"no\"" >>$INI
  echo "BONDNAME=\"${BONDNAME[$i]:-bond0}\"" >>$INI
  echo "BONDNICS=\"${BONDNICS[$i]// /,}\"" >>$INI
  echo "BONDING_MODE=\"${BONDING_MODE[$i]:-1}\"" >>$INI
  echo "BONDING_MIIMON=\"${BONDING_MIIMON[$i]:-100}\"" >>$INI
  [[ -n ${BRNICS[$i]} ]] && echo "BRIDGING=\"yes\"" >>$INI || echo "BRIDGING=\"no\"" >>$INI
  echo "BRNAME=\"${BRNAME[$i]:-br0}\"" >>$INI
  echo "BRNICS=\"${BRNICS[$i]// /,}\"" >>$INI
  echo "BRSTP=\"${BRSTP[$i]:-0}\"" >>$INI
  echo "BRFD=\"${BRFD[$i]:-0}\"" >>$INI
  echo "DESCRIPTION:0=\"${DESCRIPTION[$i]}\"" >>$INI
  echo "PROTOCOL:0=\"${PROTOCOL[$i]}\"" >>$INI
  echo "USE_DHCP:0=\"${USE_DHCP[$i]}\"" >>$INI
  echo "USE_GW4:0=\"${USE_GW4[$i]}\"" >>$INI
  if [[ ${USE_DHCP[$i]} == yes ]]; then
    # get dhcp assigned ipv4 address & mask
    NET=($(ip -4 -br addr show $IFACE | awk '{sub("/"," ",$3);print $3;exit}'))
    GW=$(ip -4 route show default dev $IFACE | awk '{print $3;exit}')
    echo "IPADDR:0=\"${NET[0]}\"" >>$INI
    echo "NETMASK:0=\"$(mask ${NET[1]})\"" >>$INI
    echo "GATEWAY:0=\"$GW\"" >>$INI
    echo "METRIC:0=\"${METRIC[$i]}\"" >>$INI
  else
    # get static assigned ipv4 address & mask
    echo "IPADDR:0=\"${IPADDR[$i]}\"" >>$INI
    echo "NETMASK:0=\"${NETMASK[$i]}\"" >>$INI
    echo "GATEWAY:0=\"${GATEWAY[$i]}\"" >>$INI
    echo "METRIC:0=\"${METRIC[$i]}\"" >>$INI
  fi
  echo "USE_DHCP6:0=\"${USE_DHCP6[$i]}\"" >>$INI
  echo "USE_GW6:0=\"${USE_GW6[$i]}\"" >>$INI
  if [[ ${USE_DHCP6[$i]} == yes ]]; then
    # get auto assigned ipv6 address & prefix
    NET6=($(ip -6 -br addr show $IFACE scope global -temporary -deprecated | awk '{sub("/"," ",$3);print $3;exit}'))
    GW6=$(ip -6 route show default dev $IFACE | awk '{print $3;exit}')
    echo "IPADDR6:0=\"${NET6[0]}\"" >>$INI
    echo "NETMASK6:0=\"${NET6[1]}\"" >>$INI
    echo "GATEWAY6:0=\"$GW6\"" >>$INI
    echo "METRIC6:0=\"${METRIC6[$i]}\"" >>$INI
    echo "PRIVACY6:0=\"${PRIVACY6[$i]}\"" >>$INI
  else
    # get static assigned ipv6 address & prefix
    echo "IPADDR6:0=\"${IPADDR6[$i]}\"" >>$INI
    echo "NETMASK6:0=\"${NETMASK6[$i]}\"" >>$INI
    echo "GATEWAY6:0=\"${GATEWAY6[$i]}\"" >>$INI
    echo "METRIC6:0=\"${METRIC6[$i]}\"" >>$INI
    echo "PRIVACY6:0=\"\"" >>$INI
  fi
  echo "USE_MTU=\"${USE_MTU[$i]}\"" >>$INI
  echo "MTU=\"${MTU[$i]}\"" >>$INI
  if [[ -n ${VLANS[$i]} ]]; then
    # process VLAN interfaces
    echo "TYPE=\"trunk\"" >>$INI
    for ((j=1;j<${VLANS[$i]};j++)); do
      echo "VLANID:$j=\"${VLANID[$i,$j]}\"" >>$INI
      echo "DESCRIPTION:$j=\"${DESCRIPTION[$i,$j]}\"" >>$INI
      echo "PROTOCOL:$j=\"${PROTOCOL[$i,$j]}\"" >>$INI
      echo "USE_DHCP:$j=\"${USE_DHCP[$i,$j]}\"" >>$INI
      echo "USE_GW4:$j=\"${USE_GW4[$i,$j]}\"" >>$INI
      if [[ ${USE_DHCP[$i,$j]} == yes ]]; then
        DEV=$IFACE.${VLANID[$i,$j]}
        # get dhcp assigned ipv4 address & mask
        NET=($(ip -4 -br addr show $DEV | awk '{sub("/"," ",$3);print $3;exit}'))
        GW=$(ip -4 route show default dev $DEV | awk '{print $3;exit}')
        echo "IPADDR:$j=\"${NET[0]}\"" >>$INI
        echo "NETMASK:$j=\"$(mask ${NET[1]})\"" >>$INI
        echo "GATEWAY:$j=\"$GW\"" >>$INI
        echo "METRIC:$j=\"${METRIC[$i,$j]}\"" >>$INI
      else
        # get static assigned ipv4 address & mask
        echo "IPADDR:$j=\"${IPADDR[$i,$j]}\"" >>$INI
        echo "NETMASK:$j=\"${NETMASK[$i,$j]}\"" >>$INI
        echo "GATEWAY:$j=\"${GATEWAY[$i,$j]}\"" >>$INI
        echo "METRIC:$j=\"${METRIC[$i,$j]}\"" >>$INI
      fi
      echo "USE_DHCP6:$j=\"${USE_DHCP6[$i,$j]}\"" >>$INI
      echo "USE_GW6:$j=\"${USE_GW6[$i,$j]}\"" >>$INI
      if [[ ${USE_DHCP6[$i,$j]} == yes ]]; then
        DEV=$IFACE.${VLANID[$i,$j]}
        # get auto assigned ipv6 address & prefix
        NET6=($(ip -6 -br addr show $DEV scope global -temporary -deprecated | awk '{sub("/"," ",$3);print $3;exit}'))
        GW6=$(ip -6 route show default dev $DEV | awk '{print $3;exit}')
        echo "IPADDR6:$j=\"${NET6[0]}\"" >>$INI
        echo "NETMASK6:$j=\"${NET6[1]}\"" >>$INI
        echo "GATEWAY6:$j=\"$GW6\"" >>$INI
        echo "METRIC6:$j=\"${METRIC6[$i,$j]}\"" >>$INI
        echo "PRIVACY6:$j=\"${PRIVACY6[$i,$j]}\"" >>$INI
      else
        # get static assigned ipv6 address & prefix
        echo "IPADDR6:$j=\"${IPADDR6[$i,$j]}\"" >>$INI
        echo "NETMASK6:$j=\"${NETMASK6[$i,$j]}\"" >>$INI
        echo "GATEWAY6:$j=\"${GATEWAY6[$i,$j]}\"" >>$INI
        echo "METRIC6:$j=\"${METRIC6[$i,$j]}\"" >>$INI
        echo "PRIVACY6:$j=\"\"" >>$INI
      fi
    done
  else
    # interface without VLANs
    echo "TYPE=\"access\"" >>$INI
  fi
done
# atomically update file
mv $INI ${INI%.*}

log "interface=${interface:-$1}, reason=$reason, protocol=$protocol"
# delayed execution
/usr/local/emhttp/webGui/scripts/update_services 20

# generate our welcome text (management interface only)
if [[ -z $interface || "eth0 br0 bond0 wlan0" =~ $interface ]]; then
  . /etc/unraid-version
  echo -e "Unraid Server OS version: $version" >/etc/issue
  # find management interface
  [[ -e $SYS/bond0 ]] && dev=bond0 || dev=eth0
  [[ -e $SYS/br0 ]] && dev=br0
  IPv4=$(ip -4 -br addr show $dev scope global | awk '{print $3;exit}' | sed -r 's/\/[0-9]+//')
  IPv6=$(ip -6 -br addr show $dev scope global -temporary -deprecated | awk '{print $3;exit}' | sed -r 's/\/[0-9]+//')
  # show current IP assignment
  [[ -n $IPv4 ]] && echo " IPv4 address: $IPv4" >>/etc/issue || echo " IPv4 address: not set" >>/etc/issue
  [[ -n $IPv6 ]] && echo " IPv6 address: $IPv6" >>/etc/issue || echo " IPv6 address: not set" >>/etc/issue
  if [[ -e $SYS/wlan0 ]]; then
    echo "Wireless network:" >>/etc/issue
    IPv4=$(ip -4 -br addr show wlan0 scope global | awk '{print $3;exit}' | sed -r 's/\/[0-9]+//')
    IPv6=$(ip -6 -br addr show wlan0 scope global -temporary -deprecated | awk '{print $3;exit}' | sed -r 's/\/[0-9]+//')
    [[ -n $IPv4 ]] && echo " IPv4 address: $IPv4" >>/etc/issue || echo " IPv4 address: not set" >>/etc/issue
    [[ -n $IPv6 ]] && echo " IPv6 address: $IPv6" >>/etc/issue || echo " IPv6 address: not set" >>/etc/issue
  fi
  echo >>/etc/issue
fi
exit 0
