#!/usr/bin/sh

# Copyright (c) 2017, Mellanox Technologies
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
#    list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright notice,
#    this list of conditions and the following disclaimer in the documentation
#    and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# The views and conclusions contained in the software and documentation are those
# of the authors and should not be interpreted as representing official policies,
# either expressed or implied, of the FreeBSD Project.

TMP_MNT=

usage() {
  echo "syntax: bfpxe [-d <rootfs-device>]"
}

#
# Convert netmask to prefix length.
# For example, convert 255.255.255.0 to /24
#
netmask_to_prefix() {
   c=0 x=0"$(printf '%o' "$(echo "$1" | sed -r 's/[\.]+/ /g')")"
   while [ "$x" -gt 0 ]; do
     c=$((c+$((x%2))))
     x=$((x/2))
   done
   echo /$c
}

#
# Create network interfaces.
# Create network configuration if root_device is specified.
#
create_net() {
  bfnet=""
  bfmac=""
  eval "$1"
  if [ -n "$bfnet" ] || [ -n "$bfmac" ]; then
    if [ -n "$bfnet" ]; then
        ifname=$(echo $bfnet | cut -d: -f1)
        ifaddr=$(echo $bfnet | cut -d: -f2)
        ifnet=$(echo $bfnet | cut -d: -f3)
    fi
    if [ -n "$bfmac" ]; then
        realbfmac=$(echo $bfmac | cut -d: -f1 | tr '-' ':')
        ifname=$(grep $realbfmac /sys/class/net/*/address | awk -F\/ {'print $5'})
        ifaddr=$(echo $bfmac | cut -d: -f2)
        ifnet=$(echo $bfmac | cut -d: -f3)
    fi
    # vlan check
    if echo "$ifname" | grep -q -E ".+\..+" >/dev/null; then
      vname=$(echo "$ifname" | cut -d. -f1)
      vid=$(echo "$ifname" | cut -d. -f2)
      if [ -n "$vname" ] && [ -n "$vid" ]; then
        modprobe 8021q
        ip link add link "$vname" name "$ifname" type vlan id "$vid"
        ifconfig "$vname" 0.0.0.0 up
        if [ -n "$TMP_MNT" ]; then
          mkdir -p "$TMP_MNT"/etc/systemd/network 2>/dev/null
          cat  >"$TMP_MNT"/etc/systemd/network/04-"${ifname}".netdev <<EOF
[NetDev]
Name = $ifname
Kind = vlan

[VLAN]
Id = $vid
EOF
          cat  >"$TMP_MNT"/etc/systemd/network/05-"${ifname}".network <<EOF
[Match]
Name = $ifname

[Network]
EOF
          echo "VLAN=${ifname}" >> "$TMP_MNT"/etc/systemd/network/05-"${vname}".network
        fi
      fi
    fi

    # IPv6 address
    if [ "$ifaddr" = "dhcp6" ]; then
      if [ -x /sbin/dhclient ]; then
        /sbin/dhclient -6 "$ifname"
      fi
      if [ -n "$TMP_MNT" ]; then
        sed -i '/Address =/d' "$TMP_MNT"/etc/systemd/network/05-"${ifname}".network
        echo "DHCP = ipv6" >> "$TMP_MNT"/etc/systemd/network/05-"${ifname}".network
      fi
    # IPv4 address
    elif [ "$ifaddr" = "dhcp" ]; then
      ifconfig "$ifname" 0.0.0.0 up
      if [ -x /sbin/dhclient ]; then
        /sbin/dhclient "$ifname"
      else
        udhcpc -R -b -A 5 -T 3 -t 10 -i "$ifname"
      fi
      if [ -n "$TMP_MNT" ]; then
        sed -i '/Address =/d' "$TMP_MNT"/etc/systemd/network/05-"${ifname}".network
        echo "DHCP = ipv4" >> "$TMP_MNT"/etc/systemd/network/05-"${ifname}".network
      fi
    else
      ifconfig "$ifname" "$ifaddr" ${ifnet:+netmask $ifnet} up
      if [ -n "$TMP_MNT" ]; then
        sed -i '/DHCP =/d' "$TMP_MNT"/etc/systemd/network/05-"${ifname}".network
        echo "Address = ${ifaddr}${ifnet:+$(netmask_to_prefix "$ifnet")}" \
          >> "$TMP_MNT"/etc/systemd/network/05-"${ifname}".network
      fi
    fi
  fi
}

# Parse the argument.
while getopts ":d:" opt; do
    case $opt in
        d)
            root_device=$OPTARG
            ;;
        \?)
            usage >&2
            exit 1
        ;;
    esac
done

# Mount the root device.
if [ -e "$root_device" ]; then
  TMP_MNT=/tmp/.bfmnt
  mkdir -p $TMP_MNT 2>/dev/null
  mount "$root_device" $TMP_MNT
  if [ ! -d $TMP_MNT/bin ]; then
    umount $TMP_MNT 2>/dev/null
    TMP_MNT=
  else
    # Delete the default static IP address if bfnet is configured.
    tmp=$(grep bfnet /proc/cmdline)
    if [ -n "$tmp" ]; then
      sed -i '/Address =/d' $TMP_MNT/etc/systemd/network/05-tmfifo_net0.network
    fi
  fi
fi

# Parse kernel arguments.
# Note: Disable check for reading lines, we are only reading words here.
# shellcheck disable=SC2013
for i in $(cat /proc/cmdline); do
  # Network config.
  if [ "$(echo "$i" | cut -c1-6)" = "bfnet=" ] || [ "$(echo "$i" | cut -c1-6)" = "bfmac=" ]; then
    create_net "$i"
  fi

  # Kickstart script.
  if [ "$(echo "$i" | cut -c1-5)" = "bfks=" ]; then
    eval "$i"
  fi
done

# Unmount the root device.
# It is provided for the kickstart script to install network cofiguration,
# so exit here once it's done.
if [ -n "$TMP_MNT" ]; then
  umount $TMP_MNT 2>/dev/null
  rmdir $TMP_MNT 2>/dev/null
  exit
fi

# Run the kickstart script.
if [ -n "$bfks" ]; then
  # shellcheck disable=SC2164
  cd /tmp
  count=0
  # Networking is complicated and can be slow. This loop will fail fast
  # when wget succeeds.
  until wget "$bfks"; do
    sleep 3
    count=$((count+1))
    if [ $count -eq 60 ]; then
      break
    fi
  done
  bfks=$(basename "$bfks")
  if [ -f "$bfks" ]; then
    chmod +x "$bfks"
    ./"$bfks"
  else
    echo "ERROR: Failed to download $bfks"
  fi
fi
