Skip to content
This repository has been archived by the owner on Nov 11, 2024. It is now read-only.

Commit

Permalink
POSSIBLY BREAKING CHANGE to cellular: APN username/password fields. (…
Browse files Browse the repository at this point in the history
…#1028)

Preface: you only need to worry about any of this if you are required to specify a user name and password with the APN supplied by your service provider. The "POSSIBLY BREAKING" aspect of it is that three new fields are added to the end of the uNetworkCfgCell_t structure that is passed to uNetworkInterfaceUp() for a cellular device: assuming you are using a const struct then your compiler will initialise these extra fields to zero and that will be fine; if for some reason you are not using a const struct, please be sure to memset() it to zero before populating it.

The cellular uCellNetConnect() and uCellNetActivate() functions support being given a pUsername/pPassword pair to go with the APN, where required by the service provider. There are two problems with this:

- no "authentication mode" field is included: for SARA-U201 and SARA-R5 this is fine as those modules support figuring out the authentication mode automatically, however, SARA-R4, LARA-R6 and the up-coming LENA-R8 do not, hence it is necessary for the application to set the authentication mode explicitly,
- the pUsername and pPassword fields are not included in the uNetworkCfgCell_t structure passed to uNetworkInterfaceUp() for a cellular device: this was done as a deliberate simplification, since the user name and password fields are relatively rarely required (having to specify the APN is bad enough); however, there are some cases where the fields are required, meaning the application has to drop back to calling uCellNetConnect() directly, which is not ideal.

With this commit the following changes are made:

- two new functions, uCellNetGetAuthenticationMode() and uCellNetSetAuthenticationMode(), are added to the uCellNet API. Where a module supports automatic authentication mode, uCellNetGetAuthenticationMode() will return U_CELL_NET_AUTHENTICATION_MODE_AUTOMATIC and all will be fine. If a module does NOT support automatic authentication mode (i.e. for SARA-R4, LARA-R6 and the up-coming LENA-R8) then, if you are required to supply a user name and password with your APN, you must call uCellNetSetAuthenticationMode() to set the appropriate mode explicitly; your service provider will indicate PAP (U_CELL_NET_AUTHENTICATION_MODE_PAP) or CHAP (U_CELL_NET_AUTHENTICATION_MODE_CHAP).
- three new fields, pUsername, pPassword and authenticationMode, are added to the end of the uNetworkCfgCell_t structure that is passed to uNetworkInterfaceUp() for a cellular device; if you need to pass a user name or password along with the APN then you may populate pUsername and pPassword and, if your module does not support automatic authentication mode, you must also populate the authenticationMode field; there is no harm in populating the authenticationMode field anyway, even if your module does support automatic authentication mode.
  • Loading branch information
RobMeades authored Oct 30, 2023
1 parent 457b22a commit 362dc2f
Show file tree
Hide file tree
Showing 21 changed files with 604 additions and 161 deletions.
58 changes: 58 additions & 0 deletions cell/api/u_cell_net.h
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,16 @@ typedef enum {
U_CELL_NET_REG_DOMAIN_MAX_NUM
} uCellNetRegDomain_t;

/** The possible authentication modes for the network connection.
*/
typedef enum {
U_CELL_NET_AUTHENTICATION_MODE_NONE = 0,
U_CELL_NET_AUTHENTICATION_MODE_PAP = 1,
U_CELL_NET_AUTHENTICATION_MODE_CHAP = 2,
U_CELL_NET_AUTHENTICATION_MODE_AUTOMATIC = 3, /**< not supported by all module types. */
U_CELL_NET_AUTHENTICATION_MODE_MAX_NUM
} uCellNetAuthenticationMode_t;

/** Information on a cell, passed to the callback of uCellNetDeepScan(),
* could be used in a call to uCellTimeSyncCellEnable().
*/
Expand Down Expand Up @@ -279,6 +289,10 @@ typedef struct {
* deactivated (and potentially deregistration may occur) then
* [registration will occur and] the new context will be activated.
*
* Note: if you are required to set a user name and password then
* you MAY also need to set the authentication mode that will be
* used; see uCellNetSetAuthenticationMode() for this.
*
* @param cellHandle the handle of the cellular instance.
* @param[in] pMccMnc pointer to a string giving the MCC and
* MNC of the PLMN to use (for example "23410")
Expand Down Expand Up @@ -364,6 +378,10 @@ int32_t uCellNetRegister(uDeviceHandle_t cellHandle,
* this will result in de-registration and re-registration with the
* network.
*
* Note: if you are required to set a user name and password then
* you MAY also need to set the authentication mode that will be
* used; see uCellNetSetAuthenticationMode() for this.
*
* @param cellHandle the handle of the cellular instance.
* @param[in] pApn pointer to a string giving the APN to
* use; set to NULL if no APN is specified
Expand Down Expand Up @@ -844,6 +862,46 @@ int32_t uCellNetGetDataCounterRx(uDeviceHandle_t cellHandle);
*/
int32_t uCellNetResetDataCounters(uDeviceHandle_t cellHandle);

/** Get the authentication mode that the module will use if a
* user name and password is included with uCellNetConnect()
* and uCellNetActivate().
*
* @param cellHandle the handle of the cellular instance.
* @return on success the authentication mode, from
* #uCellNetAuthenticationMode_t, else negative
* error code.
*/
int32_t uCellNetGetAuthenticationMode(uDeviceHandle_t cellHandle);

/** Set the authentication mode: this is ONLY relevant if a user name
* and password is required by the network (see uCellNetConnect()
* and uCellNetActivate()) and the cellular module does NOT support
* automatic authentication mode. You may determine if automatic
* authentication mode is supported by calling
* uCellNetGetAuthenticationMode(): if automatic authentication mode
* is supported then it will be the default and
* #U_CELL_NET_AUTHENTICATION_MODE_AUTOMATIC will be returned, else
* the default authentication mode will be
* #U_CELL_NET_AUTHENTICATION_MODE_NONE and you must call
* uCellNetSetAuthenticationMode() to set it. If the authentication
* mode turns out to be #U_CELL_NET_AUTHENTICATION_MODE_NONE then
* you MUST call this function before you call uCellNetConnect() or
* uCellNetActivate() with a non-NULL user name and password,
* otherwise those functions will return an error and no connection
* will be made.
*
* Note: there is no need to set the authentication mode to
* #U_CELL_NET_AUTHENTICATION_MODE_NONE; the setting will only be
* applied if a username and password are in use, should they not
* be in use then the authentiction mode will in any case be "none".
*
* @param cellHandle the handle of the cellular instance.
* @param mode the authentication mode.
* @return zero on success, else negative error code.
*/
int32_t uCellNetSetAuthenticationMode(uDeviceHandle_t cellHandle,
uCellNetAuthenticationMode_t mode);

#ifdef __cplusplus
}
#endif
Expand Down
5 changes: 5 additions & 0 deletions cell/src/u_cell.c
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,11 @@ int32_t uCellAdd(uCellModuleType_t moduleType,
uCellPrivateClearRadioParameters(&(pInstance->radioParameters), false);
pInstance->pModule = &(gUCellPrivateModuleList[moduleType]);
pInstance->sockNextLocalPort = -1;
if (U_CELL_PRIVATE_HAS(pInstance->pModule,
U_CELL_PRIVATE_FEATURE_AUTHENTICATION_MODE_AUTOMATIC)) {
// Set automatic authentication mode where supported
pInstance->authenticationMode = U_CELL_NET_AUTHENTICATION_MODE_AUTOMATIC;
}
pInstance->deepSleepBlockedBy = -1;
pInstance->gnssAidMode = U_CELL_LOC_GNSS_AIDING_TYPES;
pInstance->gnssSystemTypesBitMap = U_CELL_LOC_GNSS_SYSTEM_TYPES;
Expand Down
111 changes: 92 additions & 19 deletions cell/src/u_cell_net.c
Original file line number Diff line number Diff line change
Expand Up @@ -1337,23 +1337,45 @@ static int32_t setAuthenticationMode(const uCellPrivateInstance_t *pInstance,
const char *pUsername,
const char *pPassword)
{
int32_t errorCode = (int32_t) U_ERROR_COMMON_INVALID_PARAMETER;
uAtClientHandle_t atHandle = pInstance->atHandle;

uAtClientLock(atHandle);
uAtClientCommandStart(atHandle, "AT+UAUTHREQ=");
uAtClientWriteInt(atHandle, contextId);
uAtClientWriteInt(atHandle, 3); // Automatic choice of authentication type
if (!U_CELL_PRIVATE_MODULE_IS_SARA_R4(pInstance->pModule->moduleType) &&
(pInstance->pModule->moduleType != U_CELL_MODULE_TYPE_LARA_R6)) {
uAtClientWriteString(atHandle, pUsername, true);
uAtClientWriteString(atHandle, pPassword, true);
} else {
// For SARA-R4 and LARA-R6 modules the parameters are reversed
uAtClientWriteString(atHandle, pPassword, true);
uAtClientWriteString(atHandle, pUsername, true);
uCellNetAuthenticationMode_t authenticationMode = pInstance->authenticationMode;

// Only continue if we either have a non-NONE authentication
// mode or if we have no credentials to enter
if ((authenticationMode != U_CELL_NET_AUTHENTICATION_MODE_NONE) ||
((pUsername == NULL) && (pPassword == NULL))) {
if ((pUsername == NULL) && (pPassword == NULL)) {
// No authentication is required
authenticationMode = U_CELL_NET_AUTHENTICATION_MODE_NONE;
if ((pInstance->pModule->moduleType == U_CELL_MODULE_TYPE_SARA_R5) ||
(pInstance->pModule->moduleType == U_CELL_MODULE_TYPE_SARA_U201)) {
// For SARA-R5 and U201 the user name and password cannot
// be omitted, must be set to an empty string
pUsername = "";
pPassword = "";
}
}
uAtClientLock(atHandle);
uAtClientCommandStart(atHandle, "AT+UAUTHREQ=");
uAtClientWriteInt(atHandle, contextId);
uAtClientWriteInt(atHandle, authenticationMode);
if ((pUsername != NULL) && (pPassword != NULL)) {
if (!U_CELL_PRIVATE_MODULE_IS_SARA_R4(pInstance->pModule->moduleType) &&
(pInstance->pModule->moduleType != U_CELL_MODULE_TYPE_LARA_R6)) {
uAtClientWriteString(atHandle, pUsername, true);
uAtClientWriteString(atHandle, pPassword, true);
} else {
// For SARA-R4 and LARA-R6 modules the parameters are reversed
uAtClientWriteString(atHandle, pPassword, true);
uAtClientWriteString(atHandle, pUsername, true);
}
}
uAtClientCommandStopReadResponse(atHandle);
errorCode = uAtClientUnlock(atHandle);
}
uAtClientCommandStopReadResponse(atHandle);
return uAtClientUnlock(atHandle);

return errorCode;
}

// Get the APN currently in use 3GPP commands, required
Expand Down Expand Up @@ -2159,8 +2181,7 @@ int32_t uCellNetConnect(uDeviceHandle_t cellHandle,
errorCode = defineContext(pInstance,
U_CELL_NET_CONTEXT_ID,
pApn);
if ((errorCode == 0) && (pUsername != NULL) &&
(pPassword != NULL)) {
if (errorCode == 0) {
// Set the authentication mode
errorCode = setAuthenticationMode(pInstance,
U_CELL_NET_CONTEXT_ID,
Expand Down Expand Up @@ -2425,8 +2446,7 @@ int32_t uCellNetActivate(uDeviceHandle_t cellHandle,
errorCode = defineContext(pInstance,
U_CELL_NET_CONTEXT_ID,
pApn);
if ((errorCode == 0) && (pUsername != NULL) &&
(pPassword != NULL)) {
if (errorCode == 0) {
// Set the authentication mode
errorCode = setAuthenticationMode(pInstance,
U_CELL_NET_CONTEXT_ID,
Expand Down Expand Up @@ -3430,4 +3450,57 @@ int32_t uCellNetResetDataCounters(uDeviceHandle_t cellHandle)
return errorCode;
}

// Get the authentication mode.
int32_t uCellNetGetAuthenticationMode(uDeviceHandle_t cellHandle)
{
int32_t errorCodeOrAuthenticationMode = (int32_t) U_ERROR_COMMON_NOT_INITIALISED;
uCellPrivateInstance_t *pInstance;

if (gUCellPrivateMutex != NULL) {

U_PORT_MUTEX_LOCK(gUCellPrivateMutex);

pInstance = pUCellPrivateGetInstance(cellHandle);
errorCodeOrAuthenticationMode = (int32_t) U_ERROR_COMMON_INVALID_PARAMETER;
if (pInstance != NULL) {
errorCodeOrAuthenticationMode = (int32_t) pInstance->authenticationMode;
}

U_PORT_MUTEX_UNLOCK(gUCellPrivateMutex);
}

return errorCodeOrAuthenticationMode;
}

// Set the authentication mode.
int32_t uCellNetSetAuthenticationMode(uDeviceHandle_t cellHandle,
uCellNetAuthenticationMode_t mode)
{
int32_t errorCode = (int32_t) U_ERROR_COMMON_NOT_INITIALISED;
uCellPrivateInstance_t *pInstance;

if (gUCellPrivateMutex != NULL) {

U_PORT_MUTEX_LOCK(gUCellPrivateMutex);

pInstance = pUCellPrivateGetInstance(cellHandle);
errorCode = (int32_t) U_ERROR_COMMON_INVALID_PARAMETER;
if ((pInstance != NULL) && (mode >= 0) &&
(mode != U_CELL_NET_AUTHENTICATION_MODE_NONE) &&
(mode < U_CELL_NET_AUTHENTICATION_MODE_MAX_NUM)) {
errorCode = (int32_t) U_ERROR_COMMON_NOT_SUPPORTED;
if ((mode != U_CELL_NET_AUTHENTICATION_MODE_AUTOMATIC) ||
U_CELL_PRIVATE_HAS(pInstance->pModule,
U_CELL_PRIVATE_FEATURE_AUTHENTICATION_MODE_AUTOMATIC)) {
pInstance->authenticationMode = mode;
errorCode = (int32_t) U_ERROR_COMMON_SUCCESS;
}
}

U_PORT_MUTEX_UNLOCK(gUCellPrivateMutex);
}

return errorCode;
}

// End of file
6 changes: 4 additions & 2 deletions cell/src/u_cell_private.c
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,9 @@ const uCellPrivateModule_t gUCellPrivateModuleList[] = {
(1ULL << (int32_t) U_CELL_PRIVATE_FEATURE_AT_PROFILES) |
(1ULL << (int32_t) U_CELL_PRIVATE_FEATURE_CTS_CONTROL) |
(1ULL << (int32_t) U_CELL_PRIVATE_FEATURE_SOCK_SET_LOCAL_PORT) |
(1ULL << (int32_t) U_CELL_PRIVATE_FEATURE_UART_POWER_SAVING) /* features */
(1ULL << (int32_t) U_CELL_PRIVATE_FEATURE_UART_POWER_SAVING) |
// CMUX is supported here but we do not test it hence it is not marked as supported
(1ULL << (int32_t) U_CELL_PRIVATE_FEATURE_AUTHENTICATION_MODE_AUTOMATIC) /* features */
),
6 /* Default CMUX channel for GNSS */
},
Expand Down Expand Up @@ -232,7 +233,8 @@ const uCellPrivateModule_t gUCellPrivateModuleList[] = {
(1ULL << (int32_t) U_CELL_PRIVATE_FEATURE_FOTA) |
(1ULL << (int32_t) U_CELL_PRIVATE_FEATURE_UART_POWER_SAVING) |
(1ULL << (int32_t) U_CELL_PRIVATE_FEATURE_CMUX) |
(1ULL << (int32_t) U_CELL_PRIVATE_FEATURE_SNR_REPORTED) /* features */
(1ULL << (int32_t) U_CELL_PRIVATE_FEATURE_SNR_REPORTED) |
(1ULL << (int32_t) U_CELL_PRIVATE_FEATURE_AUTHENTICATION_MODE_AUTOMATIC) /* features */
),
4 /* Default CMUX channel for GNSS */
},
Expand Down
4 changes: 3 additions & 1 deletion cell/src/u_cell_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,8 @@ typedef enum {
U_CELL_PRIVATE_FEATURE_FOTA,
U_CELL_PRIVATE_FEATURE_UART_POWER_SAVING,
U_CELL_PRIVATE_FEATURE_CMUX,
U_CELL_PRIVATE_FEATURE_SNR_REPORTED
U_CELL_PRIVATE_FEATURE_SNR_REPORTED,
U_CELL_PRIVATE_FEATURE_AUTHENTICATION_MODE_AUTOMATIC
} uCellPrivateFeature_t;

/** The characteristics that may differ between cellular modules.
Expand Down Expand Up @@ -441,6 +442,7 @@ typedef struct uCellPrivateInstance_t {
void (*pGreetingCallback) (uDeviceHandle_t, void *);
void *pGreetingCallbackParameter;
uCellPrivateNet_t *pScanResults; /**< Anchor for list of network scan results. */
uCellNetAuthenticationMode_t authenticationMode; /**< Authentication mode for PDP context. */
int32_t sockNextLocalPort;
uint32_t gnssAidMode; /**< A bit-map of the types of aiding to use (AssistNow Online, Offline, Autonomous, etc.). */
uint32_t gnssSystemTypesBitMap; /**< A bit-map of the GNSS system types (GPS, GLONASS, etc.) a GNSS chip should use. */
Expand Down
29 changes: 29 additions & 0 deletions cell/test/u_cell_net_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,35 @@ U_PORT_TEST_FUNCTION("[cellNet]", "cellNetConnectDisconnectPlus")

U_PORT_TEST_ASSERT(gLastNetStatus == U_CELL_NET_STATUS_UNKNOWN);

// Read the authentication mode for PDP contexts
x = uCellNetGetAuthenticationMode(cellHandle);
U_PORT_TEST_ASSERT(x >= 0);
U_PORT_TEST_ASSERT(x < U_CELL_NET_AUTHENTICATION_MODE_MAX_NUM);
if (U_CELL_PRIVATE_HAS(pModule, U_CELL_PRIVATE_FEATURE_AUTHENTICATION_MODE_AUTOMATIC)) {
U_PORT_TEST_ASSERT(x == U_CELL_NET_AUTHENTICATION_MODE_AUTOMATIC);
} else {
U_PORT_TEST_ASSERT(x == U_CELL_NET_AUTHENTICATION_MODE_NONE);
}

// Try setting all of the permitted authentication modes
U_PORT_TEST_ASSERT(uCellNetSetAuthenticationMode(cellHandle,
U_CELL_NET_AUTHENTICATION_MODE_NONE) < 0);
U_PORT_TEST_ASSERT(uCellNetSetAuthenticationMode(cellHandle,
U_CELL_NET_AUTHENTICATION_MODE_PAP) == 0);
U_PORT_TEST_ASSERT(uCellNetGetAuthenticationMode(cellHandle) == U_CELL_NET_AUTHENTICATION_MODE_PAP);
U_PORT_TEST_ASSERT(uCellNetSetAuthenticationMode(cellHandle,
U_CELL_NET_AUTHENTICATION_MODE_CHAP) == 0);
U_PORT_TEST_ASSERT(uCellNetGetAuthenticationMode(cellHandle) ==
U_CELL_NET_AUTHENTICATION_MODE_CHAP);
x = uCellNetSetAuthenticationMode(cellHandle, U_CELL_NET_AUTHENTICATION_MODE_AUTOMATIC);
if (U_CELL_PRIVATE_HAS(pModule, U_CELL_PRIVATE_FEATURE_AUTHENTICATION_MODE_AUTOMATIC)) {
U_PORT_TEST_ASSERT(x == 0);
U_PORT_TEST_ASSERT(uCellNetGetAuthenticationMode(cellHandle) ==
U_CELL_NET_AUTHENTICATION_MODE_AUTOMATIC);
} else {
U_PORT_TEST_ASSERT(x < 0);
}

// Connect with a very short time-out to show that aborts work
gStopTimeMs = uPortGetTickTimeMs() + 1000;
x = uCellNetConnect(cellHandle, NULL,
Expand Down
16 changes: 16 additions & 0 deletions common/network/api/u_network_config_cell.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,22 @@ typedef struct {
the field to zero, and
timeoutSeconds will be
obeyed instead. */
const char *pUsername; /** ONLY REQUIRED if you must use a user name
and password with the APN provided to you
by your service provider; let your compiler
initialised= this to zero otherwise. */
const char *pPassword; /** ONLY REQUIRED if you must use a user name
and password with the APN provided to you
by your service provider; let your compiler
initialised= this to zero otherwise. */
int32_t authenticationMode; /** ONLY REQUIRED if you must give a user name
and password with the APN provided to
you by your service provider and your
cellular module does NOT support figuring
out the authentication mode automatically;
there is no harm in populating this field
even if your module _does_ support figuring
out the authentication mode automatically. */
/* This is the end of version 0 of this
structure: should any fields be added to
this structure in future they must be
Expand Down
27 changes: 21 additions & 6 deletions common/network/src/u_network_private_cell.c
Original file line number Diff line number Diff line change
Expand Up @@ -172,12 +172,27 @@ int32_t uNetworkPrivateChangeStateCell(uDeviceHandle_t devHandle,
(((int64_t) pCfg->timeoutSeconds) * 1000);
}
if (upNotDown) {
// Connect using automatic selection,
// default no user name or password for the APN
errorCode = uCellNetConnect(devHandle, NULL,
pCfg->pApn,
NULL, NULL,
pKeepGoingCallback);
errorCode = (int32_t) U_ERROR_COMMON_SUCCESS;
if ((pCfg->pUsername != NULL) && (pCfg->pPassword != NULL)) {
// If we have a user name and password then set the
// authentication mode
errorCode = uCellNetSetAuthenticationMode(devHandle, pCfg->authenticationMode);
if (errorCode < 0) {
// uCellNetSetAuthenticationMode() will return "not supported"
// for modules that do not support automatic mode but that is
// a bit confusing as a return value for uNetworkInterfaceUp(),
// so change it to "invalid parameter"
errorCode = (int32_t) U_ERROR_COMMON_INVALID_PARAMETER;
}
}
if (errorCode == 0) {
// Connect using automatic selection
errorCode = uCellNetConnect(devHandle, NULL,
pCfg->pApn,
pCfg->pUsername,
pCfg->pPassword,
pKeepGoingCallback);
}
} else {
// Disconnect
errorCode = uCellNetDisconnect(devHandle, pKeepGoingCallback);
Expand Down
17 changes: 16 additions & 1 deletion common/network/test/u_network_test_shared_cfg.c
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,22 @@ static const uNetworkCfgCell_t gNetworkCfgCell = {
# else
.pApn = NULL,
# endif
.timeoutSeconds = U_CELL_TEST_CFG_CONNECT_TIMEOUT_SECONDS
.timeoutSeconds = U_CELL_TEST_CFG_CONNECT_TIMEOUT_SECONDS,
# ifdef U_CELL_TEST_CFG_USERNAME
.pUsername = U_PORT_STRINGIFY_QUOTED(U_CELL_TEST_CFG_USERNAME),
# else
.pUsername = NULL,
# endif
# ifdef U_CELL_TEST_CFG_PASSWORD
.pPassword = U_PORT_STRINGIFY_QUOTED(U_CELL_TEST_CFG_PASSWORD),
# else
.pPassword = NULL,
# endif
# ifdef U_CELL_TEST_CFG_AUTHENTICATION_MODE
.authenticationMode = (int32_t) U_CELL_TEST_CFG_AUTHENTICATION_MODE,
# else
.authenticationMode = 0,
# endif
#else
.type = U_NETWORK_TYPE_NONE
#endif
Expand Down
Loading

0 comments on commit 362dc2f

Please sign in to comment.