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