]> git.ipfire.org Git - people/pmueller/ipfire-2.x.git/commitdiff
network: Add support for QMI modems
authorMichael Tremer <michael.tremer@ipfire.org>
Thu, 1 Dec 2022 17:22:55 +0000 (17:22 +0000)
committerPeter Müller <peter.mueller@ipfire.org>
Sat, 17 Dec 2022 17:20:45 +0000 (17:20 +0000)
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 <michael.tremer@ipfire.org>
src/initscripts/networking/dhcpcd.exe
src/initscripts/networking/functions.network
src/initscripts/networking/red

index 8a409d0109c1c701c7d4bfe5ea70be9fe8a62395..be6d63708862a130475202765e37907eaefe2b4d 100644 (file)
@@ -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}"
        ;;
index f246919decec4f5385e42eeeb958c2f5119e59d0..9698424fd1ccb0ceac2de51389a0a76e7e3708bb 100644 (file)
@@ -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
+}
index fc10e077abce37ef6af0bb4dbe40e362c3656e50..7df61c1cf6704ed776392a900ce5333d9992d40b 100644 (file)
@@ -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