#!/bin/bash
#
#  Copyright (c) 2024, The OpenThread Authors.
#  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.
#  3. Neither the name of the copyright holder nor the
#     names of its contributors may be used to endorse or promote products
#     derived from this software without specific prior written permission.
#
#  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 HOLDER 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.
#
#   Description:
#       This script manipulates DHCPv6-PD-REF configuration.
#

# TODO: set the upstream interface according to the environment variables of `script/setup`.
UPSTREAM_INTERFACE="eth0"
WPAN_INTERFACE="wpan0"
LARGE_METRIC="2048" # A high metric value for loopback to make its routes less preferred.

DHCPCD_ENTER_HOOK="/etc/dhcpcd.enter-hook"
DHCPCD_EXIT_HOOK="/etc/dhcpcd.exit-hook"

PD_DAEMON_DIR="/opt/pd-daemon"
PD_DAEMON_PATH="${PD_DAEMON_DIR}/dhcp6_pd_daemon.py"
PD_DAEMON_SERVICE_NAME="dhcp6_pd_daemon.service"
PD_DAEMON_SERVICE_PATH="/etc/systemd/system/${PD_DAEMON_SERVICE_NAME}"

DHCP_CONFIG_PATH="/etc/dhcpcd.conf"
DHCP_CONFIG_ORIG_PATH="/etc/dhcpcd.conf.orig"
DHCP_CONFIG_PD_PATH="/etc/dhcpcd.conf.pd"
DHCP_CONFIG_NO_PD_PATH="/etc/dhcpcd.conf.no-pd"

# Create dhcpcd configuration file with ipv6 prefix request.
create_dhcpcd_conf_pd()
{
    sudo tee ${DHCP_CONFIG_PD_PATH} >/dev/null <<EOF
noipv6rs # disable router solicitation
# The 'interface lo' block with a high 'metric' value ensures that dhcpcd's
# default reject route for unassigned delegated prefixes on loopback interface,
# are less preferred.
# OpenThread will later install an active route for the delegated prefix on
# ${WPAN_INTERFACE} with a standard (lower) metric, ensuring it takes precedence.
interface lo
  metric ${LARGE_METRIC}
interface ${UPSTREAM_INTERFACE}
  iaid 1
  ia_pd 2/::/64
release
# Disable Router Solicitations (RS) again, specifically for ${UPSTREAM_INTERFACE}.
# This ensures that accept_ra is prevented from being set to 0, allowing
# the interface to accepting Router Advertisements and configuring IPv6
# based on them. The exact reason for requiring 'noipv6rs' twice
# is not fully understood but has been observed to be necessary through 
# experimentation.
noipv6rs
EOF
}

# Create dhcpcd configuration file with no prefix request.
create_dhcpcd_conf_no_pd()
{
    sudo tee ${DHCP_CONFIG_NO_PD_PATH} >/dev/null <<EOF
noipv6rs # disable router solicitation
EOF
}

create_dhcp6_pd_daemon_service()
{
    sudo tee ${PD_DAEMON_SERVICE_PATH} <<EOF
[Unit]
Description=Daemon to manage dhcpcd based on otbr-agent's PD state change
ConditionPathExists=${PD_DAEMON_PATH}
Requires=otbr-agent.service
After=otbr-agent.service

[Service]
Type=simple
User=root
ExecStart=/usr/bin/python3 ${PD_DAEMON_PATH}
Restart=on-failure

[Install]
WantedBy=multi-user.target
EOF
}

dhcpv6_pd_ref_uninstall()
{
    with DHCPV6_PD_REF || return 0

    if have systemctl; then
        sudo systemctl disable ${PD_DAEMON_SERVICE_NAME} || true
        sudo systemctl stop ${PD_DAEMON_SERVICE_NAME} || true
        sudo rm -f ${PD_DAEMON_SERVICE_PATH} || true
    fi

    if [[ -f ${DHCP_CONFIG_ORIG_PATH} ]]; then
        sudo mv ${DHCP_CONFIG_ORIG_PATH} ${DHCP_CONFIG_PATH}
    fi

    sudo rm -f ${DHCPCD_ENTER_HOOK} ${DHCPCD_EXIT_HOOK}
    sudo rm -f ${PD_DAEMON_PATH}

    if have systemctl; then
        sudo systemctl daemon-reload

        if systemctl is-active dhcpcd; then
            sudo systemctl restart dhcpcd || true
        fi
    fi
}

dhcpv6_pd_ref_install()
{
    with DHCPV6_PD_REF || return 0

    if [[ -f ${DHCP_CONFIG_PATH} ]]; then
        sudo mv ${DHCP_CONFIG_PATH} ${DHCP_CONFIG_ORIG_PATH}
    fi

    # Add dhcpcd.hooks
    sudo install -m 755 "$(dirname "$0")"/reference-device/dhcpcd.enter-hook ${DHCPCD_ENTER_HOOK}
    sudo install -m 755 "$(dirname "$0")"/reference-device/dhcpcd.exit-hook ${DHCPCD_EXIT_HOOK}
    sudo mkdir -p ${PD_DAEMON_DIR}
    sudo install -m 755 "$(dirname "$0")"/reference-device/dhcp6_pd_daemon.py ${PD_DAEMON_PATH}

    create_dhcpcd_conf_pd
    create_dhcpcd_conf_no_pd
    create_dhcp6_pd_daemon_service

    # The dhcp6_pd_daemon is currently disabled because it restarts dhcpcd
    # when the PD state changes. This restart disrupts mDNS, causing
    # connectivity issues. The daemon and its associated systemd service
    # files are still installed for potential future use.
    #
    # TODO: Re-enable and start the daemon when a solution is found
    #       for dhcpcd restarts breaking mDNS.
    #
    # if have systemctl; then
    #    sudo systemctl daemon-reload
    #    sudo systemctl enable ${PD_DAEMON_SERVICE_NAME}
    #    sudo systemctl start ${PD_DAEMON_SERVICE_NAME}
    # fi

    # Always enable PD, which is a workaround for the currently disabled
    # dhcp6_pd_daemon which caused mDNS disruptions.
    sudo cp ${DHCP_CONFIG_PD_PATH} ${DHCP_CONFIG_PATH}

    if have systemctl; then
        sudo systemctl daemon-reload

        # Restart dhcpcd only if it's running. This is unnecessary when the dhcp6_pd_daemon
        # is enabled, as the daemon will handle dhcpcd restarts based on PD state changes.
        if systemctl is-active dhcpcd; then
            sudo systemctl restart dhcpcd || true
        fi

        sudo systemctl enable radvd
    fi
}
