Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F115947373
D32356.id100069.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
32 KB
Referenced Files
None
Subscribers
None
D32356.id100069.diff
View Options
diff --git a/sys/kern/uipc_ktls.c b/sys/kern/uipc_ktls.c
--- a/sys/kern/uipc_ktls.c
+++ b/sys/kern/uipc_ktls.c
@@ -299,7 +299,7 @@
static void ktls_cleanup(struct ktls_session *tls);
#if defined(INET) || defined(INET6)
-static void ktls_reset_send_tag(void *context, int pending);
+static void ktls_reset_send_receive_tag(void *context, int pending);
#endif
static void ktls_work_thread(void *ctx);
static void ktls_alloc_thread(void *ctx);
@@ -503,7 +503,7 @@
#if defined(INET) || defined(INET6)
static int
ktls_create_session(struct socket *so, struct tls_enable *en,
- struct ktls_session **tlsp)
+ struct ktls_session **tlsp, int direction)
{
struct ktls_session *tls;
int error;
@@ -619,9 +619,10 @@
counter_u64_add(ktls_offload_active, 1);
refcount_init(&tls->refcount, 1);
- TASK_INIT(&tls->reset_tag_task, 0, ktls_reset_send_tag, tls);
+ TASK_INIT(&tls->reset_tag_task, 0, ktls_reset_send_receive_tag, tls);
tls->wq_index = ktls_get_cpu(so);
+ tls->direction = direction;
tls->params.cipher_algorithm = en->cipher_algorithm;
tls->params.auth_algorithm = en->auth_algorithm;
@@ -754,11 +755,12 @@
counter_u64_add(ktls_offload_active, 1);
refcount_init(&tls_new->refcount, 1);
- TASK_INIT(&tls_new->reset_tag_task, 0, ktls_reset_send_tag, tls_new);
+ TASK_INIT(&tls_new->reset_tag_task, 0, ktls_reset_send_receive_tag, tls_new);
/* Copy fields from existing session. */
tls_new->params = tls->params;
tls_new->wq_index = tls->wq_index;
+ tls_new->direction = tls->direction;
/* Deep copy keys. */
if (tls_new->params.auth_key != NULL) {
@@ -808,8 +810,8 @@
counter_u64_add(ktls_ifnet_chacha20, -1);
break;
}
- if (tls->snd_tag != NULL)
- m_snd_tag_rele(tls->snd_tag);
+ if (tls->snd_rcv_tag != NULL)
+ m_snd_tag_rele(tls->snd_rcv_tag);
break;
#ifdef TCP_OFFLOAD
case TCP_TLS_MODE_TOE:
@@ -983,28 +985,132 @@
return (error);
}
+/*
+ * Common code for allocating a TLS receive tag for doing HW
+ * decryption of TLS data.
+ *
+ * This function allocates a new TLS receive tag on whatever interface
+ * the connection is currently routed over.
+ */
static int
-ktls_try_ifnet(struct socket *so, struct ktls_session *tls, bool force)
+ktls_alloc_rcv_tag(struct inpcb *inp, struct ktls_session *tls, bool force,
+ struct m_snd_tag **mstp)
{
- struct m_snd_tag *mst;
+ union if_snd_tag_alloc_params params;
+ struct ifnet *ifp;
+ struct nhop_object *nh;
+ struct tcpcb *tp;
int error;
- error = ktls_alloc_snd_tag(so->so_pcb, tls, force, &mst);
- if (error == 0) {
- tls->mode = TCP_TLS_MODE_IFNET;
- tls->snd_tag = mst;
- switch (tls->params.cipher_algorithm) {
- case CRYPTO_AES_CBC:
- counter_u64_add(ktls_ifnet_cbc, 1);
- break;
- case CRYPTO_AES_NIST_GCM_16:
- counter_u64_add(ktls_ifnet_gcm, 1);
- break;
- case CRYPTO_CHACHA20_POLY1305:
- counter_u64_add(ktls_ifnet_chacha20, 1);
- break;
+ INP_RLOCK(inp);
+ if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) {
+ INP_RUNLOCK(inp);
+ return (ECONNRESET);
+ }
+ if (inp->inp_socket == NULL) {
+ INP_RUNLOCK(inp);
+ return (ECONNRESET);
+ }
+ tp = intotcpcb(inp);
+
+ /*
+ * Check administrative controls on ifnet TLS to determine if
+ * ifnet TLS should be denied.
+ *
+ * - Always permit 'force' requests.
+ * - ktls_ifnet_permitted == 0: always deny.
+ */
+ if (!force && ktls_ifnet_permitted == 0) {
+ INP_RUNLOCK(inp);
+ return (ENXIO);
+ }
+
+ /*
+ * XXX: Use the cached route in the inpcb to find the
+ * interface. This should perhaps instead use
+ * rtalloc1_fib(dst, 0, 0, fibnum). Since KTLS is only
+ * enabled after a connection has completed key negotiation in
+ * userland, the cached route will be present in practice.
+ */
+ nh = inp->inp_route.ro_nh;
+ if (nh == NULL) {
+ INP_RUNLOCK(inp);
+ return (ENXIO);
+ }
+ ifp = nh->nh_ifp;
+ if_ref(ifp);
+
+ params.hdr.type = IF_SND_TAG_TYPE_TLS_RX;
+ params.hdr.flowid = inp->inp_flowid;
+ params.hdr.flowtype = inp->inp_flowtype;
+ params.hdr.numa_domain = inp->inp_numa_domain;
+ params.tls_rx.inp = inp;
+ params.tls_rx.tls = tls;
+ params.tls_rx.vlan_id = 0;
+
+ INP_RUNLOCK(inp);
+
+ if ((ifp->if_capenable & IFCAP_MEXTPG) == 0) {
+ error = EOPNOTSUPP;
+ goto out;
+ }
+
+ /* XXX reusing TXTLS flags */
+ if (inp->inp_vflag & INP_IPV6) {
+ if ((ifp->if_capenable & IFCAP_TXTLS6) == 0) {
+ error = EOPNOTSUPP;
+ goto out;
+ }
+ } else {
+ if ((ifp->if_capenable & IFCAP_TXTLS4) == 0) {
+ error = EOPNOTSUPP;
+ goto out;
}
}
+ error = m_snd_tag_alloc(ifp, ¶ms, mstp);
+out:
+ if_rele(ifp);
+ return (error);
+}
+
+static int
+ktls_try_ifnet(struct socket *so, struct ktls_session *tls, int direction, bool force)
+{
+ struct m_snd_tag *mst;
+ int error;
+
+ switch (direction) {
+ case KTLS_TX:
+ error = ktls_alloc_snd_tag(so->so_pcb, tls, force, &mst);
+ if (__predict_false(error != 0))
+ goto done;
+ break;
+ case KTLS_RX:
+ error = ktls_alloc_rcv_tag(so->so_pcb, tls, force, &mst);
+ if (__predict_false(error != 0))
+ goto done;
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ tls->mode = TCP_TLS_MODE_IFNET;
+ tls->snd_rcv_tag = mst;
+
+ switch (tls->params.cipher_algorithm) {
+ case CRYPTO_AES_CBC:
+ counter_u64_add(ktls_ifnet_cbc, 1);
+ break;
+ case CRYPTO_AES_NIST_GCM_16:
+ counter_u64_add(ktls_ifnet_gcm, 1);
+ break;
+ case CRYPTO_CHACHA20_POLY1305:
+ counter_u64_add(ktls_ifnet_chacha20, 1);
+ break;
+ default:
+ break;
+ }
+done:
return (error);
}
@@ -1185,7 +1291,7 @@
if (en->cipher_algorithm == CRYPTO_AES_CBC && !ktls_cbc_enable)
return (ENOTSUP);
- error = ktls_create_session(so, en, &tls);
+ error = ktls_create_session(so, en, &tls, KTLS_RX);
if (error)
return (error);
@@ -1195,12 +1301,6 @@
return (error);
}
-#ifdef TCP_OFFLOAD
- error = ktls_try_toe(so, tls, KTLS_RX);
- if (error)
-#endif
- ktls_use_sw(tls);
-
/* Mark the socket as using TLS offload. */
SOCKBUF_LOCK(&so->so_rcv);
so->so_rcv.sb_tls_seqno = be64dec(en->rec_seq);
@@ -1214,6 +1314,15 @@
}
SOCKBUF_UNLOCK(&so->so_rcv);
+ /* Prefer TOE -> ifnet TLS -> software TLS. */
+#ifdef TCP_OFFLOAD
+ error = ktls_try_toe(so, tls, KTLS_RX);
+ if (error)
+#endif
+ error = ktls_try_ifnet(so, tls, KTLS_RX, false);
+ if (error)
+ ktls_use_sw(tls);
+
counter_u64_add(ktls_offload_total, 1);
return (0);
@@ -1254,7 +1363,7 @@
if (mb_use_ext_pgs == 0)
return (ENXIO);
- error = ktls_create_session(so, en, &tls);
+ error = ktls_create_session(so, en, &tls, KTLS_TX);
if (error)
return (error);
@@ -1263,7 +1372,7 @@
error = ktls_try_toe(so, tls, KTLS_TX);
if (error)
#endif
- error = ktls_try_ifnet(so, tls, false);
+ error = ktls_try_ifnet(so, tls, KTLS_TX, false);
if (error)
error = ktls_try_sw(so, tls, KTLS_TX);
@@ -1319,6 +1428,36 @@
return (0);
}
+int
+ktls_get_rx_sequence(struct inpcb *inp, uint32_t *tcpseq, uint64_t *tlsseq)
+{
+ struct socket *so;
+ struct tcpcb *tp;
+
+ INP_RLOCK(inp);
+ so = inp->inp_socket;
+ if (__predict_false(so == NULL)) {
+ INP_RUNLOCK(inp);
+ return (EINVAL);
+ }
+ if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) {
+ INP_RUNLOCK(inp);
+ return (ECONNRESET);
+ }
+
+ tp = intotcpcb(inp);
+ MPASS(tp != NULL);
+
+ SOCKBUF_LOCK(&so->so_rcv);
+ *tcpseq = tp->rcv_nxt - so->so_rcv.sb_tlscc;
+ *tlsseq = so->so_rcv.sb_tls_seqno;
+ SOCKBUF_UNLOCK(&so->so_rcv);
+
+ INP_RUNLOCK(inp);
+
+ return (0);
+}
+
int
ktls_get_tx_mode(struct socket *so, int *modep)
{
@@ -1380,7 +1519,7 @@
tls_new = ktls_clone_session(tls);
if (mode == TCP_TLS_MODE_IFNET)
- error = ktls_try_ifnet(so, tls_new, true);
+ error = ktls_try_ifnet(so, tls_new, KTLS_TX, true);
else
error = ktls_try_sw(so, tls_new, KTLS_TX);
if (error) {
@@ -1439,19 +1578,21 @@
}
/*
- * Try to allocate a new TLS send tag. This task is scheduled when
- * ip_output detects a route change while trying to transmit a packet
- * holding a TLS record. If a new tag is allocated, replace the tag
- * in the TLS session. Subsequent packets on the connection will use
- * the new tag. If a new tag cannot be allocated, drop the
- * connection.
+ * ktls_reset_send_receive_tag - try to allocate a new TLS send or receive tag.
+ *
+ * This task is scheduled when ip_output detects a route change while
+ * trying to transmit a packet holding a TLS record. If a new tag is
+ * allocated, replace the tag in the TLS session. Subsequent packets
+ * on the connection will use the new tag. If a new tag cannot be
+ * allocated, drop the connection.
*/
static void
-ktls_reset_send_tag(void *context, int pending)
+ktls_reset_send_receive_tag(void *context, int pending)
{
struct epoch_tracker et;
struct ktls_session *tls;
- struct m_snd_tag *old, *new;
+ struct m_snd_tag *snd_rcv_old;
+ struct m_snd_tag *snd_rcv_new;
struct inpcb *inp;
struct tcpcb *tp;
int error;
@@ -1467,72 +1608,81 @@
* an ifp mismatch and drop packets until a new tag is
* allocated.
*
- * Write-lock the INP when changing tls->snd_tag since
+ * Write-lock the INP when changing tls->snd_rcv_tag since
* ip[6]_output_send() holds a read-lock when reading the
* pointer.
*/
INP_WLOCK(inp);
- old = tls->snd_tag;
- tls->snd_tag = NULL;
+ snd_rcv_old = tls->snd_rcv_tag;
+ tls->snd_rcv_tag = NULL;
INP_WUNLOCK(inp);
- if (old != NULL)
- m_snd_tag_rele(old);
- error = ktls_alloc_snd_tag(inp, tls, true, &new);
+ if (snd_rcv_old != NULL)
+ m_snd_tag_rele(snd_rcv_old);
- if (error == 0) {
- INP_WLOCK(inp);
- tls->snd_tag = new;
- mtx_pool_lock(mtxpool_sleep, tls);
- tls->reset_pending = false;
- mtx_pool_unlock(mtxpool_sleep, tls);
- if (!in_pcbrele_wlocked(inp))
- INP_WUNLOCK(inp);
+ switch (tls->direction) {
+ case KTLS_TX:
+ error = ktls_alloc_snd_tag(inp, tls, true, &snd_rcv_new);
+ break;
+ case KTLS_RX:
+ error = ktls_alloc_rcv_tag(inp, tls, true, &snd_rcv_new);
+ break;
+ default:
+ goto drop_connection;
+ }
+ if (error != 0)
+ goto drop_connection;
- counter_u64_add(ktls_ifnet_reset, 1);
+ INP_WLOCK(inp);
+ tls->snd_rcv_tag = snd_rcv_new;
- /*
- * XXX: Should we kick tcp_output explicitly now that
- * the send tag is fixed or just rely on timers?
- */
- } else {
- NET_EPOCH_ENTER(et);
- INP_WLOCK(inp);
- if (!in_pcbrele_wlocked(inp)) {
- if (!(inp->inp_flags & INP_TIMEWAIT) &&
- !(inp->inp_flags & INP_DROPPED)) {
- tp = intotcpcb(inp);
- CURVNET_SET(tp->t_vnet);
- tp = tcp_drop(tp, ECONNABORTED);
- CURVNET_RESTORE();
- if (tp != NULL)
- INP_WUNLOCK(inp);
- counter_u64_add(ktls_ifnet_reset_dropped, 1);
- } else
- INP_WUNLOCK(inp);
- }
- NET_EPOCH_EXIT(et);
+ mtx_pool_lock(mtxpool_sleep, tls);
+ tls->reset_pending = false;
+ mtx_pool_unlock(mtxpool_sleep, tls);
- counter_u64_add(ktls_ifnet_reset_failed, 1);
+ if (!in_pcbrele_wlocked(inp))
+ INP_WUNLOCK(inp);
- /*
- * Leave reset_pending true to avoid future tasks while
- * the socket goes away.
- */
- }
+ counter_u64_add(ktls_ifnet_reset, 1);
ktls_free(tls);
-}
-int
-ktls_output_eagain(struct inpcb *inp, struct ktls_session *tls)
-{
+ /*
+ * XXX: Should we kick tcp_output explicitly now that
+ * the send tag is fixed or just rely on timers?
+ */
+ return;
- if (inp == NULL)
- return (ENOBUFS);
+drop_connection:
+ NET_EPOCH_ENTER(et);
+ INP_WLOCK(inp);
+ if (!in_pcbrele_wlocked(inp)) {
+ if (!(inp->inp_flags & INP_TIMEWAIT) &&
+ !(inp->inp_flags & INP_DROPPED)) {
+ tp = intotcpcb(inp);
+ CURVNET_SET(tp->t_vnet);
+ tp = tcp_drop(tp, ECONNABORTED);
+ CURVNET_RESTORE();
+ if (tp != NULL)
+ INP_WUNLOCK(inp);
+ counter_u64_add(ktls_ifnet_reset_dropped, 1);
+ } else
+ INP_WUNLOCK(inp);
+ }
+ NET_EPOCH_EXIT(et);
- INP_LOCK_ASSERT(inp);
+ counter_u64_add(ktls_ifnet_reset_failed, 1);
+ /*
+ * Leave reset_pending true to avoid future tasks while
+ * the socket goes away.
+ */
+ ktls_free(tls);
+}
+
+static void
+ktls_output_eagain_tls(struct inpcb *inp, struct ktls_session *tls)
+{
/*
* See if we should schedule a task to update the send tag for
* this session.
@@ -1546,6 +1696,30 @@
taskqueue_enqueue(taskqueue_thread, &tls->reset_tag_task);
}
mtx_pool_unlock(mtxpool_sleep, tls);
+}
+
+int
+ktls_output_eagain(struct inpcb *inp)
+{
+ struct socket *so;
+ struct ktls_session *tls;
+
+ if (__predict_false(inp == NULL))
+ goto done;
+ INP_LOCK_ASSERT(inp);
+
+ so = inp->inp_socket;
+ if (__predict_false(so == NULL))
+ goto done;
+
+ tls = so->so_rcv.sb_tls_info;
+ if (__predict_true(tls != NULL))
+ ktls_output_eagain_tls(inp, tls);
+
+ tls = so->so_snd.sb_tls_info;
+ if (__predict_true(tls != NULL))
+ ktls_output_eagain_tls(inp, tls);
+done:
return (ENOBUFS);
}
@@ -1564,7 +1738,7 @@
MPASS(tls->mode == TCP_TLS_MODE_IFNET);
- if (tls->snd_tag == NULL) {
+ if (tls->snd_rcv_tag == NULL) {
/*
* Resetting send tag, ignore this change. The
* pending reset may or may not see this updated rate
@@ -1574,10 +1748,11 @@
return (0);
}
- MPASS(tls->snd_tag != NULL);
- MPASS(tls->snd_tag->sw->type == IF_SND_TAG_TYPE_TLS_RATE_LIMIT);
+ mst = tls->snd_rcv_tag;
+
+ MPASS(mst != NULL);
+ MPASS(mst->sw->type == IF_SND_TAG_TYPE_TLS_RATE_LIMIT);
- mst = tls->snd_tag;
return (mst->sw->snd_tag_modify(mst, ¶ms));
}
#endif
@@ -1860,7 +2035,7 @@
return (NULL);
}
}
- n->m_flags |= M_NOTREADY;
+ n->m_flags |= (m->m_flags & (M_NOTREADY | M_DECRYPTED));
/* Store remainder in 'n'. */
n->m_len = m->m_len - remain;
@@ -1945,6 +2120,110 @@
return (0);
}
+/*
+ * Check if a mbuf chain is fully decrypted at the given offset and
+ * length. Returns KTLS_MBUF_CRYPTO_ST_DECRYPTED if all data is
+ * decrypted. KTLS_MBUF_CRYPTO_ST_MIXED if there is a mix of encrypted
+ * and decrypted data. Else KTLS_MBUF_CRYPTO_ST_ENCRYPTED if all data
+ * is encrypted.
+ */
+int
+ktls_mbuf_crypto_state(struct mbuf *mb, int offset, int len)
+{
+ int m_flags_ored = 0;
+ int m_flags_anded = -1;
+
+ for (; mb != NULL; mb = mb->m_next) {
+ if (offset < mb->m_len)
+ break;
+ offset -= mb->m_len;
+ }
+ offset += len;
+
+ for (; mb != NULL; mb = mb->m_next) {
+ m_flags_ored |= mb->m_flags;
+ m_flags_anded &= mb->m_flags;
+
+ if (offset <= mb->m_len)
+ break;
+ offset -= mb->m_len;
+ }
+ MPASS(mb != NULL || offset == 0);
+
+ if ((m_flags_ored ^ m_flags_anded) & M_DECRYPTED)
+ return (KTLS_MBUF_CRYPTO_ST_MIXED);
+ else
+ return ((m_flags_ored & M_DECRYPTED) ?
+ KTLS_MBUF_CRYPTO_ST_DECRYPTED :
+ KTLS_MBUF_CRYPTO_ST_ENCRYPTED);
+}
+
+/*
+ * ktls_resync_ifnet - get HW TLS RX back on track after packet loss
+ */
+static int
+ktls_resync_ifnet(struct socket *so, uint32_t tls_len, uint64_t tls_rcd_num)
+{
+ union if_snd_tag_modify_params params;
+ struct m_snd_tag *mst;
+ struct inpcb *inp;
+ struct tcpcb *tp;
+
+ mst = so->so_rcv.sb_tls_info->snd_rcv_tag;
+ if (__predict_false(mst == NULL))
+ return (EINVAL);
+
+ inp = sotoinpcb(so);
+ if (__predict_false(inp == NULL))
+ return (EINVAL);
+
+ INP_RLOCK(inp);
+ if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) {
+ INP_RUNLOCK(inp);
+ return (ECONNRESET);
+ }
+
+ tp = intotcpcb(inp);
+ MPASS(tp != NULL);
+
+ /* Get the TCP sequence number of the next valid TLS header. */
+ SOCKBUF_LOCK(&so->so_rcv);
+ params.tls_rx.tls_hdr_tcp_sn =
+ tp->rcv_nxt - so->so_rcv.sb_tlscc - tls_len;
+ params.tls_rx.tls_rec_length = tls_len;
+ params.tls_rx.tls_rec_number = tls_rcd_num;
+ SOCKBUF_UNLOCK(&so->so_rcv);
+
+ INP_RUNLOCK(inp);
+
+ MPASS(mst->sw->type == IF_SND_TAG_TYPE_TLS_RX);
+ return (mst->sw->snd_tag_modify(mst, ¶ms));
+}
+
+static struct mbuf *
+ktls_dup_rx_chain(struct mbuf *m)
+{
+ struct mbuf *top;
+ struct mbuf **pp;
+
+ MPASS(m != NULL);
+
+ pp = ⊤
+
+ do {
+ *pp = m_get2(m->m_len, M_WAITOK, MT_DATA, M_NOTREADY);
+ if (m->m_flags & M_DECRYPTED)
+ m_copydata(m, 0, ((*pp)->m_len = m->m_len), mtod(*pp, caddr_t));
+ else
+ memset(mtod(*pp, caddr_t), 0, ((*pp)->m_len = m->m_len));
+ pp = &(*pp)->m_next;
+ } while ((m = m->m_next) != NULL);
+
+ *pp = NULL;
+
+ return (top);
+}
+
static void
ktls_decrypt(struct socket *so)
{
@@ -1953,9 +2232,9 @@
struct sockbuf *sb;
struct tls_record_layer *hdr;
struct tls_get_record tgr;
- struct mbuf *control, *data, *m;
+ struct mbuf *control, *data, *m, *m0, *m1, **pp;
uint64_t seqno;
- int error, remain, tls_len, trail_len;
+ int error, remain, tls_len, trail_len, state;
bool tls13;
uint8_t vminor, record_type;
@@ -2026,13 +2305,63 @@
SBCHECK(sb);
SOCKBUF_UNLOCK(sb);
- error = tls->sw_decrypt(tls, hdr, data, seqno, &trail_len);
- if (error == 0) {
- if (tls13)
+ /* get crypto state for this TLS record */
+ state = ktls_mbuf_crypto_state(data, 0, tls_len);
+
+ switch (state) {
+ case KTLS_MBUF_CRYPTO_ST_MIXED:
+ pp = &data;
+ m0 = data;
+ m1 = ktls_dup_rx_chain(data);
+
+ /* Perform XOR of crypto sequence. */
+ error = tls->sw_recrypt(tls, hdr, m1, seqno);
+ if (__predict_false(error != 0)) {
+ m_freem(m1);
+ break;
+ }
+
+ /* Reconstruct encrypted mbuf data. */
+ while (m0 != NULL) {
+ if (m0->m_flags & M_DECRYPTED) {
+ *pp = m1;
+ pp = &(*pp)->m_next;
+ m0 = m_free(m0);
+ m1 = m1->m_next;
+ } else {
+ *pp = m0;
+ pp = &(*pp)->m_next;
+ m0 = m0->m_next;
+ m1 = m_free(m1);
+ }
+ }
+ *pp = NULL;
+
+ MPASS(m1 == NULL);
+
+ /* FALLTHROUGH */
+ case KTLS_MBUF_CRYPTO_ST_ENCRYPTED:
+ error = tls->sw_decrypt(tls, hdr, data, seqno, &trail_len);
+ if (error == 0) {
+ if (tls13) {
+ error = tls13_find_record_type(tls, data,
+ tls_len, &trail_len, &record_type);
+ } else {
+ record_type = hdr->tls_type;
+ }
+ }
+ break;
+ default:
+ if (tls13) {
+ trail_len = tls->params.tls_tlen - 1; /* XXX */
error = tls13_find_record_type(tls, data,
tls_len, &trail_len, &record_type);
- else
+ } else {
+ trail_len = tls->params.tls_tlen; /* XXX */
+ error = 0;
record_type = hdr->tls_type;
+ }
+ break;
}
if (error) {
counter_u64_add(ktls_offload_failed_crypto, 1);
@@ -2112,19 +2441,31 @@
remain = be16toh(tgr.tls_length);
m = data;
for (m = data; remain > m->m_len; m = m->m_next) {
- m->m_flags &= ~M_NOTREADY;
+ m->m_flags &= ~(M_NOTREADY | M_DECRYPTED);
remain -= m->m_len;
}
m->m_len = remain;
m_freem(m->m_next);
m->m_next = NULL;
- m->m_flags &= ~M_NOTREADY;
+ m->m_flags &= ~(M_NOTREADY | M_DECRYPTED);
/* Set EOR on the final mbuf. */
m->m_flags |= M_EOR;
}
sbappendcontrol_locked(sb, data, control, 0);
+
+ if (__predict_false(state != -1)) {
+ sb->sb_flags |= SB_TLS_RX_RESYNC;
+ SOCKBUF_UNLOCK(sb);
+ ktls_resync_ifnet(so, tls_len, seqno);
+ SOCKBUF_LOCK(sb);
+ } else if (__predict_false(sb->sb_flags & SB_TLS_RX_RESYNC)) {
+ sb->sb_flags &= ~SB_TLS_RX_RESYNC;
+ SOCKBUF_UNLOCK(sb);
+ ktls_resync_ifnet(so, 0, seqno);
+ SOCKBUF_LOCK(sb);
+ }
}
sb->sb_flags &= ~SB_TLS_RX_RUNNING;
diff --git a/sys/kern/uipc_sockbuf.c b/sys/kern/uipc_sockbuf.c
--- a/sys/kern/uipc_sockbuf.c
+++ b/sys/kern/uipc_sockbuf.c
@@ -54,6 +54,8 @@
#include <sys/sx.h>
#include <sys/sysctl.h>
+#include <netinet/in.h>
+
/*
* Function pointer set by the AIO routines so that the socket buffer code
* can call back into the AIO module if it is loaded.
@@ -924,14 +926,33 @@
sbappend_ktls_rx(struct sockbuf *sb, struct mbuf *m)
{
struct mbuf *n;
+ int flags = 0;
SBLASTMBUFCHK(sb);
+ MPASS((m->m_flags & M_PKTHDR) != 0);
+
/* Remove all packet headers and mbuf tags to get a pure data chain. */
- m_demote(m, 1, 0);
+ for (n = m; n != NULL; n = n->m_next) {
+ if (n->m_flags & M_PKTHDR) {
+ switch (n->m_pkthdr.csum_flags & CSUM_TLS_MASK) {
+ case CSUM_TLS_DECRYPTED:
+ m_demote_pkthdr(n);
+ /* mark all subsequent packets decrypted */
+ flags = M_NOTREADY | M_DECRYPTED;
+ break;
+ default:
+ m_demote_pkthdr(n);
+ /* mark all subsequent packets not ready */
+ flags = M_NOTREADY;
+ break;
+ }
+ }
+
+ n->m_flags &= M_DEMOTEFLAGS;
+ n->m_flags |= flags;
+ }
- for (n = m; n != NULL; n = n->m_next)
- n->m_flags |= M_NOTREADY;
sbcompress_ktls_rx(sb, m, sb->sb_mtlstail);
ktls_check_rx(sb);
}
@@ -1386,7 +1407,8 @@
if (n &&
M_WRITABLE(n) &&
((sb->sb_flags & SB_NOCOALESCE) == 0) &&
- !(n->m_flags & (M_EXTPG)) &&
+ !((m->m_flags ^ n->m_flags) & M_DECRYPTED) &&
+ !(n->m_flags & M_EXTPG) &&
m->m_len <= MCLBYTES / 4 && /* XXX: Don't copy too much */
m->m_len <= M_TRAILINGSPACE(n)) {
m_copydata(m, 0, m->m_len, mtodo(n, n->m_len));
diff --git a/sys/net/if_lagg.c b/sys/net/if_lagg.c
--- a/sys/net/if_lagg.c
+++ b/sys/net/if_lagg.c
@@ -1766,6 +1766,14 @@
.type = IF_SND_TAG_TYPE_TLS
};
+static const struct if_snd_tag_sw lagg_snd_tag_tls_rx_sw = {
+ .snd_tag_modify = lagg_snd_tag_modify,
+ .snd_tag_query = lagg_snd_tag_query,
+ .snd_tag_free = lagg_snd_tag_free,
+ .next_snd_tag = lagg_next_snd_tag,
+ .type = IF_SND_TAG_TYPE_TLS_RX
+};
+
#ifdef RATELIMIT
static const struct if_snd_tag_sw lagg_snd_tag_tls_rl_sw = {
.snd_tag_modify = lagg_snd_tag_modify,
@@ -1851,6 +1859,9 @@
case IF_SND_TAG_TYPE_TLS:
sw = &lagg_snd_tag_tls_sw;
break;
+ case IF_SND_TAG_TYPE_TLS_RX:
+ sw = &lagg_snd_tag_tls_rx_sw;
+ break;
#ifdef RATELIMIT
case IF_SND_TAG_TYPE_TLS_RATE_LIMIT:
sw = &lagg_snd_tag_tls_rl_sw;
diff --git a/sys/net/if_var.h b/sys/net/if_var.h
--- a/sys/net/if_var.h
+++ b/sys/net/if_var.h
@@ -192,7 +192,8 @@
#define IF_SND_TAG_TYPE_UNLIMITED 1
#define IF_SND_TAG_TYPE_TLS 2
#define IF_SND_TAG_TYPE_TLS_RATE_LIMIT 3
-#define IF_SND_TAG_TYPE_MAX 4
+#define IF_SND_TAG_TYPE_TLS_RX 4
+#define IF_SND_TAG_TYPE_MAX 5
struct if_snd_tag_alloc_header {
uint32_t type; /* send tag type, see IF_SND_TAG_XXX */
@@ -214,6 +215,13 @@
const struct ktls_session *tls;
};
+struct if_snd_tag_alloc_tls_rx {
+ struct if_snd_tag_alloc_header hdr;
+ struct inpcb *inp;
+ const struct ktls_session *tls;
+ uint16_t vlan_id; /* valid if non-zero */
+};
+
struct if_snd_tag_alloc_tls_rate_limit {
struct if_snd_tag_alloc_header hdr;
struct inpcb *inp;
@@ -229,11 +237,26 @@
uint32_t flags; /* M_NOWAIT or M_WAITOK */
};
+struct if_snd_tag_modify_tls_rx {
+ /* TCP sequence number of TLS header in host endian format */
+ uint32_t tls_hdr_tcp_sn;
+
+ /*
+ * TLS record length, including all headers, data and trailers.
+ * If the tls_rec_length is zero, it means HW encryption resumed.
+ */
+ uint32_t tls_rec_length;
+
+ /* TLS record number in host endian format */
+ uint64_t tls_rec_number;
+};
+
union if_snd_tag_alloc_params {
struct if_snd_tag_alloc_header hdr;
struct if_snd_tag_alloc_rate_limit rate_limit;
struct if_snd_tag_alloc_rate_limit unlimited;
struct if_snd_tag_alloc_tls tls;
+ struct if_snd_tag_alloc_tls_rx tls_rx;
struct if_snd_tag_alloc_tls_rate_limit tls_rate_limit;
};
@@ -241,6 +264,7 @@
struct if_snd_tag_rate_limit_params rate_limit;
struct if_snd_tag_rate_limit_params unlimited;
struct if_snd_tag_rate_limit_params tls_rate_limit;
+ struct if_snd_tag_modify_tls_rx tls_rx;
};
union if_snd_tag_query_params {
diff --git a/sys/net/if_vlan.c b/sys/net/if_vlan.c
--- a/sys/net/if_vlan.c
+++ b/sys/net/if_vlan.c
@@ -363,6 +363,14 @@
.type = IF_SND_TAG_TYPE_TLS
};
+static const struct if_snd_tag_sw vlan_snd_tag_tls_rx_sw = {
+ .snd_tag_modify = vlan_snd_tag_modify,
+ .snd_tag_query = vlan_snd_tag_query,
+ .snd_tag_free = vlan_snd_tag_free,
+ .next_snd_tag = vlan_next_snd_tag,
+ .type = IF_SND_TAG_TYPE_TLS_RX
+};
+
#ifdef RATELIMIT
static const struct if_snd_tag_sw vlan_snd_tag_tls_rl_sw = {
.snd_tag_modify = vlan_snd_tag_modify,
@@ -2166,6 +2174,9 @@
struct ifnet *parent;
int error;
+ NET_EPOCH_ENTER(et);
+ ifv = ifp->if_softc;
+
switch (params->hdr.type) {
#ifdef RATELIMIT
case IF_SND_TAG_TYPE_UNLIMITED:
@@ -2179,6 +2190,12 @@
case IF_SND_TAG_TYPE_TLS:
sw = &vlan_snd_tag_tls_sw;
break;
+ case IF_SND_TAG_TYPE_TLS_RX:
+ sw = &vlan_snd_tag_tls_rx_sw;
+ if (params->tls_rx.vlan_id != 0)
+ goto failure;
+ params->tls_rx.vlan_id = ifv->ifv_vid;
+ break;
#ifdef RATELIMIT
case IF_SND_TAG_TYPE_TLS_RATE_LIMIT:
sw = &vlan_snd_tag_tls_rl_sw;
@@ -2186,19 +2203,15 @@
#endif
#endif
default:
- return (EOPNOTSUPP);
+ goto failure;
}
- NET_EPOCH_ENTER(et);
- ifv = ifp->if_softc;
if (ifv->ifv_trunk != NULL)
parent = PARENT(ifv);
else
parent = NULL;
- if (parent == NULL) {
- NET_EPOCH_EXIT(et);
- return (EOPNOTSUPP);
- }
+ if (parent == NULL)
+ goto failure;
if_ref(parent);
NET_EPOCH_EXIT(et);
@@ -2219,6 +2232,9 @@
*ppmt = &vst->com;
return (0);
+failure:
+ NET_EPOCH_EXIT(et);
+ return (EOPNOTSUPP);
}
static struct m_snd_tag *
diff --git a/sys/netinet/ip_output.c b/sys/netinet/ip_output.c
--- a/sys/netinet/ip_output.c
+++ b/sys/netinet/ip_output.c
@@ -231,7 +231,7 @@
*/
if (m->m_next != NULL && mbuf_has_tls_session(m->m_next)) {
tls = ktls_hold(m->m_next->m_epg_tls);
- mst = tls->snd_tag;
+ mst = tls->snd_rcv_tag;
/*
* If a TLS session doesn't have a valid tag, it must
@@ -279,7 +279,7 @@
#ifdef KERN_TLS
if (tls != NULL) {
if (error == EAGAIN)
- error = ktls_output_eagain(inp, tls);
+ error = ktls_output_eagain(inp);
ktls_free(tls);
}
#endif
diff --git a/sys/netinet/tcp_ratelimit.c b/sys/netinet/tcp_ratelimit.c
--- a/sys/netinet/tcp_ratelimit.c
+++ b/sys/netinet/tcp_ratelimit.c
@@ -1354,14 +1354,14 @@
if (rte)
rl_increment_using(rte);
#ifdef KERN_TLS
- if (rte != NULL && tls != NULL && tls->snd_tag != NULL) {
+ if (rte != NULL && tls != NULL && tls->snd_rcv_tag != NULL) {
/*
* Fake a route change error to reset the TLS
* send tag. This will convert the existing
* tag to a TLS ratelimit tag.
*/
- MPASS(tls->snd_tag->sw->type == IF_SND_TAG_TYPE_TLS);
- ktls_output_eagain(tp->t_inpcb, tls);
+ MPASS(tls->snd_rcv_tag->sw->type == IF_SND_TAG_TYPE_TLS);
+ ktls_output_eagain(tp->t_inpcb);
}
#endif
} else {
@@ -1404,8 +1404,8 @@
if (tp->t_inpcb->inp_socket->so_snd.sb_flags & SB_TLS_IFNET) {
tls = tp->t_inpcb->inp_socket->so_snd.sb_tls_info;
MPASS(tls->mode == TCP_TLS_MODE_IFNET);
- if (tls->snd_tag != NULL &&
- tls->snd_tag->sw->type != IF_SND_TAG_TYPE_TLS_RATE_LIMIT) {
+ if (tls->snd_rcv_tag != NULL &&
+ tls->snd_rcv_tag->sw->type != IF_SND_TAG_TYPE_TLS_RATE_LIMIT) {
/*
* NIC probably doesn't support ratelimit TLS
* tags if it didn't allocate one when an
diff --git a/sys/netinet6/ip6_output.c b/sys/netinet6/ip6_output.c
--- a/sys/netinet6/ip6_output.c
+++ b/sys/netinet6/ip6_output.c
@@ -328,7 +328,7 @@
*/
if (m->m_next != NULL && mbuf_has_tls_session(m->m_next)) {
tls = ktls_hold(m->m_next->m_epg_tls);
- mst = tls->snd_tag;
+ mst = tls->snd_rcv_tag;
/*
* If a TLS session doesn't have a valid tag, it must
@@ -376,7 +376,7 @@
#ifdef KERN_TLS
if (tls != NULL) {
if (error == EAGAIN)
- error = ktls_output_eagain(inp, tls);
+ error = ktls_output_eagain(inp);
ktls_free(tls);
}
#endif
diff --git a/sys/opencrypto/ktls_ocf.c b/sys/opencrypto/ktls_ocf.c
--- a/sys/opencrypto/ktls_ocf.c
+++ b/sys/opencrypto/ktls_ocf.c
@@ -96,6 +96,11 @@
CTLFLAG_RD, &ocf_tls12_gcm_encrypts,
"Total number of OCF TLS 1.2 GCM encryption operations");
+static COUNTER_U64_DEFINE_EARLY(ocf_tls12_gcm_recrypts);
+SYSCTL_COUNTER_U64(_kern_ipc_tls_stats_ocf, OID_AUTO, tls12_gcm_recrypts,
+ CTLFLAG_RD, &ocf_tls12_gcm_recrypts,
+ "Total number of OCF TLS 1.2 GCM recryption operations");
+
static COUNTER_U64_DEFINE_EARLY(ocf_tls12_chacha20_decrypts);
SYSCTL_COUNTER_U64(_kern_ipc_tls_stats_ocf, OID_AUTO, tls12_chacha20_decrypts,
CTLFLAG_RD, &ocf_tls12_chacha20_decrypts,
@@ -106,6 +111,11 @@
CTLFLAG_RD, &ocf_tls12_chacha20_encrypts,
"Total number of OCF TLS 1.2 Chacha20-Poly1305 encryption operations");
+static COUNTER_U64_DEFINE_EARLY(ocf_tls12_chacha20_recrypts);
+SYSCTL_COUNTER_U64(_kern_ipc_tls_stats_ocf, OID_AUTO, tls12_chacha20_recrypts,
+ CTLFLAG_RD, &ocf_tls12_chacha20_recrypts,
+ "Total number of OCF TLS 1.2 Chacha20-Poly1305 recryption operations");
+
static COUNTER_U64_DEFINE_EARLY(ocf_tls13_gcm_decrypts);
SYSCTL_COUNTER_U64(_kern_ipc_tls_stats_ocf, OID_AUTO, tls13_gcm_decrypts,
CTLFLAG_RD, &ocf_tls13_gcm_decrypts,
@@ -532,6 +542,79 @@
return (error);
}
+static int
+ktls_ocf_tls12_aead_recrypt(struct ktls_session *tls,
+ const struct tls_record_layer *hdr, struct mbuf *m, uint64_t seqno)
+{
+ char tag[MAX(AES_GMAC_HASH_LEN, POLY1305_HASH_LEN)] __aligned(8);
+ struct tls_aead_data ad;
+ struct cryptop crp;
+ struct ktls_ocf_session *os;
+ int error;
+ uint16_t tls_comp_len;
+
+ if (tls->params.tls_tlen > sizeof(tag))
+ return (ENOMEM);
+
+ os = tls->ocf_session;
+
+ crypto_initreq(&crp, os->sid);
+
+ /* Setup the IV. */
+ if (tls->params.cipher_algorithm == CRYPTO_AES_NIST_GCM_16) {
+ memcpy(crp.crp_iv, tls->params.iv, TLS_AEAD_GCM_LEN);
+ memcpy(crp.crp_iv + TLS_AEAD_GCM_LEN, hdr + 1,
+ sizeof(uint64_t));
+ } else {
+ /*
+ * Chacha20-Poly1305 constructs the IV for TLS 1.2
+ * identically to constructing the IV for AEAD in TLS
+ * 1.3.
+ */
+ memcpy(crp.crp_iv, tls->params.iv, tls->params.iv_len);
+ *(uint64_t *)(crp.crp_iv + 4) ^= htobe64(seqno);
+ }
+
+ /* Setup the AAD. */
+ if (tls->params.cipher_algorithm == CRYPTO_AES_NIST_GCM_16)
+ tls_comp_len = ntohs(hdr->tls_length) -
+ (AES_GMAC_HASH_LEN + sizeof(uint64_t));
+ else
+ tls_comp_len = ntohs(hdr->tls_length) - POLY1305_HASH_LEN;
+ ad.seq = htobe64(seqno);
+ ad.type = hdr->tls_type;
+ ad.tls_vmajor = hdr->tls_vmajor;
+ ad.tls_vminor = hdr->tls_vminor;
+ ad.tls_length = htons(tls_comp_len);
+ crp.crp_aad = &ad;
+ crp.crp_aad_length = sizeof(ad);
+
+ crp.crp_payload_start = tls->params.tls_hlen;
+ crp.crp_payload_length = tls_comp_len;
+ crp.crp_digest_start = crp.crp_payload_start + crp.crp_payload_length;
+
+ crp.crp_op = CRYPTO_OP_ENCRYPT | CRYPTO_OP_COMPUTE_DIGEST;
+ crp.crp_flags = CRYPTO_F_CBIMM | CRYPTO_F_IV_SEPARATE;
+ crypto_use_mbuf(&crp, m);
+
+ if (tls->params.cipher_algorithm == CRYPTO_AES_NIST_GCM_16)
+ counter_u64_add(ocf_tls12_gcm_recrypts, 1);
+ else
+ counter_u64_add(ocf_tls12_chacha20_recrypts, 1);
+
+ counter_u64_add(ocf_inplace, 1);
+
+ m_copydata(m, tls->params.tls_hlen + tls_comp_len, tls->params.tls_tlen, tag);
+
+ error = ktls_ocf_dispatch(os, &crp);
+
+ m_copyback(m, tls->params.tls_hlen + tls_comp_len, tls->params.tls_tlen, tag);
+
+ crypto_destroyreq(&crp);
+
+ return (error);
+}
+
static int
ktls_ocf_tls13_aead_encrypt(struct ktls_ocf_encrypt_state *state,
struct ktls_session *tls, struct mbuf *m, struct iovec *outiov,
@@ -815,6 +898,8 @@
tls->sw_decrypt = ktls_ocf_tls13_aead_decrypt;
else
tls->sw_decrypt = ktls_ocf_tls12_aead_decrypt;
+
+ tls->sw_recrypt = ktls_ocf_tls12_aead_recrypt;
}
} else {
tls->sw_encrypt = ktls_ocf_tls_cbc_encrypt;
diff --git a/sys/sys/ktls.h b/sys/sys/ktls.h
--- a/sys/sys/ktls.h
+++ b/sys/sys/ktls.h
@@ -180,16 +180,22 @@
int (*sw_encrypt)(struct ktls_ocf_encrypt_state *state,
struct ktls_session *tls, struct mbuf *m,
struct iovec *outiov, int outiovcnt);
- int (*sw_decrypt)(struct ktls_session *tls,
- const struct tls_record_layer *hdr, struct mbuf *m,
- uint64_t seqno, int *trailer_len);
+ struct {
+ int (*sw_decrypt)(struct ktls_session *tls,
+ const struct tls_record_layer *hdr, struct mbuf *m,
+ uint64_t seqno, int *trailer_len);
+ int (*sw_recrypt)(struct ktls_session *tls,
+ const struct tls_record_layer *hdr, struct mbuf *m,
+ uint64_t seqno);
+ };
};
struct ktls_ocf_session *ocf_session;
- struct m_snd_tag *snd_tag;
+ struct m_snd_tag *snd_rcv_tag;
struct tls_session_params params;
u_int wq_index;
volatile u_int refcount;
int mode;
+ int direction;
struct task reset_tag_task;
struct task disable_ifnet_task;
@@ -207,6 +213,10 @@
extern unsigned int ktls_ifnet_max_rexmit_pct;
void ktls_check_rx(struct sockbuf *sb);
+int ktls_mbuf_crypto_state(struct mbuf *mb, int offset, int len);
+#define KTLS_MBUF_CRYPTO_ST_MIXED 0
+#define KTLS_MBUF_CRYPTO_ST_ENCRYPTED 1
+#define KTLS_MBUF_CRYPTO_ST_DECRYPTED -1
void ktls_disable_ifnet(void *arg);
int ktls_enable_rx(struct socket *so, struct tls_enable *en);
int ktls_enable_tx(struct socket *so, struct tls_enable *en);
@@ -219,7 +229,8 @@
int ktls_get_rx_mode(struct socket *so, int *modep);
int ktls_set_tx_mode(struct socket *so, int mode);
int ktls_get_tx_mode(struct socket *so, int *modep);
-int ktls_output_eagain(struct inpcb *inp, struct ktls_session *tls);
+int ktls_get_rx_sequence(struct inpcb *inp, uint32_t *tcpseq, uint64_t *tlsseq);
+int ktls_output_eagain(struct inpcb *inp);
#ifdef RATELIMIT
int ktls_modify_txrtlmt(struct ktls_session *tls, uint64_t max_pacing_rate);
#endif
diff --git a/sys/sys/sockbuf.h b/sys/sys/sockbuf.h
--- a/sys/sys/sockbuf.h
+++ b/sys/sys/sockbuf.h
@@ -53,6 +53,7 @@
#define SB_STOP 0x1000 /* backpressure indicator */
#define SB_AIO_RUNNING 0x2000 /* AIO operation running */
#define SB_TLS_IFNET 0x4000 /* has used / is using ifnet KTLS */
+#define SB_TLS_RX_RESYNC 0x8000 /* KTLS RX lost HW sync */
#define SBS_CANTSENDMORE 0x0010 /* can't send more data to peer */
#define SBS_CANTRCVMORE 0x0020 /* can't receive more data from peer */
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Thu, May 1, 6:19 PM (14 h, 59 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
17888142
Default Alt Text
D32356.id100069.diff (32 KB)
Attached To
Mode
D32356: ktls: Add full support for TLS RX offloading via network interface.
Attached
Detach File
Event Timeline
Log In to Comment