# Contributed by Max Bülte # Version: 0.1.3 # shellcheck disable=SC2148,SC2016,SC1091,SC2034 . "$SUBR_DIR/ip" . "$SUBR_DIR/rfkill" DEPENDENCIES=('mmcli' 'jq') function check_deps() { local _prog="$1" if ! command -v "$_prog" >/dev/null 2>&1; then report_error "'$_prog' not found! Check required dependencies." return 1 fi } function configure_interface() { local _bearer="$1" local _interface_address _interface_prefix _interface_gateway _interface_dns if [[ -n "$IP" ]] && [[ "$IP" != 'no' ]]; then if [[ "$(mmcli -J -m "$MODEM" -b "$_bearer" 2>/dev/null | jq -r '.[]."ipv4-config".method')" == "static" ]]; then _interface_address="$(mmcli -J -m "$MODEM" -b "$_bearer" 2>/dev/null | jq -r '.[]."ipv4-config".address')" _interface_prefix="$(mmcli -J -m "$MODEM" -b "$_bearer" 2>/dev/null | jq -r '.[]."ipv4-config".prefix')" _interface_gateway="$(mmcli -J -m "$MODEM" -b "$_bearer" 2>/dev/null | jq -r '.[]."ipv4-config".gateway')" _interface_dns="$(mmcli -J -m "$MODEM" -b "$_bearer" 2>/dev/null | jq -r '.[]."ipv4-config".dns[0]')" Address=("${_interface_address}/${_interface_prefix}") Gateway="$_interface_gateway" elif [[ "$(mmcli -J -m "$MODEM" -b "$_bearer" 2>/dev/null | jq -r '.[]."ipv4-config".method')" == "dhcp" ]]; then IP="dhcp" fi fi if [[ -n "$IP6" ]] && [[ "$IP6" != 'no' ]]; then if [[ "$(mmcli -J -m "$MODEM" -b "$_bearer" 2>/dev/null | jq -r '.[]."ipv6-config".method')" == "static" ]]; then _interface_address="$(mmcli -J -m "$MODEM" -b "$_bearer" 2>/dev/null | jq -r '.[]."ipv6-config".address')" _interface_prefix="$(mmcli -J -m "$MODEM" -b "$_bearer" 2>/dev/null | jq -r '.[]."ipv6-config".prefix')" _interface_gateway="$(mmcli -J -m "$MODEM" -b "$_bearer" 2>/dev/null | jq -r '.[]."ipv6-config".gateway')" _interface_dns="$(mmcli -J -m "$MODEM" -b "$_bearer" 2>/dev/null | jq -r '.[]."ipv6-config".dns[0]')" Address6=("${_interface_address}/${_interface_prefix}") Gateway6="$_interface_gateway" elif [[ "$(mmcli -J -m "$MODEM" -b "$_bearer" 2>/dev/null | jq -r '.[]."ipv6-config".method')" == "dhcp" ]]; then IP6="dhcp" fi fi [[ "${UsePeerDNS:-true}" == 'true' ]] && DNS=("$_interface_dns") } function mobile_mm_up { local _mmcli_args=() if [[ -n "$RFKill" ]]; then report_debug "Unblocking device: '$RFKill'." rf_enable "$Interface" "$RFKill" || return 1 fi if [[ -n "$AccessPointName" ]]; then _mmcli_args+=("apn=${AccessPointName}") else report_error "Option 'AccessPointName' is required!" return 1 fi if [[ -n "$Pin" ]]; then _mmcli_args+=("pin=${Pin}") fi local _ip_type="ipv4" if [[ -n "$IP6" ]] && [[ "$IP6" != 'no' ]]; then _ip_type="ipv4v6" # FIXME: report_notice "IPv6 is currently untested. Good luck!" fi _mmcli_args+=("ip-type=${_ip_type}") if [[ -n "$User" ]] && [[ -n "$Password" ]]; then _mmcli_args+=("user=${User}") _mmcli_args+=("password=${Password}") fi local _roaming=0 is_yes "${Roaming:-0}" && _roaming=1 _mmcli_args+=("allow-roaming=${_roaming}") _mmcli_args+=("number=${PhoneNumber:-*99#}") local _connect_args _connect_args=$(printf ",%s" "${_mmcli_args[@]}") _connect_args=${_connect_args:1} if [[ -z "$Mode" ]]; then Mode="4G" elif [[ ! "$Mode" =~ ^(ANY|2G|3G|4G)$ ]]; then report_error "Mode must be one of (ANY, 2G, 3G, 4G)!" return 1 fi report_debug "Full connection string: mmcli -m ${MODEM} --set-allowed-modes=ANY --set-preferred-mode=${Mode} --simple-connect=$_connect_args" # Trying to establish the broadband connection until the modem got its bearer (and the bearer_interface) or the timeout is reached mmcli -m "$MODEM" --set-allowed-modes=ANY --set-preferred-mode="${Mode}" --simple-connect="$_connect_args" local _bearer='' _bearer_interface='' TIMEOUT_CONNECT=${TimeoutConnect:-25} until [[ -n "$_bearer" ]] || [[ "$TIMEOUT_CONNECT" -eq 0 ]]; do _bearer="$(mmcli -J -m "$MODEM" 2>/dev/null | jq -r '.[].generic.bearers[0]' 2>/dev/null)" _bearer="${_bearer##--}" sleep 1 ((TIMEOUT_CONNECT--)) until [[ -n "$_bearer_interface" ]] || [[ "$TIMEOUT_CONNECT" -eq 0 ]]; do _bearer_interface="$(mmcli -J -m "$MODEM" -b "$_bearer" 2>/dev/null | jq -r '.[].status.interface' 2>/dev/null)" _bearer_interface="${_bearer_interface##--}" sleep 1 ((TIMEOUT_CONNECT--)) done done if [[ -z "$_bearer" ]]; then report_error "No bearer found!" return 1 fi if [[ -z "$_bearer_interface" ]]; then report_error "No bearer interface found. Consider increasing \$TimeoutConnect a bit." return 1 elif [[ "$_bearer_interface" != "$Interface" ]]; then report_error "Network interface used by the bearer is not the same as provided by your connection profile. Please update the profile using the right bearer interface: '$_bearer_interface'" return 1 fi if [[ -n "$_bearer" ]] && [[ -n "$_bearer_interface" ]]; then report_debug "Bearer '$_bearer' found on interface '$_bearer_interface'." fi # Disable IPv6 before bringing the interface up to prevent SLAAC if [[ "$IP6" == "no" ]]; then sysctl -q -w "net.ipv6.conf.${Interface/.//}.disable_ipv6=1" fi if ! bring_interface_up "$Interface"; then report_error "Failed to bring interface '$Interface' up" return 1 fi # Some cards are plain slow to come up. Don't fail immediately. if ! timeout_wait "${TimeoutCarrier:-5}" '(( $(< "/sys/class/net/$Interface/carrier") ))'; then report_error "No connection found on interface '$Interface' (timeout)!" bring_interface_down "$Interface" return 1 fi # Getting interface configuration configure_interface "$_bearer" if ! ip_set; then bring_interface_down "$Interface" return 1 fi } function mobile_mm_down { local _bearer _bearer="$(mmcli -J -m "$MODEM" 2>/dev/null | jq -r '.[].generic.bearers[0]' 2>/dev/null)" # Getting interface configuration configure_interface "$_bearer" mmcli -m "$MODEM" --simple-disconnect 2>/dev/null ip_unset interface_delete "$Interface" if [[ -n $RFKill ]]; then rf_disable "$Interface" "$RFKill" fi } # Check that the dependencies are available for PROGRAM in "${DEPENDENCIES[@]}"; do check_deps "$PROGRAM" done # Try getting the modem path MODEM="$(mmcli -J -L 2>/dev/null | jq -r '.[][0]' 2>/dev/null)" if [[ -z "$MODEM" ]]; then report_debug "No modem found yet." if ! systemctl is-active ModemManager.service &>/dev/null; then report_notice "ModemManager service not running, trying to start it.." systemctl start ModemManager.service fi # Wait until ModemManager has initialised the modem report_debug "Sleeping until modem is available.." TIMEOUT_MODEM=${TimeoutModem:-15} until [[ "$(mmcli -J -L 2>/dev/null | jq -r '.[][0]' 2>/dev/null)" =~ ^/org/freedesktop/ModemManager[0-9]+/Modem/[0-9]+$ ]] \ || [[ "$TIMEOUT_MODEM" -eq 0 ]]; do sleep 1 ((TIMEOUT_MODEM--)) done # Finally get the modem path MODEM="$(mmcli -J -L 2>/dev/null | jq -r '.[][0]' 2>/dev/null)" fi MODEM_ID="$(mmcli -J -L 2>/dev/null | jq -r '.[][0] | [splits("/")] | (.[5] | tonumber)' 2>/dev/null)" if [[ ! "$MODEM_ID" =~ ^[0-9]+$ ]]; then report_error "No modem found! Please ensure that ModemManager is capable of finding your modem." exit 1 fi # Get the modem status MODEM_STATUS="$(mmcli -J -m "$MODEM" 2>/dev/null | jq -r '.[].generic.state')" if [[ "$MODEM_STATUS" == 'locked' ]]; then report_notice "Modem is locked. Trying to supply PUK/PIN, if provided in the connection profile.." # Getting SIM for modem SIM="$(mmcli -J -m "$MODEM" 2>/dev/null | jq -r '.[].generic.sim')" if [[ -n "$SIM" ]]; then if [[ -n "$Puk" ]]; then report_notice "Trying to unlock using PUK.." mmcli -m "$MODEM" -i "$SIM" --puk="${Pin}" >/dev/null 2>&1 || true fi if [[ -n "$Pin" ]]; then report_notice "Trying to unlock using PIN.." mmcli -m "$MODEM" -i "$SIM" --pin="${Pin}" >/dev/null 2>&1 fi MODEM_STATUS="$(mmcli -J -m "$MODEM" 2>/dev/null | jq -r '.[].generic.state')" if [[ "$MODEM_STATUS" == 'locked' ]]; then report_error "Modem is still locked. Maybe the supplied PIN/PUK is wrong?" exit 1 fi else report_error "No SIM card found!" exit 1 fi fi # vim: ft=sh ts=2 et sw=2: