4b92e922ef
USB tethering allows users to use their phone's WiFi or WWAN interface as an access point for the device they connect the phone with over USB. This way, they can use their data plan with a laptop or desktop when regular Internet access is absent. Configure NetworkManager to manage the usb0 iface from initfs so users can configure it in GNOME Settings or KDE. Include a NetworkManager dispatcher script as well to handle postmarketOS' setup with unudhcpd as we want to provide an SSH login over USB when tethering is disabled. If tethering is enabled, unudhpcd is stopped and its functionality is taken over by NetworkManager through an instance of dnsmasq. NetworkManager will also configure the necessary settings to allow IP forwarding and firewall rules during tethering. Once disabled, NetworkManager cleans up these settings and our dispatcher script starts unudhcpd again. Due to upstream changes, NetworkManager requires networkmanager-dnsmasq subpackage as dependency as dnsmasq needs DBus support for NetworkManager. Without it, NetworkManager will silently fail spawning dnsmasq instances. [ci:skip-build]: already built successfully in CI
119 lines
4 KiB
Bash
119 lines
4 KiB
Bash
#!/bin/sh -e
|
|
# 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"
|
|
interface="usb0"
|
|
host_ip="172.16.42.1"
|
|
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"
|
|
|
|
# If unudhpcd is not running, start it and configure it similar to initfs
|
|
if [ ! "$(pidof unudhcpd)" ]; then
|
|
(unudhcpd -i "$interface" -s "$host_ip" -c "$client_ip") &
|
|
logger -t nm-tethering "unudhcpd started"
|
|
|
|
reactivate_gadget
|
|
logger -t nm-tethering "USB tethering disabled"
|
|
fi
|
|
|
|
}
|
|
|
|
# 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
|
|
if [ "$(pidof unudhcpd)" ]; then
|
|
killall unudhcpd
|
|
logger -t nm-tethering "unudhcpd stopped"
|
|
|
|
reactivate_gadget
|
|
logger -t nm-tethering "USB tethering enabled"
|
|
fi
|
|
|
|
}
|
|
|
|
# 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
|