You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

550 lines
17 KiB

  1. #!/bin/sh
  2. # Copyright (C) 2016-2019 Aleksander Morgado <aleksander@aleksander.es>
  3. [ -x /usr/bin/mmcli ] || exit 0
  4. [ -x /usr/sbin/pppd ] || exit 0
  5. [ -n "$INCLUDE_ONLY" ] || {
  6. . /lib/functions.sh
  7. . ../netifd-proto.sh
  8. . ./ppp.sh
  9. init_proto "$@"
  10. }
  11. cdr2mask ()
  12. {
  13. # Number of args to shift, 255..255, first non-255 byte, zeroes
  14. set -- $(( 5 - ($1 / 8) )) 255 255 255 255 $(( (255 << (8 - ($1 % 8))) & 255 )) 0 0 0
  15. if [ "$1" -gt 1 ]
  16. then
  17. shift "$1"
  18. else
  19. shift
  20. fi
  21. echo "${1-0}"."${2-0}"."${3-0}"."${4-0}"
  22. }
  23. # This method expects as first argument a list of key-value pairs, as returned by mmcli --output-keyvalue
  24. # The second argument must be exactly the name of the field to read
  25. #
  26. # Sample output:
  27. # $ mmcli -m 0 -K
  28. # modem.dbus-path : /org/freedesktop/ModemManager1/Modem/0
  29. # modem.generic.device-identifier : ed6eff2e3e0f90463da1c2a755b2acacd1335752
  30. # modem.generic.manufacturer : Dell Inc.
  31. # modem.generic.model : DW5821e Snapdragon X20 LTE
  32. # modem.generic.revision : T77W968.F1.0.0.4.0.GC.009\n026
  33. # modem.generic.carrier-configuration : GCF
  34. # modem.generic.carrier-configuration-revision : 08E00009
  35. # modem.generic.hardware-revision : DW5821e Snapdragon X20 LTE
  36. # ....
  37. modemmanager_get_field() {
  38. local list=$1
  39. local field=$2
  40. local value=""
  41. [ -z "${list}" ] || [ -z "${field}" ] && return
  42. # there is always at least a whitespace after each key, and we use that as part of the
  43. # key matching we do (e.g. to avoid getting 'modem.generic.state-failed-reason' as a result
  44. # when grepping for 'modem.generic.state'.
  45. line=$(echo "${list}" | grep "${field} ")
  46. value=$(echo ${line#*:})
  47. # not found?
  48. [ -n "${value}" ] || return 2
  49. # only print value if set
  50. [ "${value}" != "--" ] && echo "${value}"
  51. return 0
  52. }
  53. # build a comma-separated list of values from the list
  54. modemmanager_get_multivalue_field() {
  55. local list=$1
  56. local field=$2
  57. local value=""
  58. local length idx item
  59. [ -z "${list}" ] || [ -z "${field}" ] && return
  60. length=$(modemmanager_get_field "${list}" "${field}.length")
  61. [ -n "${length}" ] || return 0
  62. [ "$length" -ge 1 ] || return 0
  63. idx=1
  64. while [ $idx -le "$length" ]; do
  65. item=$(modemmanager_get_field "${list}" "${field}.value\[$idx\]")
  66. [ -n "${item}" ] && [ "${item}" != "--" ] && {
  67. [ -n "${value}" ] && value="${value}, "
  68. value="${value}${item}"
  69. }
  70. idx=$((idx + 1))
  71. done
  72. # nothing built?
  73. [ -n "${value}" ] || return 2
  74. # only print value if set
  75. echo "${value}"
  76. return 0
  77. }
  78. modemmanager_cleanup_connection() {
  79. local modemstatus="$1"
  80. local bearercount idx bearerpath
  81. bearercount=$(modemmanager_get_field "${modemstatus}" "modem.generic.bearers.length")
  82. # do nothing if no bearers reported
  83. [ -n "${bearercount}" ] && [ "$bearercount" -ge 1 ] && {
  84. # explicitly disconnect just in case
  85. mmcli --modem="${device}" --simple-disconnect >/dev/null 2>&1
  86. # and remove all bearer objects, if any found
  87. idx=1
  88. while [ $idx -le "$bearercount" ]; do
  89. bearerpath=$(modemmanager_get_field "${modemstatus}" "modem.generic.bearers.value\[$idx\]")
  90. mmcli --modem "${device}" --delete-bearer="${bearerpath}" >/dev/null 2>&1
  91. idx=$((idx + 1))
  92. done
  93. }
  94. }
  95. modemmanager_connected_method_ppp_ipv4() {
  96. local interface="$1"
  97. local ttyname="$2"
  98. local username="$3"
  99. local password="$4"
  100. local allowedauth="$5"
  101. # all auth types are allowed unless a user given list is given
  102. local authopts
  103. local pap=1
  104. local chap=1
  105. local mschap=1
  106. local mschapv2=1
  107. local eap=1
  108. [ -n "$allowedauth" ] && {
  109. pap=0 chap=0 mschap=0 mschapv2=0 eap=0
  110. for auth in $allowedauth; do
  111. case $auth in
  112. "pap") pap=1 ;;
  113. "chap") chap=1 ;;
  114. "mschap") mschap=1 ;;
  115. "mschapv2") mschapv2=1 ;;
  116. "eap") eap=1 ;;
  117. *) ;;
  118. esac
  119. done
  120. }
  121. [ $pap -eq 1 ] || append authopts "refuse-pap"
  122. [ $chap -eq 1 ] || append authopts "refuse-chap"
  123. [ $mschap -eq 1 ] || append authopts "refuse-mschap"
  124. [ $mschapv2 -eq 1 ] || append authopts "refuse-mschap-v2"
  125. [ $eap -eq 1 ] || append authopts "refuse-eap"
  126. proto_run_command "${interface}" /usr/sbin/pppd \
  127. "${ttyname}" \
  128. 115200 \
  129. nodetach \
  130. noaccomp \
  131. nobsdcomp \
  132. nopcomp \
  133. novj \
  134. noauth \
  135. $authopts \
  136. ${username:+ user $username} \
  137. ${password:+ password $password} \
  138. lcp-echo-failure 5 \
  139. lcp-echo-interval 15 \
  140. lock \
  141. crtscts \
  142. nodefaultroute \
  143. usepeerdns \
  144. ipparam "${interface}" \
  145. ip-up-script /lib/netifd/ppp-up \
  146. ip-down-script /lib/netifd/ppp-down
  147. }
  148. modemmanager_disconnected_method_ppp_ipv4() {
  149. local interface="$1"
  150. echo "running disconnection (ppp method)"
  151. [ -n "${ERROR}" ] && {
  152. local errorstring
  153. errorstring=$(ppp_exitcode_tostring "${ERROR}")
  154. case "$ERROR" in
  155. 0)
  156. ;;
  157. 2)
  158. proto_notify_error "$interface" "$errorstring"
  159. proto_block_restart "$interface"
  160. ;;
  161. *)
  162. proto_notify_error "$interface" "$errorstring"
  163. ;;
  164. esac
  165. } || echo "pppd result code not given"
  166. proto_kill_command "$interface"
  167. }
  168. modemmanager_connected_method_dhcp_ipv4() {
  169. local interface="$1"
  170. local wwan="$2"
  171. local metric="$3"
  172. proto_init_update "${wwan}" 1
  173. proto_set_keep 1
  174. proto_send_update "${interface}"
  175. json_init
  176. json_add_string name "${interface}_4"
  177. json_add_string ifname "@${interface}"
  178. json_add_string proto "dhcp"
  179. proto_add_dynamic_defaults
  180. [ -n "$metric" ] && json_add_int metric "${metric}"
  181. json_close_object
  182. ubus call network add_dynamic "$(json_dump)"
  183. }
  184. modemmanager_connected_method_static_ipv4() {
  185. local interface="$1"
  186. local wwan="$2"
  187. local address="$3"
  188. local prefix="$4"
  189. local gateway="$5"
  190. local mtu="$6"
  191. local dns1="$7"
  192. local dns2="$8"
  193. local metric="$9"
  194. local mask=""
  195. [ -n "${address}" ] || {
  196. proto_notify_error "${interface}" ADDRESS_MISSING
  197. return
  198. }
  199. [ -n "${prefix}" ] || {
  200. proto_notify_error "${interface}" PREFIX_MISSING
  201. return
  202. }
  203. mask=$(cdr2mask "${prefix}")
  204. [ -n "${mtu}" ] && /sbin/ip link set dev "${wwan}" mtu "${mtu}"
  205. proto_init_update "${wwan}" 1
  206. proto_set_keep 1
  207. echo "adding IPv4 address ${address}, netmask ${mask}"
  208. proto_add_ipv4_address "${address}" "${mask}"
  209. [ -n "${gateway}" ] && {
  210. echo "adding default IPv4 route via ${gateway}"
  211. proto_add_ipv4_route "0.0.0.0" "0" "${gateway}" "${address}"
  212. }
  213. [ -n "${dns1}" ] && {
  214. echo "adding primary DNS at ${dns1}"
  215. proto_add_dns_server "${dns1}"
  216. }
  217. [ -n "${dns2}" ] && {
  218. echo "adding secondary DNS at ${dns2}"
  219. proto_add_dns_server "${dns2}"
  220. }
  221. [ -n "$metric" ] && json_add_int metric "${metric}"
  222. proto_send_update "${interface}"
  223. }
  224. modemmanager_connected_method_dhcp_ipv6() {
  225. local interface="$1"
  226. local wwan="$2"
  227. local metric="$3"
  228. proto_init_update "${wwan}" 1
  229. proto_set_keep 1
  230. proto_send_update "${interface}"
  231. json_init
  232. json_add_string name "${interface}_6"
  233. json_add_string ifname "@${interface}"
  234. json_add_string proto "dhcpv6"
  235. proto_add_dynamic_defaults
  236. json_add_string extendprefix 1 # RFC 7278: Extend an IPv6 /64 Prefix to LAN
  237. [ -n "$metric" ] && json_add_int metric "${metric}"
  238. json_close_object
  239. ubus call network add_dynamic "$(json_dump)"
  240. }
  241. modemmanager_connected_method_static_ipv6() {
  242. local interface="$1"
  243. local wwan="$2"
  244. local address="$3"
  245. local prefix="$4"
  246. local gateway="$5"
  247. local mtu="$6"
  248. local dns1="$7"
  249. local dns2="$8"
  250. local metric="$9"
  251. [ -n "${address}" ] || {
  252. proto_notify_error "${interface}" ADDRESS_MISSING
  253. return
  254. }
  255. [ -n "${prefix}" ] || {
  256. proto_notify_error "${interface}" PREFIX_MISSING
  257. return
  258. }
  259. [ -n "${mtu}" ] && /sbin/ip link set dev "${wwan}" mtu "${mtu}"
  260. proto_init_update "${wwan}" 1
  261. proto_set_keep 1
  262. echo "adding IPv6 address ${address}, prefix ${prefix}"
  263. proto_add_ipv6_address "${address}" "128"
  264. proto_add_ipv6_prefix "${address}/${prefix}"
  265. [ -n "${gateway}" ] && {
  266. echo "adding default IPv6 route via ${gateway}"
  267. proto_add_ipv6_route "${gateway}" "128"
  268. proto_add_ipv6_route "::0" "0" "${gateway}" "" "" "${address}/${prefix}"
  269. }
  270. [ -n "${dns1}" ] && {
  271. echo "adding primary DNS at ${dns1}"
  272. proto_add_dns_server "${dns1}"
  273. }
  274. [ -n "${dns2}" ] && {
  275. echo "adding secondary DNS at ${dns2}"
  276. proto_add_dns_server "${dns2}"
  277. }
  278. [ -n "$metric" ] && json_add_int metric "${metric}"
  279. proto_send_update "${interface}"
  280. }
  281. modemmanager_disconnected_method_common() {
  282. local interface="$1"
  283. echo "running disconnection (common)"
  284. proto_notify_error "${interface}" MM_DISCONNECT_IN_PROGRESS
  285. proto_init_update "*" 0
  286. proto_send_update "${interface}"
  287. }
  288. proto_modemmanager_init_config() {
  289. available=1
  290. no_device=1
  291. proto_config_add_string device
  292. proto_config_add_string apn
  293. proto_config_add_string 'allowedauth:list(string)'
  294. proto_config_add_string username
  295. proto_config_add_string password
  296. proto_config_add_string pincode
  297. proto_config_add_string iptype
  298. proto_config_add_int signalrate
  299. proto_config_add_boolean lowpower
  300. proto_config_add_defaults
  301. }
  302. proto_modemmanager_setup() {
  303. local interface="$1"
  304. local modempath modemstatus bearercount bearerpath connectargs bearerstatus beareriface
  305. local bearermethod_ipv4 bearermethod_ipv6 auth cliauth
  306. local operatorname operatorid registration accesstech signalquality
  307. local device apn allowedauth username password pincode iptype metric signalrate
  308. local address prefix gateway mtu dns1 dns2
  309. json_get_vars device apn allowedauth username password pincode iptype metric signalrate
  310. # validate sysfs path given in config
  311. [ -n "${device}" ] || {
  312. echo "No device specified"
  313. proto_notify_error "${interface}" NO_DEVICE
  314. proto_set_available "${interface}" 0
  315. return 1
  316. }
  317. [ -e "${device}" ] || {
  318. echo "Device not found in sysfs"
  319. proto_set_available "${interface}" 0
  320. return 1
  321. }
  322. # validate that ModemManager is handling the modem at the sysfs path
  323. modemstatus=$(mmcli --modem="${device}" --output-keyvalue)
  324. modempath=$(modemmanager_get_field "${modemstatus}" "modem.dbus-path")
  325. [ -n "${modempath}" ] || {
  326. echo "Device not managed by ModemManager"
  327. proto_notify_error "${interface}" DEVICE_NOT_MANAGED
  328. proto_set_available "${interface}" 0
  329. return 1
  330. }
  331. echo "modem available at ${modempath}"
  332. # always cleanup before attempting a new connection, just in case
  333. modemmanager_cleanup_connection "${modemstatus}"
  334. # if allowedauth list given, build option string
  335. for auth in $allowedauth; do
  336. cliauth="${cliauth}${cliauth:+|}$auth"
  337. done
  338. # setup connect args; APN mandatory (even if it may be empty)
  339. echo "starting connection with apn '${apn}'..."
  340. proto_notify_error "${interface}" MM_CONNECT_IN_PROGRESS
  341. connectargs="apn=${apn}${iptype:+,ip-type=${iptype}}${cliauth:+,allowed-auth=${cliauth}}${username:+,user=${username}}${password:+,password=${password}}${pincode:+,pin=${pincode}}"
  342. mmcli --modem="${device}" --timeout 120 --simple-connect="${connectargs}" || {
  343. proto_notify_error "${interface}" MM_CONNECT_FAILED
  344. proto_block_restart "${interface}"
  345. return 1
  346. }
  347. # check if Signal refresh rate is set
  348. if [ -n "${signalrate}" ] && [ "${signalrate}" -eq "${signalrate}" ] 2>/dev/null; then
  349. echo "setting signal refresh rate to ${signalrate} seconds"
  350. mmcli --modem="${device}" --signal-setup="${signalrate}"
  351. else
  352. echo "signal refresh rate is not set"
  353. fi
  354. # log additional useful information
  355. modemstatus=$(mmcli --modem="${device}" --output-keyvalue)
  356. operatorname=$(modemmanager_get_field "${modemstatus}" "modem.3gpp.operator-name")
  357. [ -n "${operatorname}" ] && echo "network operator name: ${operatorname}"
  358. operatorid=$(modemmanager_get_field "${modemstatus}" "modem.3gpp.operator-code")
  359. [ -n "${operatorid}" ] && echo "network operator MCCMNC: ${operatorid}"
  360. registration=$(modemmanager_get_field "${modemstatus}" "modem.3gpp.registration-state")
  361. [ -n "${registration}" ] && echo "registration type: ${registration}"
  362. accesstech=$(modemmanager_get_multivalue_field "${modemstatus}" "modem.generic.access-technologies")
  363. [ -n "${accesstech}" ] && echo "access technology: ${accesstech}"
  364. signalquality=$(modemmanager_get_field "${modemstatus}" "modem.generic.signal-quality.value")
  365. [ -n "${signalquality}" ] && echo "signal quality: ${signalquality}%"
  366. # we won't like it if there are more than one bearers, as that would mean the
  367. # user manually created them, and that's unsupported by this proto
  368. bearercount=$(modemmanager_get_field "${modemstatus}" "modem.generic.bearers.length")
  369. [ -n "${bearercount}" ] && [ "$bearercount" -eq 1 ] || {
  370. proto_notify_error "${interface}" INVALID_BEARER_LIST
  371. return 1
  372. }
  373. # load connected bearer information
  374. bearerpath=$(modemmanager_get_field "${modemstatus}" "modem.generic.bearers.value\[1\]")
  375. bearerstatus=$(mmcli --bearer "${bearerpath}" --output-keyvalue)
  376. # load network interface and method information
  377. beareriface=$(modemmanager_get_field "${bearerstatus}" "bearer.status.interface")
  378. bearermethod_ipv4=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv4-config.method")
  379. bearermethod_ipv6=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv6-config.method")
  380. # setup IPv4
  381. [ -n "${bearermethod_ipv4}" ] && {
  382. echo "IPv4 connection setup required in interface ${interface}: ${bearermethod_ipv4}"
  383. case "${bearermethod_ipv4}" in
  384. "dhcp")
  385. modemmanager_connected_method_dhcp_ipv4 "${interface}" "${beareriface}" "${metric}"
  386. ;;
  387. "static")
  388. address=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv4-config.address")
  389. prefix=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv4-config.prefix")
  390. gateway=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv4-config.gateway")
  391. mtu=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv4-config.mtu")
  392. dns1=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv4-config.dns.value\[1\]")
  393. dns2=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv4-config.dns.value\[2\]")
  394. modemmanager_connected_method_static_ipv4 "${interface}" "${beareriface}" "${address}" "${prefix}" "${gateway}" "${mtu}" "${dns1}" "${dns2}" "${metric}"
  395. ;;
  396. "ppp")
  397. modemmanager_connected_method_ppp_ipv4 "${interface}" "${beareriface}" "${username}" "${password}" "${allowedauth}"
  398. ;;
  399. *)
  400. proto_notify_error "${interface}" UNKNOWN_METHOD
  401. return 1
  402. ;;
  403. esac
  404. }
  405. # setup IPv6
  406. # note: if using ipv4v6, both IPv4 and IPv6 settings will have the same MTU and metric values reported
  407. [ -n "${bearermethod_ipv6}" ] && {
  408. echo "IPv6 connection setup required in interface ${interface}: ${bearermethod_ipv6}"
  409. case "${bearermethod_ipv6}" in
  410. "dhcp")
  411. modemmanager_connected_method_dhcp_ipv6 "${interface}" "${beareriface}" "${metric}"
  412. ;;
  413. "static")
  414. address=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv6-config.address")
  415. prefix=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv6-config.prefix")
  416. gateway=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv6-config.gateway")
  417. mtu=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv6-config.mtu")
  418. dns1=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv6-config.dns.value\[1\]")
  419. dns2=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv6-config.dns.value\[2\]")
  420. modemmanager_connected_method_static_ipv6 "${interface}" "${beareriface}" "${address}" "${prefix}" "${gateway}" "${mtu}" "${dns1}" "${dns2}" "${metric}"
  421. ;;
  422. "ppp")
  423. proto_notify_error "${interface}" "unsupported method"
  424. return 1
  425. ;;
  426. *)
  427. proto_notify_error "${interface}" UNKNOWN_METHOD
  428. return 1
  429. ;;
  430. esac
  431. }
  432. return 0
  433. }
  434. proto_modemmanager_teardown() {
  435. local interface="$1"
  436. local modemstatus bearerpath errorstring
  437. local bearermethod_ipv4 bearermethod_ipv6
  438. local device lowpower iptype
  439. json_get_vars device lowpower iptype
  440. echo "stopping network"
  441. proto_notify_error "${interface}" MM_TEARDOWN_IN_PROGRESS
  442. # load connected bearer information, just the first one should be ok
  443. modemstatus=$(mmcli --modem="${device}" --output-keyvalue)
  444. bearerpath=$(modemmanager_get_field "${modemstatus}" "modem.generic.bearers.value\[1\]")
  445. [ -n "${bearerpath}" ] || {
  446. echo "couldn't load bearer path"
  447. return
  448. }
  449. # load bearer connection methods
  450. bearerstatus=$(mmcli --bearer "${bearerpath}" --output-keyvalue)
  451. bearermethod_ipv4=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv4-config.method")
  452. [ -n "${bearermethod_ipv4}" ] &&
  453. echo "IPv4 connection teardown required in interface ${interface}: ${bearermethod_ipv4}"
  454. bearermethod_ipv6=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv6-config.method")
  455. [ -n "${bearermethod_ipv6}" ] &&
  456. echo "IPv6 connection teardown required in interface ${interface}: ${bearermethod_ipv6}"
  457. # disconnection handling only requires special treatment in IPv4/PPP
  458. [ "${bearermethod_ipv4}" = "ppp" ] && modemmanager_disconnected_method_ppp_ipv4 "${interface}"
  459. modemmanager_disconnected_method_common "${interface}"
  460. # disconnect
  461. mmcli --modem="${device}" --simple-disconnect ||
  462. proto_notify_error "${interface}" DISCONNECT_FAILED
  463. # disable
  464. mmcli --modem="${device}" --disable
  465. proto_notify_error "${interface}" MM_MODEM_DISABLED
  466. # low power, only if requested
  467. [ "${lowpower:-0}" -lt 1 ] ||
  468. mmcli --modem="${device}" --set-power-state-low
  469. }
  470. [ -n "$INCLUDE_ONLY" ] || {
  471. add_protocol modemmanager
  472. }