OpenSSL: Get network card / linux kernel timestamps

98 Views Asked by At

I use C to solve the current issue, so I'll refer to C functions. I prefer to get help forn C/C++, but other languages are also okayish (because I assume I can translate the solution to C/C++).

The problem:

Linux allows it to timestamp incoming network data either on the network card or in the kernel (https://www.kernel.org/doc/Documentation/networking/timestamping.txt).

An easy example code can be found here: https://raw.githubusercontent.com/majek/openonload/master/src/tests/onload/hwtimestamping/rx_timestamping.c

The most important detail is that this requires using the recvmsg function in the receiver-code. Now I would like to get those timestamps for a connection which uses SSL. For SSL I use OpenSSL and finally the function SSL_read to get data from the stream. Unfortnately, I don't see a way to get the timestamps without modifying OpenSSL, but I assume I'm wrong and it is possible to do it.

Maybe someone knows how that feature can be used in combination with OpenSSL?

Thanks a lot

2

There are 2 best solutions below

0
Matt Caswell On

OpenSSL doesn't support this directly, but you could write a custom BIO to do this. OpenSSL does all its network interaction via a BIO. Normally you just use one of the in-built ones and then set it on your SSL object via SSL_set_bio:

https://www.openssl.org/docs/man3.1/man3/SSL_set_bio.html

You can also have separate BIOs for the read and write side. So in your case you might just use an in-built BIO for the write side, and a custom one for the read side.

In outline what you need to do is create a custom BIO_METHOD using BIO_meth_new:

https://www.openssl.org/docs/man3.1/man3/BIO_meth_new.html

Set a custom network read function on it using BIO_meth_set_read or BIO_meth_set_read_ex (described on the same man page as above). You may need to implement some of the other functions depending on how your BIO works. Your custom network read function would call recvmsg and get the timestamps you are interested in.

Construct your custom BIO object using BIO_new() and pass your newly construction BIO_METHOD as an argument.

Finally set your custom BIO on the read side of the SSL object using SSL_set_bio or SSL_set0_rbio

0
Misha T On

You could

  • bind SSL object to BIO object. And than
  • bind BIO object to previously created socket
  • read timestamp information from socket file descritor
  • after that read data with SSL_read

Something about this

int main(int argc, *char argv[])
{
    // Some code
      ..............
      ..............

    // Create and initialize socket (for example connect to some server)
    int sockfd = ...; /* socket connection code */
    
    // Enable timestamps
    int enable = SOF_TIMESTAMPING_RX_HARDWARE | SOF_TIMESTAMPING_RAW_HARDWARE 
                |SOF_TIMESTAMPING_SYS_HARDWARE | SOF_TIMESTAMPING_SOFTWARE;
    setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING, &enable, sizeof(int))s;
 
    // Start SSL code
    SSL_CTX ctx = SSL_CTX_new(/*some crypto context method, for example*/ TLS_method());
    SSL * ssl = SSL_new(ctx);
    BIO * bio = BIO_new(NULL /*your BIO method*/);

    BIO_set_fd(bio, fd, 0);
    SSL_set_bio(ssl, bio, bio);

    // Initialize msghdr
    struct msghdr msg;
    struct iovec iov;
    struct sockaddr_in host_address;
    char buffer[2048];
    char control[1024];
    int got;

    /* recvmsg header structure */
    make_address(0, &host_address);
    iov.iov_base = buffer;
    iov.iov_len = 2048;
    msg.msg_iov = &iov;
    msg.msg_iovlen = 1;
    msg.msg_name = &host_address;
    msg.msg_namelen = sizeof(struct sockaddr_in);
    msg.msg_control = control;
    msg.msg_controllen = 1024;

    // Wait for message
    // MSG_PEEK
    // Peeks at the data present on the socket; the data is returned but not
    // consumed, so that a subsequent receive operation will
    // see the same data
    while ( (got = readmsg(sockfd, &msg, MSG_PEEK) <= 0 ) {
        if( (got <= 0) && (errno = EWOULDBLOCK) ) {
             usleep(1000); // sleep for 1ms
        }
        else if (got <= 0) {
             printf("Something unexpected!\n");
             exit(-1);
        }
    }

    // now we can read timestamps code (see in link to example in question):
    ........

    // We've read timestamps, now lets read data
    char databuf[2048];
    int datalen = SSL_read(ssl, databuf, sizeof(databuf));
    
    // Process decrypted data here: