From: Nikos Mavrogiannopoulos <nmav at redhat.com> This allows keeping track of clients which have their DTLS stream come from a different IP location than their CSTP stream. Relates #61 --- src/main.c | 3 ++ src/main.h | 7 ++++- src/proc-search.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++----- src/proc-search.h | 1 + 4 files changed, 90 insertions(+), 8 deletions(-) V1->V2: - Use a dedicated hash table for the DTLS IPs. Storing (dtls_hash, proc) pairs in the db_ip table stops working once the htable code has to call double_table() and then invoke the rehash() callback to recompute the keys. - Write a proc_table_update_dtls_ip() function that keeps the DTLS connection info up-to-date, in case the IP changes. - Clean up length comparisons in local_ip_cmp(). - Removed "total++" (not sure where this is used?) This IP -> proc_st mapping still relies heavily on guesswork, and it will break if there are multiple clients behind the same IP. Is there a standard way to notify a DTLS client that it is out of sync and needs to redo the handshake? Merely dropping the unrecognized packets leads to a poor user experience as it causes the VPN session to enter a "living dead" state. diff --git a/src/main.c b/src/main.c index cbfdd52..6376af2 100644 --- a/src/main.c +++ b/src/main.c @@ -880,6 +880,9 @@ int sfd = -1; if (match_ip_only != 0) { msg.hello = 0; /* by default this is one */ + } else { + /* a new DTLS session, store the DTLS IPs into proc and add it into hash table */ + proc_table_update_dtls_ip(s, proc_to_send, &cli_addr, cli_addr_size); } msg.data.data = s->msg_buffer; diff --git a/src/main.h b/src/main.h index 1761e4f..97eb869 100644 --- a/src/main.h +++ b/src/main.h @@ -98,8 +98,12 @@ typedef struct proc_st { struct ip_lease_st *ipv6; unsigned leases_in_use; /* someone else got our IP leases */ - struct sockaddr_storage remote_addr; /* peer address */ + struct sockaddr_storage remote_addr; /* peer address (CSTP) */ socklen_t remote_addr_len; + /* It can happen that the peer's DTLS stream comes through a different + * address. Most likely that's due to interception of the initial TLS/CSTP session */ + struct sockaddr_storage dtls_remote_addr; /* peer address (DTLS) */ + socklen_t dtls_remote_addr_len; struct sockaddr_storage our_addr; /* our address */ socklen_t our_addr_len; @@ -166,6 +170,7 @@ struct script_list_st { struct proc_hash_db_st { struct htable *db_ip; + struct htable *db_dtls_ip; struct htable *db_dtls_id; struct htable *db_sid; unsigned total; diff --git a/src/proc-search.c b/src/proc-search.c index 1678165..ec6e7d0 100644 --- a/src/proc-search.c +++ b/src/proc-search.c @@ -48,6 +48,15 @@ const struct proc_st * proc = _p; SA_IN_SIZE(proc->remote_addr_len), 0); } +static size_t rehash_dtls_ip(const void* _p, void* unused) +{ +const struct proc_st * proc = _p; + + return hash_any( + SA_IN_P_GENERIC(&proc->dtls_remote_addr, proc->dtls_remote_addr_len), + SA_IN_SIZE(proc->dtls_remote_addr_len), 0); +} + static size_t rehash_dtls_id(const void* _p, void* unused) { const struct proc_st * proc = _p; @@ -65,9 +74,11 @@ const struct proc_st * proc = _p; void proc_table_init(main_server_st *s) { s->proc_table.db_ip = talloc(s, struct htable); + s->proc_table.db_dtls_ip = talloc(s, struct htable); s->proc_table.db_dtls_id = talloc(s, struct htable); s->proc_table.db_sid = talloc(s, struct htable); htable_init(s->proc_table.db_ip, rehash_ip, NULL); + htable_init(s->proc_table.db_dtls_ip, rehash_dtls_ip, NULL); htable_init(s->proc_table.db_dtls_id, rehash_dtls_id, NULL); htable_init(s->proc_table.db_sid, rehash_sid, NULL); s->proc_table.total = 0; @@ -76,13 +87,18 @@ void proc_table_init(main_server_st *s) void proc_table_deinit(main_server_st *s) { htable_clear(s->proc_table.db_ip); + htable_clear(s->proc_table.db_dtls_ip); htable_clear(s->proc_table.db_dtls_id); htable_clear(s->proc_table.db_sid); - talloc_free(s->proc_table.db_dtls_id); talloc_free(s->proc_table.db_ip); + talloc_free(s->proc_table.db_dtls_ip); + talloc_free(s->proc_table.db_dtls_id); talloc_free(s->proc_table.db_sid); } +/* Adds the IP of the CSTP channel into the IPs hash table and + * the session ID into the IDs hash table. + */ int proc_table_add(main_server_st *s, struct proc_st *proc) { size_t ip_hash = rehash_ip(proc, NULL); @@ -125,8 +141,46 @@ int proc_table_update_ip(main_server_st *s, struct proc_st *proc, struct sockadd return 0; } +/* Adds the IP of the DTLS channel into the DTLS IP hash table. It + * only adds the IP if it is different than the CSTP channel IP. + */ +int proc_table_update_dtls_ip(main_server_st *s, struct proc_st *proc, struct sockaddr_storage *addr, unsigned addr_size) +{ + if (proc->dtls_remote_addr_len) { + if (proc->dtls_remote_addr_len == addr_size && + memcmp(SA_IN_P_GENERIC(&proc->dtls_remote_addr, addr_size), + SA_IN_P_GENERIC(addr, addr_size), + SA_IN_SIZE(addr_size)) == 0) { + return -1; /* DTLS address is already up to date */ + } + htable_del(s->proc_table.db_dtls_ip, rehash_dtls_ip(proc, NULL), proc); + } + + proc->dtls_remote_addr_len = 0; + if (addr_size == 0) + return 0; + + if (proc->remote_addr_len == addr_size && + memcmp(SA_IN_P_GENERIC(&proc->remote_addr, addr_size), + SA_IN_P_GENERIC(addr, addr_size), + SA_IN_SIZE(addr_size)) == 0) { + return -1; /* CSTP and DTLS peer addresses match; do nothing */ + } + + proc->dtls_remote_addr_len = addr_size; + memcpy(&proc->dtls_remote_addr, addr, addr_size); + + if (htable_add(s->proc_table.db_dtls_ip, rehash_dtls_ip(proc, NULL), proc) == 0) + return -1; + + return 0; +} + void proc_table_del(main_server_st *s, struct proc_st *proc) { + if (proc->dtls_remote_addr_len > 0) + htable_del(s->proc_table.db_dtls_ip, rehash_dtls_ip(proc, NULL), proc); + htable_del(s->proc_table.db_ip, rehash_ip(proc, NULL), proc); htable_del(s->proc_table.db_dtls_id, rehash_dtls_id(proc, NULL), proc); htable_del(s->proc_table.db_sid, rehash_sid(proc, NULL), proc); @@ -137,10 +191,21 @@ static bool local_ip_cmp(const void* _c1, void* _c2) const struct proc_st* c1 = _c1; struct find_ip_st* c2 = _c2; - if (c1->remote_addr_len != c2->sockaddr_size) + if (c2->sockaddr_size == 0) return 0; - if (memcmp(SA_IN_P_GENERIC(&c1->remote_addr, c1->remote_addr_len), + /* Test if peer IP matches DTLS IP */ + if (c1->dtls_remote_addr_len == c2->sockaddr_size && + memcmp(SA_IN_P_GENERIC(&c1->dtls_remote_addr, c1->dtls_remote_addr_len), + SA_IN_P_GENERIC(c2->sockaddr, c2->sockaddr_size), + SA_IN_SIZE(c1->dtls_remote_addr_len)) == 0) { + c2->found_ips++; + return 1; + } + + /* Test if peer IP matches CSTP IP */ + if (c1->remote_addr_len == c2->sockaddr_size && + memcmp(SA_IN_P_GENERIC(&c1->remote_addr, c1->remote_addr_len), SA_IN_P_GENERIC(c2->sockaddr, c2->sockaddr_size), SA_IN_SIZE(c1->remote_addr_len)) == 0) { c2->found_ips++; @@ -162,15 +227,21 @@ struct proc_st *proc_search_single_ip(struct main_server_st *s, fip.sockaddr = sockaddr; fip.sockaddr_size = sockaddr_size; - fip.found_ips = 0; h = hash_any(SA_IN_P_GENERIC(sockaddr, sockaddr_size), SA_IN_SIZE(sockaddr_size), 0); + + fip.found_ips = 0; + proc = htable_get(s->proc_table.db_dtls_ip, h, local_ip_cmp, &fip); + if (proc && fip.found_ips == 1) + return proc; + + fip.found_ips = 0; proc = htable_get(s->proc_table.db_ip, h, local_ip_cmp, &fip); + if (proc && fip.found_ips == 1) + return proc; - if (fip.found_ips > 1) - return NULL; - return proc; + return NULL; } static bool dtls_id_cmp(const void* _c1, void* _c2) @@ -189,6 +260,7 @@ struct find_dtls_id_st* c2 = _c2; return 0; } + struct proc_st *proc_search_dtls_id(struct main_server_st *s, const uint8_t *id, unsigned id_size) { @@ -213,6 +285,7 @@ struct find_sid_st* c2 = _c2; return 0; } + struct proc_st *proc_search_sid(struct main_server_st *s, const uint8_t sid[SID_SIZE]) { diff --git a/src/proc-search.h b/src/proc-search.h index 9003698..e4fd2c3 100644 --- a/src/proc-search.h +++ b/src/proc-search.h @@ -39,5 +39,6 @@ void proc_table_deinit(main_server_st *s); int proc_table_add(main_server_st *s, struct proc_st *proc); void proc_table_del(main_server_st *s, struct proc_st *proc); int proc_table_update_ip(main_server_st *s, struct proc_st *proc, struct sockaddr_storage *addr, unsigned addr_size); +int proc_table_update_dtls_ip(main_server_st *s, struct proc_st *proc, struct sockaddr_storage *addr, unsigned addr_size); #endif -- 2.7.4