diff --git a/CHANGELOG b/CHANGELOG index 3bf2b2bd2..324cd433e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,7 @@ +2018-05-30 + + - [FEATURE, API CHANGE] Support zero-sized CIDs in received packets + 2018-05-24 - Close connection properly when packet encryption fails diff --git a/include/lsquic.h b/include/lsquic.h index 4ff48471f..48170522d 100644 --- a/include/lsquic.h +++ b/include/lsquic.h @@ -319,8 +319,7 @@ struct lsquic_engine_settings { * If set to true value, the server will not include connection ID in * outgoing packets if client's CHLO specifies TCID=0. * - * For client, this means including TCID=0 into CHLO message. TODO: - * this does not work yet. + * For client, this means including TCID=0 into CHLO message. */ int es_support_tcid0; @@ -492,7 +491,8 @@ lsquic_engine_new (unsigned lsquic_engine_flags, * 1350 for IPv6 and 1370 for IPv4. */ lsquic_conn_t * -lsquic_engine_connect (lsquic_engine_t *, const struct sockaddr *peer_sa, +lsquic_engine_connect (lsquic_engine_t *, const struct sockaddr *local_sa, + const struct sockaddr *peer_sa, void *peer_ctx, lsquic_conn_ctx_t *conn_ctx, const char *hostname, unsigned short max_packet_size); diff --git a/src/liblsquic/lsquic_conn.c b/src/liblsquic/lsquic_conn.c index 6fbc81acb..26889ac95 100644 --- a/src/liblsquic/lsquic_conn.c +++ b/src/liblsquic/lsquic_conn.c @@ -51,23 +51,6 @@ lsquic_conn_record_sockaddr (lsquic_conn_t *lconn, } -void -lsquic_conn_record_peer_sa (lsquic_conn_t *lconn, const struct sockaddr *peer) -{ - switch (peer->sa_family) - { - case AF_INET: - lconn->cn_flags |= LSCONN_HAS_PEER_SA; - memcpy(lconn->cn_peer_addr, peer, sizeof(struct sockaddr_in)); - break; - case AF_INET6: - lconn->cn_flags |= LSCONN_HAS_PEER_SA; - memcpy(lconn->cn_peer_addr, peer, sizeof(struct sockaddr_in6)); - break; - } -} - - int lsquic_conn_get_sockaddr (const lsquic_conn_t *lconn, const struct sockaddr **local, const struct sockaddr **peer) diff --git a/src/liblsquic/lsquic_conn.h b/src/liblsquic/lsquic_conn.h index 92c1cfc7e..9745cd535 100644 --- a/src/liblsquic/lsquic_conn.h +++ b/src/liblsquic/lsquic_conn.h @@ -113,9 +113,6 @@ void lsquic_conn_record_sockaddr (lsquic_conn_t *lconn, const struct sockaddr *local, const struct sockaddr *peer); -void -lsquic_conn_record_peer_sa (lsquic_conn_t *lconn, const struct sockaddr *peer); - int lsquic_conn_decrypt_packet (lsquic_conn_t *lconn, struct lsquic_engine_public *, struct lsquic_packet_in *); diff --git a/src/liblsquic/lsquic_conn_hash.c b/src/liblsquic/lsquic_conn_hash.c index 4e39ee954..7024092d5 100644 --- a/src/liblsquic/lsquic_conn_hash.c +++ b/src/liblsquic/lsquic_conn_hash.c @@ -18,9 +18,61 @@ #define conn_hash_mask(conn_hash) ((1 << (conn_hash)->ch_nbits) - 1) #define conn_hash_bucket_no(conn_hash, hash) (hash & conn_hash_mask(conn_hash)) +#if FULL_LOCAL_ADDR_SUPPORTED +#define HASHBUF_SZ (2 + sizeof(((struct sockaddr_in6 *) 0)->sin6_addr)) +#else +#define HASHBUF_SZ 2 +#endif + + +static const unsigned char * +conn2hash_server (const struct lsquic_conn *lconn, unsigned char *buf, + size_t *sz) +{ + *sz = sizeof(lconn->cn_cid); + return (unsigned char *) &lconn->cn_cid; +} + + +static void +sockaddr2hash (const struct sockaddr *sa, unsigned char *buf, size_t *sz) +{ + if (sa->sa_family == AF_INET) + { + const struct sockaddr_in *const sa4 = (void *) sa; + memcpy(buf, &sa4->sin_port, 2); +#if FULL_LOCAL_ADDR_SUPPORTED + memcpy(buf + 2, &sa4->sin_addr, sizeof(sa4->sin_addr)); + *sz = 2 + sizeof(sa4->sin_addr); +#else + *sz = 2; +#endif + } + else + { + const struct sockaddr_in6 *const sa6 = (void *) sa; + memcpy(buf, &sa6->sin6_port, 2); +#if FULL_LOCAL_ADDR_SUPPORTED + memcpy(buf + 2, &sa6->sin6_addr, sizeof(sa6->sin6_addr)); + *sz = 2 + sizeof(sa6->sin6_addr); +#else + *sz = 2; +#endif + } +} + + +static const unsigned char * +conn2hash_client (const struct lsquic_conn *lconn, unsigned char *buf, + size_t *sz) +{ + sockaddr2hash((struct sockaddr *) &lconn->cn_local_addr, buf, sz); + return buf; +} + int -conn_hash_init (struct conn_hash *conn_hash) +conn_hash_init (struct conn_hash *conn_hash, int server) { unsigned n; @@ -32,6 +84,10 @@ conn_hash_init (struct conn_hash *conn_hash) return -1; for (n = 0; n < n_buckets(conn_hash->ch_nbits); ++n) TAILQ_INIT(&conn_hash->ch_buckets[n]); + if (server) + conn_hash->ch_conn2hash = conn2hash_server; + else + conn_hash->ch_conn2hash = conn2hash_client; LSQ_INFO("initialized"); return 0; } @@ -45,7 +101,7 @@ conn_hash_cleanup (struct conn_hash *conn_hash) struct lsquic_conn * -conn_hash_find (struct conn_hash *conn_hash, lsquic_cid_t cid) +conn_hash_find_by_cid (struct conn_hash *conn_hash, lsquic_cid_t cid) { const unsigned hash = XXH32(&cid, sizeof(cid), (uintptr_t) conn_hash); const unsigned buckno = conn_hash_bucket_no(conn_hash, hash); @@ -57,6 +113,31 @@ conn_hash_find (struct conn_hash *conn_hash, lsquic_cid_t cid) } +struct lsquic_conn * +conn_hash_find_by_addr (struct conn_hash *conn_hash, const struct sockaddr *sa) +{ + unsigned char hash_buf[HASHBUF_SZ][2]; + struct lsquic_conn *lconn; + unsigned hash, buckno; + size_t hash_sz[2]; + + sockaddr2hash(sa, hash_buf[0], &hash_sz[0]); + hash = XXH32(hash_buf, hash_sz[0], (uintptr_t) conn_hash); + buckno = conn_hash_bucket_no(conn_hash, hash); + TAILQ_FOREACH(lconn, &conn_hash->ch_buckets[buckno], cn_next_hash) + if (lconn->cn_hash == hash) + { + sockaddr2hash((struct sockaddr *) lconn->cn_local_addr, hash_buf[1], + &hash_sz[1]); + if (hash_sz[0] == hash_sz[1] + && 0 == memcmp(hash_buf[0], hash_buf[1], hash_sz[0])) + return lconn; + } + + return NULL; +} + + static int double_conn_hash_buckets (struct conn_hash *conn_hash) { @@ -98,8 +179,13 @@ double_conn_hash_buckets (struct conn_hash *conn_hash) int conn_hash_add (struct conn_hash *conn_hash, struct lsquic_conn *lconn) { - const unsigned hash = XXH32(&lconn->cn_cid, sizeof(lconn->cn_cid), - (uintptr_t) conn_hash); + unsigned char hash_buf[HASHBUF_SZ]; + const unsigned char *key; + size_t key_sz; + unsigned hash, buckno; + + key = conn_hash->ch_conn2hash(lconn, hash_buf, &key_sz); + hash = XXH32(key, key_sz, (uintptr_t) conn_hash); if (conn_hash->ch_count >= n_buckets(conn_hash->ch_nbits) * CONN_HASH_MAX_PER_BUCKET && conn_hash->ch_nbits < sizeof(hash) * 8 - 1 && @@ -107,7 +193,7 @@ conn_hash_add (struct conn_hash *conn_hash, struct lsquic_conn *lconn) { return -1; } - const unsigned buckno = conn_hash_bucket_no(conn_hash, hash); + buckno = conn_hash_bucket_no(conn_hash, hash); lconn->cn_hash = hash; TAILQ_INSERT_TAIL(&conn_hash->ch_buckets[buckno], lconn, cn_next_hash); ++conn_hash->ch_count; diff --git a/src/liblsquic/lsquic_conn_hash.h b/src/liblsquic/lsquic_conn_hash.h index 4ee27376a..ce2a88874 100644 --- a/src/liblsquic/lsquic_conn_hash.h +++ b/src/liblsquic/lsquic_conn_hash.h @@ -16,6 +16,7 @@ #define CONN_HASH_MAX_PER_BUCKET 2 struct lsquic_conn; +struct sockaddr; TAILQ_HEAD(lsquic_conn_head, lsquic_conn); @@ -28,19 +29,24 @@ struct conn_hash } ch_iter; unsigned ch_count; unsigned ch_nbits; + const unsigned char * (*ch_conn2hash)(const struct lsquic_conn *, + unsigned char *, size_t *); }; #define conn_hash_count(conn_hash) (+(conn_hash)->ch_count) /* Returns -1 if malloc fails */ int -conn_hash_init (struct conn_hash *); +conn_hash_init (struct conn_hash *, int server); void conn_hash_cleanup (struct conn_hash *); struct lsquic_conn * -conn_hash_find (struct conn_hash *conn_hash, lsquic_cid_t); +conn_hash_find_by_cid (struct conn_hash *, lsquic_cid_t); + +struct lsquic_conn * +conn_hash_find_by_addr (struct conn_hash *, const struct sockaddr *); /* Returns -1 if limit has been reached or if malloc fails */ int diff --git a/src/liblsquic/lsquic_engine.c b/src/liblsquic/lsquic_engine.c index 5e6ebd352..5ef598843 100644 --- a/src/liblsquic/lsquic_engine.c +++ b/src/liblsquic/lsquic_engine.c @@ -323,7 +323,7 @@ lsquic_engine_new (unsigned flags, engine->pub.enp_pmi_ctx = NULL; } engine->pub.enp_engine = engine; - conn_hash_init(&engine->conns_hash); + conn_hash_init(&engine->conns_hash, flags & ENG_SERVER); engine->attq = attq_create(); eng_hist_init(&engine->history); engine->batch_size = INITIAL_OUT_BATCH_SIZE; @@ -409,50 +409,28 @@ new_full_conn_client (lsquic_engine_t *engine, const char *hostname, if (!conn) return NULL; ++engine->n_conns; - if (0 != conn_hash_add(&engine->conns_hash, conn)) - { - LSQ_WARN("cannot add connection %"PRIu64" to hash - destroy", - conn->cn_cid); - destroy_conn(engine, conn); - return NULL; - } - assert(!(conn->cn_flags & - (CONN_REF_FLAGS - & ~LSCONN_TICKABLE /* This flag may be set as effect of user - callbacks */ - ))); - conn->cn_flags |= LSCONN_HASHED; return conn; } static lsquic_conn_t * -find_or_create_conn (lsquic_engine_t *engine, lsquic_packet_in_t *packet_in, - struct packin_parse_state *ppstate, const struct sockaddr *sa_peer, - void *peer_ctx) +find_conn (lsquic_engine_t *engine, lsquic_packet_in_t *packet_in, + struct packin_parse_state *ppstate, const struct sockaddr *sa_local) { lsquic_conn_t *conn; - if (lsquic_packet_in_is_prst(packet_in) - && !engine->pub.enp_settings.es_honor_prst) - { - LSQ_DEBUG("public reset packet: discarding"); + conn = conn_hash_find_by_addr(&engine->conns_hash, sa_local); + if (!conn) return NULL; - } - if (!(packet_in->pi_flags & PI_CONN_ID)) + conn->cn_pf->pf_parse_packet_in_finish(packet_in, ppstate); + if ((packet_in->pi_flags & PI_CONN_ID) + && conn->cn_cid != packet_in->pi_conn_id) { - LSQ_DEBUG("packet header does not have connection ID: discarding"); + LSQ_DEBUG("connection IDs do not match"); return NULL; } - conn = conn_hash_find(&engine->conns_hash, packet_in->pi_conn_id); - if (conn) - { - conn->cn_pf->pf_parse_packet_in_finish(packet_in, ppstate); - return conn; - } - return conn; } @@ -508,7 +486,16 @@ process_packet_in (lsquic_engine_t *engine, lsquic_packet_in_t *packet_in, { lsquic_conn_t *conn; - conn = find_or_create_conn(engine, packet_in, ppstate, sa_peer, peer_ctx); + if (lsquic_packet_in_is_prst(packet_in) + && !engine->pub.enp_settings.es_honor_prst) + { + lsquic_mm_put_packet_in(&engine->pub.enp_mm, packet_in); + LSQ_DEBUG("public reset packet: discarding"); + return 1; + } + + conn = find_conn(engine, packet_in, ppstate, sa_local); + if (!conn) { lsquic_mm_put_packet_in(&engine->pub.enp_mm, packet_in); @@ -567,7 +554,8 @@ lsquic_engine_destroy (lsquic_engine_t *engine) lsquic_conn_t * -lsquic_engine_connect (lsquic_engine_t *engine, const struct sockaddr *peer_sa, +lsquic_engine_connect (lsquic_engine_t *engine, const struct sockaddr *local_sa, + const struct sockaddr *peer_sa, void *peer_ctx, lsquic_conn_ctx_t *conn_ctx, const char *hostname, unsigned short max_packet_size) { @@ -596,9 +584,22 @@ lsquic_engine_connect (lsquic_engine_t *engine, const struct sockaddr *peer_sa, conn = new_full_conn_client(engine, hostname, max_packet_size); if (!conn) goto err; + lsquic_conn_record_sockaddr(conn, local_sa, peer_sa); + if (0 != conn_hash_add(&engine->conns_hash, conn)) + { + LSQ_WARN("cannot add connection %"PRIu64" to hash - destroy", + conn->cn_cid); + destroy_conn(engine, conn); + goto err; + } + assert(!(conn->cn_flags & + (CONN_REF_FLAGS + & ~LSCONN_TICKABLE /* This flag may be set as effect of user + callbacks */ + ))); + conn->cn_flags |= LSCONN_HASHED; lsquic_mh_insert(&engine->conns_tickable, conn, conn->cn_last_ticked); engine_incref_conn(conn, LSCONN_TICKABLE); - lsquic_conn_record_peer_sa(conn, peer_sa); conn->cn_peer_ctx = peer_ctx; lsquic_conn_set_ctx(conn, conn_ctx); full_conn_client_call_on_new(conn); diff --git a/src/liblsquic/lsquic_handshake.c b/src/liblsquic/lsquic_handshake.c index 846324ad6..d8cf4a74e 100644 --- a/src/liblsquic/lsquic_handshake.c +++ b/src/liblsquic/lsquic_handshake.c @@ -964,6 +964,10 @@ lsquic_enc_session_gen_chlo (lsquic_enc_session_t *enc_session, } else ua_len = 0; + if (settings->es_support_tcid0) + { + MSG_LEN_ADD(msg_len, 4); ++n_tags; /* TCID */ + } MSG_LEN_ADD(msg_len, lsquic_str_len(&enc_session->hs_ctx.sni)); ++n_tags; /* SNI */ MSG_LEN_ADD(msg_len, lsquic_str_len(ccs)); ++n_tags; /* CCS */ @@ -1034,6 +1038,8 @@ lsquic_enc_session_gen_chlo (lsquic_enc_session_t *enc_session, if (lsquic_str_len(&enc_session->info->scfg) > 0) MW_WRITE_BUFFER(&mw, QTAG_SCID, enc_session->info->sscid, sizeof(enc_session->info->sscid)); + if (settings->es_support_tcid0) + MW_WRITE_UINT32(&mw, QTAG_TCID, 0); MW_WRITE_UINT32(&mw, QTAG_PDMD, settings->es_pdmd); MW_WRITE_UINT32(&mw, QTAG_SMHL, 1); MW_WRITE_UINT32(&mw, QTAG_ICSL, settings->es_idle_conn_to / 1000000); diff --git a/test/prog.c b/test/prog.c index ac815e203..7415a3758 100644 --- a/test/prog.c +++ b/test/prog.c @@ -251,6 +251,7 @@ prog_connect (struct prog *prog) sport = TAILQ_FIRST(prog->prog_sports); if (NULL == lsquic_engine_connect(prog->prog_engine, + (struct sockaddr *) &sport->sp_local_addr, (struct sockaddr *) &sport->sas, sport, NULL, prog->prog_hostname ? prog->prog_hostname : sport->host, prog->prog_max_packet_size)) diff --git a/test/test_common.c b/test/test_common.c index 7870dd869..e3bc2f9ae 100644 --- a/test/test_common.c +++ b/test/test_common.c @@ -512,7 +512,7 @@ read_one_packet (struct read_iter *iter) #endif local_addr = &packs_in->local_addresses[iter->ri_idx]; - memcpy(local_addr, &sport->sas, sizeof(*local_addr)); + memcpy(local_addr, &sport->sp_local_addr, sizeof(*local_addr)); #if __linux__ n_dropped = 0; #endif @@ -753,6 +753,9 @@ sport_init_client (struct service_port *sport, struct lsquic_engine *engine, return -1; } + memcpy((void *) &sport->sp_local_addr, sa_local, + sa_local->sa_family == AF_INET ? + sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)); switch (sa_local->sa_family) { case AF_INET: LSQ_DEBUG("local address: %s:%d", diff --git a/test/test_common.h b/test/test_common.h index 3e8fffff4..dc3fc99d6 100644 --- a/test/test_common.h +++ b/test/test_common.h @@ -47,6 +47,7 @@ struct service_port { void *conn_ctx; char host[80]; struct sockaddr_storage sas; + struct sockaddr_storage sp_local_addr; struct packets_in *packs_in; enum sport_flags sp_flags; int sp_sndbuf; /* If SPORT_SET_SNDBUF is set */ diff --git a/test/unittests/test_conn_hash.c b/test/unittests/test_conn_hash.c index 35d73c2a7..8ff0bc5ab 100644 --- a/test/unittests/test_conn_hash.c +++ b/test/unittests/test_conn_hash.c @@ -48,14 +48,14 @@ main (int argc, char **argv) lsquic_set_log_level("info"); malo = lsquic_malo_create(sizeof(*lconn)); - s = conn_hash_init(&conn_hash); + s = conn_hash_init(&conn_hash, 1); assert(0 == s); for (n = 0; n < nelems; ++n) { lconn = get_new_lsquic_conn(malo); lconn->cn_if = (void *) (uintptr_t) n; /* This will be used for verification later the test */ - find_lsconn = conn_hash_find(&conn_hash, lconn->cn_cid); + find_lsconn = conn_hash_find_by_cid(&conn_hash, lconn->cn_cid); assert(!find_lsconn); s = conn_hash_add(&conn_hash, lconn); assert(0 == s); @@ -66,10 +66,10 @@ main (int argc, char **argv) for (lconn = lsquic_malo_first(malo); lconn; lconn = lsquic_malo_next(malo)) { - find_lsconn = conn_hash_find(&conn_hash, lconn->cn_cid); + find_lsconn = conn_hash_find_by_cid(&conn_hash, lconn->cn_cid); assert(find_lsconn == lconn); conn_hash_remove(&conn_hash, lconn); - find_lsconn = conn_hash_find(&conn_hash, lconn->cn_cid); + find_lsconn = conn_hash_find_by_cid(&conn_hash, lconn->cn_cid); assert(!find_lsconn); }