Skip to content

Commit

Permalink
Add pd daemon service: using systemd to run pd daemon service
Browse files Browse the repository at this point in the history
  • Loading branch information
yangsong-cnyn committed Nov 4, 2024
1 parent 872a8cb commit 558ca76
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 46 deletions.
71 changes: 62 additions & 9 deletions script/_dhcpv6_pd_ref
Original file line number Diff line number Diff line change
Expand Up @@ -30,28 +30,68 @@
# This script manipulates DHCPv6-PD-REF configuration.
#

customise_dhcpcd_conf()
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}"

create_pd_dhcpcd_conf()
{
# This has to be run after script/_border_routing, and this will
# invalidate all changes to dhcpcd.conf made by script/_border_routing.
sudo tee /etc/dhcpcd.conf >/dev/null <<EOF
sudo tee /etc/dhcpcd.conf.pd >/dev/null <<EOF
noipv6rs # disable router solicitation
interface eth0
iaid 1
ia_pd 2/::/64 -
noipv6rs
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
After=multi-user.service
ConditionPathExists=${PD_DAEMON_PATH}
[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 "/etc/dhcpcd.conf.orig" ]]; then
sudo mv /etc/dhcpcd.conf.orig /etc/dhcpcd.conf
fi
sudo systemctl restart dhcpcd
sudo rm -f /etc/dhcpcd.enter-hook /etc/dhcpcd.exit-hook

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()
Expand All @@ -60,9 +100,22 @@ dhcpv6_pd_ref_install()

if [[ -f "/etc/dhcpcd.conf" ]]; then
sudo mv /etc/dhcpcd.conf /etc/dhcpcd.conf.orig
else
sudo touch /etc/dhcpcd.conf.orig
fi
customise_dhcpcd_conf

# Add dhcpcd.hooks
sudo install -m 755 "$(dirname "$0")"/reference-device/dhcpcd.enter-hook /etc/dhcpcd.enter-hook
sudo install -m 755 "$(dirname "$0")"/reference-device/dhcpcd.exit-hook /etc/dhcpcd.exit-hook
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_pd_dhcpcd_conf
create_dhcp6_pd_daemon_service

if have systemctl; then
sudo systemctl daemon-reload
sudo systemctl enable ${PD_DAEMON_SERVICE_NAME}
sudo systemctl start ${PD_DAEMON_SERVICE_NAME}
fi
}
91 changes: 59 additions & 32 deletions script/reference-device/dhcp6_pd_daemon.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import dbus
import gi.repository.GLib as GLib
import subprocess
import threading
import os

from dbus.mainloop.glib import DBusGMainLoop
Expand All @@ -39,55 +40,81 @@
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

bus = dbus.SystemBus()
thread_dbus_obj = None
iface = None
backup_dir = "/var/lib/ot-dhcpcd-backup"
intended_dhcp6pd_state = None

DHCP_CONFIG_FILE = "/etc/dhcpcd.conf"
DHCP_CONFIG_PD_FILE = "/etc/dhcpcd.conf.pd"
DHCP_CONFIG_ORIG_FILE = "/etc/dhcpcd.conf.orig"

def restart_dhcpcd_with_pd_config():
global intended_dhcp6pd_state
if os.path.isfile(DHCP_CONFIG_PD_FILE):
try:
subprocess.run(["sudo", "cp", DHCP_CONFIG_PD_FILE, DHCP_CONFIG_FILE], check=True)
subprocess.run(["sudo", "systemctl", "daemon-reload"], check=True)
subprocess.run(["sudo", "service", "dhcpcd", "restart"], check=True)
logging.info("Successfully restarted dhcpcd service.")
intended_dhcp6pd_state = None
except subprocess.CalledProcessError as e:
logging.error(f"Error restarting dhcpcd service: {e}")
intended_dhcp6pd_state = None
else:
logging.error(f"{DHCP_CONFIG_PD_FILE} not found. Cannot apply configuration with prefix delegation.")
intended_dhcp6pd_state = None

def restore_default_config():
if os.path.isfile(DHCP_CONFIG_ORIG_FILE):
try:
subprocess.run(["sudo", "cp", DHCP_CONFIG_ORIG_FILE, DHCP_CONFIG_FILE], check=True)
subprocess.run(["sudo", "systemctl", "daemon-reload"], check=True)
subprocess.run(["sudo", "dhcpcd", "--release"], check=True)
subprocess.run(["sudo", "service", "dhcpcd", "restart"], check=True)
logging.info("Successfully restored default dhcpcd config.")
except subprocess.CalledProcessError as e:
logging.error(f"Error restoring dhcpcd config: {e}")
else:
logging.error(f"{DHCP_CONFIG_ORIG_FILE} not found. Cannot restore default configuration.")

def properties_changed_handler(interface_name, changed_properties, invalidated_properties):
global intended_dhcp6pd_state
if "Dhcp6PdState" in changed_properties:
new_state = changed_properties["Dhcp6PdState"]
logging.info(f"Dhcp6PdState changed to: {new_state}")
if new_state == "running":
os.makedirs(backup_dir, exist_ok=True)
subprocess.run(["sudo", "cp", "/etc/dhcpcd.conf", f"{backup_dir}/dhcpcd.conf.orig"], check=True)
try:
subprocess.run(["sudo", "systemctl", "daemon-reload"], check=True)
subprocess.run(["sudo", "service", "dhcpcd", "restart"], check=True)
logging.info("Successfully restarted dhcpcd service.")
except subprocess.CalledProcessError as e:
logging.error(f"Error restarting dhcpcd service: {e}")
elif new_state == "stopped":
if os.path.isfile(f"{backup_dir}/dhcpcd.conf.orig"):
try:
subprocess.run(["sudo", "cp", f"{backup_dir}/dhcpcd.conf.orig", "/etc/dhcpcd.conf"], check=True)
logging.info("Successfully restored default dhcpcd config.")
except subprocess.CalledProcessError as e:
logging.error(f"Error restoring dhcpcd config: {e}")
else:
logging.error("Backup configuration file not found. Cannot restore default.")
if new_state == "running" and intended_dhcp6pd_state != "running":
intended_dhcp6pd_state = "running"
thread = threading.Thread(target=restart_dhcpcd_with_pd_config)
thread.start()
elif new_state in ("stopped", "disabled") and intended_dhcp6pd_state is None:
restore_default_config()

def connect_to_signal():
global thread_dbus_obj, thread_properties_dbus_iface
try:
thread_dbus_obj = bus.get_object('io.openthread.BorderRouter.wpan0', '/io/openthread/BorderRouter/wpan0')
thread_properties_dbus_iface = dbus.Interface(thread_dbus_obj, 'org.freedesktop.DBus.Properties')
thread_dbus_obj.connect_to_signal("PropertiesChanged", properties_changed_handler, dbus_interface=thread_properties_dbus_iface.dbus_interface)
dbus_obj = bus.get_object('io.openthread.BorderRouter.wpan0', '/io/openthread/BorderRouter/wpan0')
properties_dbus_iface = dbus.Interface(dbus_obj, 'org.freedesktop.DBus.Properties')
dbus_obj.connect_to_signal("PropertiesChanged", properties_changed_handler, dbus_interface=properties_dbus_iface.dbus_interface)
logging.info("Connected to D-Bus signal.")
return dbus_obj, properties_dbus_iface
except dbus.DBusException as e:
logging.error(f"Error connecting to D-Bus: {e}")
thread_dbus_obj = None
thread_properties_dbus_iface = None
return None, None

def check_and_reconnect():
if thread_dbus_obj is None:
connect_to_signal()
def check_and_reconnect(dbus_obj, properties_dbus_iface):
if dbus_obj is None:
dbus_obj, properties_dbus_iface = connect_to_signal()

def main():
# always restart dhcpcd to begin with a previous saved state
try:
subprocess.run(["sudo", "systemctl", "reload-or-restart", "dhcpcd"], check=True)
logging.info("Successfully restarting dhcpcd service.")
except subprocess.CalledProcessError as e:
logging.error(f"Error restarting dhcpcd service: {e}")

loop = GLib.MainLoop()

connect_to_signal()
thread_dbus_obj, thread_properties_dbus_iface = connect_to_signal()

GLib.timeout_add_seconds(5, check_and_reconnect)
GLib.timeout_add_seconds(5, check_and_reconnect, thread_dbus_obj, thread_properties_dbus_iface)

loop.run()

Expand Down
13 changes: 10 additions & 3 deletions script/reference-device/dhcpcd.enter-hook
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,16 @@ if [ ${interface} = ${UPSTREAM_INTERFACE} ]; then

case $reason in
DELEGATED6 | REBIND6 | RENEW6 | BOUND6 )
logger "$LOG_TAG prefix is $new_dhcp6_ia_pd1_prefix1 length is $new_dhcp6_ia_pd1_prefix1_length pltime is $new_dhcp6_ia_pd1_prefix1_pltime vltime is $new_dhcp6_ia_pd1_prefix1_vltime"
config_ra $new_dhcp6_ia_pd1_prefix1 $new_dhcp6_ia_pd1_prefix1_length $new_dhcp6_ia_pd1_prefix1_pltime $new_dhcp6_ia_pd1_prefix1_vltime
sudo systemctl reload radvd || logger "$LOG_TAG Failed to reload radvd"
if [ -z "$new_dhcp6_ia_pd1_prefix1" ] || [ -z "$new_dhcp6_ia_pd1_prefix1_length" ] || \
[ -z "$new_dhcp6_ia_pd1_prefix1_pltime" ] || [ -z "$new_dhcp6_ia_pd1_prefix1_vltime" ]; then
logger "$LOG_TAG WARNING: Missing DHCPv6 prefix information. Skipping radvd configuration."
else
logger "$LOG_TAG prefix is $new_dhcp6_ia_pd1_prefix1 length is $new_dhcp6_ia_pd1_prefix1_length pltime is $new_dhcp6_ia_pd1_prefix1_pltime vltime is $new_dhcp6_ia_pd1_prefix1_vltime"
config_ra $new_dhcp6_ia_pd1_prefix1 $new_dhcp6_ia_pd1_prefix1_length $new_dhcp6_ia_pd1_prefix1_pltime $new_dhcp6_ia_pd1_prefix1_vltime
if systemctl is-active network.target; then
sudo systemctl reload-or-restart radvd || logger "$LOG_TAG Failed to reload radvd"
fi
fi
;;
esac
fi
10 changes: 8 additions & 2 deletions script/reference-device/dhcpcd.exit-hook
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,14 @@ if [ ${interface} = ${UPSTREAM_INTERFACE} ]; then

case $reason in
EXPIRE6 | STOP6)
config_ra $old_dhcp6_ia_pd1_prefix1 $old_dhcp6_ia_pd1_prefix1_length 0 0
sudo systemctl reload radvd
if [ -z "$old_dhcp6_ia_pd1_prefix1" ] || [ -z "$old_dhcp6_ia_pd1_prefix1_length" ]; then
logger "$LOG_TAG WARNING: Missing DHCPv6 prefix information. Skipping radvd configuration."
else
config_ra $old_dhcp6_ia_pd1_prefix1 $old_dhcp6_ia_pd1_prefix1_length 0 0
if systemctl is-active network.target; then
sudo systemctl reload-or-restart radvd
fi
fi
;;
esac
fi
Expand Down

0 comments on commit 558ca76

Please sign in to comment.