diff options
| author | Yevhen Mykhno <yevhen.mykhno@globallogic.com> | 2023-02-09 20:25:11 +0200 | 
|---|---|---|
| committer | Yevhen Mykhno <yevhen.mykhno@globallogic.com> | 2023-02-27 17:07:32 +0200 | 
| commit | 33ed089d4b59bd79ae35f626ea6fc3da20c2edb9 (patch) | |
| tree | a1b556855040dcf34670b755d514271c1a4e5ff2 | |
| parent | 5c3e9d188bfd1309d366d755a70a37d5291d290a (diff) | |
| download | libmts-io-33ed089d4b59bd79ae35f626ea6fc3da20c2edb9.tar.gz libmts-io-33ed089d4b59bd79ae35f626ea6fc3da20c2edb9.tar.bz2 libmts-io-33ed089d4b59bd79ae35f626ea6fc3da20c2edb9.zip | |
[GP-1597] mPower R.6.3.X: Cellular Provider Profiles - LTE Authentication
| -rw-r--r-- | include/mts/MTS_IO_CellularRadio.h | 167 | ||||
| -rw-r--r-- | include/mts/MTS_IO_ICellularRadio.h | 51 | ||||
| -rw-r--r-- | include/mts/MTS_IO_QuectelRadio.h | 6 | ||||
| -rw-r--r-- | include/mts/MTS_IO_SequansRadio.h | 5 | ||||
| -rw-r--r-- | include/mts/MTS_IO_TelitRadio.h | 5 | ||||
| -rw-r--r-- | src/MTS_IO_CellularRadio.cpp | 299 | ||||
| -rw-r--r-- | src/MTS_IO_ICellularRadio.cpp | 11 | ||||
| -rw-r--r-- | src/MTS_IO_QuectelRadio.cpp | 108 | ||||
| -rw-r--r-- | src/MTS_IO_SequansRadio.cpp | 21 | ||||
| -rw-r--r-- | src/MTS_IO_TelitRadio.cpp | 103 | 
10 files changed, 713 insertions, 63 deletions
| diff --git a/include/mts/MTS_IO_CellularRadio.h b/include/mts/MTS_IO_CellularRadio.h index 68c1394..991dc47 100644 --- a/include/mts/MTS_IO_CellularRadio.h +++ b/include/mts/MTS_IO_CellularRadio.h @@ -269,6 +269,7 @@ namespace MTS {                  virtual CODE sendData(const char* pData, size_t nBytes);                  virtual CODE sendBasicQuery(const std::string& sCmd, const std::string& sLabel, std::string& sResult, int32_t timeoutMillis = 100, const char& ESC = ICellularRadio::CR); +                virtual CODE isCommandSupported(const std::string& sCmd, bool& bIsSupported);                  /**                   * @brief getDiagCommands - returns the list of Cellular Diagnostics commands for this radio.                   * @@ -278,6 +279,172 @@ namespace MTS {                   */                  virtual const std::vector<std::string>& getDiagCommands(bool bIsSimReady = true) = 0; +                struct PdpContextInfo { +                    std::string sId; +                    std::string sIpMode; +                    std::string sApn; +                    std::string sAuthType; +                    std::string sUsername; +                    std::string sPassword; +                }; + +                virtual std::vector<std::string> getSupportedPdpContextAuthTypes() const = 0; +                static CODE convertPdpContextAuthTypeToString(PDP_CONTEXT_AUTH_TYPE eAuthType, std::string& sAuthType); +                static CODE convertStringToPdpContextAuthType(const std::string& sAuthType, PDP_CONTEXT_AUTH_TYPE& eAuthType); + +                /** +                 * @brief checks if PDP context authentication is supported by the modem. +                 * +                 * @param isSupported - sets to "true" if PDP context authentication is supported, "false" otherwise. +                 * +                 * @return CODE::SUCCESS when the response from the radio is received +                 *         CODE::NO_RESPONSE when the modem doesn't respond, +                 *         CODE::FAILURE otherwise. +                 */ +                virtual CODE isPdpContextAuthSupported(bool& isSupported) = 0; + +                /** +                * @brief Get the list of PDP contexts with the base info about them from the radio. +                * +                * @param jData - a JSON object to be filled with data. +                *                   { +                *                       "context_number : STRING": { +                *                           "apn" : "apn_value: STRING", +                *                           "ipMode" : "ip_mode_value: STRING", +                *                       } +                *                   } +                * +                * @return CODE::SUCCESS when fetched successfully, +                *         CODE::NO_RESPONSE when the modem doesn't respond, +                *         CODE::ERROR when the radio returns "ERROR", +                *         CODE::FAILURE otherwise (unexpected response). +                */ +                virtual CODE getPdpContextsBase(Json::Value& jData); + +                /** +                * @brief fills jData with the information about PDP context authentication +                * +                * @param jData - a JSON object that is pre-filled with the list of PDP contexts: +                *                   { +                *                       "context_number : STRING": { +                *                           "apn" : "apn_value: STRING", +                *                           "ipMode" : "ip_mode_value: STRING", +                *                       } +                *                   } +                *                On success the method will complement jData with the PDP context authentication fields: +                *                   { +                *                       "context_number : STRING": { +                *                           "apn" : "apn_value: STRING", +                *                           "authType" : "auth_type_value: STRING", +                *                           "ipMode" : "ip_mode_value: STRING", +                *                           "password" : "password_value (optional): STRING", +                *                           "username" : "username_value (optional): STRING" +                *                       } +                *                   } +                * +                * @return CODE::SUCCESS when fetched successfully, +                *         CODE::NO_RESPONSE when the modem doesn't respond, +                *         CODE::ERROR when the radio returns "ERROR", +                *         CODE::FAILURE otherwise (unexpected response). +                */ +                virtual CODE fillPdpContextAuthFields(Json::Value& jData) = 0; + +                /** +                * @brief merges the current configuration of the PDP context with the one requested by user +                * +                * @param sId          - ID of the PDP context to change. +                * @param jCurrentContext - a JSON object filled with current configuration data +                *                      { +                *                           "apn" : "apn_value (optional): STRING", +                *                           "authType" : "auth_type_value: STRING", +                *                           "ipMode" : "ip_mode_value: STRING", +                *                           "password" : "password_value (optional): STRING", +                *                           "username" : "username_value (optional): STRING" +                *                      } +                * @param jChanges      - a JSON object filled with new configuration data +                *                      { +                *                           "apn" : "apn_value (optional): STRING", +                *                           "authType" : "auth_type_value (optional): STRING", +                *                           "ipMode" : "ip_mode_value (optional): STRING", +                *                           "password" : "password_value (optional): STRING", +                *                           "username" : "username_value (optional): STRING" +                *                      } +                * @param jResult      - a JSON object to be filled with merged data +                *                      { +                *                           "apn" : "apn_value (optional): STRING", +                *                           "authType" : "auth_type_value (optional): STRING", +                *                           "id" : "ctx_id (optional): STRING", +                *                           "ipMode" : "ip_mode_value (optional): STRING", +                *                           "password" : "password_value (optional): STRING", +                *                           "username" : "username_value (optional): STRING" +                *                      } +                * @return CODE::SUCCESS. +                */ +                virtual CODE mergePdpContexts(const std::string& sId, const Json::Value& jCurrentContext, const Json::Value& jChanges, Json::Value& jResult); + +                /** +                * @brief fills a PdpContextInfo instance with the data from JSON +                * @param jData - a JSON object that contains: +                *                      { +                *                           "apn" : "apn_value: STRING", +                *                           "authType" : "auth_type_value: STRING", +                *                           "id" : "ctx_id: STRING", +                *                           "ipMode" : "ip_mode_value: STRING", +                *                           "password" : "password_value: STRING", +                *                           "username" : "username_value: STRING" +                *                      } +                * @param pdpContextResult - PdpContextInfo object to be filled with parameters. +                * @return CODE::SUCCESS if validated successfully, +                *         CODE::INVALID_ARGS if validation failed. +                */ +                virtual CODE initPdpContextInfo(const Json::Value& jConfig, PdpContextInfo& pdpContextResult); + +                /** +                * @brief checks if PDP context authentication should be configured +                * @param jData - a JSON object that contains: +                *                      { +                *                           "apn" : "apn_value (optional): STRING", +                *                           "authType" : "auth_type_value: STRING", +                *                           "ipMode" : "ip_mode_value: STRING", +                *                           "password" : "password_value (optional): STRING", +                *                           "username" : "username_value (optional): STRING" +                *                      } +                * @param bIsAuthEditRequired - sets to "false" if there are no authType, username, and password fields in the json, otherwise sets to "true". +                * @return CODE::SUCCESS. +                */ +                static CODE isPdpContextAuthEditRequired(const Json::Value& jConfig, bool& bIsAuthEditRequired); + +                /** +                * @brief Sets the base PDP context parameters: IP Mode and APN +                * @param pdpContext - The new PDP context configuration +                * @return CODE::SUCCESS when set successfully, +                *         CODE::NO_RESPONSE when the modem doesn't respond, +                *         CODE::ERROR when the radio returns "ERROR", +                *         CODE::FAILURE otherwise (unexpected response). +                */ +                virtual CODE setPdpContextBase(const PdpContextInfo& pdpContext); + +                /** +                * @brief Sets the Cellular Authentication parameters for this PDP context: Authentication Type, Username and Password. +                * NOTE: Some implementations (Quectel) override the current IP Mode and APN in addition to the authentication fields. +                * @param pdpContext - The new PDP context configuration +                * @return CODE::SUCCESS when set successfully, +                *         CODE::NO_RESPONSE when the modem doesn't respond, +                *         CODE::ERROR when the radio returns "ERROR", +                *         CODE::FAILURE otherwise (unexpected response). +                */ +                virtual CODE setPdpContextAuth(const PdpContextInfo& pdpContext) = 0; + +                /** +                * @brief Delete the PDP context by its ID. +                * @param sId - PDP context ID +                * @return CODE::SUCCESS when delete successfully, +                *         CODE::NO_RESPONSE when the modem doesn't respond, +                *         CODE::ERROR when the radio returns "ERROR", +                *         CODE::FAILURE otherwise (unexpected response). +                */ +                virtual CODE deletePdpContext(const std::string& sId); +                  class RadioBandMap : public MTS::NonCopyable {                      public:                          RadioBandMap() diff --git a/include/mts/MTS_IO_ICellularRadio.h b/include/mts/MTS_IO_ICellularRadio.h index 0523236..a698647 100644 --- a/include/mts/MTS_IO_ICellularRadio.h +++ b/include/mts/MTS_IO_ICellularRadio.h @@ -78,6 +78,13 @@ namespace MTS {                      CS_MODE2 // only non-EPS services are allowed, the usage is "data centric"                  }; +                enum PDP_CONTEXT_AUTH_TYPE : uint8_t { +                    NONE = 0, +                    PAP, +                    CHAP, +                    PAP_CHAP +                }; +                  static CODE convertModelToType(const std::string& sModel, std::string& sType);                  static CODE convertModelToMtsShortCode(const std::string& sModel, std::string& sCode, ICellularRadio *radioObj = NULL);                  static CODE convertServiceDomainToString(SERVICEDOMAIN eSd, std::string& sSd); @@ -206,8 +213,19 @@ namespace MTS {                  static const char *KEY_ATTEMPTS_PUK;       //!< The number of attempts left to unlock the SIM card using PUK code                  //PDP Context +                static const char *KEY_PDP_CONTEXT_ID;                  static const char *KEY_PDP_CONTEXT_APN;                  static const char *KEY_PDP_CONTEXT_IPMODE; +                static const char *KEY_PDP_CONTEXT_AUTH_TYPE; +                static const char *KEY_PDP_CONTEXT_AUTH_USERNAME; +                static const char *KEY_PDP_CONTEXT_AUTH_PASSWORD; +                static const char *VALUE_PDP_CONTEXT_IP_MODE_IP; +                static const char *VALUE_PDP_CONTEXT_IP_MODE_IPV6; +                static const char *VALUE_PDP_CONTEXT_IP_MODE_IPV4V6; +                static const char *VALUE_PDP_CONTEXT_AUTH_TYPE_NONE; +                static const char *VALUE_PDP_CONTEXT_AUTH_TYPE_PAP; +                static const char *VALUE_PDP_CONTEXT_AUTH_TYPE_CHAP; +                static const char *VALUE_PDP_CONTEXT_AUTH_TYPE_PAP_CHAP;                  //Values - Type                  static const char *VALUE_TYPE_LTE; @@ -662,12 +680,18 @@ namespace MTS {                  /**                  * @brief Get the list of PDP contexts from the radio +                * NOTE: authType, password and username are only present if the radio supports Cellular Authentication on +                * the PDP Context Level. “password” and “username” are only present if the current authentication type is +                * other than NONE. The “password” field is omitted on radios that do not allow reading the current password (i.e. on Telit LE910Cx).                  *                  * @param jData - an object to be filled with data.                  *                   { -                *                       "<context_number: str>": { -                *                           "apn": "<apn_value: str>", -                *                           "ipMode": "<ip_mode: str>" +                *                       "context_number : STRING": { +                *                           "apn" : "apn_value: STRING", +                *                           "authType" : "auth_type_value: STRING", +                *                           "ipMode" : "ip_mode_value: STRING", +                *                           "password" : "password_value (optional): STRING", +                *                           "username" : "username_value (optional): STRING"                  *                       }                  *                   }                  * @@ -679,20 +703,27 @@ namespace MTS {                  virtual CODE getPdpContexts(Json::Value& jData) = 0;                  /** -                * @brief Set the PDP context to the radio +                * @brief Set the PDP context to the radio. If any of the fields are omitted, +                *        the system re-uses the current configuration for such fields. +                *        If all fields are omitted - the system deletes the context. +                *        "username" and "password" are mandatory if "ipMode" is PAP, CHAP or PAP-CHAP. +                *        "username", "password" and "authType" shall be omitted on radios that do NOT +                *         support Cellular Authentication on the PDP Context Level.                  * -                * @param sId     - a string value that contains an ID of the PDP context to change. -                * @param jConfig - a JSON-object that contains: -                *                  an IP mode for the specified PDP context, -                *                  an APN for the specified PDP context. +                * @param sId     - ID of the PDP context that should be edited. +                * @param jConfig - a JSON object that contains:                  *                     { -                *                          "apn": "<APN>", -                *                          "ipMode": "<IP MODE>" +                *                          "apn" : "apn_value (optional): STRING", +                *                          "authType" : "auth_type_value (optional): STRING", +                *                          "ipMode" : "ip_mode_value (optional): STRING", +                *                          "password" : "password_value (optional): STRING", +                *                          "username" : "username_value (optional): STRING"                  *                     }                  *                  * @return CODE::SUCCESS when fetched successfully,                  *         CODE::NO_RESPONSE when the modem doesn't respond,                  *         CODE::ERROR when the radio returns "ERROR", +                *         CODE::INVALID_ARGS when one of the JSON object values is incorrect OR not supported by the current radio,                  *         CODE::FAILURE otherwise (unexpected response).                  */                  virtual CODE setPdpContext(const std::string& sId, const Json::Value& jConfig) = 0; diff --git a/include/mts/MTS_IO_QuectelRadio.h b/include/mts/MTS_IO_QuectelRadio.h index 4315cb3..b3881cb 100644 --- a/include/mts/MTS_IO_QuectelRadio.h +++ b/include/mts/MTS_IO_QuectelRadio.h @@ -85,6 +85,12 @@ namespace MTS {                  CODE setRxDiversity(const Json::Value& jArgs) override; +                std::vector<std::string> getSupportedPdpContextAuthTypes() const override; +                static CODE convertStringToPdpContextType(const std::string& sStringType, std::string& sIntType); +                CODE isPdpContextAuthSupported(bool& isSupported) override; +                CODE fillPdpContextAuthFields(Json::Value& jData) override; +                CODE setPdpContextAuth(const PdpContextInfo& pdpContext) override; +                  const std::vector<std::string>& getDiagCommands(bool bIsSimReady = true) override;              private: diff --git a/include/mts/MTS_IO_SequansRadio.h b/include/mts/MTS_IO_SequansRadio.h index 11f2c91..b5a3a38 100644 --- a/include/mts/MTS_IO_SequansRadio.h +++ b/include/mts/MTS_IO_SequansRadio.h @@ -56,6 +56,11 @@ namespace MTS {                  CODE getIsSimInserted(bool& bData) override;                  CODE getSimLockAttempts(int& iAttemptsPin, int& iAttemptsPuk) override; +                std::vector<std::string> getSupportedPdpContextAuthTypes() const override; +                CODE isPdpContextAuthSupported(bool& isSupported) override; +                CODE fillPdpContextAuthFields(Json::Value& jData) override; +                CODE setPdpContextAuth(const PdpContextInfo& pdpContext) override; +                  const std::vector<std::string>& getDiagCommands(bool bIsSimReady = true) override;              private: diff --git a/include/mts/MTS_IO_TelitRadio.h b/include/mts/MTS_IO_TelitRadio.h index d198969..dc02755 100644 --- a/include/mts/MTS_IO_TelitRadio.h +++ b/include/mts/MTS_IO_TelitRadio.h @@ -73,6 +73,11 @@ namespace MTS {                  virtual CODE fumoWriteGroupsABD(int fd, UpdateCb& stepCb);                  //virtual CODE fumoWriteGroupC(int fd, UpdateCb& stepCb); +                std::vector<std::string> getSupportedPdpContextAuthTypes() const override; +                CODE isPdpContextAuthSupported(bool& isSupported) override; +                CODE fillPdpContextAuthFields(Json::Value &jData) override; +                CODE setPdpContextAuth(const PdpContextInfo& pdpContext) override; +                  const std::vector<std::string>& getDiagCommands(bool bIsSimReady = true) override;                  static bool isContainsSignChar(const std::string& str); diff --git a/src/MTS_IO_CellularRadio.cpp b/src/MTS_IO_CellularRadio.cpp index 3ce97ec..7200658 100644 --- a/src/MTS_IO_CellularRadio.cpp +++ b/src/MTS_IO_CellularRadio.cpp @@ -22,6 +22,7 @@  #include <unistd.h>  #include <sys/stat.h> +#include <algorithm>  #include <mts/MTS_IO_MccMncTable.h>  #include <mts/MTS_Thread.h> @@ -1760,27 +1761,81 @@ ICellularRadio::CODE CellularRadio::sendBasicQuery(const std::string& sCmd, cons      return SUCCESS;  } +ICellularRadio::CODE CellularRadio::isCommandSupported(const std::string& sCmd, bool& bIsSupported) { +    CODE rc; +    bIsSupported = false; + +    rc = sendBasicCommand(sCmd); + +    if (SUCCESS == rc) { +        bIsSupported = true; +    } else if (ERROR == rc) { +        // if not applicable +        rc = SUCCESS; +    } + +    return rc; +} +  ICellularRadio::CODE CellularRadio::getSelectedBandsRaw(std::string& sRawBands)  {      printTrace("%s| Acquiring selected bands: not applicable", m_sName.c_str());      return NOT_APPLICABLE;  } -ICellularRadio::CODE CellularRadio::getPdpContexts(Json::Value& jData) { -    printTrace("%s| Fetching the list of PDP contexts from the radio", getName().c_str()); +ICellularRadio::CODE CellularRadio::convertPdpContextAuthTypeToString(PDP_CONTEXT_AUTH_TYPE eAuthType, std::string& sAuthType) { +    CODE rc = FAILURE; + +    switch (eAuthType) { +        case NONE: sAuthType = VALUE_PDP_CONTEXT_AUTH_TYPE_NONE; rc = SUCCESS; break; +        case PAP: sAuthType = VALUE_PDP_CONTEXT_AUTH_TYPE_PAP; rc = SUCCESS; break; +        case CHAP: sAuthType = VALUE_PDP_CONTEXT_AUTH_TYPE_CHAP; rc = SUCCESS; break; +        case PAP_CHAP: sAuthType = VALUE_PDP_CONTEXT_AUTH_TYPE_PAP_CHAP; rc = SUCCESS; break; +        default: sAuthType = ICellularRadio::VALUE_UNKNOWN; break; +    } +    return rc; +} + +ICellularRadio::CODE MTS::IO::CellularRadio::convertStringToPdpContextAuthType(const std::string& sAuthType, PDP_CONTEXT_AUTH_TYPE& eAuthType) { +    CODE rc = FAILURE; +    if (VALUE_PDP_CONTEXT_AUTH_TYPE_NONE == sAuthType) { +        eAuthType = PDP_CONTEXT_AUTH_TYPE::NONE; +        rc = SUCCESS; +    } else if (VALUE_PDP_CONTEXT_AUTH_TYPE_PAP == sAuthType) { +        eAuthType = PDP_CONTEXT_AUTH_TYPE::PAP; +        rc = SUCCESS; +    } else if (VALUE_PDP_CONTEXT_AUTH_TYPE_CHAP == sAuthType) { +        eAuthType = PDP_CONTEXT_AUTH_TYPE::CHAP; +        rc = SUCCESS; +    } else if (VALUE_PDP_CONTEXT_AUTH_TYPE_PAP_CHAP == sAuthType) { +        eAuthType = PDP_CONTEXT_AUTH_TYPE::PAP_CHAP; +        rc = SUCCESS; +    } +    return rc; +} + +// When there are no defined contexts +// Telit returns: +// OK +// Quectel returns: +// +CGDCONT: +// +// OK +ICellularRadio::CODE CellularRadio::getPdpContextsBase(Json::Value& jData) {      CODE rc;      const std::string sCommand = "AT+CGDCONT?";      const int dTimeout = 1000;      std::string sResult; +    jData = Json::objectValue; +      rc = sendBasicQuery(sCommand, "", sResult, dTimeout);      if (rc != SUCCESS) {          return rc;      } -    std::vector<std::string> vContexts = MTS::Text::split(sResult, "+CGDCONT: "); -    std::vector<std::string> vContextParams; +    std::vector<std::string> vContexts = MTS::Text::split(sResult, "+CGDCONT:");      for (size_t i = 0; i < vContexts.size(); i++) {          vContexts[i] = MTS::Text::trim(vContexts[i]); @@ -1789,7 +1844,7 @@ ICellularRadio::CODE CellularRadio::getPdpContexts(Json::Value& jData) {              continue;          } -        vContextParams = MTS::Text::split(vContexts[i], ",", 4); +        std::vector<std::string> vContextParams = MTS::Text::split(vContexts[i], ",", 4);          if (vContextParams.size() < 3) {              return FAILURE; @@ -1806,78 +1861,216 @@ ICellularRadio::CODE CellularRadio::getPdpContexts(Json::Value& jData) {      return SUCCESS;  } -ICellularRadio::CODE CellularRadio::setPdpContext(const std::string& sId, const Json::Value& jConfig) { -    printTrace("%s| Setting context to the radio", getName().c_str()); +ICellularRadio::CODE CellularRadio::getPdpContexts(Json::Value& jData) { +    printTrace("%s| Fetching the list of PDP contexts from the radio", getName().c_str());      CODE rc; +    bool bPdpAuthSupported; -    if (sId.empty()) { -        printError("%s| PDP Context ID is not specified", getName().c_str()); -        return FAILURE; +    // check if pdp context authentication is applicable +    rc = isPdpContextAuthSupported(bPdpAuthSupported); +    if (SUCCESS != rc) { +        return rc;      } -    std::string sCommand = "AT+CGDCONT=" + sId; -    const int dTimeout = 1000; - -    Json::Value jAllContexts; +    rc = getPdpContextsBase(jData); -    rc = getPdpContexts(jAllContexts); -    if (rc != SUCCESS) { -        printError("%s| Failed to retrieve the current PDP context configuration: [%d]", getName().c_str(), rc); +    if (SUCCESS != rc) { +        printError("%s| Failed to fetch the list of the defined PDP contexts from the radio", getName().c_str());          return rc;      } -    // Remove the context if no parameters defined -    if (!jConfig.isMember(ICellularRadio::KEY_PDP_CONTEXT_IPMODE) && !jConfig.isMember(ICellularRadio::KEY_PDP_CONTEXT_APN)) { -        if (jAllContexts.isMember(sId)) { -            rc = sendBasicCommand(sCommand, dTimeout); +    if (bPdpAuthSupported) { +        rc = fillPdpContextAuthFields(jData); + +        if (SUCCESS != rc) { +            printError("%s| Failed to fetch the authentication parameters for PDP contexts", getName().c_str());              return rc; -        } else { -            printError("%s| PDP Context [%s] does not exist", getName().c_str(), sId.c_str()); -            return FAILURE;          }      } -    std::string sIpMode; +    return SUCCESS; +} -    if (jConfig.isMember(ICellularRadio::KEY_PDP_CONTEXT_IPMODE)) { -        if (jConfig[ICellularRadio::KEY_PDP_CONTEXT_IPMODE] == "IP" || jConfig[ICellularRadio::KEY_PDP_CONTEXT_IPMODE] == "PPP" || -            jConfig[ICellularRadio::KEY_PDP_CONTEXT_IPMODE] == "IPV6" || jConfig[ICellularRadio::KEY_PDP_CONTEXT_IPMODE] == "IPV4V6") { -                sIpMode = jConfig[ICellularRadio::KEY_PDP_CONTEXT_IPMODE].asString(); -        } else { -            printError("%s| Invalid IP Mode defined: [%s]", getName().c_str(), jConfig[ICellularRadio::KEY_PDP_CONTEXT_IPMODE].asString().c_str()); -            return FAILURE; +ICellularRadio::CODE CellularRadio::mergePdpContexts(const std::string& sId, const Json::Value& jCurrentContext, const Json::Value& jChanges, Json::Value& jResult) { +    jResult[KEY_PDP_CONTEXT_ID] = sId; + +    if (jChanges.isMember(KEY_PDP_CONTEXT_IPMODE)) { +        jResult[KEY_PDP_CONTEXT_IPMODE] = jChanges[KEY_PDP_CONTEXT_IPMODE].asString(); +    } else if (jCurrentContext.isMember(KEY_PDP_CONTEXT_IPMODE)) { +        printInfo("%s| Re-using IP Mode [%s] for PDP context [%s]", getName().c_str(), jCurrentContext[KEY_PDP_CONTEXT_IPMODE].asString().c_str(), sId.c_str()); +        jResult[KEY_PDP_CONTEXT_IPMODE] = jCurrentContext[KEY_PDP_CONTEXT_IPMODE].asString(); +    } + +    if (jChanges.isMember(KEY_PDP_CONTEXT_APN)) { +        jResult[KEY_PDP_CONTEXT_APN] = jChanges[KEY_PDP_CONTEXT_APN].asString(); +    } else if (jCurrentContext.isMember(KEY_PDP_CONTEXT_APN)) { +        printInfo("%s| Re-using APN [%s] for PDP context [%s]", getName().c_str(), jCurrentContext[KEY_PDP_CONTEXT_APN].asString().c_str(), sId.c_str()); +        jResult[KEY_PDP_CONTEXT_APN] = jCurrentContext[KEY_PDP_CONTEXT_APN].asString(); +    } + +    if (jChanges.isMember(KEY_PDP_CONTEXT_AUTH_TYPE)) { +        jResult[KEY_PDP_CONTEXT_AUTH_TYPE] = jChanges[KEY_PDP_CONTEXT_AUTH_TYPE].asString(); +    } + +    if (jChanges.isMember(KEY_PDP_CONTEXT_AUTH_USERNAME)) { +        jResult[KEY_PDP_CONTEXT_AUTH_USERNAME] = jChanges[KEY_PDP_CONTEXT_AUTH_USERNAME].asString(); +    } + +    if (jChanges.isMember(KEY_PDP_CONTEXT_AUTH_PASSWORD)) { +        jResult[KEY_PDP_CONTEXT_AUTH_PASSWORD] = jChanges[KEY_PDP_CONTEXT_AUTH_PASSWORD].asString(); +    } + +    return SUCCESS; +} + +ICellularRadio::CODE CellularRadio::initPdpContextInfo(const Json::Value& jConfig, PdpContextInfo& pdpContextResult) { +    // validate IP mode +    if (jConfig[KEY_PDP_CONTEXT_IPMODE].asString() != VALUE_PDP_CONTEXT_IP_MODE_IP && +        jConfig[KEY_PDP_CONTEXT_IPMODE].asString() != VALUE_PDP_CONTEXT_IP_MODE_IPV6 && +        jConfig[KEY_PDP_CONTEXT_IPMODE].asString() != VALUE_PDP_CONTEXT_IP_MODE_IPV4V6) { +            printError("%s| The PDP context IP mode value is invalid [%s]", getName().c_str(), jConfig[KEY_PDP_CONTEXT_IPMODE].asString().c_str()); +            return INVALID_ARGS; +    } + +    // validate authentication fields only if it is present +    if (jConfig.isMember(KEY_PDP_CONTEXT_AUTH_TYPE)) { + +        // check if the PDP context authentication is supported by the radio +        const auto& sRequested = jConfig[KEY_PDP_CONTEXT_AUTH_TYPE].asString(); +        const auto& vSupported = getSupportedPdpContextAuthTypes(); + +        auto supportCheck = [sRequested](const std::string& sElement) { +            return sRequested == sElement; +        }; + +        if (std::none_of(vSupported.begin(), vSupported.end(), supportCheck)) { +            printError("%s| PDP context parameters: authentication type [%s] is not supported", getName().c_str(), jConfig[KEY_PDP_CONTEXT_AUTH_TYPE].asString().c_str()); +            return INVALID_ARGS;          } -    } else if (jAllContexts.isMember(sId)) { -        printInfo("%s| Re-using IP Mode [%s] for PDP context [%s]", getName().c_str(), jAllContexts[sId][ICellularRadio::KEY_PDP_CONTEXT_IPMODE].asString().c_str(), sId.c_str()); -        sIpMode = jAllContexts[sId][ICellularRadio::KEY_PDP_CONTEXT_IPMODE].asString(); -    } else { -        printError("%s| Failed to edit PDP context [%s] - no such context defined", getName().c_str(), sId.c_str()); -        return FAILURE;      } -    sCommand += ",\""; -    sCommand += sIpMode; -    sCommand += "\""; +    if (! jConfig.isMember(KEY_PDP_CONTEXT_AUTH_TYPE) && (jConfig.isMember(KEY_PDP_CONTEXT_AUTH_USERNAME) || jConfig.isMember(KEY_PDP_CONTEXT_AUTH_PASSWORD))) { +        printError("%s| PDP context authentication type is not provided, but username or password are given", getName().c_str()); +        return INVALID_ARGS; +    } -    std::string sApn; +    if (jConfig.isMember(KEY_PDP_CONTEXT_AUTH_TYPE) && jConfig[KEY_PDP_CONTEXT_AUTH_TYPE].asString() != VALUE_PDP_CONTEXT_AUTH_TYPE_NONE && +        ! (jConfig.isMember(KEY_PDP_CONTEXT_AUTH_USERNAME) && jConfig.isMember(KEY_PDP_CONTEXT_AUTH_PASSWORD))) { +            printError("%s| PDP context authentication type [%s]. The username or password are not provided", getName().c_str(), jConfig[KEY_PDP_CONTEXT_AUTH_TYPE].asString().c_str()); +            return INVALID_ARGS; +    } -    if (jConfig.isMember(ICellularRadio::KEY_PDP_CONTEXT_APN)) { -        sApn = jConfig[ICellularRadio::KEY_PDP_CONTEXT_APN].asString(); -    } else if (jAllContexts.isMember(sId)) { -        printInfo("%s| Re-using APN [%s] for PDP context [%s]", getName().c_str(), jAllContexts[sId][ICellularRadio::KEY_PDP_CONTEXT_APN].asString().c_str(), sId.c_str()); -        sApn = jAllContexts[sId][ICellularRadio::KEY_PDP_CONTEXT_APN].asString(); -    } else { -        printError("%s| Failed to edit PDP context [%s] - no such context defined", getName().c_str(), sId.c_str()); -        return FAILURE; +    // fills with empty string if there are no such fields +    pdpContextResult.sId       = jConfig[KEY_PDP_CONTEXT_ID].asString(); +    pdpContextResult.sIpMode   = jConfig[KEY_PDP_CONTEXT_IPMODE].asString(); +    pdpContextResult.sApn      = jConfig[KEY_PDP_CONTEXT_APN].asString(); +    pdpContextResult.sAuthType = jConfig[KEY_PDP_CONTEXT_AUTH_TYPE].asString(); +    pdpContextResult.sUsername = jConfig[KEY_PDP_CONTEXT_AUTH_USERNAME].asString(); +    pdpContextResult.sPassword = jConfig[KEY_PDP_CONTEXT_AUTH_PASSWORD].asString(); + +    return SUCCESS; +} + +ICellularRadio::CODE CellularRadio::isPdpContextAuthEditRequired(const Json::Value& jConfig, bool& bIsAuthEditRequired) { +    bIsAuthEditRequired = false; + +    if (jConfig.isMember(KEY_PDP_CONTEXT_AUTH_TYPE) || +        jConfig.isMember(KEY_PDP_CONTEXT_AUTH_PASSWORD) || +        jConfig.isMember(KEY_PDP_CONTEXT_AUTH_USERNAME)) { +            bIsAuthEditRequired = true;      } +    return SUCCESS; +} + +ICellularRadio::CODE CellularRadio::setPdpContextBase(const PdpContextInfo& pdpContext) { +    std::string sCommand = "AT+CGDCONT="; +    const int dTimeout = 1000; + +    sCommand += pdpContext.sId;      sCommand += ",\""; -    sCommand += sApn; +    sCommand += pdpContext.sIpMode; +    sCommand += "\",\""; +    sCommand += pdpContext.sApn;      sCommand += "\""; -    rc = sendBasicCommand(sCommand, dTimeout); +    return sendBasicCommand(sCommand, dTimeout); +} + +ICellularRadio::CODE CellularRadio::deletePdpContext(const std::string& sId) { +    std::string sCommand = "AT+CGDCONT=" + sId; +    const int dTimeout = 1000; -    return rc; +    return sendBasicCommand(sCommand, dTimeout); +} + +ICellularRadio::CODE CellularRadio::setPdpContext(const std::string& sId, const Json::Value& jConfig) { +    printTrace("%s| Setting PDP context [%s] to the radio", getName().c_str(), sId.c_str()); + +    CODE rc; +    Json::Value jCurrentContexts; +    bool bPdpAuthSupported; +    bool bPdpAuthEditRequired; +    PdpContextInfo pdpContext; + +    if (sId.empty()) { +        printError("%s| PDP Context ID is not specified", getName().c_str()); +        return INVALID_ARGS; +    } + +    rc = getPdpContexts(jCurrentContexts); +    if (SUCCESS != rc) { +        return rc; +    } + +    // DELETE context + +    if (jConfig.empty()) { +        rc = deletePdpContext(sId); +        if (SUCCESS != rc) { +            printError("%s| Failed to delete PDP context [%s]", getName().c_str(), sId.c_str()); +        } +        return rc; +    } + +    // ADD or EDIT context + +    // check if PDP context authentication is applicable + +    (void) isPdpContextAuthEditRequired(jConfig, bPdpAuthEditRequired); + +    rc = isPdpContextAuthSupported(bPdpAuthSupported); +    if (SUCCESS != rc) { +        return rc; +    } + +    if (! bPdpAuthSupported && bPdpAuthEditRequired) { +        printError("%s| PDP context authentication is not supported by this radio, but arguments are provided", getName().c_str()); +        return INVALID_ARGS; +    } + +    Json::Value jResult; + +    // Edit - fetch missing arguments from the current state. +    (void) mergePdpContexts(sId, jCurrentContexts[sId], jConfig, jResult); + +    // validate fields and create a PDP context structure to use in the commands +    rc = initPdpContextInfo(jResult, pdpContext); +    if (SUCCESS != rc) { +        return rc; +    } + +    // setting context +    rc = setPdpContextBase(pdpContext); +    if (SUCCESS != rc) { +        return rc; +    } + +    if (bPdpAuthSupported && bPdpAuthEditRequired) { +        return setPdpContextAuth(pdpContext); +    } + +    return SUCCESS;  }  ICellularRadio::CODE CellularRadio::getDiagnostics(std::string& sReport) { diff --git a/src/MTS_IO_ICellularRadio.cpp b/src/MTS_IO_ICellularRadio.cpp index e42713d..f93cb21 100644 --- a/src/MTS_IO_ICellularRadio.cpp +++ b/src/MTS_IO_ICellularRadio.cpp @@ -97,8 +97,19 @@ const char *MTS::IO::ICellularRadio::KEY_MIP_REVTUN = "revTun";  const char *MTS::IO::ICellularRadio::KEY_MIP_MNAAASS = "mnAaaSs";                 //!< Mobile Node Authentication, Authorization, and Accounting Server Shared Secret  const char *MTS::IO::ICellularRadio::KEY_MIP_MNHASS = "mnHaSs";                   //!< Mobile Node Home Agent Shared Secret +const char *MTS::IO::ICellularRadio::KEY_PDP_CONTEXT_ID = "id";  const char *MTS::IO::ICellularRadio::KEY_PDP_CONTEXT_APN = "apn";  const char *MTS::IO::ICellularRadio::KEY_PDP_CONTEXT_IPMODE = "ipMode"; +const char *MTS::IO::ICellularRadio::KEY_PDP_CONTEXT_AUTH_TYPE = "authType"; +const char *MTS::IO::ICellularRadio::KEY_PDP_CONTEXT_AUTH_USERNAME = "username"; +const char *MTS::IO::ICellularRadio::KEY_PDP_CONTEXT_AUTH_PASSWORD = "password"; +const char *MTS::IO::ICellularRadio::VALUE_PDP_CONTEXT_IP_MODE_IP = "IP"; +const char *MTS::IO::ICellularRadio::VALUE_PDP_CONTEXT_IP_MODE_IPV6 = "IPV6"; +const char *MTS::IO::ICellularRadio::VALUE_PDP_CONTEXT_IP_MODE_IPV4V6 = "IPV4V6"; +const char *MTS::IO::ICellularRadio::VALUE_PDP_CONTEXT_AUTH_TYPE_NONE = "NONE"; +const char *MTS::IO::ICellularRadio::VALUE_PDP_CONTEXT_AUTH_TYPE_PAP = "PAP"; +const char *MTS::IO::ICellularRadio::VALUE_PDP_CONTEXT_AUTH_TYPE_CHAP = "CHAP"; +const char *MTS::IO::ICellularRadio::VALUE_PDP_CONTEXT_AUTH_TYPE_PAP_CHAP = "PAP-CHAP";  const char *MTS::IO::ICellularRadio::VALUE_TYPE_GSM = "GSM";  const char *MTS::IO::ICellularRadio::VALUE_TYPE_LTE = "LTE"; diff --git a/src/MTS_IO_QuectelRadio.cpp b/src/MTS_IO_QuectelRadio.cpp index 0c0c8ab..b01bbb1 100644 --- a/src/MTS_IO_QuectelRadio.cpp +++ b/src/MTS_IO_QuectelRadio.cpp @@ -1629,6 +1629,114 @@ ICellularRadio::CODE QuectelRadio::setRxDiversity(const Json::Value& jArgs) {      return SUCCESS;  } +ICellularRadio::CODE QuectelRadio::convertStringToPdpContextType(const std::string& sStringType, std::string& sIntType) { +    CODE rc = FAILURE; + +    if ("IP" == sStringType) { +        sIntType = "1"; +        rc = SUCCESS; +    } else if ("IPV6" == sStringType) { +        sIntType = "2"; +        rc = SUCCESS; +    } else if ("IPV4V6" == sStringType) { +        sIntType = "3"; +        rc = SUCCESS; +    } + +    return rc; +} + +std::vector<std::string> QuectelRadio::getSupportedPdpContextAuthTypes() const { +    return { +        VALUE_PDP_CONTEXT_AUTH_TYPE_NONE, +        VALUE_PDP_CONTEXT_AUTH_TYPE_PAP, +        VALUE_PDP_CONTEXT_AUTH_TYPE_CHAP, +        VALUE_PDP_CONTEXT_AUTH_TYPE_PAP_CHAP +    }; +} + +ICellularRadio::CODE QuectelRadio::isPdpContextAuthSupported(bool& isSupported) { +    std::string sCmd("AT+QICSGP=?"); +    return isCommandSupported(sCmd, isSupported); +} + +ICellularRadio::CODE QuectelRadio::fillPdpContextAuthFields(Json::Value& jData) { +    CODE rc; +    std::string sCmd("AT+QICSGP="); +    std::string sResult; +    uint8_t iValue; + +    // iterate over PDP contex IDs to get context settings +    for (std::string & sContextId : jData.getMemberNames()) { +        // +QICSGP: <context_type>,<APN>,<username>,<password>,<authentication> +        // +QICSGP: 0,"","","",0 -- if the context does not exist + +        rc = sendBasicQuery(sCmd+sContextId, "+QICSGP:", sResult, 300); + +        if (SUCCESS != rc) { +            return rc; +        } + +        //  1,"test","login","password",1 +        // [0] [1]     [2]      [3]    [4] +        auto vAuthParams = MTS::Text::split(sResult, ","); + +        if (vAuthParams.size() < 5) { +            printError("%s| Failed to parse PDP context authentication string [%s]", getName().c_str(), sResult.c_str()); +            return FAILURE; +        } + +        if (! MTS::Text::parse(iValue, vAuthParams[4])) { +            printError("%s| Failed to parse PDP context authentication type [%s]", getName().c_str(), vAuthParams[4].c_str()); +            return FAILURE; +        } + +        rc = convertPdpContextAuthTypeToString(static_cast<PDP_CONTEXT_AUTH_TYPE>(iValue), sResult); +        if (SUCCESS != rc) { +            return rc; +        } + +        jData[sContextId][KEY_PDP_CONTEXT_AUTH_TYPE] = sResult; + +        if (iValue != PDP_CONTEXT_AUTH_TYPE::NONE) { +            jData[sContextId][KEY_PDP_CONTEXT_AUTH_USERNAME] = MTS::Text::trim(vAuthParams[2], '"'); +            jData[sContextId][KEY_PDP_CONTEXT_AUTH_PASSWORD] = MTS::Text::trim(vAuthParams[3], '"'); +        } +    } + +    return SUCCESS; +} + +// AT+QICSGP=<contextID>[,<context_type>,<APN>[,<username>,<password>)[,<authentication>[,<cdma_pwd>]]]] +ICellularRadio::CODE QuectelRadio::setPdpContextAuth(const PdpContextInfo& pdpContext) { +    printTrace("%s| Setting PDP context authentication to the radio", getName().c_str()); + +    CODE rc; +    std::string sContextType; +    std::string sCmd = "AT+QICSGP="; +    PDP_CONTEXT_AUTH_TYPE eAuthType; + +    rc = convertStringToPdpContextType(pdpContext.sIpMode, sContextType); +    if (SUCCESS != rc) { +        return rc; +    } + +    rc = convertStringToPdpContextAuthType(pdpContext.sAuthType, eAuthType); +    if (SUCCESS != rc) { +        return rc; +    } + +    sCmd += pdpContext.sId + "," + sContextType + ",\"" + pdpContext.sApn + "\""; + +    if (PDP_CONTEXT_AUTH_TYPE::NONE == eAuthType) { +        sCmd += ",\"\",\"\"," + std::to_string(eAuthType); +    } else { +        sCmd += ",\"" + pdpContext.sUsername + "\",\"" + pdpContext.sPassword +"\"," + std::to_string(eAuthType); +    } + +    return sendBasicCommand(sCmd); +} +  const std::vector<std::string>& QuectelRadio::getDiagCommands(bool) {      // Declare as static to initialize only when used, but cache the results.      const static std::vector<std::string> vCommands { diff --git a/src/MTS_IO_SequansRadio.cpp b/src/MTS_IO_SequansRadio.cpp index a48a309..8133bbf 100644 --- a/src/MTS_IO_SequansRadio.cpp +++ b/src/MTS_IO_SequansRadio.cpp @@ -534,6 +534,27 @@ ICellularRadio::CODE SequansRadio::getSimLockAttemptsByType(const std::string& s      return SUCCESS;  } +std::vector<std::string> SequansRadio::getSupportedPdpContextAuthTypes() const { +    return { +        VALUE_PDP_CONTEXT_AUTH_TYPE_NONE, +        VALUE_PDP_CONTEXT_AUTH_TYPE_PAP, +        VALUE_PDP_CONTEXT_AUTH_TYPE_CHAP +    }; +} + +ICellularRadio::CODE SequansRadio::isPdpContextAuthSupported(bool& isSupported) { +    isSupported = false; +    return SUCCESS; +} + +ICellularRadio::CODE SequansRadio::fillPdpContextAuthFields(Json::Value& jData) { +    return NOT_APPLICABLE; +} + +ICellularRadio::CODE SequansRadio::setPdpContextAuth(const PdpContextInfo& pdpContext) { +    return NOT_APPLICABLE; +} +  const std::vector<std::string>& SequansRadio::getDiagCommands(bool) {      // Declare as static to initialize only when used, but cache the results.      const static std::vector<std::string> vCommands { diff --git a/src/MTS_IO_TelitRadio.cpp b/src/MTS_IO_TelitRadio.cpp index 21af65d..00c1686 100644 --- a/src/MTS_IO_TelitRadio.cpp +++ b/src/MTS_IO_TelitRadio.cpp @@ -1178,6 +1178,109 @@ bool MTS::IO::TelitRadio::isContainsSignChar(const std::string& str) {      return true;  } +std::vector<std::string> TelitRadio::getSupportedPdpContextAuthTypes() const { +    return { +        VALUE_PDP_CONTEXT_AUTH_TYPE_NONE, +        VALUE_PDP_CONTEXT_AUTH_TYPE_PAP, +        VALUE_PDP_CONTEXT_AUTH_TYPE_CHAP +    }; +} + +ICellularRadio::CODE TelitRadio::isPdpContextAuthSupported(bool& isSupported) { +    std::string sCmd("AT#PDPAUTH=?"); +    return isCommandSupported(sCmd, isSupported); +} + +ICellularRadio::CODE TelitRadio::fillPdpContextAuthFields(Json::Value& jData) { +    CODE rc; +    std::string sCmd("AT#PDPAUTH?"); +    std::string sResult; + +    // #PDPAUTH: 1,0 +    // #PDPAUTH: 2,0 +    // #PDPAUTH: 3,0 +    // #PDPAUTH: 4,1,"admin" +    rc = sendBasicQuery(sCmd, "", sResult, 300); + +    if (SUCCESS != rc) { +        return rc; +    } + +    std::vector<std::string> vContexts = MTS::Text::split(sResult, "#PDPAUTH:"); +    std::string sContextId; + +    for (std::string& sLine : vContexts) { +        sLine = MTS::Text::trim(sLine); + +        if (sLine.empty()) { +            continue; +        } + +        //   4,1,"admin" +        // [0][1] [2] +        auto vAuthParams = MTS::Text::split(sLine, ","); + +        // Contains 2 elements when the authentication type is NONE, contains 3 elements in other cases +        if (vAuthParams.size() < 2) { +            printError("%s| Failed to parse PDP context authentication string [%s]", getName().c_str(), sLine.c_str()); +            return FAILURE; +        } + +        sContextId = vAuthParams[0]; + +        uint8_t iValue; +        if (! MTS::Text::parse(iValue, vAuthParams[1])) { +            printError("%s| Failed to parse PDP context authentication type [%s]", getName().c_str(), vAuthParams[1].c_str()); +            return FAILURE; +        } + +        rc = convertPdpContextAuthTypeToString(static_cast<PDP_CONTEXT_AUTH_TYPE>(iValue), sResult); +        if (SUCCESS != rc) { +            printError("%s| Failed to convert PDP context authentication type to string [%d]", getName().c_str(), iValue); +            return rc; +        } + +        jData[sContextId][KEY_PDP_CONTEXT_AUTH_TYPE] = sResult; + +        if (iValue == PDP_CONTEXT_AUTH_TYPE::NONE) { +            continue; +        } + +        if (vAuthParams.size() < 3) { +            printError("%s| Failed to parse PDP context authentication string [%s]", getName().c_str(), sLine.c_str()); +            return FAILURE; +        } + +        jData[sContextId][KEY_PDP_CONTEXT_AUTH_USERNAME] = MTS::Text::trim(vAuthParams[2], '"'); +    } + +    return SUCCESS; +} + +ICellularRadio::CODE TelitRadio::setPdpContextAuth(const PdpContextInfo& pdpContext) { +    printTrace("%s| Setting PDP context authentication to the radio", getName().c_str()); + +    CODE rc; +    std::string sCmd = "AT#PDPAUTH="; +    PDP_CONTEXT_AUTH_TYPE eAuthType; + +    sCmd += pdpContext.sId + ","; + +    rc = convertStringToPdpContextAuthType(pdpContext.sAuthType, eAuthType); + +    if (SUCCESS != rc) { +        return rc; +    } + +    sCmd += std::to_string(eAuthType); + +    if (PDP_CONTEXT_AUTH_TYPE::NONE != eAuthType) { +        sCmd += "," + pdpContext.sUsername + "," + pdpContext.sPassword; +    } + +    return sendBasicCommand(sCmd); +} +  const std::vector<std::string>& TelitRadio::getDiagCommands(bool) {      // Declare as static to initialize only when used, but cache the results.      const static std::vector<std::string> vCommands { | 
