From patchwork Thu Dec 1 17:22:55 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michael Tremer X-Patchwork-Id: 6244 Return-Path: Received: from mail01.ipfire.org (mail01.haj.ipfire.org [172.28.1.202]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-384) client-signature ECDSA (P-384)) (Client CN "mail01.haj.ipfire.org", Issuer "R3" (verified OK)) by web04.haj.ipfire.org (Postfix) with ESMTPS id 4NNNG23Wcdz3xkS for ; Thu, 1 Dec 2022 17:23:38 +0000 (UTC) Received: from mail02.haj.ipfire.org (mail02.haj.ipfire.org [172.28.1.201]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-384) client-signature ECDSA (P-384)) (Client CN "mail02.haj.ipfire.org", Issuer "R3" (verified OK)) by mail01.ipfire.org (Postfix) with ESMTPS id 4NNNFq0Gqmz2v4; Thu, 1 Dec 2022 17:23:27 +0000 (UTC) Received: from mail02.haj.ipfire.org (localhost [127.0.0.1]) by mail02.haj.ipfire.org (Postfix) with ESMTP id 4NNNFm6640z30Cs; Thu, 1 Dec 2022 17:23:24 +0000 (UTC) Received: from mail01.ipfire.org (mail01.haj.ipfire.org [172.28.1.202]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-384) client-signature ECDSA (P-384)) (Client CN "mail01.haj.ipfire.org", Issuer "R3" (verified OK)) by mail02.haj.ipfire.org (Postfix) with ESMTPS id 4NNNFl0VbWz2yw0 for ; Thu, 1 Dec 2022 17:23:23 +0000 (UTC) Received: from michael.haj.ipfire.org (michael.haj.ipfire.org [172.28.1.242]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-384) client-signature ECDSA (P-384)) (Client CN "michael.haj.ipfire.org", Issuer "R3" (verified OK)) by mail01.ipfire.org (Postfix) with ESMTPS id 4NNNFk5Sdtz2qD; Thu, 1 Dec 2022 17:23:22 +0000 (UTC) Received: by michael.haj.ipfire.org (Postfix, from userid 0) id 4NNNFj5f5MzTjnJ; Thu, 1 Dec 2022 17:23:21 +0000 (UTC) From: Michael Tremer To: development@lists.ipfire.org Subject: [PATCH 06/29] network: Add support for QMI modems Date: Thu, 1 Dec 2022 17:22:55 +0000 Message-Id: <20221201172318.3166615-6-michael.tremer@ipfire.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20221201172318.3166615-1-michael.tremer@ipfire.org> References: <20221201172318.3166615-1-michael.tremer@ipfire.org> MIME-Version: 1.0 X-BeenThere: development@lists.ipfire.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: IPFire development talk List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Michael Tremer Errors-To: development-bounces@lists.ipfire.org Sender: "Development" QMI is a proprietary interface from Qualcomm which are absolute pioneers when it comes to interfacing with modems. I don't think there would be any way to make this any more complicated and bloated. So, bascially we will put the modem into a raw IP mode which changes the interface into Point-to-Point mode. We then configure the provider settings using qmicli. After that, the modem will try to connect to the provider and obtain an IP address. We will then start a DHCP client which does not do any DHCP-ing because implementing that would be too complicated. Instead we do something even *more* complicated where we would launch a custom script which asks the modem for the allocated IP address and will configure it into the device. The DHCP client then reads that IP address from the device and pretends it came up with it by itself. Such an easy way to do this. Signed-off-by: Michael Tremer --- src/initscripts/networking/dhcpcd.exe | 62 ++++++++++++++ src/initscripts/networking/functions.network | 90 ++++++++++++++++++++ src/initscripts/networking/red | 36 ++++++++ 3 files changed, 188 insertions(+) diff --git a/src/initscripts/networking/dhcpcd.exe b/src/initscripts/networking/dhcpcd.exe index 8a409d010..be6d63708 100644 --- a/src/initscripts/networking/dhcpcd.exe +++ b/src/initscripts/networking/dhcpcd.exe @@ -20,6 +20,7 @@ . /etc/sysconfig/rc . $rc_functions +. /etc/init.d/networking/functions.network eval $(/usr/local/bin/readhash /var/ipfire/ethernet/settings) @@ -85,9 +86,70 @@ dhcpcd_down() fi } +# Called when dhcpcd relies on a third party to configure an IP address +dhcpcd_3rdparty() { + local qmi_device="$(qmi_find_device "${interface}")" + + if [ -n "${qmi_device}" ]; then + setup_qmi "${qmi_device}" || return $? + fi + + return 0 +} + +setup_qmi() { + local device="${1}" + + local address + local netmask + local gateway + local mtu=1500 + + local line + while read -r line; do + # Extract the value + value="${line#*: }" + + case "${line}" in + *IPv4\ address:*) + address="${value}" + ;; + *IPv4\ subnet\ mask:*) + netmask="${value}" + ;; + *IPv4\ gateway\ address:*) + gateway="${value}" + ;; + *MTU:*) + mtu="${value}" + ;; + esac + done <<< "$(qmicli --device="${device}" --wds-get-current-settings)" + + if [ -z "${address}" ] || [ -z "${netmask}" ] || [ -z "${gateway}" ]; then + logger -p "local0.info" -t "dhcpcd.exe[$$]" \ + "Could not retrieve all information from the QMI interface" + return 1 + fi + + # Flush any previous configuration + ip addr flush dev "${interface}" + + # Configure the IP address + ip addr add "${address}/${netmask}" dev "${interface}" + + # Configure the default route + ip route add default via "${gateway}" #mtu "${mtu}" + + return 0 +} + case "$reason" in BOUND|INFORM|REBIND|REBOOT|RENEW|TIMEOUT|STATIC) dhcpcd_up;; PREINIT|EXPIRE|FAIL|IPV4LL|NAK|RELEASE|STOP) dhcpcd_down;; +3RDPARTY) + dhcpcd_3rdparty + ;; *) logger -p "local0.info" -t "dhcpcd.exe[$$]" "Unhandled DHCP event: ${reason}" ;; diff --git a/src/initscripts/networking/functions.network b/src/initscripts/networking/functions.network index f246919de..9698424fd 100644 --- a/src/initscripts/networking/functions.network +++ b/src/initscripts/networking/functions.network @@ -169,3 +169,93 @@ dhcpcd_stop() { echo_failure fi } + +# QMI stuff + +qmi_find_device() { + local intf="${1}" + local _intf + + local path + for path in /dev/cdc-*; do + if [ -c "${path}" ]; then + _intf="$(qmicli --device="${path}" --device-open-proxy --get-wwan-iface)" + + # Check if the interface matches + if [ "${intf}" = "${_intf}" ]; then + echo "${path}" + return 0 + fi + fi + done + + # Nothing found + return 1 +} + +qmi_enable_rawip_mode() { + local intf="${1}" + + # Shut down the device first + ip link set "${intf}" down &>/dev/null + + echo "Y" > "/sys/class/net/${intf}/qmi/raw_ip" +} + +qmi_configure_apn() { + local device="${1}" + + # APN settings + local apn="${2}" + local auth="${3}" + local username="${4}" + local password="${5}" + + local args=( + # We only support IPv4 right now + "ip-type=4" + ) + + # Set APN + if [ -n "${apn}" ]; then + args+=( "apn=${apn}" ) + fi + + # Set auth + case "${auth}" in + PAP|CHAP) + args+=( "auth=${auth}" ) + ;; + esac + + # Set username + if [ -n "${username}" ]; then + args+=( "username=${username}" ) + fi + + # Set password + if [ -n "${password}" ]; then + args+=( "password=${password}" ) + fi + + local _args + + local arg + for arg in ${args[@]}; do + if [ -n "${_args}" ]; then + _args="${_args}," + fi + _args="${_args}${arg}" + done + + qmicli --device="${device}" --device-open-proxy \ + --wds-start-network="${_args}" \ + --client-no-release-cid +} + +qmi_reset() { + local device="${1}" + + qmicli --device="${device}" --device-open-proxy \ + --wds-reset +} diff --git a/src/initscripts/networking/red b/src/initscripts/networking/red index fc10e077a..7df61c1cf 100644 --- a/src/initscripts/networking/red +++ b/src/initscripts/networking/red @@ -210,6 +210,27 @@ case "${1}" in if [ "$TYPE" == "pptpatm" ]; then TYPE="pptp" fi + + # QMI + elif [ "$TYPE" = "qmi" ]; then + DEVICE="$(qmi_find_device "${RED_DEV}")" + + boot_mesg "Bringing up QMI on ${RED_DEV} (${DEVICE})..." + + # Enable RAW-IP mode + qmi_enable_rawip_mode "${RED_DEV}" + + # Configure APN + qmi_configure_apn "${DEVICE}" "${APN}" "${AUTH}" "${USERNAME}" "${PASSWORD}" + + # Set up the interface + ip link set "${RED_DEV}" up &>/dev/null + + # Start the DHCP client + dhcpcd_start "${RED_DEV}" --debug + + # Done + exit 0 fi if [ "$TYPE" == "vdsl" ]; then @@ -477,6 +498,21 @@ case "${1}" in run_subdir ${rc_base}/init.d/networking/red.down/ elif [ "$TYPE" == "PPPOE" ]; then + eval $(/usr/local/bin/readhash /var/ipfire/ppp/settings) + + if [ "${TYPE}" = "qmi" ]; then + boot_mesg "Bringing down the QMI interface ${RED_DEV}..." + DEVICE="$(qmi_find_device "${RED_DEV}")" + + # Stop the DHCP client on RED + dhcpcd_stop "${RED_DEV}" + + # Reset any QMI settings + qmi_reset "${DEVICE}" + + exit 0 + fi + boot_mesg "Bringing down the PPP interface ..." rm -f /var/ipfire/red/keepconnected killall -w -s TERM /usr/sbin/pppd 2>/dev/null