2 # Copyright (C) 2016-2019 Aleksander Morgado <aleksander@aleksander.es>
4 [ -x /usr
/bin
/mmcli
] ||
exit 0
5 [ -x /usr
/sbin
/pppd
] ||
exit 0
7 [ -n "$INCLUDE_ONLY" ] ||
{
16 # Number of args to shift, 255..255, first non-255 byte, zeroes
17 set -- $
(( 5 - ($1 / 8) )) 255 255 255 255 $
(( (255 << (8 - ($1 % 8))) & 255 )) 0 0 0
24 echo "${1-0}"."${2-0}"."${3-0}"."${4-0}"
27 # This method expects as first argument a list of key-value pairs, as returned by mmcli --output-keyvalue
28 # The second argument must be exactly the name of the field to read
32 # modem.dbus-path : /org/freedesktop/ModemManager1/Modem/0
33 # modem.generic.device-identifier : ed6eff2e3e0f90463da1c2a755b2acacd1335752
34 # modem.generic.manufacturer : Dell Inc.
35 # modem.generic.model : DW5821e Snapdragon X20 LTE
36 # modem.generic.revision : T77W968.F1.0.0.4.0.GC.009\n026
37 # modem.generic.carrier-configuration : GCF
38 # modem.generic.carrier-configuration-revision : 08E00009
39 # modem.generic.hardware-revision : DW5821e Snapdragon X20 LTE
41 modemmanager_get_field
() {
46 [ -z "${list}" ] ||
[ -z "${field}" ] && return
48 # there is always at least a whitespace after each key, and we use that as part of the
49 # key matching we do (e.g. to avoid getting 'modem.generic.state-failed-reason' as a result
50 # when grepping for 'modem.generic.state'.
51 line
=$
(echo "${list}" |
grep "${field} ")
52 value
=$
(echo ${line#*:})
55 [ -n "${value}" ] ||
return 2
57 # only print value if set
58 [ "${value}" != "--" ] && echo "${value}"
62 # build a comma-separated list of values from the list
63 modemmanager_get_multivalue_field
() {
69 [ -z "${list}" ] ||
[ -z "${field}" ] && return
71 length
=$
(modemmanager_get_field
"${list}" "${field}.length")
72 [ -n "${length}" ] ||
return 0
73 [ "$length" -ge 1 ] ||
return 0
76 while [ $idx -le "$length" ]; do
77 item
=$
(modemmanager_get_field
"${list}" "${field}.value\[$idx\]")
78 [ -n "${item}" ] && [ "${item}" != "--" ] && {
79 [ -n "${value}" ] && value
="${value}, "
80 value
="${value}${item}"
86 [ -n "${value}" ] ||
return 2
88 # only print value if set
93 modemmanager_cleanup_connection
() {
94 local modemstatus
="$1"
96 local bearercount idx bearerpath
98 bearercount
=$
(modemmanager_get_field
"${modemstatus}" "modem.generic.bearers.length")
100 # do nothing if no bearers reported
101 [ -n "${bearercount}" ] && [ "$bearercount" -ge 1 ] && {
102 # explicitly disconnect just in case
103 mmcli
--modem="${device}" --simple-disconnect >/dev
/null
2>&1
104 # and remove all bearer objects, if any found
106 while [ $idx -le "$bearercount" ]; do
107 bearerpath
=$
(modemmanager_get_field
"${modemstatus}" "modem.generic.bearers.value\[$idx\]")
108 mmcli
--modem "${device}" --delete-bearer="${bearerpath}" >/dev
/null
2>&1
114 modemmanager_connected_method_ppp_ipv4
() {
119 local allowedauth
="$5"
121 # all auth types are allowed unless a user given list is given
129 [ -n "$allowedauth" ] && {
130 pap
=0 chap
=0 mschap
=0 mschapv2
=0 eap
=0
131 for auth
in $allowedauth; do
135 "mschap") mschap
=1 ;;
136 "mschapv2") mschapv2
=1 ;;
143 [ $pap -eq 1 ] || append authopts
"refuse-pap"
144 [ $chap -eq 1 ] || append authopts
"refuse-chap"
145 [ $mschap -eq 1 ] || append authopts
"refuse-mschap"
146 [ $mschapv2 -eq 1 ] || append authopts
"refuse-mschap-v2"
147 [ $eap -eq 1 ] || append authopts
"refuse-eap"
149 proto_run_command
"${interface}" /usr
/sbin
/pppd \
159 ${username:+ user "$username"} \
160 ${password:+ password "$password"} \
162 lcp-echo-interval
15 \
167 ipparam
"${interface}" \
168 ip-up-script
/lib
/netifd
/ppp-up \
169 ip-down-script
/lib
/netifd
/ppp-down
172 modemmanager_disconnected_method_ppp_ipv4
() {
175 echo "running disconnection (ppp method)"
177 [ -n "${ERROR}" ] && {
179 errorstring
=$
(ppp_exitcode_tostring
"${ERROR}")
184 proto_notify_error
"$interface" "$errorstring"
185 proto_block_restart
"$interface"
188 proto_notify_error
"$interface" "$errorstring"
191 } ||
echo "pppd result code not given"
193 proto_kill_command
"$interface"
196 modemmanager_connected_method_dhcp_ipv4
() {
201 proto_init_update
"${wwan}" 1
203 proto_send_update
"${interface}"
206 json_add_string name
"${interface}_4"
207 json_add_string ifname
"@${interface}"
208 json_add_string proto
"dhcp"
209 proto_add_dynamic_defaults
210 [ -n "$metric" ] && json_add_int metric
"${metric}"
212 ubus call network add_dynamic
"$(json_dump)"
215 modemmanager_connected_method_static_ipv4
() {
228 [ -n "${address}" ] ||
{
229 proto_notify_error
"${interface}" ADDRESS_MISSING
233 [ -n "${prefix}" ] ||
{
234 proto_notify_error
"${interface}" PREFIX_MISSING
237 mask
=$
(cdr2mask
"${prefix}")
239 [ -n "${mtu}" ] && /sbin/ip link set dev "${wwan}" mtu "${mtu}"
241 proto_init_update
"${wwan}" 1
243 echo "adding IPv4 address ${address}, netmask ${mask}"
244 proto_add_ipv4_address
"${address}" "${mask}"
245 [ -n "${gateway}" ] && {
246 echo "adding default IPv4 route via ${gateway}"
247 proto_add_ipv4_route
"0.0.0.0" "0" "${gateway}" "${address}"
249 [ -n "${dns1}" ] && {
250 echo "adding primary DNS at ${dns1}"
251 proto_add_dns_server
"${dns1}"
253 [ -n "${dns2}" ] && {
254 echo "adding secondary DNS at ${dns2}"
255 proto_add_dns_server
"${dns2}"
257 [ -n "$metric" ] && json_add_int metric
"${metric}"
258 proto_send_update
"${interface}"
261 modemmanager_connected_method_dhcp_ipv6
() {
266 proto_init_update
"${wwan}" 1
268 proto_send_update
"${interface}"
271 json_add_string name
"${interface}_6"
272 json_add_string ifname
"@${interface}"
273 json_add_string proto
"dhcpv6"
274 proto_add_dynamic_defaults
275 json_add_string extendprefix
1 # RFC 7278: Extend an IPv6 /64 Prefix to LAN
276 [ -n "$metric" ] && json_add_int metric
"${metric}"
278 ubus call network add_dynamic
"$(json_dump)"
281 modemmanager_connected_method_static_ipv6
() {
292 [ -n "${address}" ] ||
{
293 proto_notify_error
"${interface}" ADDRESS_MISSING
297 [ -n "${prefix}" ] ||
{
298 proto_notify_error
"${interface}" PREFIX_MISSING
302 [ -n "${mtu}" ] && /sbin/ip link set dev "${wwan}" mtu "${mtu}"
304 proto_init_update
"${wwan}" 1
306 echo "adding IPv6 address ${address}, prefix ${prefix}"
307 proto_add_ipv6_address
"${address}" "128"
308 proto_add_ipv6_prefix
"${address}/${prefix}"
309 [ -n "${gateway}" ] && {
310 echo "adding default IPv6 route via ${gateway}"
311 proto_add_ipv6_route
"${gateway}" "128"
312 proto_add_ipv6_route
"::0" "0" "${gateway}" "" "" "${address}/${prefix}"
314 [ -n "${dns1}" ] && {
315 echo "adding primary DNS at ${dns1}"
316 proto_add_dns_server
"${dns1}"
318 [ -n "${dns2}" ] && {
319 echo "adding secondary DNS at ${dns2}"
320 proto_add_dns_server
"${dns2}"
322 [ -n "$metric" ] && json_add_int metric
"${metric}"
323 proto_send_update
"${interface}"
326 proto_modemmanager_init_config
() {
329 proto_config_add_string device
330 proto_config_add_string apn
331 proto_config_add_string
'allowedauth:list(string)'
332 proto_config_add_string username
333 proto_config_add_string password
334 proto_config_add_string allowedmode
335 proto_config_add_string preferredmode
336 proto_config_add_string pincode
337 proto_config_add_string iptype
338 proto_config_add_string plmn
339 proto_config_add_int signalrate
340 proto_config_add_boolean lowpower
341 proto_config_add_boolean allow_roaming
342 proto_config_add_defaults
345 # Append param to the global 'connectargs' variable.
349 [ -z "$param" ] && return
350 [ -z "$connectargs" ] || connectargs
="${connectargs},"
351 connectargs
="${connectargs}${param}"
354 modemmanager_set_allowed_mode
() {
357 local allowedmode
="$3"
359 echo "setting allowed mode to '${allowedmode}'"
360 mmcli
--modem="${device}" --set-allowed-modes="${allowedmode}" ||
{
361 proto_notify_error
"${interface}" MM_INVALID_ALLOWED_MODES_LIST
362 proto_block_restart
"${interface}"
367 modemmanager_check_state
() {
369 local modemstatus
="$2"
374 state
="$(modemmanager_get_field "${modemstatus}" "state
")"
376 reason
="$(modemmanager_get_field "${modemstatus}" "state-failed-reason
")"
383 proto_notify_error
"${interface}" MM_FAILED_REASON_SIM_MISSING
384 proto_block_restart
"${interface}"
388 proto_notify_error
"${interface}" MM_FAILED_REASON_UNKNOWN
389 proto_block_restart
"${interface}"
395 if [ -n "$pincode" ]; then
396 mmcli
--modem="${device}" -i any
--pin=${pincode} ||
{
397 proto_notify_error
"${interface}" MM_PINCODE_WRONG
398 proto_block_restart
"${interface}"
403 proto_notify_error
"${interface}" MM_PINCODE_REQUIRED
404 proto_block_restart
"${interface}"
411 modemmanager_set_preferred_mode
() {
414 local allowedmode
="$3"
415 local preferredmode
="$4"
417 [ -z "${preferredmode}" ] && {
418 echo "no preferred mode configured"
419 proto_notify_error
"${interface}" MM_NO_PREFERRED_MODE_CONFIGURED
420 proto_block_restart
"${interface}"
424 [ -z "${allowedmode}" ] && {
425 echo "no allowed mode configured"
426 proto_notify_error
"${interface}" MM_NO_ALLOWED_MODE_CONFIGURED
427 proto_block_restart
"${interface}"
431 echo "setting preferred mode to '${preferredmode}' (${allowedmode})"
432 mmcli
--modem="${device}" \
433 --set-preferred-mode="${preferredmode}" \
434 --set-allowed-modes="${allowedmode}" ||
{
435 proto_notify_error
"${interface}" MM_FAILED_SETTING_PREFERRED_MODE
436 proto_block_restart
"${interface}"
441 proto_modemmanager_setup
() {
444 local modempath modemstatus bearercount bearerpath connectargs bearerstatus beareriface
445 local bearermethod_ipv4 bearermethod_ipv6 auth cliauth
446 local operatorname operatorid registration accesstech signalquality
447 local allowedmode preferredmode
449 local device apn allowedauth username password pincode
450 local iptype plmn metric signalrate allow_roaming
452 local address prefix gateway mtu dns1 dns2
454 json_get_vars device apn allowedauth username password
455 json_get_vars pincode iptype plmn metric signalrate allow_roaming
456 json_get_vars allowedmode preferredmode
458 # validate sysfs path given in config
459 [ -n "${device}" ] ||
{
460 echo "No device specified"
461 proto_notify_error
"${interface}" NO_DEVICE
462 proto_set_available
"${interface}" 0
466 # validate that ModemManager is handling the modem at the sysfs path
467 modemstatus
=$
(mmcli
--modem="${device}" --output-keyvalue)
468 modempath
=$
(modemmanager_get_field
"${modemstatus}" "modem.dbus-path")
469 [ -n "${modempath}" ] ||
{
470 echo "Device not managed by ModemManager"
471 proto_notify_error
"${interface}" DEVICE_NOT_MANAGED
472 proto_set_available
"${interface}" 0
475 echo "modem available at ${modempath}"
477 modemmanager_check_state
"$device" "${modemstatus}" "$pincode"
478 [ "$?" -ne "0" ] && return 1
480 [ -z "${allowedmode}" ] ||
{
481 case "$allowedmode" in
483 modemmanager_set_allowed_mode
"$device" \
487 modemmanager_set_allowed_mode
"$device" \
491 modemmanager_set_allowed_mode
"$device" \
495 modemmanager_set_allowed_mode
"$device" \
499 modemmanager_set_preferred_mode
"$device" \
500 "$interface" "${allowedmode}" "${preferredmode}"
503 # check error for allowed_mode and preferred_mode function call
504 [ "$?" -ne "0" ] && return 1
507 # always cleanup before attempting a new connection, just in case
508 modemmanager_cleanup_connection
"${modemstatus}"
510 # if allowedauth list given, build option string
511 for auth
in $allowedauth; do
512 cliauth
="${cliauth}${cliauth:+|}$auth"
515 # setup connect args; APN mandatory (even if it may be empty)
516 echo "starting connection with apn '${apn}'..."
517 proto_notify_error
"${interface}" MM_CONNECT_IN_PROGRESS
519 # setup allow-roaming parameter
520 if [ -n "${allow_roaming}" ] && [ "${allow_roaming}" -eq 0 ];then
523 # allowed unless a user set the opposite
527 # Append options to 'connectargs' variable
528 append_param
"apn=${apn}"
529 append_param
"allow-roaming=${allow_roaming}"
530 append_param
"${iptype:+ip-type=${iptype}}"
531 append_param
"${plmn:+operator-id=${plmn}}"
532 append_param
"${cliauth:+allowed-auth=${cliauth}}"
533 append_param
"${username:+user=${username}}"
534 append_param
"${password:+password=${password}}"
536 mmcli
--modem="${device}" --timeout 120 --simple-connect="${connectargs}" ||
{
537 proto_notify_error
"${interface}" MM_CONNECT_FAILED
538 proto_block_restart
"${interface}"
542 # check if Signal refresh rate is set
543 if [ -n "${signalrate}" ] && [ "${signalrate}" -eq "${signalrate}" ] 2>/dev
/null
; then
544 echo "setting signal refresh rate to ${signalrate} seconds"
545 mmcli
--modem="${device}" --signal-setup="${signalrate}"
547 echo "signal refresh rate is not set"
550 # log additional useful information
551 modemstatus
=$
(mmcli
--modem="${device}" --output-keyvalue)
552 operatorname
=$
(modemmanager_get_field
"${modemstatus}" "modem.3gpp.operator-name")
553 [ -n "${operatorname}" ] && echo "network operator name: ${operatorname}"
554 operatorid
=$
(modemmanager_get_field
"${modemstatus}" "modem.3gpp.operator-code")
555 [ -n "${operatorid}" ] && echo "network operator MCCMNC: ${operatorid}"
556 registration
=$
(modemmanager_get_field
"${modemstatus}" "modem.3gpp.registration-state")
557 [ -n "${registration}" ] && echo "registration type: ${registration}"
558 accesstech
=$
(modemmanager_get_multivalue_field
"${modemstatus}" "modem.generic.access-technologies")
559 [ -n "${accesstech}" ] && echo "access technology: ${accesstech}"
560 signalquality
=$
(modemmanager_get_field
"${modemstatus}" "modem.generic.signal-quality.value")
561 [ -n "${signalquality}" ] && echo "signal quality: ${signalquality}%"
563 # we won't like it if there are more than one bearers, as that would mean the
564 # user manually created them, and that's unsupported by this proto
565 bearercount
=$
(modemmanager_get_field
"${modemstatus}" "modem.generic.bearers.length")
566 [ -n "${bearercount}" ] && [ "$bearercount" -eq 1 ] ||
{
567 proto_notify_error
"${interface}" INVALID_BEARER_LIST
571 # load connected bearer information
572 bearerpath
=$
(modemmanager_get_field
"${modemstatus}" "modem.generic.bearers.value\[1\]")
573 bearerstatus
=$
(mmcli
--bearer "${bearerpath}" --output-keyvalue)
575 # load network interface and method information
576 beareriface
=$
(modemmanager_get_field
"${bearerstatus}" "bearer.status.interface")
577 bearermethod_ipv4
=$
(modemmanager_get_field
"${bearerstatus}" "bearer.ipv4-config.method")
578 bearermethod_ipv6
=$
(modemmanager_get_field
"${bearerstatus}" "bearer.ipv6-config.method")
581 [ -n "${bearermethod_ipv4}" ] && {
582 echo "IPv4 connection setup required in interface ${interface}: ${bearermethod_ipv4}"
583 case "${bearermethod_ipv4}" in
585 modemmanager_connected_method_dhcp_ipv4
"${interface}" "${beareriface}" "${metric}"
588 address
=$
(modemmanager_get_field
"${bearerstatus}" "bearer.ipv4-config.address")
589 prefix
=$
(modemmanager_get_field
"${bearerstatus}" "bearer.ipv4-config.prefix")
590 gateway
=$
(modemmanager_get_field
"${bearerstatus}" "bearer.ipv4-config.gateway")
591 mtu
=$
(modemmanager_get_field
"${bearerstatus}" "bearer.ipv4-config.mtu")
592 dns1
=$
(modemmanager_get_field
"${bearerstatus}" "bearer.ipv4-config.dns.value\[1\]")
593 dns2
=$
(modemmanager_get_field
"${bearerstatus}" "bearer.ipv4-config.dns.value\[2\]")
594 modemmanager_connected_method_static_ipv4
"${interface}" "${beareriface}" "${address}" "${prefix}" "${gateway}" "${mtu}" "${dns1}" "${dns2}" "${metric}"
597 modemmanager_connected_method_ppp_ipv4
"${interface}" "${beareriface}" "${username}" "${password}" "${allowedauth}"
600 proto_notify_error
"${interface}" UNKNOWN_METHOD
607 # note: if using ipv4v6, both IPv4 and IPv6 settings will have the same MTU and metric values reported
608 [ -n "${bearermethod_ipv6}" ] && {
609 echo "IPv6 connection setup required in interface ${interface}: ${bearermethod_ipv6}"
610 case "${bearermethod_ipv6}" in
612 modemmanager_connected_method_dhcp_ipv6
"${interface}" "${beareriface}" "${metric}"
615 address
=$
(modemmanager_get_field
"${bearerstatus}" "bearer.ipv6-config.address")
616 prefix
=$
(modemmanager_get_field
"${bearerstatus}" "bearer.ipv6-config.prefix")
617 gateway
=$
(modemmanager_get_field
"${bearerstatus}" "bearer.ipv6-config.gateway")
618 mtu
=$
(modemmanager_get_field
"${bearerstatus}" "bearer.ipv6-config.mtu")
619 dns1
=$
(modemmanager_get_field
"${bearerstatus}" "bearer.ipv6-config.dns.value\[1\]")
620 dns2
=$
(modemmanager_get_field
"${bearerstatus}" "bearer.ipv6-config.dns.value\[2\]")
621 modemmanager_connected_method_static_ipv6
"${interface}" "${beareriface}" "${address}" "${prefix}" "${gateway}" "${mtu}" "${dns1}" "${dns2}" "${metric}"
624 proto_notify_error
"${interface}" "unsupported method"
628 proto_notify_error
"${interface}" UNKNOWN_METHOD
637 proto_modemmanager_teardown
() {
640 local modemstatus bearerpath errorstring
641 local bearermethod_ipv4 bearermethod_ipv6
643 local device lowpower iptype
644 json_get_vars device lowpower iptype
646 echo "stopping network"
648 # load connected bearer information, just the first one should be ok
649 modemstatus
=$
(mmcli
--modem="${device}" --output-keyvalue)
650 bearerpath
=$
(modemmanager_get_field
"${modemstatus}" "modem.generic.bearers.value\[1\]")
651 [ -n "${bearerpath}" ] ||
{
652 echo "couldn't load bearer path: disconnecting anyway"
653 mmcli
--modem="${device}" --simple-disconnect >/dev
/null
2>&1
657 # load bearer connection methods
658 bearerstatus
=$
(mmcli
--bearer "${bearerpath}" --output-keyvalue)
659 bearermethod_ipv4
=$
(modemmanager_get_field
"${bearerstatus}" "bearer.ipv4-config.method")
660 [ -n "${bearermethod_ipv4}" ] &&
661 echo "IPv4 connection teardown required in interface ${interface}: ${bearermethod_ipv4}"
662 bearermethod_ipv6
=$
(modemmanager_get_field
"${bearerstatus}" "bearer.ipv6-config.method")
663 [ -n "${bearermethod_ipv6}" ] &&
664 echo "IPv6 connection teardown required in interface ${interface}: ${bearermethod_ipv6}"
666 # disconnection handling only requires special treatment in IPv4/PPP
667 [ "${bearermethod_ipv4}" = "ppp" ] && modemmanager_disconnected_method_ppp_ipv4
"${interface}"
670 mmcli
--modem="${device}" --simple-disconnect ||
671 proto_notify_error
"${interface}" DISCONNECT_FAILED
674 mmcli
--modem="${device}" --disable
676 # low power, only if requested
677 [ "${lowpower:-0}" -lt 1 ] ||
678 mmcli
--modem="${device}" --set-power-state-low
681 [ -n "$INCLUDE_ONLY" ] ||
{
682 add_protocol modemmanager