Thread (32 messages) flat view 32 messages, 2 authors, 2023-08-15

Re: [PATCH 15/17] nvmet-tcp: enable TLS handshake upcall

From: Sagi Grimberg <sagi@grimberg.me>
Date: 2023-08-14 19:12:31
Also in: linux-nvme

quoted
quoted
@@ -1864,6 +1877,14 @@ static struct config_group 
*nvmet_ports_make(struct config_group *group,
          return ERR_PTR(-ENOMEM);
      }
+    if (nvme_keyring_id()) {
+        port->keyring = key_lookup(nvme_keyring_id());
+        if (IS_ERR(port->keyring)) {
+            pr_warn("NVMe keyring not available, disabling TLS\n");
+            port->keyring = NULL;
why setting this to NULL?
It's check when changing TSAS; we can only enable TLS if the nvme 
keyring is available.
ok
quoted
quoted
+        }
+    }
+
      for (i = 1; i <= NVMET_MAX_ANAGRPS; i++) {
          if (i == NVMET_DEFAULT_ANA_GRPID)
              port->ana_state[1] = NVME_ANA_OPTIMIZED;
diff --git a/drivers/nvme/target/nvmet.h b/drivers/nvme/target/nvmet.h
index 8cfd60f3b564..7f9ae53c1df5 100644
--- a/drivers/nvme/target/nvmet.h
+++ b/drivers/nvme/target/nvmet.h
@@ -158,6 +158,7 @@ struct nvmet_port {
      struct config_group        ana_groups_group;
      struct nvmet_ana_group        ana_default_group;
      enum nvme_ana_state        *ana_state;
+    struct key            *keyring;
      void                *priv;
      bool                enabled;
      int                inline_data_size;
diff --git a/drivers/nvme/target/tcp.c b/drivers/nvme/target/tcp.c
index f19ea9d923fd..77fa339008e1 100644
--- a/drivers/nvme/target/tcp.c
+++ b/drivers/nvme/target/tcp.c
@@ -8,9 +8,13 @@
  #include <linux/init.h>
  #include <linux/slab.h>
  #include <linux/err.h>
+#include <linux/key.h>
  #include <linux/nvme-tcp.h>
+#include <linux/nvme-keyring.h>
  #include <net/sock.h>
  #include <net/tcp.h>
+#include <net/tls.h>
+#include <net/handshake.h>
  #include <linux/inet.h>
  #include <linux/llist.h>
  #include <crypto/hash.h>
@@ -66,6 +70,16 @@ device_param_cb(idle_poll_period_usecs, 
&set_param_ops,
  MODULE_PARM_DESC(idle_poll_period_usecs,
          "nvmet tcp io_work poll till idle time period in usecs: 
Default 0");
+#ifdef CONFIG_NVME_TARGET_TCP_TLS
+/*
+ * TLS handshake timeout
+ */
+static int tls_handshake_timeout = 10;
+module_param(tls_handshake_timeout, int, 0644);
+MODULE_PARM_DESC(tls_handshake_timeout,
+         "nvme TLS handshake timeout in seconds (default 10)");
+#endif
+
  #define NVMET_TCP_RECV_BUDGET        8
  #define NVMET_TCP_SEND_BUDGET        8
  #define NVMET_TCP_IO_WORK_BUDGET    64
@@ -122,11 +136,13 @@ struct nvmet_tcp_cmd {
  enum nvmet_tcp_queue_state {
      NVMET_TCP_Q_CONNECTING,
+    NVMET_TCP_Q_TLS_HANDSHAKE,
      NVMET_TCP_Q_LIVE,
      NVMET_TCP_Q_DISCONNECTING,
  };
  struct nvmet_tcp_queue {
+    struct kref        kref;
Why is kref the first member of the struct?
Habit.
I don't mind where it'll end up.
Move it to the back together with the tls section.
quoted
quoted
      struct socket        *sock;
      struct nvmet_tcp_port    *port;
      struct work_struct    io_work;
@@ -155,6 +171,10 @@ struct nvmet_tcp_queue {
      struct ahash_request    *snd_hash;
      struct ahash_request    *rcv_hash;
+    /* TLS state */
+    key_serial_t        tls_pskid;
+    struct delayed_work    tls_handshake_work;
+
      unsigned long           poll_end;
      spinlock_t        state_lock;
@@ -1283,12 +1303,21 @@ static int nvmet_tcp_try_recv(struct 
nvmet_tcp_queue *queue,
      return ret;
  }
+static void nvmet_tcp_release_queue(struct kref *kref)
+{
+    struct nvmet_tcp_queue *queue =
+        container_of(kref, struct nvmet_tcp_queue, kref);
+
+    WARN_ON(queue->state != NVMET_TCP_Q_DISCONNECTING);
+    queue_work(nvmet_wq, &queue->release_work);
+}
+
  static void nvmet_tcp_schedule_release_queue(struct nvmet_tcp_queue 
*queue)
  {
      spin_lock_bh(&queue->state_lock);
      if (queue->state != NVMET_TCP_Q_DISCONNECTING) {
          queue->state = NVMET_TCP_Q_DISCONNECTING;
-        queue_work(nvmet_wq, &queue->release_work);
+        kref_put(&queue->kref, nvmet_tcp_release_queue);
      }
      spin_unlock_bh(&queue->state_lock);
  }
@@ -1485,6 +1514,8 @@ static void nvmet_tcp_release_queue_work(struct 
work_struct *w)
      mutex_unlock(&nvmet_tcp_queue_mutex);
      nvmet_tcp_restore_socket_callbacks(queue);
+    tls_handshake_cancel(queue->sock->sk);
+    cancel_delayed_work_sync(&queue->tls_handshake_work);
We should call it tls_handshake_tmo_work or something to make it
clear it is a timeout work.
Okay.
quoted
quoted
      cancel_work_sync(&queue->io_work);
      /* stop accepting incoming data */
      queue->rcv_state = NVMET_TCP_RECV_ERR;
@@ -1512,8 +1543,13 @@ static void nvmet_tcp_data_ready(struct sock *sk)
      read_lock_bh(&sk->sk_callback_lock);
      queue = sk->sk_user_data;
-    if (likely(queue))
-        queue_work_on(queue_cpu(queue), nvmet_tcp_wq, &queue->io_work);
+    if (likely(queue)) {
+        if (queue->data_ready)
+            queue->data_ready(sk);
+        if (queue->state != NVMET_TCP_Q_TLS_HANDSHAKE)
+            queue_work_on(queue_cpu(queue), nvmet_tcp_wq,
+                      &queue->io_work);
+    }
      read_unlock_bh(&sk->sk_callback_lock);
  }
@@ -1621,6 +1657,83 @@ static int nvmet_tcp_set_queue_sock(struct 
nvmet_tcp_queue *queue)
      return ret;
  }
+#ifdef CONFIG_NVME_TARGET_TCP_TLS
+static void nvmet_tcp_tls_handshake_done(void *data, int status,
+                     key_serial_t peerid)
+{
+    struct nvmet_tcp_queue *queue = data;
+
+    pr_debug("queue %d: TLS handshake done, key %x, status %d\n",
+         queue->idx, peerid, status);
+    spin_lock_bh(&queue->state_lock);
+    if (queue->state != NVMET_TCP_Q_TLS_HANDSHAKE) {
Is this even possible?
I guess it can happen when the socket closes during handshake; the 
daemon might still be sending a 'done' event but 
nvmet_tcp_schedule_release_queue() has been called.
Umm, if the socket closes during the handshake then the state
is NVMET_TCP_Q_TLS_HANDSHAKE.

p.s. you call handshake cancel in the release flow so you should be
fenced properly no?
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help