Skip to content

Commit 9749a86

Browse files
committed
Add support for SO_TIMESTAMPNS and SO_TIMESTAMPING socket options
I've tried to pretty much replicate existing code as much as possible. This commit adds support for two additional timestamp-related socket options that provide enhanced timestamping capabilities: - SO_TIMESTAMPNS: Similar to SO_TIMESTAMP but provides nanosecond precision timestamps using timespec instead of timeval. This is useful for applications requiring higher precision timing information. - SO_TIMESTAMPING: An integer bitmask option that allows fine-grained control over which timestamp types are enabled (e.g., software timestamps, hardware timestamps, etc.). This is primarily used for advanced network monitoring and performance analysis. Platform compatibility: - Linux: Full support for both options - BSD/macOS: Code compiles but options unavailable (guarded by #ifdef) - Windows: Code compiles but options unavailable These options are particularly useful for network performance monitoring, packet capture tools, and applications requiring precise timing information.
1 parent 28a4463 commit 9749a86

File tree

6 files changed

+736
-9
lines changed

6 files changed

+736
-9
lines changed

erts/emulator/nifs/common/prim_socket_nif.c

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2324,6 +2324,8 @@ static const struct in6_addr in6addr_loopback =
23242324
GLOBAL_ATOM_DECL(tcp); \
23252325
GLOBAL_ATOM_DECL(throughput); \
23262326
GLOBAL_ATOM_DECL(timestamp); \
2327+
GLOBAL_ATOM_DECL(timestampns); \
2328+
GLOBAL_ATOM_DECL(timestamping); \
23272329
GLOBAL_ATOM_DECL(tos); \
23282330
GLOBAL_ATOM_DECL(transparent); \
23292331
GLOBAL_ATOM_DECL(timeout); \
@@ -2347,6 +2349,7 @@ static const struct in6_addr in6addr_loopback =
23472349
GLOBAL_ATOM_DECL(update_accept_context); \
23482350
GLOBAL_ATOM_DECL(update_connect_context); \
23492351
GLOBAL_ATOM_DECL(usec); \
2352+
GLOBAL_ATOM_DECL(nsec); \
23502353
GLOBAL_ATOM_DECL(user); \
23512354
GLOBAL_ATOM_DECL(user_timeout); \
23522355
GLOBAL_ATOM_DECL(use_ext_recvinfo); \
@@ -3031,6 +3034,24 @@ static struct ESockOpt optLevelSocket[] =
30313034
#endif
30323035
&esock_atom_timestamp},
30333036

3037+
{
3038+
#ifdef SO_TIMESTAMPNS
3039+
SO_TIMESTAMPNS,
3040+
esock_setopt_bool_opt, esock_getopt_bool_opt,
3041+
#else
3042+
0, NULL, NULL,
3043+
#endif
3044+
&esock_atom_timestampns},
3045+
3046+
{
3047+
#ifdef SO_TIMESTAMPING
3048+
SO_TIMESTAMPING,
3049+
esock_setopt_int_opt, esock_getopt_int_opt,
3050+
#else
3051+
0, NULL, NULL,
3052+
#endif
3053+
&esock_atom_timestamping},
3054+
30343055
{
30353056
#ifdef SO_TYPE
30363057
SO_TYPE,
@@ -10986,6 +11007,40 @@ static BOOLEAN_T esock_cmsg_decode_timeval(ErlNifEnv *env,
1098611007
}
1098711008
#endif
1098811009

11010+
#ifdef SCM_TIMESTAMPNS
11011+
static
11012+
BOOLEAN_T esock_cmsg_encode_timespec(ErlNifEnv *env,
11013+
unsigned char *data,
11014+
size_t dataLen,
11015+
ERL_NIF_TERM *eResult) {
11016+
struct timespec* timeP = (struct timespec *) data;
11017+
11018+
if (dataLen < sizeof(*timeP))
11019+
return FALSE;
11020+
11021+
esock_encode_timespec(env, timeP, eResult);
11022+
return TRUE;
11023+
}
11024+
11025+
static BOOLEAN_T esock_cmsg_decode_timespec(ErlNifEnv *env,
11026+
ERL_NIF_TERM eValue,
11027+
struct cmsghdr *cmsgP,
11028+
size_t rem,
11029+
size_t *usedP)
11030+
{
11031+
struct timespec time, *timeP;
11032+
11033+
if (! esock_decode_timespec(env, eValue, &time))
11034+
return FALSE;
11035+
11036+
if ((timeP = esock_init_cmsghdr(cmsgP, rem, sizeof(*timeP), usedP)) == NULL)
11037+
return FALSE;
11038+
11039+
*timeP = time;
11040+
return TRUE;
11041+
}
11042+
#endif
11043+
1098911044

1099011045
#if defined(IP_TOS) || defined(IP_RECVTOS)
1099111046
static
@@ -11495,6 +11550,12 @@ static ESockCmsgSpec cmsgLevelSocket[] =
1149511550
&esock_cmsg_encode_timeval, esock_cmsg_decode_timeval,
1149611551
&esock_atom_timestamp},
1149711552
#endif
11553+
11554+
#if defined(SCM_TIMESTAMPNS)
11555+
{SCM_TIMESTAMPNS,
11556+
&esock_cmsg_encode_timespec, esock_cmsg_decode_timespec,
11557+
&esock_atom_timestampns},
11558+
#endif
1149811559
};
1149911560
#endif
1150011561

erts/emulator/nifs/common/socket_int.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,8 @@ typedef long ssize_t;
592592
GLOBAL_ATOM_DEF(tcp); \
593593
GLOBAL_ATOM_DEF(throughput); \
594594
GLOBAL_ATOM_DEF(timestamp); \
595+
GLOBAL_ATOM_DEF(timestampns); \
596+
GLOBAL_ATOM_DEF(timestamping); \
595597
GLOBAL_ATOM_DEF(tos); \
596598
GLOBAL_ATOM_DEF(transparent); \
597599
GLOBAL_ATOM_DEF(timeout); \
@@ -613,6 +615,7 @@ typedef long ssize_t;
613615
GLOBAL_ATOM_DEF(unspec); \
614616
GLOBAL_ATOM_DEF(up); \
615617
GLOBAL_ATOM_DEF(usec); \
618+
GLOBAL_ATOM_DEF(nsec); \
616619
GLOBAL_ATOM_DEF(user); \
617620
GLOBAL_ATOM_DEF(user_timeout); \
618621
GLOBAL_ATOM_DEF(use_ext_recvinfo); \

erts/emulator/nifs/common/socket_util.c

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1858,6 +1858,80 @@ BOOLEAN_T esock_decode_timeval(ErlNifEnv* env,
18581858

18591859

18601860

1861+
/* +++ esock_encode_timespec +++
1862+
*
1863+
* Encode a timespec struct into its erlang form, a map with two fields:
1864+
*
1865+
* sec
1866+
* nsec
1867+
*
1868+
*/
1869+
extern
1870+
void esock_encode_timespec(ErlNifEnv* env,
1871+
struct timespec* timeP,
1872+
ERL_NIF_TERM* eTime)
1873+
{
1874+
ERL_NIF_TERM keys[] = {esock_atom_sec, esock_atom_nsec};
1875+
ERL_NIF_TERM vals[] = {MKL(env, timeP->tv_sec), MKL(env, timeP->tv_nsec)};
1876+
size_t numKeys = NUM(keys);
1877+
1878+
ESOCK_ASSERT( numKeys == NUM(vals) );
1879+
ESOCK_ASSERT( MKMA(env, keys, vals, numKeys, eTime) );
1880+
}
1881+
1882+
1883+
1884+
/* +++ esock_decode_timespec +++
1885+
*
1886+
* Decode a timespec in its erlang form (a map) into its native form,
1887+
* a timespec struct.
1888+
*
1889+
*/
1890+
extern
1891+
BOOLEAN_T esock_decode_timespec(ErlNifEnv* env,
1892+
ERL_NIF_TERM eTime,
1893+
struct timespec* timeP)
1894+
{
1895+
ERL_NIF_TERM eSec, eNSec;
1896+
1897+
if (! GET_MAP_VAL(env, eTime, esock_atom_sec, &eSec))
1898+
return FALSE;
1899+
1900+
if (! GET_MAP_VAL(env, eTime, esock_atom_nsec, &eNSec))
1901+
return FALSE;
1902+
1903+
/* Use the appropriate variable type and nif function
1904+
* to decode the value from Erlang into the struct timespec fields
1905+
*/
1906+
{ /* time_t tv_sec; */
1907+
#if (SIZEOF_TIME_T == 8)
1908+
ErlNifSInt64 sec;
1909+
if (! GET_INT64(env, eSec, &sec))
1910+
return FALSE;
1911+
#elif (SIZEOF_TIME_T == SIZEOF_INT)
1912+
int sec;
1913+
if (! GET_INT(env, eSec, &sec))
1914+
return FALSE;
1915+
#else /* long or other e.g undefined */
1916+
long sec;
1917+
if (! GET_LONG(env, eSec, &sec))
1918+
return FALSE;
1919+
#endif
1920+
timeP->tv_sec = sec;
1921+
}
1922+
1923+
{ /* long tv_nsec; */
1924+
long nsec;
1925+
if (! GET_LONG(env, eNSec, &nsec))
1926+
return FALSE;
1927+
timeP->tv_nsec = nsec;
1928+
}
1929+
1930+
return TRUE;
1931+
}
1932+
1933+
1934+
18611935
/* +++ esock_decode_domain +++
18621936
*
18631937
* Decode the Erlang form of the 'domain' type, that is:

erts/emulator/nifs/common/socket_util.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,12 @@ extern void esock_encode_timeval(ErlNifEnv* env,
200200
extern BOOLEAN_T esock_decode_timeval(ErlNifEnv* env,
201201
ERL_NIF_TERM eTime,
202202
struct timeval* timeP);
203+
extern void esock_encode_timespec(ErlNifEnv* env,
204+
struct timespec* timeP,
205+
ERL_NIF_TERM* eTime);
206+
extern BOOLEAN_T esock_decode_timespec(ErlNifEnv* env,
207+
ERL_NIF_TERM eTime,
208+
struct timespec* timeP);
203209

204210
extern
205211
char* esock_domain_to_string(int domain);

lib/common_test/src/ct_suite.erl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,8 @@ The test suite information, as returned by [`Module:suite/0`](`c:suite/0`),
9191
{userdata, UserData :: term()} |
9292
{silent_connections, Conns :: [atom()]} |
9393
{stylesheet, CSSFile :: string()} |
94-
{ct_hooks, CTHs :: ct_hooks()}.
94+
{ct_hooks, CTHs :: ct_hooks()} |
95+
{async, Async :: boolean()}.
9596
-type ct_info_timetrap() :: timeout() |
9697
{seconds, integer()} |
9798
{minutes, integer()} |

0 commit comments

Comments
 (0)