network: Add support for QMI modems

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>
This commit is contained in:
Michael Tremer
2022-12-01 17:22:55 +00:00
committed by Peter Müller
parent c6551e73c2
commit 957863f754
3 changed files with 188 additions and 0 deletions

View 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}"
;;

View 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
}

View 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