Skip to content

Commit 90e7aad

Browse files
committed
Support TLS in ipfixsend
1 parent c993aca commit 90e7aad

File tree

3 files changed

+153
-8
lines changed

3 files changed

+153
-8
lines changed

src/tools/ipfixsend/ipfixsend.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ void usage()
8181
printf(" -i path IPFIX input file\n");
8282
printf(" -d ip Destination IP address (default: %s)\n", DEFAULT_IP);
8383
printf(" -p port Destination port number (default: %s)\n", DEFAULT_PORT);
84-
printf(" -t type Connection type (UDP or TCP) (default: UDP)\n");
84+
printf(" -t type Connection type (UDP/TCP/SCTP/TLS) (default: UDP)\n");
8585
printf(" -c Precache input file (for performance tests)\n");
8686
printf(" -n num How many times the file should be sent (default: infinity)\n");
8787
printf(" -s speed Maximum data sending speed/s\n");

src/tools/ipfixsend/siso.c

Lines changed: 151 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@
4141

4242
#include <stdbool.h>
4343
#include <stdlib.h>
44+
#include <assert.h>
45+
#include <signal.h>
4446

4547
#include <errno.h>
4648
#include <netdb.h>
@@ -57,6 +59,9 @@
5759
#include <stdio.h>
5860
#include <strings.h>
5961

62+
#include <openssl/ssl.h>
63+
#include <openssl/err.h>
64+
6065
/**
6166
* \brief Check that a pointer is not null. Otherwise returns #SISO_ERR
6267
* \param[in] ptr Pointer to check
@@ -71,6 +76,14 @@
7176
* \brief Get the lass error message
7277
*/
7378
#define PERROR_LAST strerror(errno)
79+
/**
80+
* @brief Get the last global OpenSSL error message
81+
*/
82+
#define TLS_LAST_ERROR tls_code_error(ERR_get_error())
83+
/**
84+
* @brief Get the last OpenSSL error message for SSL connection.
85+
*/
86+
#define TLS_LAST_SSL_ERROR(ssl, ret) tls_code_error(SSL_get_error((ssl), (ret)))
7487
/** Maximum message size that can be send over UDP */
7588
#define SISO_UDP_MAX 65000
7689
/**
@@ -86,6 +99,7 @@ enum siso_conn_type {
8699
SC_UDP,
87100
SC_TCP,
88101
SC_SCTP,
102+
SC_TLS,
89103
SC_UNKNOWN,
90104
};
91105

@@ -109,26 +123,32 @@ static const char *siso_messages[] = {
109123
static const char *siso_sc_types[] = {
110124
[SC_UDP] = "UDP",
111125
[SC_TCP] = "TCP",
112-
[SC_SCTP] = "SCTP"
126+
[SC_SCTP] = "SCTP",
127+
[SC_TLS] = "TLS",
113128
};
114129

130+
/** Buffer for error messages. */
131+
static char sisco_err_msg_buf[256] = { 0 };
132+
115133
/**
116134
* \brief Main sisolib structure
117135
*/
118136
struct sisoconf_s {
119137
const char *last_error; /**< last error message */
120-
enum siso_conn_type type; /**< UDP/TCP/SCTP */
138+
enum siso_conn_type type; /**< UDP/TCP/SCTP/TLS */
121139
struct addrinfo *servinfo; /**< server information */
122140
int sockfd; /**< socket descriptor */
123141
uint64_t max_speed; /**< max sending speed */
124142
uint64_t act_speed; /**< actual speed */
125143
struct timeval begin, end; /**< start/end time for limited transfers */
144+
SSL_CTX *ssl_ctx; /**< context for creating TLS connecitons */
145+
SSL *ssl; /**< TLS connection */
126146
};
127147

128148
/**
129149
* \brief Constructor
130150
*/
131-
sisoconf *siso_create()
151+
sisoconf *siso_create(void)
132152
{
133153
/* allocate memory */
134154
sisoconf *conf = (sisoconf *) calloc(1, sizeof(sisoconf));
@@ -154,6 +174,11 @@ void siso_destroy(sisoconf *conf)
154174
freeaddrinfo(conf->servinfo);
155175
}
156176

177+
if (conf->ssl_ctx) {
178+
SSL_CTX_free(conf->ssl_ctx);
179+
conf->ssl_ctx = NULL;
180+
}
181+
157182
free(conf);
158183
}
159184

@@ -256,6 +281,7 @@ int siso_getaddrinfo(sisoconf *conf, const char *ip, const char *port)
256281
hints.ai_socktype = SOCK_DGRAM;
257282
hints.ai_protocol = IPPROTO_UDP;
258283
break;
284+
case SC_TLS:
259285
case SC_TCP:
260286
hints.ai_socktype = SOCK_STREAM;
261287
hints.ai_protocol = IPPROTO_TCP;
@@ -281,6 +307,98 @@ int siso_getaddrinfo(sisoconf *conf, const char *ip, const char *port)
281307
return SISO_OK;
282308
}
283309

310+
/**
311+
* @brief Get OpenSSL error message for the given code
312+
*/
313+
static inline const char *tls_code_error(unsigned long code)
314+
{
315+
ERR_error_string_n(code, sisco_err_msg_buf, sizeof(sisco_err_msg_buf));
316+
return &sisco_err_msg_buf[0];
317+
}
318+
319+
/**
320+
* @brief Get SSL_CTX. Initialize it if it is not initialized.
321+
* @return NULL on failure.
322+
*/
323+
SSL_CTX *siso_get_ssl_ctx(sisoconf *conf)
324+
{
325+
if (conf->ssl_ctx) {
326+
return conf->ssl_ctx;
327+
}
328+
329+
SSL_load_error_strings();
330+
331+
SSL_CTX *ctx = SSL_CTX_new(TLS_client_method());
332+
if (!ctx) {
333+
conf->last_error = TLS_LAST_ERROR;
334+
return NULL;
335+
}
336+
337+
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
338+
339+
if (!SSL_CTX_set_default_verify_paths(ctx)) {
340+
conf->last_error = TLS_LAST_ERROR;
341+
SSL_CTX_free(ctx);
342+
return NULL;
343+
}
344+
345+
if (!SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION)) {
346+
conf->last_error = TLS_LAST_ERROR;
347+
SSL_CTX_free(ctx);
348+
return NULL;
349+
}
350+
351+
SSL_CTX_set_mode(ctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
352+
353+
// There is no way to pass MSG_SIGIGN when writing using OpenSSL without
354+
// creating custom BIO. So we can just ignore SIGPIPE.
355+
signal(SIGPIPE, SIG_IGN);
356+
357+
return conf->ssl_ctx = ctx;
358+
}
359+
360+
int siso_tls_connect(sisoconf *conf, int new_fd)
361+
{
362+
assert(!conf->ssl);
363+
364+
SSL_CTX *ctx = siso_get_ssl_ctx(conf);
365+
if (!ctx) {
366+
return SISO_ERR;
367+
}
368+
369+
SSL *ssl = SSL_new(ctx);
370+
if (!ssl) {
371+
conf->last_error = TLS_LAST_ERROR;
372+
return SISO_ERR;
373+
}
374+
375+
BIO *bio = BIO_new(BIO_s_socket());
376+
if (!bio) {
377+
conf->last_error = TLS_LAST_ERROR;
378+
SSL_free(ssl);
379+
return SISO_ERR;
380+
}
381+
382+
BIO_set_fd(bio, new_fd, BIO_NOCLOSE);
383+
SSL_set_bio(ssl, bio, bio);
384+
385+
int res = SSL_connect(ssl);
386+
if (res <= 0) {
387+
// Get the best error message.
388+
long vres = SSL_get_verify_result(ssl);
389+
if (vres == X509_V_OK) {
390+
conf->last_error = TLS_LAST_SSL_ERROR(ssl, res);
391+
} else {
392+
conf->last_error = X509_verify_cert_error_string(vres);
393+
}
394+
SSL_free(ssl);
395+
return SISO_ERR;
396+
}
397+
398+
conf->ssl = ssl;
399+
return SISO_OK;
400+
}
401+
284402
/**
285403
* \brief Create new socket
286404
*/
@@ -309,6 +427,11 @@ int siso_create_socket(sisoconf *conf)
309427
return SISO_ERR;
310428
}
311429

430+
if (conf->type == SC_TLS && siso_tls_connect(conf, new_fd) != SISO_OK) {
431+
close(new_fd);
432+
return SISO_ERR;
433+
}
434+
312435
conf->sockfd = new_fd;
313436
return SISO_OK;
314437
}
@@ -345,7 +468,16 @@ int siso_create_connection(sisoconf* conf, const char* ip, const char *port, con
345468
*/
346469
void siso_close_connection(sisoconf *conf)
347470
{
348-
if (conf && conf->sockfd > 0) {
471+
if (!conf) {
472+
return;
473+
}
474+
475+
if (conf->ssl) {
476+
SSL_free(conf->ssl);
477+
conf->ssl = NULL;
478+
}
479+
480+
if (conf->sockfd > 0) {
349481
close(conf->sockfd);
350482
conf->sockfd = -1;
351483
}
@@ -375,6 +507,7 @@ int siso_send(sisoconf *conf, const char *data, ssize_t length)
375507

376508
// data sent per cycle
377509
ssize_t sent_now = 0;
510+
int ret_code = 1;
378511

379512
// Size of remaining data
380513
ssize_t todo = length;
@@ -390,12 +523,25 @@ int siso_send(sisoconf *conf, const char *data, ssize_t length)
390523
case SC_SCTP:
391524
sent_now = send(conf->sockfd, ptr, todo, MSG_NOSIGNAL);
392525
break;
526+
case SC_TLS:
527+
ret_code = SSL_write_ex(conf->ssl, ptr, todo, (size_t *)&sent_now);
528+
break;
393529
default:
394530
break;
395531
}
396532

397533
// Check for errors
398-
if (sent_now == -1) {
534+
if (ret_code <= 0) {
535+
int err_code = SSL_get_error(conf->ssl, ret_code);
536+
if (err_code != SSL_ERROR_WANT_READ && err_code != SSL_ERROR_WANT_WRITE) {
537+
// Connection is broken, close...
538+
conf->last_error = tls_code_error(err_code);
539+
siso_close_connection(conf);
540+
return SISO_ERR;
541+
}
542+
543+
// Don't use continue here, the write may be partial.
544+
} else if (sent_now == -1) {
399545
if (errno != EAGAIN && errno != EWOULDBLOCK) {
400546
// Connection broken, close...
401547
conf->last_error = PERROR_LAST;

src/tools/ipfixsend/siso.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ enum siso_units {
7878
*
7979
* \return new sisoconf object
8080
*/
81-
sisoconf *siso_create();
81+
sisoconf *siso_create(void);
8282

8383
/**
8484
* \brief Destructor
@@ -199,4 +199,3 @@ int siso_send(sisoconf *conf, const char *data, ssize_t length);
199199
#endif
200200

201201
#endif /* SISO_H */
202-

0 commit comments

Comments
 (0)