Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

applications: slm: try to continue even no DNS record was sent #19701

Merged
merged 1 commit into from
Jan 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 33 additions & 22 deletions applications/serial_lte_modem/src/slm_ppp.c
Original file line number Diff line number Diff line change
Expand Up @@ -200,37 +200,48 @@ static int ppp_start_failure(int ret)
return ret;
}

static unsigned int ppp_retrieve_mtu(void)
{
struct pdn_dynamic_info populated_info = { 0 };

if (!pdn_dynamic_info_get(PDP_CID, &populated_info)) {
if (populated_info.ipv6_mtu) {
/* Set the PPP MTU to that of the LTE link. */
/* IPv6's MTU has more priority on dual-stack.
* Because, it must be at least 1280 for IPv6,
* while MTU of IPv4 may be less.
*/
return MIN(populated_info.ipv6_mtu, sizeof(ppp_data_buf));
}
if (populated_info.ipv4_mtu) {
/* Set the PPP MTU to that of the LTE link. */
return MIN(populated_info.ipv4_mtu, sizeof(ppp_data_buf));
}
}

LOG_DBG("Could not retrieve MTU, using fallback value.");
BUILD_ASSERT(sizeof(ppp_data_buf) >= CONFIG_SLM_PPP_FALLBACK_MTU);
return CONFIG_SLM_PPP_FALLBACK_MTU;
}

static void ppp_set_mtu(void)
{
const unsigned int mtu = ppp_retrieve_mtu();

net_if_set_mtu(ppp_iface, mtu);
LOG_DBG("MTU set to %u.", mtu);
}

static int ppp_start_internal(void)
{
int ret;
unsigned int mtu;
struct ppp_context *const ctx = net_if_l2_data(ppp_iface);

if (!configure_ppp_link_ip_addresses(ctx)) {
return -EADDRNOTAVAIL;
}

ret = pdn_dynamic_params_get(PDP_CID, &ctx->ipcp.my_options.dns1_address,
&ctx->ipcp.my_options.dns2_address, &mtu);
if (ret) {
/* If any error happened on pdn getting with IPv4, try to parse with IPv6 */
ret = pdn_dynamic_params_get_v6(PDP_CID, NULL, NULL, &mtu);
if (ret) {
return ret;
}
}

if (mtu) {
/* Set the PPP MTU to that of the LTE link. */
mtu = MIN(mtu, sizeof(ppp_data_buf));
} else {
LOG_DBG("Could not retrieve MTU, using fallback value.");
mtu = CONFIG_SLM_PPP_FALLBACK_MTU;
BUILD_ASSERT(sizeof(ppp_data_buf) >= CONFIG_SLM_PPP_FALLBACK_MTU);
}

net_if_set_mtu(ppp_iface, mtu);
LOG_DBG("MTU set to %u.", mtu);
ppp_set_mtu();

ret = net_if_up(ppp_iface);
if (ret) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -481,7 +481,8 @@ Modem libraries

* :ref:`pdn_readme` library:

* Added the :c:func:`pdn_dynamic_params_get_v6` function to get PDN parameters for IPv6-only.
* Deprecated the :c:func:`pdn_dynamic_params_get` function.
Use the new function :c:func:`pdn_dynamic_info_get` instead.

* :ref:`lte_lc_readme` library:

Expand Down
47 changes: 39 additions & 8 deletions include/modem/pdn.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,38 @@ struct pdn_pdp_opt {
uint8_t secure_pco;
};

/**
* @brief PDN context dynamic information structure.
*
* This structure holds dynamic information about the PDN context.
*/
struct pdn_dynamic_info {
trantanen marked this conversation as resolved.
Show resolved Hide resolved
/**
* @brief IPv4 Maximum Transmission Unit.
*/
uint32_t ipv4_mtu;
/**
* @brief IPv6 Maximum Transmission Unit.
*/
uint32_t ipv6_mtu;
/**
* @brief Primary IPv4 DNS address.
*/
struct in_addr dns_addr4_primary;
/**
* @brief Secondary IPv4 DNS address.
*/
struct in_addr dns_addr4_secondary;
/**
* @brief Primary IPv6 DNS address.
*/
struct in6_addr dns_addr6_primary;
/**
* @brief Secondary IPv6 DNS address.
*/
struct in6_addr dns_addr6_secondary;
};

/** @brief PDN library event */
enum pdn_event {
/** +CNEC ESM error code */
Expand Down Expand Up @@ -183,28 +215,27 @@ int pdn_id_get(uint8_t cid);
/**
* @brief Retrieve dynamic parameters of a given PDP context.
*
* @deprecated Use pdn_dynamic_info_get instead.
*
* @param cid The PDP context ID.
* @param[out] dns4_pri The address of the primary IPv4 DNS server. Optional, can be NULL.
* @param[out] dns4_sec The address of the secondary IPv4 DNS server. Optional, can be NULL.
* @param[out] ipv4_mtu The IPv4 MTU. Optional, can be NULL.
*
* @return Zero on success or an error code on failure.
*/
int pdn_dynamic_params_get(uint8_t cid, struct in_addr *dns4_pri,
__deprecated int pdn_dynamic_params_get(uint8_t cid, struct in_addr *dns4_pri,
struct in_addr *dns4_sec, unsigned int *ipv4_mtu);

/**
* @brief Retrieve dynamic parameters of a given PDP context.
* @brief Retrieve dynamic parameters of a given PDN context.
*
* @param cid The PDP context ID.
* @param[out] dns6_pri The address of the primary IPv6 DNS server. Optional, can be NULL.
* @param[out] dns6_sec The address of the secondary IPv6 DNS server. Optional, can be NULL.
* @param[out] ipv6_mtu The IPv6 MTU. Optional, can be NULL.
* @param[in] cid The PDN context ID.
* @param[out] pdn_info PDN dynamic info.
*
* @return Zero on success or an error code on failure.
*/
int pdn_dynamic_params_get_v6(uint8_t cid, struct in6_addr *dns6_pri,
struct in6_addr *dns6_sec, unsigned int *ipv6_mtu);
int pdn_dynamic_info_get(uint32_t cid, struct pdn_dynamic_info *pdn_info);

/**
* @brief Retrieve the default Access Point Name (APN).
Expand Down
178 changes: 143 additions & 35 deletions lib/pdn/pdn.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@
#include <zephyr/net/socket.h>
#include <zephyr/sys/slist.h>
#include <zephyr/logging/log.h>
#include <zephyr/posix/arpa/inet.h>
#include <nrf_modem_at.h>
#include <modem/pdn.h>
#include <modem/at_monitor.h>
#include <modem/at_parser.h>
#include <modem/nrf_modem_lib.h>

LOG_MODULE_REGISTER(pdn, CONFIG_PDN_LOG_LEVEL);
Expand All @@ -34,6 +36,15 @@ LOG_MODULE_REGISTER(pdn, CONFIG_PDN_LOG_LEVEL);
#define MODEM_CFUN_NORMAL 1
#define MODEM_CFUN_ACTIVATE_LTE 21

#define AT_CMD_PDN_CONTEXT_READ_IP_ADDR_STR_MAX_LEN (255)

#define AT_CMD_PDN_CONTEXT_READ_INFO "AT+CGCONTRDP=%d"
#define AT_CMD_PDN_CONTEXT_READ_INFO_DNS_ADDR_PRIMARY_INDEX 6
#define AT_CMD_PDN_CONTEXT_READ_INFO_DNS_ADDR_SECONDARY_INDEX 7
#define AT_CMD_PDN_CONTEXT_READ_INFO_MTU_INDEX 12

#define AT_CMD_PDN_CONTEXT_READ_RSP_DELIM "\r\n"

static K_MUTEX_DEFINE(list_mutex);

static sys_slist_t pdn_contexts = SYS_SLIST_STATIC_INIT(&pdn_context);
Expand Down Expand Up @@ -659,53 +670,150 @@ int pdn_dynamic_params_get(uint8_t cid, struct in_addr *dns4_pri,
return 0;
}

int pdn_dynamic_params_get_v6(uint8_t cid, struct in6_addr *dns6_pri,
struct in6_addr *dns6_sec, unsigned int *ipv6_mtu)
static int pdn_sa_family_from_ip_string(const char *src)
{
int matched;
const char *fmt;
unsigned int mtu;
char dns6_pri_str[INET6_ADDRSTRLEN];
char dns6_sec_str[INET6_ADDRSTRLEN];
char at_cmd[sizeof("AT+CGCONTRDP=10")];
char buf[INET6_ADDRSTRLEN];

if (snprintf(at_cmd, sizeof(at_cmd), "AT+CGCONTRDP=%u", cid) >= sizeof(at_cmd)) {
return -E2BIG;
if (inet_pton(AF_INET, src, buf)) {
trantanen marked this conversation as resolved.
Show resolved Hide resolved
return AF_INET;
} else if (inet_pton(AF_INET6, src, buf)) {
return AF_INET6;
}
/* "+CGCONTRDP: 0,,"ims","","",
* "0000:0000:0000:0000:0000:0000:0000:0000",
* "0000:0000:0000:0000:0000:0000:0000:0000",,,,,1500"
*/
fmt = "+CGCONTRDP: %*u,,\"%*[^\"]\",\"\",\"\","
"\"%45[0-9A-Fa-f:]\",\"%45[0-9A-Fa-f:]\",,,,,%u";
return -1;
}

/* If IPv6 is enabled, it will be the first response line. */
matched = nrf_modem_at_scanf(at_cmd, fmt, &dns6_pri_str, &dns6_sec_str, &mtu);
/* Need to match at least the two IP addresses, or there is an error */
if (matched < 2) {
return -EBADMSG;
int pdn_dynamic_info_get(uint32_t cid, struct pdn_dynamic_info *pdn_info)
{
int ret = 0;
struct at_parser parser;
size_t param_str_len;

char cgcontrdp_at_rsp_buf[512];

char *at_ptr;
char *tmp_ptr;
int lines = 0;
int iterator = 0;
char dns_addr_str[AT_CMD_PDN_CONTEXT_READ_IP_ADDR_STR_MAX_LEN];

char at_cmd_pdn_context_read_info_cmd_str[15];

int family;
struct in_addr *addr;
struct in6_addr *addr6;

if (!pdn_info) {
return -EINVAL;
}

if (dns6_pri) {
if (zsock_inet_pton(AF_INET6, dns6_pri_str, dns6_pri) != 1) {
return -EADDRNOTAVAIL;
}
at_ptr = cgcontrdp_at_rsp_buf;
tmp_ptr = cgcontrdp_at_rsp_buf;

sprintf(at_cmd_pdn_context_read_info_cmd_str, AT_CMD_PDN_CONTEXT_READ_INFO, cid);
ret = nrf_modem_at_cmd(cgcontrdp_at_rsp_buf, sizeof(cgcontrdp_at_rsp_buf), "%s",
at_cmd_pdn_context_read_info_cmd_str);
if (ret) {
LOG_ERR(
"nrf_modem_at_cmd returned err: %d for %s",
ret,
at_cmd_pdn_context_read_info_cmd_str);
return ret;
}
if (dns6_sec) {
if (zsock_inet_pton(AF_INET6, dns6_sec_str, dns6_sec) != 1) {
return -EADDRNOTAVAIL;

/* Check how many rows of info do we have */
while (strncmp(tmp_ptr, "OK", 2) &&
(tmp_ptr = strstr(tmp_ptr, AT_CMD_PDN_CONTEXT_READ_RSP_DELIM)) != NULL) {
tmp_ptr += 2;
lines++;
}

/* Parse the response */
ret = at_parser_init(&parser, at_ptr);
if (ret) {
LOG_ERR("Could not init AT parser for %s, error: %d\n",
at_cmd_pdn_context_read_info_cmd_str, ret);
return ret;
}

parse:
/* Read primary DNS address */
param_str_len = sizeof(dns_addr_str);
ret = at_parser_string_get(
&parser,
AT_CMD_PDN_CONTEXT_READ_INFO_DNS_ADDR_PRIMARY_INDEX,
dns_addr_str, &param_str_len);
if (ret) {
LOG_ERR("Could not parse dns str for cid %d, err: %d", cid, ret);
goto clean_exit;
}
dns_addr_str[param_str_len] = '\0';

family = pdn_sa_family_from_ip_string(dns_addr_str);

if (family == AF_INET) {
addr = &(pdn_info->dns_addr4_primary);
(void)inet_pton(AF_INET, dns_addr_str, addr);
} else if (family == AF_INET6) {
addr6 = &(pdn_info->dns_addr6_primary);
(void)inet_pton(AF_INET6, dns_addr_str, addr6);
}

/* Read secondary DNS address */
param_str_len = sizeof(dns_addr_str);

ret = at_parser_string_get(
&parser,
AT_CMD_PDN_CONTEXT_READ_INFO_DNS_ADDR_SECONDARY_INDEX,
dns_addr_str, &param_str_len);
if (ret) {
LOG_ERR("Could not parse dns str, err: %d", ret);
goto clean_exit;
}
dns_addr_str[param_str_len] = '\0';

family = pdn_sa_family_from_ip_string(dns_addr_str);

if (family == AF_INET) {
addr = &(pdn_info->dns_addr4_secondary);
(void)inet_pton(AF_INET, dns_addr_str, addr);
} else if (family == AF_INET6) {
addr6 = &(pdn_info->dns_addr6_secondary);
(void)inet_pton(AF_INET6, dns_addr_str, addr6);
}

/* Read link MTU if exists:
* AT command spec:
* Note: If the PDN connection has dual stack capabilities, at least one pair of
* lines with information is returned per <cid>: First one line with the IPv4
* parameters followed by one line with the IPv6 parameters.
*/
if (iterator == 1) {
ret = at_parser_num_get(&parser, AT_CMD_PDN_CONTEXT_READ_INFO_MTU_INDEX,
&(pdn_info->ipv6_mtu));
if (ret) {
/* Don't care if it fails */
ret = 0;
pdn_info->ipv6_mtu = 0;
}
} else {
ret = at_parser_num_get(&parser, AT_CMD_PDN_CONTEXT_READ_INFO_MTU_INDEX,
&(pdn_info->ipv4_mtu));
if (ret) {
/* Don't care if it fails */
ret = 0;
pdn_info->ipv4_mtu = 0;
}
}
if (ipv6_mtu) {
/* If we matched the MTU, copy it here, otherwise report zero */
if (matched == 3) {
*ipv6_mtu = mtu;
} else {
*ipv6_mtu = 0;

if (at_parser_cmd_next(&parser) == 0) {
iterator++;
if (iterator < lines) {
goto parse;
}
}

return 0;
clean_exit:
return ret;
}

int pdn_default_apn_get(char *buf, size_t len)
Expand Down
Loading