commit
e1078c7611
|
@ -537,10 +537,13 @@ ENDIF(CONNECTION_FILTER)
|
|||
|
||||
OPTION(SUPPORT_DTLS "Enables/Disables encryption support for IPFIX messages." OFF)
|
||||
IF (SUPPORT_DTLS)
|
||||
FIND_PACKAGE(OpenSSL)
|
||||
FIND_PACKAGE(OpenSSL 1.0.0)
|
||||
IF (NOT OPENSSL_FOUND)
|
||||
MESSAGE(FATAL_ERROR "Could not find openssl. Please install the library or turn off SUPPORT_DTLS")
|
||||
ENDIF (NOT OPENSSL_FOUND)
|
||||
IF (NOT (${OPENSSL_VERSION} VERSION_LESS 1.1.0))
|
||||
MESSAGE(FATAL_ERROR "openssl version must be less than 1.1.0")
|
||||
ENDIF (NOT (${OPENSSL_VERSION} VERSION_LESS 1.1.0))
|
||||
INCLUDE_DIRECTORIES(${OPENSSL_INCLUDE_DIR})
|
||||
TARGET_LINK_LIBRARIES(vermont ${OPENSSL_LIBRARIES})
|
||||
ADD_DEFINITIONS(-DSUPPORT_DTLS)
|
||||
|
|
60
README.md
60
README.md
|
@ -32,9 +32,7 @@ The following packages are optional:
|
|||
- libczmq-dev (for receiving IPFIX reports over ZMQ)
|
||||
==> cmake option SUPPORT_ZMQ
|
||||
|
||||
For DTLS support, OpenSSL 1.0.0 or higher is required. It is recommended
|
||||
to build OpenSSL based on the latest CVS revision. See DTLS instructions below.
|
||||
|
||||
For DTLS support, OpenSSL 1.0.0 is required.
|
||||
|
||||
## BUILDING AND INSTALLATION
|
||||
|
||||
|
@ -83,64 +81,18 @@ $ make install
|
|||
|
||||
### BUILDING WITH DTLS-OVER-UDP SUPPORT
|
||||
|
||||
VERMONT's DTLS support is based on OpenSSL version 1.0.0 (and maybe higher).
|
||||
VERMONT's DTLS support is based on OpenSSL version 1.0.0. OpenSSL 1.1.0 is not currently supported.
|
||||
|
||||
Since the DTLS implementation in OpenSSL is fairly new and not as mature as
|
||||
the TLS/SSL implementation, you should use the latest version of OpenSSL which
|
||||
you can get from http://openssl.org/source/.
|
||||
|
||||
At the time of writing (July 2010), the latest version is 1.0.0a.
|
||||
In order to compile VERMONT with DTLS-over-UDP support set the following option:
|
||||
``` shell
|
||||
$ wget http://openssl.org/source/openssl-1.0.0a.tar.gz
|
||||
$ tar xzf openssl-1.0.0a.tar.gz
|
||||
$ cd openssl-1.0.0a/
|
||||
$ cmake -DSUPPORT_DTLS=YES
|
||||
```
|
||||
|
||||
If you want to profit from the most recent bugfixes, you can check out the
|
||||
sources from the OpenSSL CVS repository instead:
|
||||
``` shell
|
||||
$ cvs -z9 -d anonymous@cvs.openssl.org:/openssl-cvs co openssl
|
||||
$ cd openssl/
|
||||
|
||||
If CMake does not find OPENSSL you can explicitly specify the include and library paths:
|
||||
```
|
||||
|
||||
In order to avoid incompatibilities with other packages of your distribution,
|
||||
you probably do not want the new version of OpenSSL to become the default
|
||||
OpenSSL library on your system. Therefore, it is recommended to install the
|
||||
new version in a local directory by using the --prefix option of the config
|
||||
script.
|
||||
|
||||
To build OpenSSL and install it into a built/ subdirectory within the OpenSSL
|
||||
source directory, call the following commands:
|
||||
``` shell
|
||||
$ ./config -d no-dso no-shared --prefix=`pwd`/built
|
||||
$ make
|
||||
$ make install
|
||||
cmake -DSUPPORT_DTLS=YES -DCMAKE_INCLUDE_PATH=/path/to/openssl/include -DCMAKE_LIBRARY_PATH=/path/to/openssl/lib
|
||||
```
|
||||
|
||||
The configure option "no-dso" turns off the use of shared-library methods which
|
||||
avoids linking problems related to libdl on the Linux platform.
|
||||
With the option "no-shared", only static libraries are built which makes it
|
||||
easier to link VERMONT to the correct version of OpenSSL.
|
||||
|
||||
In order to compile VERMONT with DTLS-over-UDP support, change into the root
|
||||
of VERMONT's source directory and execute cmake with the OpenSSL include and
|
||||
library paths (replace "/path/to/openssl" by your OpenSSL source directory):
|
||||
``` shell
|
||||
$ cmake -DSUPPORT_DTLS=YES -DCMAKE_INCLUDE_PATH=/path/to/openssl/built/include -DCMAKE_LIBRARY_PATH=/path/to/openssl/built/lib
|
||||
```
|
||||
|
||||
On 64 bit platforms, the library path might be different (mind the "64" at the
|
||||
very end!):
|
||||
``` shell
|
||||
$ cmake -DSUPPORT_DTLS=YES -DCMAKE_INCLUDE_PATH=/path/to/openssl/built/include -DCMAKE_LIBRARY_PATH=/path/to/openssl/built/lib64
|
||||
```
|
||||
|
||||
If you have previously built VERMONT with OpenSSL located in another
|
||||
directory, you might need to manually remove the file CMakeCache.txt before
|
||||
calling cmake.
|
||||
|
||||
|
||||
### BUILDING WITH DTLS-OVER-SCTP SUPPORT
|
||||
|
||||
At the time of writing (July 2010), DTLS over SCTP can be used on FreeBSD only!
|
||||
|
|
|
@ -18,10 +18,16 @@
|
|||
#
|
||||
|
||||
|
||||
ADD_LIBRARY(ipfixlolib
|
||||
set(ipfixlolib_SOURCES
|
||||
encoding.c
|
||||
ipfixlolib.c
|
||||
ipfix_names.c
|
||||
)
|
||||
|
||||
if (SUPPORT_DTLS)
|
||||
set(ipfixlolib_SOURCES ${ipfixlolib_SOURCES} ipfixlolib_dtls.c)
|
||||
endif()
|
||||
|
||||
ADD_LIBRARY(ipfixlolib ${ipfixlolib_SOURCES})
|
||||
|
||||
add_cppcheck(ipfixlolib STYLE POSSIBLE_ERROR)
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
*/
|
||||
|
||||
#include "ipfixlolib.h"
|
||||
#include "ipfixlolib_private.h"
|
||||
#include "encoding.h"
|
||||
#include "common/msg.h"
|
||||
#include <netinet/in.h>
|
||||
|
@ -52,6 +53,10 @@
|
|||
#include <stdbool.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#ifdef SUPPORT_DTLS
|
||||
#include "ipfixlolib_dtls_private.h"
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
/* Copied from linux/in.h */
|
||||
#define IP_MTU 14
|
||||
|
@ -67,25 +72,11 @@ extern "C" {
|
|||
# error OpenSSL built without SCTP support. Rebuild OpenSSL with SCTP support or turn off SUPPORT_DTLS_OVER_SCTP
|
||||
#endif
|
||||
|
||||
#ifdef SUPPORT_DTLS
|
||||
static int ensure_exporter_set_up_for_dtls(ipfix_exporter *exporter);
|
||||
static void deinit_openssl_ctx(ipfix_exporter *exporter);
|
||||
static int setup_dtls_connection(ipfix_exporter *exporter, ipfix_receiving_collector *col, ipfix_dtls_connection *con);
|
||||
static int dtls_send(ipfix_exporter *exporter, ipfix_receiving_collector *col, const struct iovec *iov, int iovcnt);
|
||||
static int dtls_connect(ipfix_receiving_collector *col, ipfix_dtls_connection *con);
|
||||
static void dtls_shutdown_and_cleanup(ipfix_dtls_connection *con);
|
||||
static void dtls_fail_connection(ipfix_dtls_connection *con);
|
||||
#endif
|
||||
#ifdef SUPPORT_SCTP
|
||||
static int init_send_sctp_socket(struct sockaddr_storage serv_addr, char *vrf_name);
|
||||
#ifdef SUPPORT_DTLS_OVER_SCTP
|
||||
static void handle_sctp_event(BIO *bio, void *context, void *buf);
|
||||
#endif
|
||||
#endif
|
||||
static int init_send_udp_socket(struct sockaddr_storage serv_addr, char *vrf_name);
|
||||
static int enable_pmtu_discovery(int s);
|
||||
static int ipfix_find_template(ipfix_exporter *exporter, uint16_t template_id);
|
||||
static void ipfix_update_header(ipfix_exporter *p_exporter, ipfix_receiving_collector *collector, ipfix_sendbuffer *sendbuf);
|
||||
static int ipfix_init_sendbuffer(export_protocol_version export_protocol, ipfix_sendbuffer **sendbufn);
|
||||
static int ipfix_reset_sendbuffer(ipfix_sendbuffer *sendbuf);
|
||||
static int ipfix_deinit_sendbuffer(ipfix_sendbuffer **sendbuf);
|
||||
|
@ -100,583 +91,18 @@ static int ipfix_update_template_sendbuffer(ipfix_exporter *exporter);
|
|||
static int ipfix_send_templates(ipfix_exporter* exporter);
|
||||
static int ipfix_send_data(ipfix_exporter* exporter);
|
||||
static int ipfix_new_file(ipfix_receiving_collector* recvcoll);
|
||||
static void update_exporter_max_message_size(ipfix_exporter *exporter);
|
||||
static int update_collector_mtu(ipfix_exporter *exporter, ipfix_receiving_collector *col);
|
||||
static int get_mtu(const int s);
|
||||
static int ipfix_enterprise_flag_set(uint16_t id);
|
||||
|
||||
|
||||
#ifdef SUPPORT_DTLS
|
||||
/* A separate SSL_CTX object is created for every ipfix_exporter.
|
||||
* Returns 0 on success, -1 on error
|
||||
* */
|
||||
static int ensure_exporter_set_up_for_dtls(ipfix_exporter *e) {
|
||||
ensure_openssl_init();
|
||||
|
||||
if (e->ssl_ctx) return 0;
|
||||
|
||||
/* This SSL_CTX object will be freed in deinit_openssl_ctx() */
|
||||
if ( ! (e->ssl_ctx=SSL_CTX_new(DTLSv1_client_method())) ) {
|
||||
msg(MSG_FATAL, "Failed to create SSL context");
|
||||
msg_openssl_errors();
|
||||
return -1;
|
||||
}
|
||||
SSL_CTX_set_read_ahead(e->ssl_ctx,1);
|
||||
|
||||
if ( (e->ca_file || e->ca_path) &&
|
||||
! SSL_CTX_load_verify_locations(e->ssl_ctx,e->ca_file,e->ca_path) ) {
|
||||
msg(MSG_FATAL,"SSL_CTX_load_verify_locations() failed.");
|
||||
msg_openssl_errors();
|
||||
return -1;
|
||||
}
|
||||
/* Load our own certificate */
|
||||
if (e->certificate_chain_file) {
|
||||
if (!SSL_CTX_use_certificate_chain_file(e->ssl_ctx, e->certificate_chain_file)) {
|
||||
msg(MSG_FATAL,"Unable to load certificate chain file %s",e->certificate_chain_file);
|
||||
msg_openssl_errors();
|
||||
return -1;
|
||||
}
|
||||
if (!SSL_CTX_use_PrivateKey_file(e->ssl_ctx, e->private_key_file, SSL_FILETYPE_PEM)) {
|
||||
msg(MSG_FATAL,"Unable to load private key file %s",e->private_key_file);
|
||||
msg_openssl_errors();
|
||||
return -1;
|
||||
}
|
||||
if (!SSL_CTX_check_private_key(e->ssl_ctx)) {
|
||||
msg(MSG_FATAL,"Private key and certificate do not match");
|
||||
msg_openssl_errors();
|
||||
return -1;
|
||||
}
|
||||
DPRINTF("We successfully loaded our certificate.");
|
||||
} else {
|
||||
DPRINTF("We do NOT have a certificate.");
|
||||
}
|
||||
/* We leave the certificate_authorities list of the Certificate Request
|
||||
* empty. See RFC 4346 7.4.4. Certificate request. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void deinit_openssl_ctx(ipfix_exporter *e) {
|
||||
if (e->ssl_ctx) { SSL_CTX_free(e->ssl_ctx); }
|
||||
e->ssl_ctx = NULL;
|
||||
}
|
||||
|
||||
static int dtls_verify_peer_cb(void *context, const char* dnsname) {
|
||||
const ipfix_receiving_collector *col =
|
||||
(const ipfix_receiving_collector *) context;
|
||||
return strcasecmp(col->peer_fqdn,dnsname) ? 0 : 1;
|
||||
}
|
||||
|
||||
static int dtls_get_replacement_connection_ready(
|
||||
ipfix_exporter *exporter,
|
||||
ipfix_receiving_collector *col) {
|
||||
int ret;
|
||||
if (!col->dtls_replacement.ssl) {
|
||||
/* No SSL object has been created yet. Let's open a socket and
|
||||
* setup a new SSL object. */
|
||||
DPRINTF("Setting up replacement connection.");
|
||||
if (setup_dtls_connection(exporter,col,&col->dtls_replacement)) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
ret = dtls_connect(col,&col->dtls_replacement);
|
||||
if (ret == 1) {
|
||||
DPRINTF("Replacement connection setup successful.");
|
||||
return 1; /* SUCCESS */
|
||||
}
|
||||
if (ret == 0) {
|
||||
if (col->dtls_connect_timeout &&
|
||||
(time(NULL) - col->dtls_replacement.last_reconnect_attempt_time > col->dtls_connect_timeout)) {
|
||||
msg(MSG_ERROR,"DTLS replacement connection setup taking too long.");
|
||||
dtls_fail_connection(&col->dtls_replacement);
|
||||
} else {
|
||||
DPRINTF("Replacement connection setup still ongoing.");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int dtls_send_templates(
|
||||
ipfix_exporter *exporter,
|
||||
ipfix_receiving_collector *col) {
|
||||
|
||||
if (exporter->template_sendbuffer->committed_data_length == 0)
|
||||
return 0;
|
||||
|
||||
ipfix_update_header(exporter, col,
|
||||
exporter->template_sendbuffer);
|
||||
col->messages_sent++;
|
||||
DPRINTF("Sending templates over DTLS.");
|
||||
return dtls_send(exporter,col,
|
||||
exporter->template_sendbuffer->entries,
|
||||
exporter->template_sendbuffer->current);
|
||||
}
|
||||
|
||||
static void dtls_swap_connections(ipfix_dtls_connection *a, ipfix_dtls_connection *b) {
|
||||
ipfix_dtls_connection tmp;
|
||||
memcpy(&tmp,a,sizeof(tmp));
|
||||
memcpy(a,b,sizeof(*b));
|
||||
memcpy(b,&tmp,sizeof(*b));
|
||||
}
|
||||
|
||||
|
||||
/* This function pushes the connection setup forward if we are in state C_NEW.
|
||||
* It also sets up the replacement connection if it's time to replace the
|
||||
* current connection with a new one.
|
||||
* If we are in state C_DISCONNECT then it sets up a new main connection.
|
||||
*
|
||||
* Return values:
|
||||
* returns 0 on success
|
||||
* returns 1 if a connection setup procedure is still ongoing. Then, this
|
||||
* function should be called again after a short period of time.
|
||||
* returns -1 on error.
|
||||
* Note that success does not mean that we are connected because the
|
||||
* connection setup might still be ongoing. Therefore you still have
|
||||
* to check the state member of ipfix_receiving_collector to determine
|
||||
* if we are connected. */
|
||||
static int dtls_manage_connection(ipfix_exporter *exporter, ipfix_receiving_collector *col) {
|
||||
int ret, rc = 0;
|
||||
|
||||
if (col->state == C_CONNECTED) {
|
||||
if( col->protocol == DTLS_OVER_SCTP ||
|
||||
col->dtls_max_connection_lifetime == 0 ||
|
||||
time(NULL) - col->connect_time < col->dtls_max_connection_lifetime)
|
||||
return 0;
|
||||
/* Alright, the connection is already very old and needs to be
|
||||
* replaced. Let's get the replacement / backup connection ready. */
|
||||
ret = dtls_get_replacement_connection_ready(exporter, col);
|
||||
rc = 1;
|
||||
if (ret == 1) { /* Connection setup completed */
|
||||
rc = 0;
|
||||
DPRINTF("Swapping connections.");
|
||||
dtls_swap_connections(&col->dtls_main,&col->dtls_replacement);
|
||||
DPRINTF("Shutting down old DTLS connection.");
|
||||
dtls_shutdown_and_cleanup(&col->dtls_replacement);
|
||||
col->connect_time = time(NULL);
|
||||
ret = dtls_send_templates(exporter, col);
|
||||
/* We do not need to check the return value because
|
||||
* dtls_send_templates already shuts down the DTLS connection
|
||||
* in case of failure.
|
||||
* It also sets state to C_DISCONNECTED in this case. */
|
||||
}
|
||||
/* We ignore all other return values of dtls_get_replacement_connection_ready() */
|
||||
}
|
||||
if (col->state == C_NEW) {
|
||||
rc = 1;
|
||||
/* Connection setup is still ongoing. Let's push it forward. */
|
||||
ret = dtls_connect(col,&col->dtls_main);
|
||||
if (ret == 1) {
|
||||
/* SUCCESS */
|
||||
col->state = C_CONNECTED;
|
||||
col->connect_time = time(NULL);
|
||||
if (update_collector_mtu(exporter, col)) {
|
||||
/* update_collector_mtu calls remove_collector
|
||||
in case of failure which in turn sets
|
||||
col->state to C_UNUSED. */
|
||||
return -1;
|
||||
}
|
||||
ret = dtls_send_templates(exporter, col);
|
||||
/* dtls_send (inside dtls_send_templates) calls
|
||||
* dtls_fail_connection() and sets col->state
|
||||
* in case of failure. */
|
||||
if (ret >= 0) return 0; else return -1;
|
||||
} else if (ret == -1) {
|
||||
/* Failure
|
||||
* dtls_connect() cleaned up SSL object already.
|
||||
* Remember that the socket is now part of the DTLS connection
|
||||
* abstraction. dtls_connect() closed the socket as well. */
|
||||
col->state = C_DISCONNECTED;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (col->state == C_DISCONNECTED) {
|
||||
/* Wait dtls_connect_timeout seconds before
|
||||
re-initiating DTLS connection */
|
||||
if (time(NULL) < col->last_reconnect_attempt_time +
|
||||
col->dtls_connect_timeout)
|
||||
return 1;
|
||||
rc = 1;
|
||||
if (setup_dtls_connection(exporter,col,&col->dtls_main)) {
|
||||
/* col->state stays in C_DISCONNECTED in this case
|
||||
* setup_dtls_connection() does not alter it. */
|
||||
return -1;
|
||||
}
|
||||
col->state = C_NEW;
|
||||
col->last_reconnect_attempt_time = time(NULL);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Return values:
|
||||
* -1 failure
|
||||
* 0 no failure but not yet connected. You need to call dtls_connect again
|
||||
* next time
|
||||
* 1 yes. now we're connected. Don't call dtls_connect again. */
|
||||
static int dtls_connect(ipfix_receiving_collector *col, ipfix_dtls_connection *con) {
|
||||
int ret, error;
|
||||
|
||||
ret = SSL_connect(con->ssl);
|
||||
error = SSL_get_error(con->ssl,ret);
|
||||
if (error == SSL_ERROR_NONE) {
|
||||
msg_openssl_return_code(MSG_DEBUG,"SSL_connect()",ret,error);
|
||||
msg(MSG_INFO, "Successfully (re)connected to %s-over-DTLS collector.", col->protocol == DTLS_OVER_SCTP ? "SCTP" : "UDP");
|
||||
msg(MSG_INFO,"TLS Cipher: %s",SSL_get_cipher_name(con->ssl));
|
||||
DPRINTF("DTLS handshake succeeded. We are now connected.");
|
||||
if (col->peer_fqdn) { /* We need to verify the identity of our peer */
|
||||
if (verify_ssl_peer(con->ssl,&dtls_verify_peer_cb,col)) {
|
||||
DPRINTF("Peer authentication successful.");
|
||||
} else {
|
||||
msg(MSG_ERROR,"Peer authentication failed. Shutting down connection.");
|
||||
dtls_fail_connection(con);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
} else if (error == SSL_ERROR_WANT_READ) {
|
||||
msg_openssl_return_code(MSG_DEBUG,"SSL_connect()",ret,error);
|
||||
return 0;
|
||||
} else {
|
||||
msg_openssl_return_code(MSG_ERROR,"SSL_connect()",ret,error);
|
||||
dtls_fail_connection(con);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return values:
|
||||
* n>0: sent n bytes
|
||||
* 0: Could not send due to OpenSSL returning SSL_ERROR_WANT_READ
|
||||
* -1: Recoverable error
|
||||
* -2: Bad Error. DTLS connection has been shutdown already.
|
||||
* You should set state to C_DISCONNECT
|
||||
* -3: Message too long. You should query the current MTU
|
||||
* estimate in this case and update the corresponding
|
||||
* property of the collector.
|
||||
*/
|
||||
|
||||
static int dtls_send_helper( ipfix_dtls_connection *con,
|
||||
const struct iovec *iov, int iovcnt) {
|
||||
int len, error, i;
|
||||
char sendbuf[IPFIX_MAX_PACKETSIZE];
|
||||
char *sendbufcur = sendbuf;
|
||||
int maxsendbuflen = sizeof(sendbuf);
|
||||
/* Collect data form iovecs */
|
||||
for (i=0;i<iovcnt;i++) {
|
||||
if (sendbufcur + iov[i].iov_len > sendbuf + maxsendbuflen) {
|
||||
msg(MSG_FATAL, "sendbuffer for dtls_send too small.");
|
||||
return -1;
|
||||
}
|
||||
memcpy(sendbufcur,iov[i].iov_base,iov[i].iov_len);
|
||||
sendbufcur+=iov[i].iov_len;
|
||||
}
|
||||
|
||||
len = SSL_write(con->ssl, sendbuf, sendbufcur - sendbuf);
|
||||
error = SSL_get_error(con->ssl,len);
|
||||
#ifdef DEBUG
|
||||
char buf[32];
|
||||
snprintf(buf,sizeof(buf),"SSL_write(%d bytes of data)",(int) (sendbufcur - sendbuf) );
|
||||
msg_openssl_return_code(MSG_DEBUG,buf,len,error);
|
||||
#endif
|
||||
switch (error) {
|
||||
case SSL_ERROR_NONE:
|
||||
if (len!=sendbufcur - sendbuf) {
|
||||
msg(MSG_FATAL, "len!=sendbuflen when calling SSL_write()");
|
||||
return -1;
|
||||
}
|
||||
return sendbufcur - sendbuf; /* SUCCESS */
|
||||
case SSL_ERROR_WANT_READ:
|
||||
return 0;
|
||||
case SSL_ERROR_SYSCALL:
|
||||
if (errno == EMSGSIZE) {
|
||||
return -3;
|
||||
}
|
||||
/* fall through */
|
||||
default:
|
||||
msg_openssl_return_code(MSG_ERROR,"SSL_write()",len,error);
|
||||
dtls_fail_connection(con);
|
||||
return -2;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return values:
|
||||
* -1 error
|
||||
* 0 could not write because OpenSSL returned SSL_ERROR_WANT_READ
|
||||
* n>0 number of bytes written
|
||||
*/
|
||||
static int dtls_send(
|
||||
ipfix_exporter *exporter,
|
||||
ipfix_receiving_collector *col,
|
||||
const struct iovec *iov, int iovcnt) {
|
||||
|
||||
int len;
|
||||
|
||||
/* DTLS negotiation has to be finished before we can send data.
|
||||
* Drop out of this function if we are not yet connected. */
|
||||
if (col->state != C_CONNECTED) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
len = dtls_send_helper(&col->dtls_main, iov, iovcnt);
|
||||
if (len == -2) {
|
||||
col->state = C_DISCONNECTED;
|
||||
return -1;
|
||||
} else if (len == -3) {
|
||||
update_collector_mtu(exporter,col);
|
||||
return -1;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
#ifdef SUPPORT_DTLS_OVER_SCTP
|
||||
/* Return values:
|
||||
* -1 error
|
||||
* 0 could not write because OpenSSL returned SSL_ERROR_WANT_READ
|
||||
* n>0 number of bytes written
|
||||
*/
|
||||
static int dtls_over_sctp_send(
|
||||
ipfix_exporter *exporter,
|
||||
ipfix_receiving_collector *col,
|
||||
const struct iovec *iov, int iovcnt, uint32_t pr_value) {
|
||||
|
||||
struct sctp_sndrcvinfo sinfo;
|
||||
memset(&sinfo, 0, sizeof(struct sctp_sndrcvinfo));
|
||||
sinfo.sinfo_timetolive = pr_value; // pr_value; FIXME
|
||||
BIO_ctrl(SSL_get_wbio(col->dtls_main.ssl), BIO_CTRL_DGRAM_SCTP_SET_SNDINFO, sizeof(struct sctp_sndrcvinfo), &sinfo);
|
||||
return dtls_send(exporter,col,iov,iovcnt);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int create_dtls_socket(ipfix_receiving_collector *col) {
|
||||
int s, type, protocol;
|
||||
#ifdef SUPPORT_DTLS_OVER_SCTP
|
||||
struct sctp_event_subscribe event;
|
||||
if (col->protocol == DTLS_OVER_SCTP) {
|
||||
/* SCTP case */
|
||||
type = SOCK_STREAM;
|
||||
protocol = IPPROTO_SCTP;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
/* UDP case */
|
||||
type = SOCK_DGRAM;
|
||||
protocol = 0;
|
||||
}
|
||||
if((s = socket(PF_INET, type, protocol)) < 0 ) {
|
||||
msg(MSG_FATAL, "error opening socket, %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (col->protocol == DTLS_OVER_UDP) {
|
||||
if(enable_pmtu_discovery(s)) {
|
||||
close(s);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// set non-blocking
|
||||
int flags;
|
||||
flags = fcntl(s, F_GETFL);
|
||||
flags |= O_NONBLOCK;
|
||||
if(fcntl(s, F_SETFL, flags) == -1) {
|
||||
msg(MSG_FATAL, "could not set socket non-blocking");
|
||||
close(s);
|
||||
return -1;
|
||||
}
|
||||
#ifdef SUPPORT_DTLS_OVER_SCTP
|
||||
/* enable the reception of SCTP_SNDRCV information on a per
|
||||
* message basis. */
|
||||
memset(&event, 0, sizeof(event));
|
||||
event.sctp_data_io_event = 1;
|
||||
if (setsockopt(s, IPPROTO_SCTP, SCTP_EVENTS, &event, sizeof(event)) != 0) {
|
||||
msg(MSG_ERROR, "SCTP: setsockopt() failed to enable sctp_data_io_event, %s", strerror(errno));
|
||||
close(s);
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
/* returns 0 on success and -1 on failure */
|
||||
static int setup_dtls_connection(ipfix_exporter *exporter, ipfix_receiving_collector *col, ipfix_dtls_connection *con) {
|
||||
BIO *bio;
|
||||
/* Resources allocated in this function. Those need to be freed in case of failure:
|
||||
* - socket
|
||||
* - SSL object
|
||||
* - BIO
|
||||
*/
|
||||
|
||||
#ifdef DEBUG
|
||||
if (con->socket!=-1) {
|
||||
msg(MSG_FATAL,"socket != -1");
|
||||
close(con->socket);
|
||||
con->socket = -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Create socket
|
||||
create_dtls_socket() also activates PMTU Discovery. */
|
||||
if ((con->socket = create_dtls_socket(col)) < 0) {
|
||||
return -1;
|
||||
}
|
||||
DPRINTF("Created socket %d",con->socket);
|
||||
|
||||
/* ensure an SSL_CTX object is set up */
|
||||
if (ensure_exporter_set_up_for_dtls(exporter)) {
|
||||
close(con->socket);con->socket = -1;
|
||||
return -1;
|
||||
}
|
||||
/* create SSL object */
|
||||
if ( ! (con->ssl = SSL_new(exporter->ssl_ctx))) {
|
||||
msg(MSG_FATAL, "Failed to create SSL object.");
|
||||
msg_openssl_errors();
|
||||
close(con->socket);con->socket = -1;
|
||||
return -1;
|
||||
}
|
||||
/* Set verification parameters and cipherlist */
|
||||
if (!col->peer_fqdn) {
|
||||
SSL_set_cipher_list(con->ssl,"ALL"); // This includes anonymous ciphers
|
||||
DPRINTF("We are NOT going to verify the certificates of the collectors b/c "
|
||||
"the peerFqdn option is NOT set.");
|
||||
} else {
|
||||
if ( ! ((exporter->ca_file || exporter->ca_path) &&
|
||||
exporter->certificate_chain_file) ) {
|
||||
msg(MSG_ERROR,"Cannot verify certificates of collectors because prerequisites not met. "
|
||||
"Prerequisites are: 1. CApath or CAfile or both set, "
|
||||
"2. We have a certificate including the private key");
|
||||
SSL_free(con->ssl);con->ssl = NULL;
|
||||
close(con->socket);con->socket = -1;
|
||||
return -1;
|
||||
} else {
|
||||
SSL_set_cipher_list(con->ssl,"DEFAULT");
|
||||
SSL_set_verify(con->ssl,SSL_VERIFY_PEER |
|
||||
SSL_VERIFY_FAIL_IF_NO_PEER_CERT,0);
|
||||
DPRINTF("We are going to request certificates from the collectors "
|
||||
"and we are going to verify these b/c "
|
||||
"the peerFqdn option is set");
|
||||
}
|
||||
}
|
||||
/* create input/output abstraction for SSL object */
|
||||
#ifdef SUPPORT_DTLS_OVER_SCTP
|
||||
if (col->protocol == DTLS_OVER_SCTP) {
|
||||
bio = BIO_new_dgram_sctp(con->socket,BIO_NOCLOSE);
|
||||
BIO_dgram_sctp_notification_cb(bio, &handle_sctp_event,con);
|
||||
}
|
||||
else /* DTLS_OVER_UDP */
|
||||
#endif
|
||||
bio = BIO_new_dgram(con->socket,BIO_NOCLOSE);
|
||||
|
||||
if ( ! bio) {
|
||||
msg(MSG_FATAL,"Failed to create datagram BIO.");
|
||||
msg_openssl_errors();
|
||||
SSL_free(con->ssl);con->ssl = NULL;
|
||||
close(con->socket);con->socket = -1;
|
||||
return -1;
|
||||
}
|
||||
#ifdef SUPPORT_DTLS_OVER_SCTP
|
||||
if (col->protocol != DTLS_OVER_SCTP)
|
||||
#endif
|
||||
(void)BIO_ctrl(bio,BIO_CTRL_DGRAM_MTU_DISCOVER,0,0);
|
||||
(void)BIO_ctrl_set_connected(bio,1,&col->addr); /* TODO: Explain, why are we doing this? */
|
||||
SSL_set_bio(con->ssl,bio,bio);
|
||||
// connect (non-blocking, i.e. handshake is initiated, not terminated)
|
||||
if((connect(con->socket, (struct sockaddr*)&col->addr, sizeof(col->addr) ) == -1) && (errno != EINPROGRESS)) {
|
||||
msg(MSG_FATAL, "connect failed, %s", strerror(errno));
|
||||
SSL_free(con->ssl);con->ssl = NULL;
|
||||
close(con->socket);con->socket = -1;
|
||||
return -1;
|
||||
}
|
||||
DPRINTF("Set up SSL object.");
|
||||
|
||||
con->last_reconnect_attempt_time = time(NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dtls_shutdown_and_cleanup(ipfix_dtls_connection *con) {
|
||||
int ret,error;
|
||||
if (!con->ssl) return;
|
||||
DPRINTF("Shutting down SSL connection.");
|
||||
ret = SSL_shutdown(con->ssl);
|
||||
error = SSL_get_error(con->ssl,ret);
|
||||
#ifdef DEBUG
|
||||
msg_openssl_return_code(MSG_DEBUG,"SSL_shutdown()",ret,error);
|
||||
#endif
|
||||
/* TODO: loop only if ret==-1 and error==WANT_READ or WANT_WRITE */
|
||||
int i = 0;
|
||||
while (ret != 1 && !(SSL_get_shutdown(con->ssl) & SSL_RECEIVED_SHUTDOWN)
|
||||
&& ( (ret == 0 && error == SSL_ERROR_SYSCALL) ||
|
||||
(ret == -1 && (error == SSL_ERROR_WANT_READ || error == SSL_ERROR_WANT_WRITE)))) {
|
||||
fd_set readfds;
|
||||
struct timeval timeout;
|
||||
timeout.tv_sec = 3;
|
||||
timeout.tv_usec = 0;
|
||||
FD_ZERO(&readfds);
|
||||
FD_SET(con->socket, &readfds);
|
||||
ret = select(con->socket + 1,&readfds,NULL,NULL,&timeout);
|
||||
DPRINTF("select returned: %d",ret);
|
||||
DPRINTF("Calling SSL_shutdown()");
|
||||
ret = SSL_shutdown(con->ssl);
|
||||
error = SSL_get_error(con->ssl,ret);
|
||||
msg_openssl_return_code(MSG_DEBUG,"SSL_shutdown()",ret,error);
|
||||
if (i++ == 3) {
|
||||
msg(MSG_ERROR,"Too many calls to select(). Breaking out.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Note: SSL_free() also frees associated sending and receiving BIOs */
|
||||
SSL_free(con->ssl);
|
||||
con->ssl = NULL;
|
||||
con->last_reconnect_attempt_time = 0;
|
||||
/* Close socket */
|
||||
if ( con->socket != -1) {
|
||||
DPRINTF("Closing socket");
|
||||
ret = close(con->socket);
|
||||
DPRINTF("close returned %d",ret);
|
||||
con->socket = -1;
|
||||
}
|
||||
}
|
||||
|
||||
static void dtls_fail_connection(ipfix_dtls_connection *con) {
|
||||
DPRINTF("Failing DTLS connection.");
|
||||
dtls_shutdown_and_cleanup(con);
|
||||
}
|
||||
|
||||
#endif /* SUPPORT_DTLS */
|
||||
|
||||
/*!
|
||||
* \brief Should be called on a regular basis to push forward DTLS connection setup procedures.
|
||||
*
|
||||
* ipfixlolib depends on the user to call this function regularly
|
||||
* if there is an ongoing connection setup procedure. This is
|
||||
* necessary because<ul>
|
||||
* <li>ipfixlolib is <em>not</em> allowed to block the calling thread,</li>
|
||||
* <li>ipfixlolib does not run in a dedicated thread and</li>
|
||||
* <li>a DTLS connection setup (i.e. handshake) may take an</li>
|
||||
* extended period of time.
|
||||
* </ul>
|
||||
*
|
||||
* \param exporter pointer to previously initialized exporter struct
|
||||
* \return 1 this function should be called again after a short period of time
|
||||
* \return 0 otherwise
|
||||
* \sa ipfix_add_collector()
|
||||
* \brief Should be called on a regular basis.
|
||||
*/
|
||||
int ipfix_beat(ipfix_exporter *exporter) {
|
||||
int ret = 0;
|
||||
#ifdef SUPPORT_DTLS
|
||||
int i;
|
||||
for (i = 0; i < exporter->collector_max_num; i++) {
|
||||
ipfix_receiving_collector *col = &exporter->collector_arr[i];
|
||||
// is the collector a valid target?
|
||||
if (col->state != C_UNUSED) {
|
||||
if (col->protocol == DTLS_OVER_UDP ||
|
||||
col->protocol == DTLS_OVER_SCTP) {
|
||||
if (dtls_manage_connection(exporter,col))
|
||||
ret = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ipfix_dtls_advance_connections(exporter);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -755,7 +181,7 @@ static int init_send_udp_socket(struct sockaddr_storage serv_addr,
|
|||
return s;
|
||||
}
|
||||
|
||||
static int enable_pmtu_discovery(int s) {
|
||||
int enable_pmtu_discovery(int s) {
|
||||
#ifdef IP_MTU_DISCOVER
|
||||
// Linux
|
||||
const int optval = IP_PMTUDISC_DO;
|
||||
|
@ -971,11 +397,7 @@ int ipfix_init_exporter(export_protocol_version export_protocol, uint32_t observ
|
|||
|
||||
tmp->collector_max_num = 0;
|
||||
#ifdef SUPPORT_DTLS
|
||||
tmp->ssl_ctx = NULL;
|
||||
tmp->certificate_chain_file = NULL;
|
||||
tmp->private_key_file = NULL;
|
||||
tmp->ca_file = NULL;
|
||||
tmp->ca_path = NULL;
|
||||
ipfix_init_dtls_certificate(&tmp->certificate);
|
||||
#endif
|
||||
|
||||
// initialize the sendbuffers
|
||||
|
@ -1077,11 +499,7 @@ int ipfix_deinit_exporter(ipfix_exporter **exporter_p) {
|
|||
ipfix_deinit_collector_array(&(exporter->collector_arr));
|
||||
|
||||
#ifdef SUPPORT_DTLS
|
||||
deinit_openssl_ctx(exporter);
|
||||
free( (void *) exporter->certificate_chain_file);
|
||||
free( (void *) exporter->private_key_file);
|
||||
free( (void *) exporter->ca_file);
|
||||
free( (void *) exporter->ca_path);
|
||||
ipfix_clear_dtls_certificate(&exporter->certificate);
|
||||
#endif
|
||||
|
||||
// free own memory
|
||||
|
@ -1091,7 +509,7 @@ int ipfix_deinit_exporter(ipfix_exporter **exporter_p) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void update_exporter_max_message_size(ipfix_exporter *exporter) {
|
||||
void update_exporter_max_message_size(ipfix_exporter *exporter) {
|
||||
ipfix_receiving_collector *col;
|
||||
int i;
|
||||
uint16_t max_message_size;
|
||||
|
@ -1142,7 +560,7 @@ static void update_exporter_max_message_size(ipfix_exporter *exporter) {
|
|||
* Calls update_exporter_max_message_size()
|
||||
* Calls remove_collector() if an error occurs.
|
||||
*/
|
||||
static int update_collector_mtu(ipfix_exporter *exporter,
|
||||
int update_collector_mtu(ipfix_exporter *exporter,
|
||||
ipfix_receiving_collector *col) {
|
||||
if (col->protocol == UDP && col->mtu_mode == IPFIX_MTU_DISCOVER) {
|
||||
int mtu = get_mtu(col->data_socket);
|
||||
|
@ -1158,13 +576,13 @@ static int update_collector_mtu(ipfix_exporter *exporter,
|
|||
int mtu = -1;
|
||||
int mtu_ssl;
|
||||
int mtu_bio;
|
||||
if (col->dtls_main.ssl) {
|
||||
mtu_ssl = col->dtls_main.ssl->d1->mtu;
|
||||
if (col->dtls_connection.dtls_main.ssl) {
|
||||
mtu_ssl = col->dtls_connection.dtls_main.ssl->d1->mtu;
|
||||
DPRINTF("MTU got from SSL object: %d",mtu_ssl);
|
||||
if (mtu_ssl > 0) {
|
||||
mtu = mtu_ssl;
|
||||
}
|
||||
mtu_bio = BIO_ctrl(SSL_get_wbio(col->dtls_main.ssl),BIO_CTRL_DGRAM_QUERY_MTU,0,0);
|
||||
mtu_bio = BIO_ctrl(SSL_get_wbio(col->dtls_connection.dtls_main.ssl),BIO_CTRL_DGRAM_QUERY_MTU,0,0);
|
||||
DPRINTF("MTU got from BIO object: %d",mtu_bio);
|
||||
if (mtu_bio > 0 && (mtu == -1 || mtu_bio < mtu)) mtu = mtu_bio;
|
||||
}
|
||||
|
@ -1225,7 +643,8 @@ static int add_collector_rawdir(ipfix_receiving_collector *collector, const char
|
|||
return 0;
|
||||
}
|
||||
#endif
|
||||
static void set_mtu_config(ipfix_receiving_collector *col,
|
||||
|
||||
void set_mtu_config(ipfix_receiving_collector *col,
|
||||
ipfix_aux_config_udp *aux_config_udp) {
|
||||
if (aux_config_udp && aux_config_udp->mtu>0) {
|
||||
col->mtu_mode = IPFIX_MTU_FIXED;
|
||||
|
@ -1236,68 +655,6 @@ static void set_mtu_config(ipfix_receiving_collector *col,
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef SUPPORT_DTLS
|
||||
static int add_collector_dtls(
|
||||
ipfix_exporter *exporter,
|
||||
ipfix_receiving_collector *col,
|
||||
void *aux_config) {
|
||||
|
||||
col->dtls_replacement.socket = -1;
|
||||
col->dtls_replacement.ssl = NULL;
|
||||
|
||||
col->dtls_max_connection_lifetime = 0;
|
||||
col->dtls_connect_timeout = 30;
|
||||
|
||||
// we need aux_config for setting up a DTLS collector
|
||||
if (!aux_config) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ipfix_aux_config_dtls *aux_config_dtls;
|
||||
if (col->protocol == DTLS_OVER_SCTP) {
|
||||
aux_config_dtls = &((ipfix_aux_config_dtls_over_sctp*)aux_config)->dtls;
|
||||
} else if (col->protocol == DTLS_OVER_UDP) {
|
||||
aux_config_dtls = &((ipfix_aux_config_dtls_over_udp*)aux_config)->dtls;
|
||||
col->dtls_max_connection_lifetime = ((ipfix_aux_config_dtls_over_udp*)aux_config)->max_connection_lifetime;
|
||||
ipfix_aux_config_udp *aux_config_udp;
|
||||
aux_config_udp = &((ipfix_aux_config_dtls_over_udp*)aux_config)->udp;
|
||||
/* Sets col->mtu_mode and col->mtu */
|
||||
set_mtu_config(col,aux_config_udp);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
if (aux_config_dtls->peer_fqdn)
|
||||
col->peer_fqdn = strdup(aux_config_dtls->peer_fqdn);
|
||||
|
||||
if (setup_dtls_connection(exporter,col,&col->dtls_main)) {
|
||||
/* failure */
|
||||
free( (void *) col->peer_fqdn);
|
||||
col->peer_fqdn = NULL;
|
||||
return -1;
|
||||
}
|
||||
col->state = C_NEW; /* By setting the state to C_NEW we are
|
||||
basically allocation the slot. */
|
||||
col->last_reconnect_attempt_time = time(NULL);
|
||||
/* col->state must *not* be C_UNUSED when we call
|
||||
update_collector_mtu(). That's why we call this function
|
||||
after setting state to C_NEW. */
|
||||
if (update_collector_mtu(exporter, col)) {
|
||||
/* update_collector_mtu calls remove_collector
|
||||
in case of failure which in turn sets
|
||||
col->state to C_UNUSED and frees col->peer_fqdn. */
|
||||
return -1;
|
||||
}
|
||||
/* We have to call update_exporter_max_message_size()
|
||||
* because update_collector_mtu *only* calls
|
||||
* update_exporter_max_message_size() if the MTU
|
||||
* mode is IPFIX_MTU_DISCOVER. */
|
||||
update_exporter_max_message_size(exporter);
|
||||
/* Initiate connection setup */
|
||||
dtls_manage_connection(exporter, col);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Remaining protocols are
|
||||
SCTP, UDP and TCP at the moment
|
||||
*/
|
||||
|
@ -1497,11 +854,11 @@ static void remove_collector(ipfix_receiving_collector *collector) {
|
|||
#ifdef SUPPORT_DTLS
|
||||
/* Shutdown DTLS connection */
|
||||
if (collector->protocol == DTLS_OVER_UDP || collector->protocol == DTLS_OVER_SCTP) {
|
||||
dtls_shutdown_and_cleanup(&collector->dtls_main);
|
||||
dtls_shutdown_and_cleanup(&collector->dtls_replacement);
|
||||
free( (void *) collector->peer_fqdn);
|
||||
dtls_shutdown_and_cleanup(&collector->dtls_connection.dtls_main);
|
||||
dtls_shutdown_and_cleanup(&collector->dtls_connection.dtls_replacement);
|
||||
free( (void *) collector->dtls_connection.peer_fqdn);
|
||||
}
|
||||
collector->peer_fqdn = NULL;
|
||||
collector->dtls_connection.peer_fqdn = NULL;
|
||||
#endif
|
||||
#ifdef IPFIXLOLIB_RAWDIR_SUPPORT
|
||||
if (collector->protocol != RAWDIR) {
|
||||
|
@ -1668,7 +1025,7 @@ int ipfix_remove_template(ipfix_exporter *exporter, uint16_t template_id) {
|
|||
*
|
||||
* Note: the first HEADER_USED_IOVEC_COUNT iovec struct are reserved for the header! These will be overwritten!
|
||||
*/
|
||||
static void ipfix_update_header(ipfix_exporter *p_exporter, ipfix_receiving_collector *collector, ipfix_sendbuffer *sendbuf)
|
||||
void ipfix_update_header(ipfix_exporter *p_exporter, ipfix_receiving_collector *collector, ipfix_sendbuffer *sendbuf)
|
||||
{
|
||||
struct timeval now;
|
||||
if (gettimeofday(&now, NULL)) {
|
||||
|
@ -1892,11 +1249,11 @@ static int ipfix_init_collector_array(ipfix_receiving_collector **col, int col_c
|
|||
c->packet_directory_path = NULL;
|
||||
#endif
|
||||
#ifdef SUPPORT_DTLS
|
||||
c->dtls_main.socket = c->dtls_replacement.socket = -1;
|
||||
c->dtls_main.ssl = c->dtls_replacement.ssl = NULL;
|
||||
c->dtls_main.last_reconnect_attempt_time =
|
||||
c->dtls_replacement.last_reconnect_attempt_time = 0;
|
||||
c->peer_fqdn = NULL;
|
||||
c->dtls_connection.dtls_main.socket = c->dtls_connection.dtls_replacement.socket = -1;
|
||||
c->dtls_connection.dtls_main.ssl = c->dtls_connection.dtls_replacement.ssl = NULL;
|
||||
c->dtls_connection.dtls_main.last_reconnect_attempt_time =
|
||||
c->dtls_connection.dtls_replacement.last_reconnect_attempt_time = 0;
|
||||
c->dtls_connection.peer_fqdn = NULL;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
@ -3539,171 +2896,12 @@ int ipfix_set_sctp_reconnect_timer(ipfix_exporter *exporter, uint32_t timer) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Setup X.509 certificate used for authentication
|
||||
*
|
||||
* Set the the names of the files in which the X.509 certificate and the
|
||||
* matching private key can be found. If private_key_file is NULL the
|
||||
* certificate chain file will be searched for the private key. See OpenSSL
|
||||
* man pages for more details
|
||||
*
|
||||
* The certificate can only be set once per exporter. Calling this function
|
||||
* twice for the same exporter is an error. This function must not be called
|
||||
* after the first DTLS connection has been set up.
|
||||
*
|
||||
* \param exporter pointer to previously initialized exporter struct
|
||||
* \param certificate_chain_file name of file in which the certificate chain is
|
||||
* stored in PEM format
|
||||
* \param private_key_file name of file in which the private key is stored in
|
||||
* PEM format. If NULL, the private key is read from certificate_chain_file.
|
||||
* \return 0 success
|
||||
* \return -1 failure
|
||||
* \sa ipfix_set_ca_locations()
|
||||
*/
|
||||
int ipfix_set_dtls_certificate(ipfix_exporter *exporter,
|
||||
const char *certificate_chain_file, const char *private_key_file) {
|
||||
#ifdef SUPPORT_DTLS
|
||||
if (exporter->ssl_ctx) {
|
||||
msg(MSG_ERROR, "Too late to set certificate. SSL context already created.");
|
||||
return -1;
|
||||
}
|
||||
if (exporter->certificate_chain_file) {
|
||||
msg(MSG_ERROR, "Certificate can not be reset.");
|
||||
return -1;
|
||||
}
|
||||
if ( ! certificate_chain_file) {
|
||||
msg(MSG_ERROR, "ipfix_set_dtls_certificate called with bad parameters.");
|
||||
return -1;
|
||||
}
|
||||
exporter->certificate_chain_file = strdup(certificate_chain_file);
|
||||
if (private_key_file) {
|
||||
exporter->private_key_file = strdup(private_key_file);
|
||||
}
|
||||
return 0;
|
||||
#else /* SUPPORT_DTLS */
|
||||
msg(MSG_FATAL, "Library compiled without DTLS support.");
|
||||
return -1;
|
||||
#endif /* SUPPORT_DTLS */
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Set the locations of the CA certificates.
|
||||
*
|
||||
* See <tt>SSL_CTX_load_verify_locations(3)</tt> for more details. These
|
||||
* locations can only be set once per exporter. Calling this function twice for
|
||||
* the same exporter is an error.
|
||||
*
|
||||
* \param exporter pointer to previously initialized exporter struct
|
||||
* \param ca_file All CA certificates found in this <em>file</em> are trusted
|
||||
* for verification of the peer's certificate. This file has to be in PEM format.
|
||||
* \param ca_path All CA certificates found in this <em>directory</em> are
|
||||
* trusted for verification of the peer's certificate. See
|
||||
* <tt>SSL_CTX_load_verify_locations(3)</tt> for details about how the files
|
||||
* have to be named. All files need to be in PEM format.
|
||||
* \return 0 success
|
||||
* \return -1 failure
|
||||
* \sa ipfix_set_dtls_certificate()
|
||||
*/
|
||||
int ipfix_set_ca_locations(ipfix_exporter *exporter, const char *ca_file, const char *ca_path) {
|
||||
#ifdef SUPPORT_DTLS
|
||||
if (exporter->ssl_ctx) {
|
||||
msg(MSG_ERROR, "Too late to set CA locations. SSL context already created.");
|
||||
return -1;
|
||||
}
|
||||
if (exporter->ca_file || exporter->ca_path) {
|
||||
msg(MSG_ERROR, "CA locations can not be reset.");
|
||||
return -1;
|
||||
}
|
||||
if (ca_file) exporter->ca_file = strdup(ca_file);
|
||||
if (ca_path) exporter->ca_path = strdup(ca_path);
|
||||
return 0;
|
||||
#else /* SUPPORT_DTLS */
|
||||
msg(MSG_FATAL, "Library compiled without DTLS support.");
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* check if the enterprise bit in an ID is set */
|
||||
static int ipfix_enterprise_flag_set(uint16_t id)
|
||||
{
|
||||
return bit_set(id, IPFIX_ENTERPRISE_BIT);
|
||||
}
|
||||
|
||||
#ifdef SUPPORT_DTLS_OVER_SCTP
|
||||
static void
|
||||
handle_sctp_event(BIO *bio, void *context, void *buf)
|
||||
{
|
||||
#if 0
|
||||
ipfix_dtls_connection *con = (ipfix_dtls_connection *) context;
|
||||
#endif
|
||||
struct sctp_assoc_change *sac;
|
||||
struct sctp_send_failed *ssf;
|
||||
struct sctp_paddr_change *spc;
|
||||
struct sctp_remote_error *sre;
|
||||
#ifdef SUPPORT_DTLS_OVER_SCTP
|
||||
struct sctp_sender_dry_event *ssde;
|
||||
#endif
|
||||
union sctp_notification *snp;
|
||||
char addrbuf[INET6_ADDRSTRLEN];
|
||||
const char *ap;
|
||||
struct sockaddr_in *sin;
|
||||
struct sockaddr_in6 *sin6;
|
||||
|
||||
snp = buf;
|
||||
|
||||
switch (snp->sn_header.sn_type) {
|
||||
case SCTP_ASSOC_CHANGE:
|
||||
sac = &snp->sn_assoc_change;
|
||||
msg(MSG_DEBUG,"SCTP Event: assoc_change: state=%hu, error=%hu, instr=%hu "
|
||||
"outstr=%hu\n", sac->sac_state, sac->sac_error,
|
||||
sac->sac_inbound_streams, sac->sac_outbound_streams);
|
||||
break;
|
||||
case SCTP_SEND_FAILED:
|
||||
ssf = &snp->sn_send_failed;
|
||||
msg(MSG_DEBUG,"SCTP Event: sendfailed: len=%hu err=%d\n", ssf->ssf_length,
|
||||
ssf->ssf_error);
|
||||
break;
|
||||
|
||||
case SCTP_PEER_ADDR_CHANGE:
|
||||
spc = &snp->sn_paddr_change;
|
||||
if (spc->spc_aaddr.ss_family == AF_INET) {
|
||||
sin = (struct sockaddr_in *)&spc->spc_aaddr;
|
||||
ap = inet_ntop(AF_INET, &sin->sin_addr,
|
||||
addrbuf, INET6_ADDRSTRLEN);
|
||||
} else {
|
||||
sin6 = (struct sockaddr_in6 *)&spc->spc_aaddr;
|
||||
ap = inet_ntop(AF_INET6, &sin6->sin6_addr,
|
||||
addrbuf, INET6_ADDRSTRLEN);
|
||||
}
|
||||
msg(MSG_DEBUG,"SCTP Event: intf_change: %s state=%d, error=%d\n", ap,
|
||||
spc->spc_state, spc->spc_error);
|
||||
break;
|
||||
case SCTP_REMOTE_ERROR:
|
||||
sre = &snp->sn_remote_error;
|
||||
msg(MSG_DEBUG,"SCTP Event: remote_error: err=%hu len=%hu\n",
|
||||
ntohs(sre->sre_error), ntohs(sre->sre_length));
|
||||
break;
|
||||
case SCTP_SHUTDOWN_EVENT:
|
||||
msg(MSG_DEBUG,"SCTP Event: shutdown event\n");
|
||||
break;
|
||||
#ifdef SUPPORT_DTLS_OVER_SCTP
|
||||
case SCTP_SENDER_DRY_EVENT:
|
||||
ssde = &snp->sn_sender_dry_event;
|
||||
msg(MSG_DEBUG,"SCTP Event: sender dry event\n");
|
||||
break;
|
||||
case SCTP_AUTHENTICATION_EVENT:
|
||||
msg(MSG_DEBUG,"SCTP Event: authentication event\n");
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
msg(MSG_DEBUG,"SCTP Event: unknown type: %hu\n", snp->sn_header.sn_type);
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -118,10 +118,7 @@ See ipfixlolib.h for details on how to use this library.
|
|||
#include <netinet/sctp.h>
|
||||
#endif
|
||||
#ifdef SUPPORT_DTLS
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/bio.h>
|
||||
#include "common/openssl/OpenSSL.h"
|
||||
#include "ipfixlolib_dtls.h"
|
||||
#endif
|
||||
|
||||
#ifdef LINUX_IF_H_FOUND
|
||||
|
@ -230,10 +227,6 @@ typedef enum export_protocol_version {
|
|||
#define IPFIX_MTU_MODE_DEFAULT IPFIX_MTU_FIXED
|
||||
#endif
|
||||
|
||||
#ifdef SUPPORT_DTLS
|
||||
#define IPFIX_DTLS_MAX_RECORD_LENGTH 16384
|
||||
#endif
|
||||
|
||||
/* Struct containing an ipfix-header */
|
||||
/* Header Format (see RFC 5101)
|
||||
|
||||
|
@ -408,44 +401,6 @@ enum ipfix_transport_protocol {
|
|||
ZMQ /* Requires libczmq */
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint16_t mtu; /*!< Maximum transmission unit (MTU).
|
||||
If set to 0, PMTU discovery will be used.
|
||||
(Only available on the Linux platform)
|
||||
Applies to UDP and DTLS over UDP only. */
|
||||
} ipfix_aux_config_udp;
|
||||
|
||||
typedef struct {
|
||||
const char *peer_fqdn; /*!< The Fully Qualified Domain Name (FQDN) of the
|
||||
peer. If set, the peer i.e. the Collector
|
||||
<em>must</em> present a certificate of which
|
||||
either the subject's Common Name (CN) or one of
|
||||
the subject alternative names matches the FQDN.
|
||||
There is no support for wildcard matching. For the
|
||||
certificate verification to work, the user must
|
||||
also call <tt>ipfix_set_ca_locations()</tt> in
|
||||
advance to specify the locations of the root CA
|
||||
certificates.
|
||||
|
||||
If set to NULL, anonymous cipher suites will be
|
||||
added to the list of permissible cipher suites.
|
||||
The identity of the peer will not be verified
|
||||
then.*/
|
||||
} ipfix_aux_config_dtls;
|
||||
|
||||
typedef struct {
|
||||
ipfix_aux_config_dtls dtls; /*!< DTLS specific configuration */
|
||||
ipfix_aux_config_udp udp; /*!< UDP specific configuration */
|
||||
unsigned max_connection_lifetime; /*!< Time in seconds after which the DTLS
|
||||
connection is replaced by a new one.
|
||||
This mechanism aims to overcome the
|
||||
dead peer problem.*/
|
||||
} ipfix_aux_config_dtls_over_udp;
|
||||
|
||||
typedef struct {
|
||||
ipfix_aux_config_dtls dtls; /*!< DTLS specific configuration */
|
||||
} ipfix_aux_config_dtls_over_sctp;
|
||||
|
||||
/*
|
||||
* These indicate, if a field is committed (i.e. can be used)
|
||||
* unused or unclean (i.e. data is not complete yet)
|
||||
|
@ -555,15 +510,6 @@ typedef struct {
|
|||
template sets. */
|
||||
} ipfix_sendbuffer;
|
||||
|
||||
#ifdef SUPPORT_DTLS
|
||||
typedef struct {
|
||||
int socket;
|
||||
// uint16_t mtu;
|
||||
SSL *ssl;
|
||||
time_t last_reconnect_attempt_time;
|
||||
} ipfix_dtls_connection;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* A collector receiving messages from this exporter
|
||||
*/
|
||||
|
@ -589,18 +535,7 @@ typedef struct {
|
|||
char* packet_directory_path; /*!< if protocol==RAWDIR: path to a directory to store packets in. Ignored otherwise. */
|
||||
#endif
|
||||
#ifdef SUPPORT_DTLS
|
||||
/* Time in seconds after which a DTLS connection
|
||||
* will be replaced by a new one. */
|
||||
unsigned dtls_max_connection_lifetime;
|
||||
unsigned dtls_connect_timeout;
|
||||
ipfix_dtls_connection dtls_main;
|
||||
ipfix_dtls_connection dtls_replacement;
|
||||
time_t connect_time; /* point in time when the connection setup
|
||||
succeeded. We need this to calculate the
|
||||
age of a connection. If DTLS is used,
|
||||
a connection rollover is performed when
|
||||
a connection reaches a certain age.*/
|
||||
const char *peer_fqdn;
|
||||
ipfix_collector_dtls_connection dtls_connection;
|
||||
#endif
|
||||
char vrf_name[IFNAMSIZ];
|
||||
} ipfix_receiving_collector;
|
||||
|
@ -679,11 +614,7 @@ typedef struct {
|
|||
int ipfix_lo_template_maxsize;
|
||||
ipfix_lo_template *template_arr;
|
||||
#ifdef SUPPORT_DTLS
|
||||
SSL_CTX *ssl_ctx;
|
||||
const char *certificate_chain_file;
|
||||
const char *private_key_file;
|
||||
const char *ca_file;
|
||||
const char *ca_path;
|
||||
ipfix_exporter_certificate certificate;
|
||||
#endif
|
||||
} ipfix_exporter;
|
||||
|
||||
|
@ -715,9 +646,6 @@ int ipfix_set_template_transmission_timer(ipfix_exporter *exporter, uint32_t tim
|
|||
int ipfix_set_sctp_lifetime(ipfix_exporter *exporter, uint32_t lifetime);
|
||||
int ipfix_set_sctp_reconnect_timer(ipfix_exporter *exporter, uint32_t timer);
|
||||
|
||||
int ipfix_set_dtls_certificate(ipfix_exporter *exporter, const char *certificate_chain_file, const char *private_key_file);
|
||||
int ipfix_set_ca_locations(ipfix_exporter *exporter, const char *ca_file, const char *ca_path);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef IPFIXLOLIB_CONFIG_H
|
||||
#define IPFIXLOLIB_CONFIG_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct {
|
||||
uint16_t mtu; /*!< Maximum transmission unit (MTU).
|
||||
If set to 0, PMTU discovery will be used.
|
||||
(Only available on the Linux platform)
|
||||
Applies to UDP and DTLS over UDP only. */
|
||||
} ipfix_aux_config_udp;
|
||||
|
||||
#endif
|
|
@ -0,0 +1,793 @@
|
|||
#include <fcntl.h>
|
||||
|
||||
#include "common/msg.h"
|
||||
#include "common/openssl/OpenSSL.h"
|
||||
#include "ipfixlolib_private.h"
|
||||
#include "ipfixlolib_dtls.h"
|
||||
#include "ipfixlolib_dtls_private.h"
|
||||
|
||||
#ifdef SUPPORT_SCTP
|
||||
#ifdef SUPPORT_DTLS_OVER_SCTP
|
||||
static void
|
||||
handle_sctp_event(BIO *bio, void *context, void *buf)
|
||||
{
|
||||
#if 0
|
||||
ipfix_dtls_connection *con = (ipfix_dtls_connection *) context;
|
||||
#endif
|
||||
struct sctp_assoc_change *sac;
|
||||
struct sctp_send_failed *ssf;
|
||||
struct sctp_paddr_change *spc;
|
||||
struct sctp_remote_error *sre;
|
||||
struct sctp_sender_dry_event *ssde;
|
||||
union sctp_notification *snp;
|
||||
char addrbuf[INET6_ADDRSTRLEN];
|
||||
const char *ap;
|
||||
struct sockaddr_in *sin;
|
||||
struct sockaddr_in6 *sin6;
|
||||
|
||||
snp = buf;
|
||||
|
||||
switch (snp->sn_header.sn_type) {
|
||||
case SCTP_ASSOC_CHANGE:
|
||||
sac = &snp->sn_assoc_change;
|
||||
msg(MSG_DEBUG,"SCTP Event: assoc_change: state=%hu, error=%hu, instr=%hu "
|
||||
"outstr=%hu\n", sac->sac_state, sac->sac_error,
|
||||
sac->sac_inbound_streams, sac->sac_outbound_streams);
|
||||
break;
|
||||
case SCTP_SEND_FAILED:
|
||||
ssf = &snp->sn_send_failed;
|
||||
msg(MSG_DEBUG,"SCTP Event: sendfailed: len=%hu err=%d\n", ssf->ssf_length,
|
||||
ssf->ssf_error);
|
||||
break;
|
||||
|
||||
case SCTP_PEER_ADDR_CHANGE:
|
||||
spc = &snp->sn_paddr_change;
|
||||
if (spc->spc_aaddr.ss_family == AF_INET) {
|
||||
sin = (struct sockaddr_in *)&spc->spc_aaddr;
|
||||
ap = inet_ntop(AF_INET, &sin->sin_addr,
|
||||
addrbuf, INET6_ADDRSTRLEN);
|
||||
} else {
|
||||
sin6 = (struct sockaddr_in6 *)&spc->spc_aaddr;
|
||||
ap = inet_ntop(AF_INET6, &sin6->sin6_addr,
|
||||
addrbuf, INET6_ADDRSTRLEN);
|
||||
}
|
||||
msg(MSG_DEBUG,"SCTP Event: intf_change: %s state=%d, error=%d\n", ap,
|
||||
spc->spc_state, spc->spc_error);
|
||||
break;
|
||||
case SCTP_REMOTE_ERROR:
|
||||
sre = &snp->sn_remote_error;
|
||||
msg(MSG_DEBUG,"SCTP Event: remote_error: err=%hu len=%hu\n",
|
||||
ntohs(sre->sre_error), ntohs(sre->sre_length));
|
||||
break;
|
||||
case SCTP_SHUTDOWN_EVENT:
|
||||
msg(MSG_DEBUG,"SCTP Event: shutdown event\n");
|
||||
break;
|
||||
case SCTP_SENDER_DRY_EVENT:
|
||||
ssde = &snp->sn_sender_dry_event;
|
||||
msg(MSG_DEBUG,"SCTP Event: sender dry event\n");
|
||||
break;
|
||||
case SCTP_AUTHENTICATION_EVENT:
|
||||
msg(MSG_DEBUG,"SCTP Event: authentication event\n");
|
||||
break;
|
||||
default:
|
||||
msg(MSG_DEBUG,"SCTP Event: unknown type: %hu\n", snp->sn_header.sn_type);
|
||||
break;
|
||||
};
|
||||
}
|
||||
#endif /*SUPPORT_DTLS_OVER_SCTP */
|
||||
#endif /* SUPPORT_SCTP */
|
||||
|
||||
/* A separate SSL_CTX object is created for every ipfix_exporter.
|
||||
* Returns 0 on success, -1 on error
|
||||
* */
|
||||
static int ensure_exporter_set_up_for_dtls(ipfix_exporter_certificate *c) {
|
||||
ensure_openssl_init();
|
||||
|
||||
if (c->ssl_ctx) return 0;
|
||||
|
||||
/* This SSL_CTX object will be freed in deinit_openssl_ctx() */
|
||||
if ( ! (c->ssl_ctx=SSL_CTX_new(DTLSv1_client_method())) ) {
|
||||
msg(MSG_FATAL, "Failed to create SSL context");
|
||||
msg_openssl_errors();
|
||||
return -1;
|
||||
}
|
||||
SSL_CTX_set_read_ahead(c->ssl_ctx,1);
|
||||
|
||||
if ( (c->ca_file || c->ca_path) &&
|
||||
! SSL_CTX_load_verify_locations(c->ssl_ctx,c->ca_file,c->ca_path) ) {
|
||||
msg(MSG_FATAL,"SSL_CTX_load_verify_locations() failed.");
|
||||
msg_openssl_errors();
|
||||
return -1;
|
||||
}
|
||||
/* Load our own certificate */
|
||||
if (c->certificate_chain_file) {
|
||||
if (!SSL_CTX_use_certificate_chain_file(c->ssl_ctx, c->certificate_chain_file)) {
|
||||
msg(MSG_FATAL,"Unable to load certificate chain file %s",c->certificate_chain_file);
|
||||
msg_openssl_errors();
|
||||
return -1;
|
||||
}
|
||||
if (!SSL_CTX_use_PrivateKey_file(c->ssl_ctx, c->private_key_file, SSL_FILETYPE_PEM)) {
|
||||
msg(MSG_FATAL,"Unable to load private key file %s",c->private_key_file);
|
||||
msg_openssl_errors();
|
||||
return -1;
|
||||
}
|
||||
if (!SSL_CTX_check_private_key(c->ssl_ctx)) {
|
||||
msg(MSG_FATAL,"Private key and certificate do not match");
|
||||
msg_openssl_errors();
|
||||
return -1;
|
||||
}
|
||||
DPRINTF("We successfully loaded our certificate.");
|
||||
} else {
|
||||
DPRINTF("We do NOT have a certificate.");
|
||||
}
|
||||
/* We leave the certificate_authorities list of the Certificate Request
|
||||
* empty. See RFC 4346 7.4.4. Certificate request. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
void deinit_openssl_ctx(ipfix_exporter_certificate *c) {
|
||||
if (c->ssl_ctx) { SSL_CTX_free(c->ssl_ctx); }
|
||||
c->ssl_ctx = NULL;
|
||||
}
|
||||
|
||||
static int create_dtls_socket(ipfix_receiving_collector *col) {
|
||||
int s, type, protocol;
|
||||
#ifdef SUPPORT_DTLS_OVER_SCTP
|
||||
struct sctp_event_subscribe event;
|
||||
if (col->protocol == DTLS_OVER_SCTP) {
|
||||
/* SCTP case */
|
||||
type = SOCK_STREAM;
|
||||
protocol = IPPROTO_SCTP;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
/* UDP case */
|
||||
type = SOCK_DGRAM;
|
||||
protocol = 0;
|
||||
}
|
||||
if((s = socket(PF_INET, type, protocol)) < 0 ) {
|
||||
msg(MSG_FATAL, "error opening socket, %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (col->protocol == DTLS_OVER_UDP) {
|
||||
if(enable_pmtu_discovery(s)) {
|
||||
close(s);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// set non-blocking
|
||||
int flags;
|
||||
flags = fcntl(s, F_GETFL);
|
||||
flags |= O_NONBLOCK;
|
||||
if(fcntl(s, F_SETFL, flags) == -1) {
|
||||
msg(MSG_FATAL, "could not set socket non-blocking");
|
||||
close(s);
|
||||
return -1;
|
||||
}
|
||||
#ifdef SUPPORT_DTLS_OVER_SCTP
|
||||
/* enable the reception of SCTP_SNDRCV information on a per
|
||||
* message basis. */
|
||||
memset(&event, 0, sizeof(event));
|
||||
event.sctp_data_io_event = 1;
|
||||
if (setsockopt(s, IPPROTO_SCTP, SCTP_EVENTS, &event, sizeof(event)) != 0) {
|
||||
msg(MSG_ERROR, "SCTP: setsockopt() failed to enable sctp_data_io_event, %s", strerror(errno));
|
||||
close(s);
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
/* returns 0 on success and -1 on failure */
|
||||
int setup_dtls_connection(ipfix_exporter *exporter, ipfix_receiving_collector *col, ipfix_dtls_connection *con) {
|
||||
BIO *bio;
|
||||
/* Resources allocated in this function. Those need to be freed in case of failure:
|
||||
* - socket
|
||||
* - SSL object
|
||||
* - BIO
|
||||
*/
|
||||
|
||||
#ifdef DEBUG
|
||||
if (con->socket!=-1) {
|
||||
msg(MSG_FATAL,"socket != -1");
|
||||
close(con->socket);
|
||||
con->socket = -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Create socket
|
||||
create_dtls_socket() also activates PMTU Discovery. */
|
||||
if ((con->socket = create_dtls_socket(col)) < 0) {
|
||||
return -1;
|
||||
}
|
||||
DPRINTF("Created socket %d",con->socket);
|
||||
|
||||
/* ensure an SSL_CTX object is set up */
|
||||
if (ensure_exporter_set_up_for_dtls(&exporter->certificate)) {
|
||||
close(con->socket);con->socket = -1;
|
||||
return -1;
|
||||
}
|
||||
/* create SSL object */
|
||||
if ( ! (con->ssl = SSL_new(exporter->certificate.ssl_ctx))) {
|
||||
msg(MSG_FATAL, "Failed to create SSL object.");
|
||||
msg_openssl_errors();
|
||||
close(con->socket);con->socket = -1;
|
||||
return -1;
|
||||
}
|
||||
/* Set verification parameters and cipherlist */
|
||||
if (!col->dtls_connection.peer_fqdn) {
|
||||
SSL_set_cipher_list(con->ssl,"ALL"); // This includes anonymous ciphers
|
||||
DPRINTF("We are NOT going to verify the certificates of the collectors b/c "
|
||||
"the peerFqdn option is NOT set.");
|
||||
} else {
|
||||
if ( ! ((exporter->certificate.ca_file || exporter->certificate.ca_path) &&
|
||||
exporter->certificate.certificate_chain_file) ) {
|
||||
msg(MSG_ERROR,"Cannot verify certificates of collectors because prerequisites not met. "
|
||||
"Prerequisites are: 1. CApath or CAfile or both set, "
|
||||
"2. We have a certificate including the private key");
|
||||
SSL_free(con->ssl);con->ssl = NULL;
|
||||
close(con->socket);con->socket = -1;
|
||||
return -1;
|
||||
} else {
|
||||
SSL_set_cipher_list(con->ssl,"DEFAULT");
|
||||
SSL_set_verify(con->ssl,SSL_VERIFY_PEER |
|
||||
SSL_VERIFY_FAIL_IF_NO_PEER_CERT,0);
|
||||
DPRINTF("We are going to request certificates from the collectors "
|
||||
"and we are going to verify these b/c "
|
||||
"the peerFqdn option is set");
|
||||
}
|
||||
}
|
||||
/* create input/output abstraction for SSL object */
|
||||
#ifdef SUPPORT_DTLS_OVER_SCTP
|
||||
if (col->protocol == DTLS_OVER_SCTP) {
|
||||
bio = BIO_new_dgram_sctp(con->socket,BIO_NOCLOSE);
|
||||
BIO_dgram_sctp_notification_cb(bio, &handle_sctp_event,con);
|
||||
}
|
||||
else /* DTLS_OVER_UDP */
|
||||
#endif
|
||||
bio = BIO_new_dgram(con->socket,BIO_NOCLOSE);
|
||||
|
||||
if ( ! bio) {
|
||||
msg(MSG_FATAL,"Failed to create datagram BIO.");
|
||||
msg_openssl_errors();
|
||||
SSL_free(con->ssl);con->ssl = NULL;
|
||||
close(con->socket);con->socket = -1;
|
||||
return -1;
|
||||
}
|
||||
#ifdef SUPPORT_DTLS_OVER_SCTP
|
||||
if (col->protocol != DTLS_OVER_SCTP)
|
||||
#endif
|
||||
(void)BIO_ctrl(bio,BIO_CTRL_DGRAM_MTU_DISCOVER,0,0);
|
||||
(void)BIO_ctrl_set_connected(bio,1,&col->addr); /* TODO: Explain, why are we doing this? */
|
||||
SSL_set_bio(con->ssl,bio,bio);
|
||||
// connect (non-blocking, i.e. handshake is initiated, not terminated)
|
||||
if((connect(con->socket, (struct sockaddr*)&col->addr, sizeof(col->addr) ) == -1) && (errno != EINPROGRESS)) {
|
||||
msg(MSG_FATAL, "connect failed, %s", strerror(errno));
|
||||
SSL_free(con->ssl);con->ssl = NULL;
|
||||
close(con->socket);con->socket = -1;
|
||||
return -1;
|
||||
}
|
||||
DPRINTF("Set up SSL object.");
|
||||
|
||||
con->last_reconnect_attempt_time = time(NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dtls_verify_peer_cb(void *context, const char* dnsname) {
|
||||
const ipfix_receiving_collector *col =
|
||||
(const ipfix_receiving_collector *) context;
|
||||
return strcasecmp(col->dtls_connection.peer_fqdn,dnsname) ? 0 : 1;
|
||||
}
|
||||
|
||||
static void dtls_fail_connection(ipfix_dtls_connection *con) {
|
||||
DPRINTF("Failing DTLS connection.");
|
||||
dtls_shutdown_and_cleanup(con);
|
||||
}
|
||||
|
||||
/* Return values:
|
||||
* -1 failure
|
||||
* 0 no failure but not yet connected. You need to call dtls_connect again
|
||||
* next time
|
||||
* 1 yes. now we're connected. Don't call dtls_connect again. */
|
||||
static int dtls_connect(ipfix_receiving_collector *col, ipfix_dtls_connection *con) {
|
||||
int ret, error;
|
||||
|
||||
ret = SSL_connect(con->ssl);
|
||||
error = SSL_get_error(con->ssl,ret);
|
||||
if (error == SSL_ERROR_NONE) {
|
||||
msg_openssl_return_code(MSG_DEBUG,"SSL_connect()",ret,error);
|
||||
msg(MSG_INFO, "Successfully (re)connected to %s-over-DTLS collector.", col->protocol == DTLS_OVER_SCTP ? "SCTP" : "UDP");
|
||||
msg(MSG_INFO,"TLS Cipher: %s",SSL_get_cipher_name(con->ssl));
|
||||
DPRINTF("DTLS handshake succeeded. We are now connected.");
|
||||
if (col->dtls_connection.peer_fqdn) { /* We need to verify the identity of our peer */
|
||||
if (verify_ssl_peer(con->ssl,&dtls_verify_peer_cb,col)) {
|
||||
DPRINTF("Peer authentication successful.");
|
||||
} else {
|
||||
msg(MSG_ERROR,"Peer authentication failed. Shutting down connection.");
|
||||
dtls_fail_connection(con);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
} else if (error == SSL_ERROR_WANT_READ) {
|
||||
msg_openssl_return_code(MSG_DEBUG,"SSL_connect()",ret,error);
|
||||
return 0;
|
||||
} else {
|
||||
msg_openssl_return_code(MSG_ERROR,"SSL_connect()",ret,error);
|
||||
dtls_fail_connection(con);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int dtls_get_replacement_connection_ready(
|
||||
ipfix_exporter *exporter,
|
||||
ipfix_receiving_collector *col) {
|
||||
int ret;
|
||||
if (!col->dtls_connection.dtls_replacement.ssl) {
|
||||
/* No SSL object has been created yet. Let's open a socket and
|
||||
* setup a new SSL object. */
|
||||
DPRINTF("Setting up replacement connection.");
|
||||
if (setup_dtls_connection(exporter,col,&col->dtls_connection.dtls_replacement)) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
ret = dtls_connect(col,&col->dtls_connection.dtls_replacement);
|
||||
if (ret == 1) {
|
||||
DPRINTF("Replacement connection setup successful.");
|
||||
return 1; /* SUCCESS */
|
||||
}
|
||||
if (ret == 0) {
|
||||
if (col->dtls_connection.dtls_connect_timeout &&
|
||||
(time(NULL) - col->dtls_connection.dtls_replacement.last_reconnect_attempt_time >
|
||||
col->dtls_connection.dtls_connect_timeout)) {
|
||||
msg(MSG_ERROR,"DTLS replacement connection setup taking too long.");
|
||||
dtls_fail_connection(&col->dtls_connection.dtls_replacement);
|
||||
} else {
|
||||
DPRINTF("Replacement connection setup still ongoing.");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void dtls_swap_connections(ipfix_dtls_connection *a, ipfix_dtls_connection *b) {
|
||||
ipfix_dtls_connection tmp;
|
||||
memcpy(&tmp,a,sizeof(tmp));
|
||||
memcpy(a,b,sizeof(*b));
|
||||
memcpy(b,&tmp,sizeof(*b));
|
||||
}
|
||||
|
||||
void dtls_shutdown_and_cleanup(ipfix_dtls_connection *con) {
|
||||
int ret,error;
|
||||
if (!con->ssl) return;
|
||||
DPRINTF("Shutting down SSL connection.");
|
||||
ret = SSL_shutdown(con->ssl);
|
||||
error = SSL_get_error(con->ssl,ret);
|
||||
#ifdef DEBUG
|
||||
msg_openssl_return_code(MSG_DEBUG,"SSL_shutdown()",ret,error);
|
||||
#endif
|
||||
/* TODO: loop only if ret==-1 and error==WANT_READ or WANT_WRITE */
|
||||
int i = 0;
|
||||
while (ret != 1 && !(SSL_get_shutdown(con->ssl) & SSL_RECEIVED_SHUTDOWN)
|
||||
&& ( (ret == 0 && error == SSL_ERROR_SYSCALL) ||
|
||||
(ret == -1 && (error == SSL_ERROR_WANT_READ || error == SSL_ERROR_WANT_WRITE)))) {
|
||||
fd_set readfds;
|
||||
struct timeval timeout;
|
||||
timeout.tv_sec = 3;
|
||||
timeout.tv_usec = 0;
|
||||
FD_ZERO(&readfds);
|
||||
FD_SET(con->socket, &readfds);
|
||||
ret = select(con->socket + 1,&readfds,NULL,NULL,&timeout);
|
||||
DPRINTF("select returned: %d",ret);
|
||||
DPRINTF("Calling SSL_shutdown()");
|
||||
ret = SSL_shutdown(con->ssl);
|
||||
error = SSL_get_error(con->ssl,ret);
|
||||
msg_openssl_return_code(MSG_DEBUG,"SSL_shutdown()",ret,error);
|
||||
if (i++ == 3) {
|
||||
msg(MSG_ERROR,"Too many calls to select(). Breaking out.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Note: SSL_free() also frees associated sending and receiving BIOs */
|
||||
SSL_free(con->ssl);
|
||||
con->ssl = NULL;
|
||||
con->last_reconnect_attempt_time = 0;
|
||||
/* Close socket */
|
||||
if ( con->socket != -1) {
|
||||
DPRINTF("Closing socket");
|
||||
ret = close(con->socket);
|
||||
DPRINTF("close returned %d",ret);
|
||||
con->socket = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* This function pushes the connection setup forward if we are in state C_NEW.
|
||||
* It also sets up the replacement connection if it's time to replace the
|
||||
* current connection with a new one.
|
||||
* If we are in state C_DISCONNECT then it sets up a new main connection.
|
||||
*
|
||||
* Return values:
|
||||
* returns 0 on success
|
||||
* returns 1 if a connection setup procedure is still ongoing. Then, this
|
||||
* function should be called again after a short period of time.
|
||||
* returns -1 on error.
|
||||
* Note that success does not mean that we are connected because the
|
||||
* connection setup might still be ongoing. Therefore you still have
|
||||
* to check the state member of ipfix_receiving_collector to determine
|
||||
* if we are connected. */
|
||||
int dtls_manage_connection(ipfix_exporter *exporter, ipfix_receiving_collector *col) {
|
||||
int ret, rc = 0;
|
||||
|
||||
if (col->state == C_CONNECTED) {
|
||||
if( col->protocol == DTLS_OVER_SCTP ||
|
||||
col->dtls_connection.dtls_max_connection_lifetime == 0 ||
|
||||
time(NULL) - col->dtls_connection.connect_time <
|
||||
col->dtls_connection.dtls_max_connection_lifetime)
|
||||
return 0;
|
||||
/* Alright, the connection is already very old and needs to be
|
||||
* replaced. Let's get the replacement / backup connection ready. */
|
||||
ret = dtls_get_replacement_connection_ready(exporter, col);
|
||||
rc = 1;
|
||||
if (ret == 1) { /* Connection setup completed */
|
||||
rc = 0;
|
||||
DPRINTF("Swapping connections.");
|
||||
dtls_swap_connections(&col->dtls_connection.dtls_main, &col->dtls_connection.dtls_replacement);
|
||||
DPRINTF("Shutting down old DTLS connection.");
|
||||
dtls_shutdown_and_cleanup(&col->dtls_connection.dtls_replacement);
|
||||
col->dtls_connection.connect_time = time(NULL);
|
||||
ret = dtls_send_templates(exporter, col);
|
||||
/* We do not need to check the return value because
|
||||
* dtls_send_templates already shuts down the DTLS connection
|
||||
* in case of failure.
|
||||
* It also sets state to C_DISCONNECTED in this case. */
|
||||
}
|
||||
/* We ignore all other return values of dtls_get_replacement_connection_ready() */
|
||||
}
|
||||
if (col->state == C_NEW) {
|
||||
rc = 1;
|
||||
/* Connection setup is still ongoing. Let's push it forward. */
|
||||
ret = dtls_connect(col,&col->dtls_connection.dtls_main);
|
||||
if (ret == 1) {
|
||||
/* SUCCESS */
|
||||
col->state = C_CONNECTED;
|
||||
col->dtls_connection.connect_time = time(NULL);
|
||||
if (update_collector_mtu(exporter, col)) {
|
||||
/* update_collector_mtu calls remove_collector
|
||||
in case of failure which in turn sets
|
||||
col->state to C_UNUSED. */
|
||||
return -1;
|
||||
}
|
||||
ret = dtls_send_templates(exporter, col);
|
||||
/* dtls_send (inside dtls_send_templates) calls
|
||||
* dtls_fail_connection() and sets col->state
|
||||
* in case of failure. */
|
||||
if (ret >= 0) return 0; else return -1;
|
||||
} else if (ret == -1) {
|
||||
/* Failure
|
||||
* dtls_connect() cleaned up SSL object already.
|
||||
* Remember that the socket is now part of the DTLS connection
|
||||
* abstraction. dtls_connect() closed the socket as well. */
|
||||
col->state = C_DISCONNECTED;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (col->state == C_DISCONNECTED) {
|
||||
/* Wait dtls_connect_timeout seconds before
|
||||
re-initiating DTLS connection */
|
||||
if (time(NULL) < col->last_reconnect_attempt_time +
|
||||
col->dtls_connection.dtls_connect_timeout)
|
||||
return 1;
|
||||
rc = 1;
|
||||
if (setup_dtls_connection(exporter,col,&col->dtls_connection.dtls_main)) {
|
||||
/* col->state stays in C_DISCONNECTED in this case
|
||||
* setup_dtls_connection() does not alter it. */
|
||||
return -1;
|
||||
}
|
||||
col->state = C_NEW;
|
||||
col->last_reconnect_attempt_time = time(NULL);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Return values:
|
||||
* n>0: sent n bytes
|
||||
* 0: Could not send due to OpenSSL returning SSL_ERROR_WANT_READ
|
||||
* -1: Recoverable error
|
||||
* -2: Bad Error. DTLS connection has been shutdown already.
|
||||
* You should set state to C_DISCONNECT
|
||||
* -3: Message too long. You should query the current MTU
|
||||
* estimate in this case and update the corresponding
|
||||
* property of the collector.
|
||||
*/
|
||||
|
||||
static int dtls_send_helper( ipfix_dtls_connection *con,
|
||||
const struct iovec *iov, int iovcnt) {
|
||||
int len, error, i;
|
||||
char sendbuf[IPFIX_MAX_PACKETSIZE];
|
||||
char *sendbufcur = sendbuf;
|
||||
int maxsendbuflen = sizeof(sendbuf);
|
||||
/* Collect data form iovecs */
|
||||
for (i=0;i<iovcnt;i++) {
|
||||
if (sendbufcur + iov[i].iov_len > sendbuf + maxsendbuflen) {
|
||||
msg(MSG_FATAL, "sendbuffer for dtls_send too small.");
|
||||
return -1;
|
||||
}
|
||||
memcpy(sendbufcur,iov[i].iov_base,iov[i].iov_len);
|
||||
sendbufcur+=iov[i].iov_len;
|
||||
}
|
||||
|
||||
len = SSL_write(con->ssl, sendbuf, sendbufcur - sendbuf);
|
||||
error = SSL_get_error(con->ssl,len);
|
||||
#ifdef DEBUG
|
||||
char buf[32];
|
||||
snprintf(buf,sizeof(buf),"SSL_write(%d bytes of data)",(int) (sendbufcur - sendbuf) );
|
||||
msg_openssl_return_code(MSG_DEBUG,buf,len,error);
|
||||
#endif
|
||||
switch (error) {
|
||||
case SSL_ERROR_NONE:
|
||||
if (len!=sendbufcur - sendbuf) {
|
||||
msg(MSG_FATAL, "len!=sendbuflen when calling SSL_write()");
|
||||
return -1;
|
||||
}
|
||||
return sendbufcur - sendbuf; /* SUCCESS */
|
||||
case SSL_ERROR_WANT_READ:
|
||||
return 0;
|
||||
case SSL_ERROR_SYSCALL:
|
||||
if (errno == EMSGSIZE) {
|
||||
return -3;
|
||||
}
|
||||
/* fall through */
|
||||
default:
|
||||
msg_openssl_return_code(MSG_ERROR,"SSL_write()",len,error);
|
||||
dtls_fail_connection(con);
|
||||
return -2;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return values:
|
||||
* -1 error
|
||||
* 0 could not write because OpenSSL returned SSL_ERROR_WANT_READ
|
||||
* n>0 number of bytes written
|
||||
*/
|
||||
int dtls_send(ipfix_exporter *exporter, ipfix_receiving_collector *col,
|
||||
const struct iovec *iov, int iovcnt) {
|
||||
|
||||
int len;
|
||||
|
||||
/* DTLS negotiation has to be finished before we can send data.
|
||||
* Drop out of this function if we are not yet connected. */
|
||||
if (col->state != C_CONNECTED) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
len = dtls_send_helper(&col->dtls_connection.dtls_main, iov, iovcnt);
|
||||
if (len == -2) {
|
||||
col->state = C_DISCONNECTED;
|
||||
return -1;
|
||||
} else if (len == -3) {
|
||||
update_collector_mtu(exporter,col);
|
||||
return -1;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
int dtls_send_templates(ipfix_exporter *exporter, ipfix_receiving_collector *col) {
|
||||
if (exporter->template_sendbuffer->committed_data_length == 0)
|
||||
return 0;
|
||||
|
||||
ipfix_update_header(exporter, col,
|
||||
exporter->template_sendbuffer);
|
||||
col->messages_sent++;
|
||||
DPRINTF("Sending templates over DTLS.");
|
||||
return dtls_send(exporter,col,
|
||||
exporter->template_sendbuffer->entries,
|
||||
exporter->template_sendbuffer->current);
|
||||
}
|
||||
|
||||
int add_collector_dtls(
|
||||
ipfix_exporter *exporter,
|
||||
ipfix_receiving_collector *col,
|
||||
void *aux_config) {
|
||||
|
||||
col->dtls_connection.dtls_replacement.socket = -1;
|
||||
col->dtls_connection.dtls_replacement.ssl = NULL;
|
||||
|
||||
col->dtls_connection.dtls_max_connection_lifetime = 0;
|
||||
col->dtls_connection.dtls_connect_timeout = 30;
|
||||
|
||||
// we need aux_config for setting up a DTLS collector
|
||||
if (!aux_config) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ipfix_aux_config_dtls *aux_config_dtls;
|
||||
if (col->protocol == DTLS_OVER_SCTP) {
|
||||
aux_config_dtls = &((ipfix_aux_config_dtls_over_sctp*)aux_config)->dtls;
|
||||
} else if (col->protocol == DTLS_OVER_UDP) {
|
||||
aux_config_dtls = &((ipfix_aux_config_dtls_over_udp*)aux_config)->dtls;
|
||||
col->dtls_connection.dtls_max_connection_lifetime =
|
||||
((ipfix_aux_config_dtls_over_udp*)aux_config)->max_connection_lifetime;
|
||||
ipfix_aux_config_udp *aux_config_udp;
|
||||
aux_config_udp = &((ipfix_aux_config_dtls_over_udp*)aux_config)->udp;
|
||||
/* Sets col->mtu_mode and col->mtu */
|
||||
set_mtu_config(col,aux_config_udp);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
if (aux_config_dtls->peer_fqdn)
|
||||
col->dtls_connection.peer_fqdn = strdup(aux_config_dtls->peer_fqdn);
|
||||
|
||||
if (setup_dtls_connection(exporter,col,&col->dtls_connection.dtls_main)) {
|
||||
/* failure */
|
||||
free( (void *) col->dtls_connection.peer_fqdn);
|
||||
col->dtls_connection.peer_fqdn = NULL;
|
||||
return -1;
|
||||
}
|
||||
col->state = C_NEW; /* By setting the state to C_NEW we are
|
||||
basically allocation the slot. */
|
||||
col->last_reconnect_attempt_time = time(NULL);
|
||||
/* col->state must *not* be C_UNUSED when we call
|
||||
update_collector_mtu(). That's why we call this function
|
||||
after setting state to C_NEW. */
|
||||
if (update_collector_mtu(exporter, col)) {
|
||||
/* update_collector_mtu calls remove_collector
|
||||
in case of failure which in turn sets
|
||||
col->state to C_UNUSED and frees col->peer_fqdn. */
|
||||
return -1;
|
||||
}
|
||||
/* We have to call update_exporter_max_message_size()
|
||||
* because update_collector_mtu *only* calls
|
||||
* update_exporter_max_message_size() if the MTU
|
||||
* mode is IPFIX_MTU_DISCOVER. */
|
||||
update_exporter_max_message_size(exporter);
|
||||
/* Initiate connection setup */
|
||||
dtls_manage_connection(exporter, col);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ipfix_init_dtls_certificate(ipfix_exporter_certificate *certificate) {
|
||||
certificate->ssl_ctx = NULL;
|
||||
certificate->certificate_chain_file = NULL;
|
||||
certificate->private_key_file = NULL;
|
||||
certificate->ca_file = NULL;
|
||||
certificate->ca_path = NULL;
|
||||
}
|
||||
|
||||
|
||||
void ipfix_clear_dtls_certificate(ipfix_exporter_certificate *certificate) {
|
||||
deinit_openssl_ctx(certificate);
|
||||
free( (void *) certificate->certificate_chain_file);
|
||||
free( (void *) certificate->private_key_file);
|
||||
free( (void *) certificate->ca_file);
|
||||
free( (void *) certificate->ca_path);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Setup X.509 certificate used for authentication
|
||||
*
|
||||
* Set the the names of the files in which the X.509 certificate and the
|
||||
* matching private key can be found. If private_key_file is NULL the
|
||||
* certificate chain file will be searched for the private key. See OpenSSL
|
||||
* man pages for more details
|
||||
*
|
||||
* The certificate can only be set once per exporter. Calling this function
|
||||
* twice for the same exporter is an error. This function must not be called
|
||||
* after the first DTLS connection has been set up.
|
||||
*
|
||||
* \param exporter pointer to previously initialized exporter struct
|
||||
* \param certificate_chain_file name of file in which the certificate chain is
|
||||
* stored in PEM format
|
||||
* \param private_key_file name of file in which the private key is stored in
|
||||
* PEM format. If NULL, the private key is read from certificate_chain_file.
|
||||
* \return 0 success
|
||||
* \return -1 failure
|
||||
* \sa ipfix_set_ca_locations()
|
||||
*/
|
||||
int ipfix_set_dtls_certificate(ipfix_exporter_certificate *certificate,
|
||||
const char *certificate_chain_file, const char *private_key_file) {
|
||||
if (certificate->ssl_ctx) {
|
||||
msg(MSG_ERROR, "Too late to set certificate. SSL context already created.");
|
||||
return -1;
|
||||
}
|
||||
if (certificate->certificate_chain_file) {
|
||||
msg(MSG_ERROR, "Certificate can not be reset.");
|
||||
return -1;
|
||||
}
|
||||
if ( ! certificate_chain_file) {
|
||||
msg(MSG_ERROR, "ipfix_set_dtls_certificate called with bad parameters.");
|
||||
return -1;
|
||||
}
|
||||
certificate->certificate_chain_file = strdup(certificate_chain_file);
|
||||
if (private_key_file) {
|
||||
certificate->private_key_file = strdup(private_key_file);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Set the locations of the CA certificates.
|
||||
*
|
||||
* See <tt>SSL_CTX_load_verify_locations(3)</tt> for more details. These
|
||||
* locations can only be set once per exporter. Calling this function twice for
|
||||
* the same exporter is an error.
|
||||
*
|
||||
* \param exporter pointer to previously initialized exporter struct
|
||||
* \param ca_file All CA certificates found in this <em>file</em> are trusted
|
||||
* for verification of the peer's certificate. This file has to be in PEM format.
|
||||
* \param ca_path All CA certificates found in this <em>directory</em> are
|
||||
* trusted for verification of the peer's certificate. See
|
||||
* <tt>SSL_CTX_load_verify_locations(3)</tt> for details about how the files
|
||||
* have to be named. All files need to be in PEM format.
|
||||
* \return 0 success
|
||||
* \return -1 failure
|
||||
* \sa ipfix_set_dtls_certificate()
|
||||
*/
|
||||
int ipfix_set_ca_locations(ipfix_exporter_certificate *certificate, const char *ca_file, const char *ca_path) {
|
||||
if (certificate->ssl_ctx) {
|
||||
msg(MSG_ERROR, "Too late to set CA locations. SSL context already created.");
|
||||
return -1;
|
||||
}
|
||||
if (certificate->ca_file || certificate->ca_path) {
|
||||
msg(MSG_ERROR, "CA locations can not be reset.");
|
||||
return -1;
|
||||
}
|
||||
if (ca_file) certificate->ca_file = strdup(ca_file);
|
||||
if (ca_path) certificate->ca_path = strdup(ca_path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Should be called on a regular basis to push forward DTLS connection setup procedures.
|
||||
*
|
||||
* ipfixlolib depends on the user to call this function regularly
|
||||
* if there is an ongoing connection setup procedure. This is
|
||||
* necessary because<ul>
|
||||
* <li>ipfixlolib is <em>not</em> allowed to block the calling thread,</li>
|
||||
* <li>ipfixlolib does not run in a dedicated thread and</li>
|
||||
* <li>a DTLS connection setup (i.e. handshake) may take an</li>
|
||||
* extended period of time.
|
||||
* </ul>
|
||||
*
|
||||
* \param exporter pointer to previously initialized exporter struct
|
||||
* \return 1 this function should be called again after a short period of time
|
||||
* \return 0 otherwise
|
||||
* \sa ipfix_add_collector()
|
||||
*/
|
||||
int ipfix_dtls_advance_connections(ipfix_exporter *exporter) {
|
||||
int ret = 0;
|
||||
for (int i = 0; i < exporter->collector_max_num; i++) {
|
||||
ipfix_receiving_collector *col = &exporter->collector_arr[i];
|
||||
// is the collector a valid target?
|
||||
if (col->state != C_UNUSED) {
|
||||
if (col->protocol == DTLS_OVER_UDP ||
|
||||
col->protocol == DTLS_OVER_SCTP) {
|
||||
if (dtls_manage_connection(exporter,col))
|
||||
ret = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef SUPPORT_DTLS_OVER_SCTP
|
||||
/* Return values:
|
||||
* -1 error
|
||||
* 0 could not write because OpenSSL returned SSL_ERROR_WANT_READ
|
||||
* n>0 number of bytes written
|
||||
*/
|
||||
static int dtls_over_sctp_send(
|
||||
ipfix_exporter *exporter,
|
||||
ipfix_receiving_collector *col,
|
||||
const struct iovec *iov, int iovcnt, uint32_t pr_value) {
|
||||
|
||||
struct sctp_sndrcvinfo sinfo;
|
||||
memset(&sinfo, 0, sizeof(struct sctp_sndrcvinfo));
|
||||
sinfo.sinfo_timetolive = pr_value; // pr_value; FIXME
|
||||
BIO_ctrl(SSL_get_wbio(col->dtls_main.ssl), BIO_CTRL_DGRAM_SCTP_SET_SNDINFO, sizeof(struct sctp_sndrcvinfo), &sinfo);
|
||||
return dtls_send(exporter,col,iov,iovcnt);
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,87 @@
|
|||
#ifndef IPFIXLOLIB_DTLS_H
|
||||
#define IPFIXLOLIB_DTLS_H
|
||||
|
||||
#include <time.h>
|
||||
#include <openssl/ssl.h>
|
||||
|
||||
#include "ipfixlolib_config.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define IPFIX_DTLS_MAX_RECORD_LENGTH 16384
|
||||
|
||||
typedef struct {
|
||||
int socket;
|
||||
// uint16_t mtu;
|
||||
SSL *ssl;
|
||||
time_t last_reconnect_attempt_time;
|
||||
} ipfix_dtls_connection;
|
||||
|
||||
typedef struct {
|
||||
/* Time in seconds after which a DTLS connection
|
||||
* will be replaced by a new one. */
|
||||
unsigned dtls_max_connection_lifetime;
|
||||
unsigned dtls_connect_timeout;
|
||||
ipfix_dtls_connection dtls_main;
|
||||
ipfix_dtls_connection dtls_replacement;
|
||||
time_t connect_time; /* point in time when the connection setup
|
||||
succeeded. We need this to calculate the
|
||||
age of a connection. If DTLS is used,
|
||||
a connection rollover is performed when
|
||||
a connection reaches a certain age.*/
|
||||
const char *peer_fqdn;
|
||||
} ipfix_collector_dtls_connection;
|
||||
|
||||
typedef struct {
|
||||
const char *peer_fqdn; /*!< The Fully Qualified Domain Name (FQDN) of the
|
||||
peer. If set, the peer i.e. the Collector
|
||||
<em>must</em> present a certificate of which
|
||||
either the subject's Common Name (CN) or one of
|
||||
the subject alternative names matches the FQDN.
|
||||
There is no support for wildcard matching. For the
|
||||
certificate verification to work, the user must
|
||||
also call <tt>ipfix_set_ca_locations()</tt> in
|
||||
advance to specify the locations of the root CA
|
||||
certificates.
|
||||
|
||||
If set to NULL, anonymous cipher suites will be
|
||||
added to the list of permissible cipher suites.
|
||||
The identity of the peer will not be verified
|
||||
then.*/
|
||||
} ipfix_aux_config_dtls;
|
||||
|
||||
typedef struct {
|
||||
ipfix_aux_config_dtls dtls; /*!< DTLS specific configuration */
|
||||
ipfix_aux_config_udp udp; /*!< UDP specific configuration */
|
||||
unsigned max_connection_lifetime; /*!< Time in seconds after which the DTLS
|
||||
connection is replaced by a new one.
|
||||
This mechanism aims to overcome the
|
||||
dead peer problem.*/
|
||||
} ipfix_aux_config_dtls_over_udp;
|
||||
|
||||
typedef struct {
|
||||
ipfix_aux_config_dtls dtls; /*!< DTLS specific configuration */
|
||||
} ipfix_aux_config_dtls_over_sctp;
|
||||
|
||||
typedef struct {
|
||||
SSL_CTX *ssl_ctx;
|
||||
const char *certificate_chain_file;
|
||||
const char *private_key_file;
|
||||
const char *ca_file;
|
||||
const char *ca_path;
|
||||
} ipfix_exporter_certificate;
|
||||
|
||||
void ipfix_init_dtls_certificate(ipfix_exporter_certificate *certificate);
|
||||
void ipfix_clear_dtls_certificate(ipfix_exporter_certificate *certificate);
|
||||
int ipfix_set_dtls_certificate(ipfix_exporter_certificate *certificate,
|
||||
const char *certificate_chain_file, const char *private_key_file);
|
||||
int ipfix_set_ca_locations(ipfix_exporter_certificate *certificate,
|
||||
const char *ca_file, const char *ca_path);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,16 @@
|
|||
#ifndef IPFIXLOLIB_DTLS_PRIVATE_H
|
||||
#define IPFIXLOLIB_DTLS_PRIVATE_H
|
||||
|
||||
#include "ipfixlolib.h"
|
||||
#include "ipfixlolib_dtls.h"
|
||||
|
||||
int dtls_manage_connection(ipfix_exporter *exporter, ipfix_receiving_collector *col);
|
||||
void deinit_openssl_ctx(ipfix_exporter_certificate *certificate);
|
||||
int setup_dtls_connection(ipfix_exporter *exporter, ipfix_receiving_collector *col, ipfix_dtls_connection *con);
|
||||
void dtls_shutdown_and_cleanup(ipfix_dtls_connection *con);
|
||||
int dtls_send_templates(ipfix_exporter *exporter, ipfix_receiving_collector *col);
|
||||
int dtls_send(ipfix_exporter *exporter, ipfix_receiving_collector *col, const struct iovec *iov, int iovcnt);
|
||||
int add_collector_dtls(ipfix_exporter *exporter, ipfix_receiving_collector *col, void *aux_config);
|
||||
int ipfix_dtls_advance_connections(ipfix_exporter *exporter);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef IPFIXLOLIB_PRIVATE_H
|
||||
#define IPFIXLOLIB_PRIVATE_H
|
||||
|
||||
#include "ipfixlolib.h"
|
||||
#include "ipfixlolib_config.h"
|
||||
|
||||
int enable_pmtu_discovery(int s);
|
||||
int update_collector_mtu(ipfix_exporter *exporter, ipfix_receiving_collector *col);
|
||||
void ipfix_update_header(ipfix_exporter *p_exporter, ipfix_receiving_collector *collector, ipfix_sendbuffer *sendbuf);
|
||||
void set_mtu_config(ipfix_receiving_collector *col, ipfix_aux_config_udp *aux_config_udp);
|
||||
void update_exporter_max_message_size(ipfix_exporter *exporter);
|
||||
|
||||
#endif
|
|
@ -18,6 +18,7 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include "common/ipfixlolib/ipfixlolib_dtls.h"
|
||||
#include "IpfixExporterCfg.h"
|
||||
|
||||
IpfixExporterCfg::IpfixExporterCfg(XMLElement* elem)
|
||||
|
|
|
@ -62,7 +62,8 @@ class IpfixReceiverDtlsUdpIpV4 : public IpfixReceiver, Sensor {
|
|||
IpfixReceiverDtlsUdpIpV4(int port, const std::string ipAddr = "",
|
||||
const std::string &certificateChainFile = "", const std::string &privateKeyFile = "",
|
||||
const std::string &caFile = "", const std::string &caPath = "",
|
||||
const std::set<string> &peerFqdns = std::set<string>(), const uint32_t buffer = 0);
|
||||
const std::set<string> &peerFqdns = std::set<string>(), const uint32_t buffer = 0,
|
||||
unsigned int moduleId = 0);
|
||||
virtual ~IpfixReceiverDtlsUdpIpV4();
|
||||
|
||||
virtual void run();
|
||||
|
|
|
@ -21,6 +21,9 @@
|
|||
|
||||
#include "IpfixSender.hpp"
|
||||
#include "common/ipfixlolib/ipfix.h"
|
||||
#ifdef SUPPORT_DTLS
|
||||
#include "common/ipfixlolib/ipfixlolib_dtls.h"
|
||||
#endif
|
||||
|
||||
#include "common/msg.h"
|
||||
#include "common/Time.h"
|
||||
|
@ -94,14 +97,18 @@ IpfixSender::IpfixSender(uint32_t observationDomainId, uint32_t maxRecordRate,
|
|||
private_key_file = privateKeyFile.c_str();
|
||||
/* Private key will be searched for in the certificate chain file if
|
||||
* no private key file is set */
|
||||
#ifdef SUPPORT_DTLS
|
||||
if (certificate_chain_file || private_key_file)
|
||||
ipfix_set_dtls_certificate(ipfixExporter,
|
||||
ipfix_set_dtls_certificate(&ipfixExporter->certificate,
|
||||
certificate_chain_file, private_key_file);
|
||||
#endif
|
||||
|
||||
if ( ! caFile.empty() ) ca_file = caFile.c_str();
|
||||
if ( ! caPath.empty() ) ca_path = caPath.c_str();
|
||||
#ifdef SUPPORT_DTLS
|
||||
if (ca_file || ca_path)
|
||||
ipfix_set_ca_locations(ipfixExporter, ca_file, ca_path);
|
||||
ipfix_set_ca_locations(&ipfixExporter->certificate, ca_file, ca_path);
|
||||
#endif
|
||||
|
||||
msg(MSG_DEBUG, "IpfixSender: running");
|
||||
return;
|
||||
|
@ -770,13 +777,11 @@ void IpfixSender::onSendRecordsTimeout(void) {
|
|||
sendRecords(Always);
|
||||
}
|
||||
}
|
||||
void IpfixSender::onBeatTimeout(void) {
|
||||
ipfix_beat(ipfixExporter);
|
||||
}
|
||||
|
||||
void IpfixSender::onTimeout(void* dataPtr)
|
||||
{
|
||||
onSendRecordsTimeout();
|
||||
onBeatTimeout();
|
||||
ipfix_beat(ipfixExporter);
|
||||
registerBeatTimeout();
|
||||
}
|
||||
|
||||
|
|
|
@ -95,7 +95,6 @@ private:
|
|||
void removeRecordReferences();
|
||||
void registerTimeout();
|
||||
void onSendRecordsTimeout(void);
|
||||
void onBeatTimeout(void);
|
||||
void registerBeatTimeout();
|
||||
|
||||
TemplateInfo::TemplateId getUnusedTemplateId();
|
||||
|
|
|
@ -54,3 +54,5 @@ IF (JOURNALD_FOUND)
|
|||
TARGET_LINK_LIBRARIES(example_code ${JOURNALD_LIBRARIES})
|
||||
TARGET_LINK_LIBRARIES(example_code_2 ${JOURNALD_LIBRARIES})
|
||||
ENDIF (JOURNALD_FOUND)
|
||||
|
||||
ADD_TEST(example_2 example_code_2)
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
#include <stdio.h>
|
||||
#include "common/ipfixlolib/ipfixlolib.h"
|
||||
#include "common/ipfixlolib/ipfixlolib_config.h"
|
||||
#include "common/ipfixlolib/encoding.h" // because we need htonll()
|
||||
#include "common/msg.h"
|
||||
|
||||
|
|
|
@ -15,6 +15,10 @@
|
|||
#include <stdio.h>
|
||||
#include <getopt.h>
|
||||
#include "common/ipfixlolib/ipfixlolib.h"
|
||||
#ifdef SUPPORT_DTLS
|
||||
#include "common/ipfixlolib/ipfixlolib_dtls.h"
|
||||
#endif
|
||||
#include "common/ipfixlolib/ipfixlolib_config.h"
|
||||
#include "common/ipfixlolib/encoding.h"
|
||||
#include "common/msg.h"
|
||||
|
||||
|
@ -66,7 +70,6 @@ int parse_command_line_arguments(int argc, char **argv);
|
|||
int add_collector(ipfix_exporter *exporter);
|
||||
int get_sample_data1(meter_data *mdat);
|
||||
int get_sample_data2(meter_data *mdat);
|
||||
int set_config_on_exporter(ipfix_exporter *exporter);
|
||||
void print_usage(char *argv0);
|
||||
|
||||
|
||||
|
@ -80,6 +83,7 @@ int add_collector(ipfix_exporter *exporter) {
|
|||
ipfix_aux_config_udp acu = {
|
||||
.mtu = myconfig.mtu
|
||||
};
|
||||
#ifdef SUPPORT_DTLS
|
||||
ipfix_aux_config_dtls_over_udp acdou = {
|
||||
.udp = { .mtu = myconfig.mtu},
|
||||
.dtls = { .peer_fqdn = myconfig.peer_fqdn}
|
||||
|
@ -87,12 +91,15 @@ int add_collector(ipfix_exporter *exporter) {
|
|||
ipfix_aux_config_dtls_over_sctp acdos = {
|
||||
.dtls = { .peer_fqdn = myconfig.peer_fqdn}
|
||||
};
|
||||
#endif
|
||||
if (myconfig.transport_protocol == UDP) {
|
||||
aux_config = &acu;
|
||||
#ifdef SUPPORT_DTLS
|
||||
} else if (myconfig.transport_protocol == DTLS_OVER_UDP) {
|
||||
aux_config = &acdou;
|
||||
} else if (myconfig.transport_protocol == DTLS_OVER_SCTP) {
|
||||
aux_config = &acdos;
|
||||
#endif
|
||||
}
|
||||
/* The type of the 5th parameter to ipfix_add_collector() depends
|
||||
* on the transport protocol that has been chose. */
|
||||
|
@ -150,6 +157,16 @@ int get_sample_data2(meter_data *mdat) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int set_config_on_exporter(ipfix_exporter *exporter) {
|
||||
int ret = 0;
|
||||
#ifdef SUPPORT_DTLS
|
||||
ret = ipfix_set_ca_locations(&exporter->certificate, myconfig.ca_file, myconfig.ca_path);
|
||||
if (ret) return ret;
|
||||
if (myconfig.certificate_chain_file)
|
||||
ret = ipfix_set_dtls_certificate(&exporter->certificate, myconfig.certificate_chain_file, myconfig.private_key_file);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
|
@ -504,7 +521,7 @@ out:
|
|||
|
||||
/* if you no longer need the exporter: free resources */
|
||||
ret=ipfix_remove_collector(my_exporter, myconfig.coll_ip4_addr, myconfig.coll_port);
|
||||
ipfix_deinit_exporter(my_exporter);
|
||||
ipfix_deinit_exporter(&my_exporter);
|
||||
|
||||
printf("Done.\n");
|
||||
exit(EXIT_SUCCESS);
|
||||
|
@ -605,15 +622,6 @@ int parse_command_line_arguments(int argc, char **argv) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int set_config_on_exporter(ipfix_exporter *exporter) {
|
||||
int ret = 0;
|
||||
ret = ipfix_set_ca_locations(exporter, myconfig.ca_file, myconfig.ca_path);
|
||||
if (ret) return ret;
|
||||
if (myconfig.certificate_chain_file)
|
||||
ret = ipfix_set_dtls_certificate(exporter, myconfig.certificate_chain_file, myconfig.private_key_file);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void print_usage(char *argv0) {
|
||||
printf(
|
||||
"Usage: %s [OPTIONS]\n"
|
||||
|
|
|
@ -20,6 +20,10 @@
|
|||
|
||||
#include <stdio.h>
|
||||
#include "common/ipfixlolib/ipfixlolib.h"
|
||||
#include "common/ipfixlolib/ipfixlolib_config.h"
|
||||
#ifdef SUPPORT_DTLS
|
||||
#include "common/ipfixlolib/ipfixlolib_dtls.h"
|
||||
#endif
|
||||
#include "common/ipfixlolib/ipfix.h"
|
||||
#include "common/msg.h"
|
||||
|
||||
|
@ -73,6 +77,7 @@ void add_udp_collector(ipfix_exporter *exporter) {
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef SUPPORT_DTLS
|
||||
void add_dtls_over_udp_collector(ipfix_exporter *exporter) {
|
||||
ipfix_aux_config_dtls_over_udp acu = {
|
||||
.udp = { .mtu = MTU},
|
||||
|
@ -85,6 +90,7 @@ void add_dtls_over_udp_collector(ipfix_exporter *exporter) {
|
|||
while(ipfix_beat(exporter))
|
||||
usleep(10000);
|
||||
}
|
||||
#endif
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int i;
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include "common/ipfixlolib/ipfixlolib.h"
|
||||
#include "common/ipfixlolib/ipfixlolib_config.h"
|
||||
#include "common/msg.h"
|
||||
|
||||
#define MY_SOURCE_ID 70538
|
||||
|
|
Loading…
Reference in New Issue