Skip to content

Commit 996d5b3

Browse files
trantanennordicjm
authored andcommitted
samples: cellular: modem_shell: sock: 'option' set/get commands
Added support for setting/getting socket options in a generic manner. Jira: MOSH-636 Signed-off-by: Tommi Rantanen <[email protected]> Signed-off-by: Tommi Kangas <[email protected]>
1 parent 5b80e46 commit 996d5b3

File tree

4 files changed

+525
-2
lines changed

4 files changed

+525
-2
lines changed

doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,7 @@ Cellular samples
579579

580580
* :ref:`modem_shell_application` sample:
581581

582+
* Added support for setting and getting socket options using the ``sock option set`` and ``sock option get`` commands.
582583
* Removed the ``CONFIG_MOSH_LINK`` Kconfig option.
583584
The link control functionality is now always enabled and cannot be disabled.
584585

samples/cellular/modem_shell/src/sock/sock.c

Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ extern struct k_work_q mosh_common_work_q;
3939
#define SOCK_RECEIVE_BUFFER_SIZE 1536
4040
#define SOCK_RECEIVE_STACK_SIZE 1280
4141
#define SOCK_RECEIVE_PRIORITY 5
42+
#define SOCK_OPT_VALUE_LIST_SIZE 20
4243
/* Timeout (in ms) for polling socket events such as receive data,
4344
* permission to send more, disconnected socket etc.
4445
* This limits how quickly data can be received after socket creation.
@@ -1305,6 +1306,249 @@ int sock_rai(int socket_id, bool rai_last, bool rai_no_data,
13051306
return 0;
13061307
}
13071308

1309+
int sock_setopt(int socket_id, int sock_level, int sock_opt_id, char *sock_opt_value)
1310+
{
1311+
struct sock_info *socket_info = get_socket_info_by_id(socket_id);
1312+
int err;
1313+
char *end;
1314+
1315+
if (socket_info == NULL) {
1316+
return -EINVAL;
1317+
}
1318+
1319+
if (sock_opt_id == 0) {
1320+
mosh_error("Socket option is mandatory.");
1321+
return -EINVAL;
1322+
}
1323+
1324+
if (sock_opt_value == NULL &&
1325+
!(sock_level == SOL_TLS && sock_opt_id == TLS_HOSTNAME) &&
1326+
!(sock_level == SOL_TLS && sock_opt_id == TLS_SESSION_CACHE_PURGE) &&
1327+
!(sock_level == SOL_TLS && sock_opt_id == TLS_DTLS_CONN_SAVE) &&
1328+
!(sock_level == SOL_TLS && sock_opt_id == TLS_DTLS_CONN_LOAD)) {
1329+
mosh_error("Socket option value is mandatory.");
1330+
return -EINVAL;
1331+
}
1332+
1333+
switch (sock_level) {
1334+
case SOL_TLS:
1335+
switch (sock_opt_id) {
1336+
case TLS_SEC_TAG_LIST:
1337+
case TLS_CIPHERSUITE_LIST:
1338+
/* Value is a comma separated list of integers or hexadecimals, for example
1339+
* "42,43,44" or "0xC024,0xC00A,0xC023".
1340+
*/
1341+
int value_list[SOCK_OPT_VALUE_LIST_SIZE];
1342+
unsigned int value_count = 0;
1343+
1344+
for (int i = 0; i < strlen(sock_opt_value); i++) {
1345+
if (sock_opt_value[i] == ',') {
1346+
sock_opt_value[i] = ' ';
1347+
}
1348+
}
1349+
1350+
while (value_count < ARRAY_SIZE(value_list)) {
1351+
int value = strtoul(sock_opt_value, &end, 0);
1352+
1353+
if (end == sock_opt_value) {
1354+
break;
1355+
}
1356+
value_list[value_count++] = value;
1357+
sock_opt_value = end;
1358+
}
1359+
1360+
err = setsockopt(socket_info->fd, sock_level, sock_opt_id, value_list,
1361+
value_count * sizeof(int));
1362+
goto exit;
1363+
1364+
case TLS_HOSTNAME:
1365+
/* Value can be empty to clear hostname. */
1366+
size_t hostname_len;
1367+
1368+
if (sock_opt_value == NULL) {
1369+
hostname_len = 0;
1370+
} else {
1371+
hostname_len = strlen(sock_opt_value);
1372+
}
1373+
err = setsockopt(socket_info->fd, sock_level, sock_opt_id, sock_opt_value,
1374+
hostname_len);
1375+
goto exit;
1376+
1377+
case TLS_SESSION_CACHE_PURGE:
1378+
case TLS_DTLS_CONN_SAVE:
1379+
case TLS_DTLS_CONN_LOAD:
1380+
/* No value used for these options. */
1381+
err = setsockopt(socket_info->fd, sock_level, sock_opt_id, NULL, 0);
1382+
goto exit;
1383+
1384+
default:
1385+
break;
1386+
}
1387+
break; /* SOL_TLS */
1388+
1389+
case SOL_SOCKET:
1390+
switch (sock_opt_id) {
1391+
case SO_RCVTIMEO:
1392+
case SO_SNDTIMEO:
1393+
/* Value is in milliseconds and needs to be converted into
1394+
* a timeval structure.
1395+
*/
1396+
int timeout_us;
1397+
struct timeval tv;
1398+
1399+
timeout_us = atoi(sock_opt_value) * USEC_PER_MSEC;
1400+
tv.tv_sec = timeout_us / USEC_PER_SEC;
1401+
tv.tv_usec = timeout_us % USEC_PER_SEC;
1402+
err = setsockopt(socket_info->fd, sock_level, sock_opt_id, &tv,
1403+
sizeof(tv));
1404+
goto exit;
1405+
1406+
default:
1407+
break;
1408+
}
1409+
break; /* SOL_SOCKET */
1410+
1411+
default:
1412+
break;
1413+
}
1414+
1415+
/* All other values are integers. */
1416+
int sock_opt_value_int;
1417+
1418+
sock_opt_value_int = atoi(sock_opt_value);
1419+
err = setsockopt(socket_info->fd, sock_level, sock_opt_id, &sock_opt_value_int,
1420+
sizeof(sock_opt_value_int));
1421+
1422+
exit:
1423+
if (err) {
1424+
mosh_error("setsockopt() for level=%d, option=%d, value=%s failed with error %d",
1425+
sock_level, sock_opt_id, sock_opt_value, errno);
1426+
}
1427+
1428+
return err;
1429+
}
1430+
1431+
int sock_getopt(int socket_id, int sock_level, int sock_opt_id)
1432+
{
1433+
struct sock_info *socket_info = get_socket_info_by_id(socket_id);
1434+
int err;
1435+
int sock_opt_value = 0;
1436+
int sock_opt_value_len = sizeof(sock_opt_value);
1437+
1438+
if (socket_info == NULL) {
1439+
return -EINVAL;
1440+
}
1441+
1442+
switch (sock_level) {
1443+
case SOL_TLS:
1444+
switch (sock_opt_id) {
1445+
case TLS_SEC_TAG_LIST:
1446+
case TLS_CIPHERSUITE_LIST:
1447+
/* Value is a list of sec_tags or ciphersuites. */
1448+
int value_list[SOCK_OPT_VALUE_LIST_SIZE];
1449+
unsigned int value_list_size = sizeof(value_list);
1450+
1451+
err = getsockopt(socket_info->fd, sock_level, sock_opt_id, value_list,
1452+
&value_list_size);
1453+
if (!err) {
1454+
char value_str[128] = { 0 };
1455+
size_t value_str_len = 0;
1456+
1457+
for (unsigned int i = 0; i < value_list_size / sizeof(int); i++) {
1458+
if (sock_opt_id == TLS_SEC_TAG_LIST) {
1459+
value_str_len +=
1460+
snprintf(value_str + value_str_len,
1461+
sizeof(value_str) - value_str_len,
1462+
"%d,", value_list[i]);
1463+
} else {
1464+
value_str_len +=
1465+
snprintf(value_str + value_str_len,
1466+
sizeof(value_str) - value_str_len,
1467+
"0x%X,", value_list[i]);
1468+
}
1469+
if (value_str_len >= sizeof(value_str) - 1) {
1470+
break;
1471+
}
1472+
}
1473+
1474+
if (value_str_len > 0 && value_str_len <= sizeof(value_str)) {
1475+
if (value_str[value_str_len - 1] == ',') {
1476+
value_str[value_str_len - 1] = '\0';
1477+
}
1478+
}
1479+
1480+
mosh_print(
1481+
"getsockopt() socket_id=%d, level=%d, option=%d, value=%s",
1482+
socket_id, sock_level, sock_opt_id, value_str);
1483+
}
1484+
goto exit;
1485+
1486+
case TLS_HOSTNAME:
1487+
/* Value is a string. */
1488+
char hostname[64] = { 0 };
1489+
size_t hostname_len = sizeof(hostname) - 1;
1490+
1491+
err = getsockopt(socket_info->fd, sock_level, sock_opt_id,
1492+
hostname, &hostname_len);
1493+
if (!err) {
1494+
mosh_print(
1495+
"getsockopt() socket_id=%d, level=%d, option=%d, value=%s",
1496+
socket_id, sock_level, sock_opt_id, hostname);
1497+
}
1498+
goto exit;
1499+
1500+
default:
1501+
break;
1502+
}
1503+
break; /* SOL_TLS */
1504+
1505+
case SOL_SOCKET:
1506+
switch (sock_opt_id) {
1507+
case SO_RCVTIMEO:
1508+
case SO_SNDTIMEO:
1509+
/* Value is a timeval structure. */
1510+
struct timeval tv;
1511+
size_t tv_len = sizeof(tv);
1512+
1513+
err = getsockopt(socket_info->fd, sock_level, sock_opt_id, &tv, &tv_len);
1514+
if (!err) {
1515+
int timeout_ms = tv.tv_sec * MSEC_PER_SEC +
1516+
tv.tv_usec / USEC_PER_MSEC;
1517+
mosh_print(
1518+
"getsockopt() socket_id=%d, level=%d, option=%d, "
1519+
"value=%d ms",
1520+
socket_id, sock_level, sock_opt_id, timeout_ms);
1521+
}
1522+
goto exit;
1523+
1524+
default:
1525+
break;
1526+
}
1527+
break; /* SOL_SOCKET */
1528+
1529+
default:
1530+
break;
1531+
}
1532+
1533+
/* All other values are integers. */
1534+
err = getsockopt(socket_info->fd, sock_level, sock_opt_id, &sock_opt_value,
1535+
&sock_opt_value_len);
1536+
if (!err) {
1537+
mosh_print(
1538+
"getsockopt() socket_id=%d, level=%d, option=%d, value=%d",
1539+
socket_id, sock_level, sock_opt_id, sock_opt_value);
1540+
}
1541+
1542+
exit:
1543+
if (err) {
1544+
mosh_error(
1545+
"getsockopt() socket_id=%d, level=%d, option=%d failed with error %d",
1546+
socket_id, sock_level, sock_opt_id, errno);
1547+
}
1548+
1549+
return err;
1550+
}
1551+
13081552
int sock_list(void)
13091553
{
13101554
bool opened_sockets = false;

samples/cellular/modem_shell/src/sock/sock.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#define MOSH_SOCK_H
99

1010
#define SOCK_ID_NONE -1
11+
#define SOCK_OPT_SOL_NONE -1
1112
#define SOCK_RAI_NONE -1
1213
#define SOCK_BUFFER_SIZE_NONE -1
1314
#define SOCK_SEND_DATA_INTERVAL_NONE -1
@@ -40,6 +41,8 @@ int sock_close(int socket_id);
4041
int sock_rai(
4142
int socket_id, bool rai_last, bool rai_no_data, bool rai_one_resp,
4243
bool rai_ongoing, bool rai_wait_more);
44+
int sock_setopt(int socket_id, int sock_level, int sock_opt_id, char *sock_opt_value);
45+
int sock_getopt(int socket_id, int sock_level, int sock_opt_id);
4346
int sock_list(void);
4447

4548
#endif /* MOSH_SOCK_H */

0 commit comments

Comments
 (0)