pmaports/main/postmarketos-base-ui/rootfs-usr-lib-NetworkManager-dispatcher.d-50-tethering.sh
Arnav Singh ffd803d1dd
main/postmarketos-base-ui: read USB network interface name from configfs gadget (MR 4750)
The configfs gadget can provide the actual interface name, which may not be
the default "usb0" since another gadget driver might have claimed "usb0".
This code is based on the `start_unudhcpd` function in `init_functions.sh`

[ci:skip-build] already built successfully in CI
2024-01-23 12:21:24 -08:00

128 lines
4.5 KiB
Bash

#!/bin/sh -e
# shellcheck disable=SC1091
# Handle USB tethering with unudhcpd and NetworkManager while also
# keeping SSH login over USB working when tethering is disabled.
#
# Copyright (c) 2023 Dylan Van Assche
# SPDX-License-Identifier: GPL-3.0-or-later
# Must match with the supplied connection profile,
# using UUID allows the user to change the connection name if they want to.
con_uuid="83bd1823-feca-4c2b-9205-4b83dc792e1f"
. /usr/share/misc/source_deviceinfo
usb_network_function="${deviceinfo_usb_network_function:-ncm.usb0}"
usb_network_function_fallback="rndis.usb0"
interface="$(
cat "/sys/kernel/config/usb_gadget/g1/functions/$usb_network_function/ifname" 2>/dev/null ||
cat "/sys/kernel/config/usb_gadget/g1/functions/$usb_network_function_fallback/ifname" 2>/dev/null ||
echo 'usb0'
)"
[ -e /etc/unudhcpd.conf ] && . /etc/unudhcpd.conf
host_ip="${unudhcpd_host_ip:-172.16.42.1}"
client_ip="${unudhcpd_client_ip:-172.16.42.2}"
# Skip if iface does not match
if [ "$DEVICE_IFACE" != "$interface" ]; then
exit 0
fi
# Trigger a disconnect/connect event on the client side to request a new DHCP lease.
reactivate_gadget() {
# Mount configFS
mkdir -p /config
mount -t configfs none /config || true
logger -t nm-tethering "configFS mounted"
# Reactivate gadget
udc=$(cat /config/usb_gadget/g1/UDC)
echo "" > /config/usb_gadget/g1/UDC
sleep 1
echo "$udc" > /config/usb_gadget/g1/UDC
logger -t nm-tethering "gadget reactivated"
# Unmount configFS
umount /config
rm -rf /config
logger -t nm-tethering "configFS unmounted"
}
# Default static IP for SSH acccess
# 1. Configure NetworkManager connection to use IP 172.16.42.1, same as initfs.
# 2. Disable IPv6 as unudhcpd only supplies IPv4 addresses.
# 3. Start unudhcpd to handle DHCP requests.
# 4. Reactivate the USB Ethernet gadget to force the clients to reactivate the interface.
disable_tethering() {
# Configure static IP and bring up connection automatically
nmcli connection modify "$con_uuid" ipv4.address "$host_ip/16"
nmcli connection modify "$con_uuid" ipv4.method "manual"
nmcli connection modify "$con_uuid" ipv6.method "link-local"
nmcli connection modify "$con_uuid" connection.autoconnect "true"
# Restart unudhpcd and configure it similar to initfs
killall unudhpcd || true
(unudhcpd -i "$interface" -s "$host_ip" -c "$client_ip") &
logger -t nm-tethering "unudhcpd started"
# Reactivate gadget to apply changes
reactivate_gadget
logger -t nm-tethering "USB tethering disabled"
}
# USB tethering
# 1. Enforce 172.16.42.1 as host IP even in tethering mode.
# 2. Stop unudhcpd as NetworkManager will spawn dnsmasq to handle DNS and DHCP requests.
# 3. Reactivate the USB Ethernet gadget to force the clients to reactivate the interface.
enable_tethering() {
# Enforce the same IP range as when tethering is disabled, this will retrigger
# the script as we have to reapply again, also bring up connection automatically.
ip=$(nmcli connection show "$con_uuid" --active | grep ipv4.addresses | tr -s " " | cut -d " " -f2)
if [ "$ip" != "$host_ip/16" ]; then
logger -t nm-tethering "Enforcing $host_ip/16 as DHCP range"
nmcli connection modify "$con_uuid" ipv4.address "$host_ip/16"
nmcli connection modify "$con_uuid" connection.autoconnect "true"
nmcli device reapply "$interface"
return
fi
# Kill unudhcpd if needed
killall unudhcpd || true
logger -t nm-tethering "unudhcpd stopped"
# Reactivate gadget to apply changes
reactivate_gadget
logger -t nm-tethering "USB tethering enabled"
}
# Handle dispatcher events for tethering
method=$(nmcli connection show "$con_uuid" --active | grep ipv4.method | tr -s " " | cut -d " " -f2)
case $NM_DISPATCHER_ACTION in
# Always disable tethering on insert or removal for security
"up"|"down")
disable_tethering
# The connection may have been configured before to tethering which
# is not desired when iface comes up or is shut down as tethering must be
# disabled. Enforce this by triggering a reapply after modifying the connection
# which will cause NetworkManager to trigger the script again.
# If the iface is already down since dispatcher scripts are executed async,
# the command will fail which is fine to ignore
if [ "$method" = "shared" ]; then
logger -t nm-tethering "Enforcing tethering disabled on iface up/down"
nmcli device reapply "$interface" || true
fi
;;
# Enable tethering if the user explicitly enabled it
"reapply")
if [ "$method" = "shared" ]; then
enable_tethering
elif [ "$method" = "manual" ]; then
disable_tethering
fi
;;
esac