libcli/smb/smb_direct.c
authorStefan Metzmacher <metze@samba.org>
Wed, 28 Sep 2016 06:15:46 +0000 (08:15 +0200)
committerStefan Metzmacher <metze@samba.org>
Fri, 1 Jun 2018 12:35:05 +0000 (14:35 +0200)
libcli/smb/smb_direct.c [new file with mode: 0644]
libcli/smb/smb_direct.h
libcli/smb/wscript

diff --git a/libcli/smb/smb_direct.c b/libcli/smb/smb_direct.c
new file mode 100644 (file)
index 0000000..92c8fee
--- /dev/null
@@ -0,0 +1,1819 @@
+/*
+   Unix SMB/CIFS implementation.
+   Infrastructure for SMB-Direct RDMA as transport
+   Copyright (C) Stefan Metzmacher 2012,2016
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/network.h"
+#include <tevent.h>
+#include "../util/tevent_ntstatus.h"
+#include "../lib/tsocket/tsocket.h"
+#include "lib/util/util_net.h"
+#include "libcli/smb/smb_direct.h"
+#include "lib/util/dlinklist.h"
+
+#ifdef SMB_TRANSPORT_ENABLE_RDMA
+#include <rdma/rdma_cma_abi.h>
+#include <rdma/rdma_cma.h>
+#include <infiniband/verbs.h>
+
+struct smb_direct_io;
+
+struct smb_direct_connection {
+       struct {
+               uint32_t max_send_size;
+               uint32_t max_receive_size;
+               uint32_t max_fragmented_size;
+               uint32_t max_read_write_size;
+               uint16_t send_credit_target;
+               uint16_t send_credits;
+               uint16_t receive_credit_max;
+               uint16_t receive_credit_target;
+               uint16_t receive_credits;
+               uint32_t keep_alive_internal;
+       } state;
+       struct {
+               int tmp_fd; /* given to the caller end */
+               int fd;
+               struct tevent_fd *fde;
+       } sock;
+       struct {
+               struct rdma_cm_id *cm_id;
+               struct rdma_event_channel *cm_channel;
+               struct tevent_fd *fde_channel;
+               enum rdma_cm_event_type expected_event;
+               struct rdma_cm_event *cm_event;
+       } rdma;
+       struct {
+               struct ibv_pd *pd;
+               struct ibv_comp_channel *comp_channel;
+               struct tevent_fd *fde_channel;
+               struct ibv_cq *send_cq;
+               struct ibv_cq *recv_cq;
+               struct ibv_qp *qp;
+               struct ibv_qp_init_attr init_attr;
+       } ibv;
+
+       TALLOC_CTX *io_mem_ctx;
+       struct {
+               struct smb_direct_io *idle;
+               struct smb_direct_io *pending;
+               struct smb_direct_io *ready;
+       } in;
+       struct {
+               struct smb_direct_io *idle;
+               struct smb_direct_io *pending;
+               struct smb_direct_io *ready;
+       } out;
+};
+
+#define SMB_DIRECT_IO_MAX_DATA 8192
+
+struct smb_direct_io {
+       struct smb_direct_io *prev, *next;
+
+       struct ibv_mr *hdr_mr;
+       struct ibv_mr *data_mr;
+       struct ibv_sge sge[2];
+
+       struct ibv_recv_wr rwr;
+       struct ibv_send_wr swr;
+
+       uint8_t nbt_hdr[0x04];
+       uint8_t smbd_hdr[0x18];
+       uint8_t data[SMB_DIRECT_IO_MAX_DATA];
+};
+
+static int smb_direct_io_destructor(struct smb_direct_io *io);
+
+static struct smb_direct_io *smb_direct_io_create(struct smb_direct_connection *c)
+{
+       struct smb_direct_io *io;
+
+       if (c->io_mem_ctx == NULL) {
+               return NULL;
+       }
+
+       io = talloc_zero(c->io_mem_ctx, struct smb_direct_io);
+       if (io == NULL) {
+               return NULL;
+       }
+       talloc_set_destructor(io, smb_direct_io_destructor);
+
+       io->hdr_mr = ibv_reg_mr(c->ibv.pd,
+                               io->smbd_hdr,
+                               sizeof(io->smbd_hdr),
+                               IBV_ACCESS_LOCAL_WRITE);
+       if (io->hdr_mr == NULL) {
+               TALLOC_FREE(io);
+               return NULL;
+       }
+
+       io->data_mr = ibv_reg_mr(c->ibv.pd,
+                                io->data,
+                                sizeof(io->data),
+                                IBV_ACCESS_LOCAL_WRITE);
+       if (io->data_mr == NULL) {
+               TALLOC_FREE(io);
+               return NULL;
+       }
+
+       return io;
+}
+
+static int smb_direct_io_destructor(struct smb_direct_io *io)
+{
+       if (io->hdr_mr != NULL) {
+               ibv_dereg_mr(io->hdr_mr);
+               io->hdr_mr = NULL;
+       }
+
+       if (io->data_mr != NULL) {
+               ibv_dereg_mr(io->data_mr);
+               io->data_mr = NULL;
+       }
+
+       return 0;
+}
+
+static int smb_direct_connection_destructor(struct smb_direct_connection *c);
+
+struct smb_direct_connection *smb_direct_connection_create(TALLOC_CTX *mem_ctx)
+{
+       struct smb_direct_connection *c;
+       int sfd[2];
+       int ret;
+       uint16_t i;
+
+       c = talloc_zero(mem_ctx, struct smb_direct_connection);
+       if (c == NULL) {
+               return NULL;
+       }
+       c->sock.fd = -1;
+       c->sock.tmp_fd = -1;
+
+       talloc_set_destructor(c, smb_direct_connection_destructor);
+
+       c->state.max_send_size       = 1364;
+       c->state.max_receive_size    = SMB_DIRECT_IO_MAX_DATA;
+       c->state.max_fragmented_size = 1048576;
+       c->state.max_read_write_size = 0;
+       c->state.receive_credit_max  = 16;
+       c->state.send_credit_target  = 255;
+       c->state.keep_alive_internal = 5;
+
+       ret = socketpair(AF_UNIX, 0, SOCK_STREAM, sfd);
+       if (ret == -1) {
+               int saved_errno = errno;
+               TALLOC_FREE(c);
+               errno = saved_errno;
+               return NULL;
+       }
+       c->sock.tmp_fd = sfd[0];
+       c->sock.fd = sfd[1];
+
+       smb_set_close_on_exec(c->sock.tmp_fd);
+       smb_set_close_on_exec(c->sock.fd);
+       set_blocking(c->sock.fd, false);
+
+       c->rdma.cm_channel = rdma_create_event_channel();
+       if (c->rdma.cm_channel == NULL) {
+               TALLOC_FREE(c);
+               return NULL;
+       }
+       smb_set_close_on_exec(c->rdma.cm_channel->fd);
+       set_blocking(c->rdma.cm_channel->fd, false);
+
+#if RDMA_USER_CM_MAX_ABI_VERSION >= 2
+       ret = rdma_create_id(c->rdma.cm_channel,
+                            &c->rdma.cm_id,
+                            c, RDMA_PS_TCP);
+#else
+#error
+       ret = rdma_create_id(c->rdma.cm_channel,
+                            &c->rdma.cm_id,
+                            c);
+#endif
+       if (ret != 0) {
+               TALLOC_FREE(c);
+               return NULL;
+       }
+
+       c->ibv.pd = ibv_alloc_pd(c->rdma.cm_id->verbs);
+       if (c->ibv.pd == NULL) {
+               TALLOC_FREE(c);
+               return NULL;
+       }
+
+       c->ibv.comp_channel = ibv_create_comp_channel(c->rdma.cm_id->verbs);
+       if (c->ibv.comp_channel == NULL) {
+               TALLOC_FREE(c);
+               return NULL;
+       }
+       smb_set_close_on_exec(c->ibv.comp_channel->fd);
+       set_blocking(c->ibv.comp_channel->fd, false);
+
+       c->ibv.init_attr.cap.max_send_wr = 2;
+       c->ibv.init_attr.cap.max_recv_wr = 2;
+       c->ibv.init_attr.cap.max_recv_sge = 2;
+       c->ibv.init_attr.cap.max_send_sge = 2;
+       c->ibv.init_attr.qp_type = IBV_QPT_RC;
+       c->ibv.init_attr.sq_sig_all = 1;
+
+       c->ibv.send_cq = ibv_create_cq(c->rdma.cm_id->verbs,
+                                      c->ibv.init_attr.cap.max_send_wr,
+                                      c, c->ibv.comp_channel, 0);
+       if (c->ibv.send_cq == NULL) {
+               TALLOC_FREE(c);
+               return NULL;
+       }
+       c->ibv.init_attr.send_cq = c->ibv.send_cq;
+
+       c->ibv.recv_cq = ibv_create_cq(c->rdma.cm_id->verbs,
+                                      c->ibv.init_attr.cap.max_recv_wr,
+                                      c, c->ibv.comp_channel, 0);
+       if (c->ibv.recv_cq == NULL) {
+               TALLOC_FREE(c);
+               return NULL;
+       }
+       c->ibv.init_attr.recv_cq = c->ibv.recv_cq;
+
+       ret = ibv_req_notify_cq(c->ibv.send_cq, 0);
+       if (ret != 0) {
+               TALLOC_FREE(c);
+               return NULL;
+       }
+
+       ret = ibv_req_notify_cq(c->ibv.recv_cq, 0);
+       if (ret != 0) {
+               TALLOC_FREE(c);
+               return NULL;
+       }
+
+       ret = rdma_create_qp(c->rdma.cm_id, c->ibv.pd, &c->ibv.init_attr);
+       if (ret != 0) {
+               TALLOC_FREE(c);
+               return NULL;
+       }
+       c->ibv.qp = c->rdma.cm_id->qp;
+
+       c->io_mem_ctx = talloc_named_const(c, 0, "io_mem_ctx");
+       if (c->io_mem_ctx == NULL) {
+               TALLOC_FREE(c);
+               return NULL;
+       }
+
+       for (i = 0; i < c->state.receive_credit_max; i++) {
+               struct smb_direct_io *io;
+
+               io = smb_direct_io_create(c);
+               DLIST_ADD_END(c->in.idle, io);
+       }
+
+       for (i = 0; i < c->state.send_credit_target; i++) {
+               struct smb_direct_io *io;
+
+               io = smb_direct_io_create(c);
+               DLIST_ADD_END(c->out.idle, io);
+       }
+
+       return c;
+}
+
+static int smb_direct_connection_destructor(struct smb_direct_connection *c)
+{
+       TALLOC_FREE(c->sock.fde);
+
+       if (c->sock.fd != -1) {
+               close(c->sock.fd);
+               c->sock.fd = -1;
+       }
+
+       if (c->sock.tmp_fd != -1) {
+               close(c->sock.tmp_fd);
+               c->sock.tmp_fd = -1;
+       }
+
+       TALLOC_FREE(c->ibv.fde_channel);
+       TALLOC_FREE(c->rdma.fde_channel);
+
+       TALLOC_FREE(c->io_mem_ctx);
+       ZERO_STRUCT(c->in);
+       ZERO_STRUCT(c->out);
+
+       if (c->rdma.cm_event != NULL) {
+               rdma_ack_cm_event(c->rdma.cm_event);
+               c->rdma.cm_event = NULL;
+       }
+
+       if (c->ibv.qp != NULL) {
+               ibv_destroy_qp(c->ibv.qp);
+               c->ibv.qp = NULL;
+       }
+
+       if (c->ibv.send_cq != NULL) {
+               ibv_destroy_cq(c->ibv.send_cq);
+               c->ibv.send_cq = NULL;
+       }
+
+       if (c->ibv.recv_cq != NULL) {
+               ibv_destroy_cq(c->ibv.recv_cq);
+               c->ibv.recv_cq = NULL;
+       }
+
+       if (c->ibv.comp_channel != NULL) {
+               ibv_destroy_comp_channel(c->ibv.comp_channel);
+               c->ibv.comp_channel = NULL;
+       }
+
+       if (c->ibv.pd != NULL) {
+               ibv_dealloc_pd(c->ibv.pd);
+               c->ibv.pd = NULL;
+       }
+
+       if (c->rdma.cm_id != NULL) {
+               rdma_destroy_id(c->rdma.cm_id);
+               c->rdma.cm_id = NULL;
+       }
+
+       if (c->rdma.cm_channel != NULL) {
+               rdma_destroy_event_channel(c->rdma.cm_channel);
+               c->rdma.cm_channel = NULL;
+       }
+
+       return 0;
+}
+
+struct smb_direct_connection_rdma_connect_state {
+       struct smb_direct_connection *c;
+};
+
+static void smb_direct_connection_rdma_connect_handler(struct tevent_context *ev,
+                                           struct tevent_fd *fde,
+                                           uint16_t flags,
+                                           void *private_data);
+
+static struct tevent_req *smb_direct_connection_rdma_connect_send(TALLOC_CTX *mem_ctx,
+                                       struct tevent_context *ev,
+                                       struct smb_direct_connection *c,
+                                       const struct sockaddr_storage *src,
+                                       const struct sockaddr_storage *dst,
+                                       struct tsocket_address *local_addr,
+                                       struct tsocket_address *remote_addr)
+{
+       struct tevent_req *req;
+       struct smb_direct_connection_rdma_connect_state *state;
+       int ret;
+       //struct sockaddr *src_addr = (const struct sockaddr *)src;
+       struct sockaddr *src_addr = NULL;
+       struct sockaddr_storage _dst_addr = *dst;
+       struct sockaddr *dst_addr = (struct sockaddr *)&_dst_addr;
+
+       set_sockaddr_port(dst_addr, 5445);
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct smb_direct_connection_rdma_connect_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       state->c = c;
+
+       state->c->rdma.fde_channel = tevent_add_fd(ev, state->c,
+                                       state->c->rdma.cm_channel->fd,
+                                       TEVENT_FD_READ,
+                                       smb_direct_connection_rdma_connect_handler,
+                                       req);
+       if (tevent_req_nomem(state->c->rdma.fde_channel, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       errno = 0;
+       ret = rdma_resolve_addr(state->c->rdma.cm_id,
+                               src_addr, dst_addr,
+                               5000);
+       if (ret != 0) {
+               NTSTATUS status = map_nt_error_from_unix_common(errno);
+               DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                       __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+               tevent_req_nterror(req, status);
+               return tevent_req_post(req, ev);
+       }
+       state->c->rdma.expected_event = RDMA_CM_EVENT_ADDR_RESOLVED;
+
+       return req;
+}
+
+static void smb_direct_connection_rdma_connect_handler(struct tevent_context *ev,
+                                           struct tevent_fd *fde,
+                                           uint16_t flags,
+                                           void *private_data)
+{
+       struct tevent_req *req =
+               talloc_get_type_abort(private_data,
+               struct tevent_req);
+       struct smb_direct_connection_rdma_connect_state *state =
+               tevent_req_data(req,
+               struct smb_direct_connection_rdma_connect_state);
+       struct rdma_conn_param conn_param;
+       uint8_t ird_ord_hdr[8];
+       NTSTATUS status = NT_STATUS_INTERNAL_ERROR;
+       int ret;
+
+       errno = 0;
+
+       ret = rdma_get_cm_event(state->c->rdma.cm_channel,
+                               &state->c->rdma.cm_event);
+       if (ret != 0) {
+               status = map_nt_error_from_unix_common(errno);
+               DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                       __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+               tevent_req_nterror(req, status);
+               return;
+       }
+
+       errno = 0;
+       if (state->c->rdma.cm_event->status != 0) {
+               errno = state->c->rdma.cm_event->status;
+               status = map_nt_error_from_unix_common(errno);
+               DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                       __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+               tevent_req_nterror(req, status);
+               return;
+       }
+
+       if (state->c->rdma.cm_event->event != state->c->rdma.expected_event) {
+               DEBUG(0,("%s:%s: ret[%d] errno[%d]\n",
+                       __location__, __FUNCTION__, ret, errno));
+
+       }
+
+       switch (state->c->rdma.cm_event->event) {
+       case RDMA_CM_EVENT_ADDR_RESOLVED:
+       errno = 0;
+               ret = rdma_resolve_route(state->c->rdma.cm_id, 5000);
+               if (ret != 0) {
+                       status = map_nt_error_from_unix_common(errno);
+               DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                       __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+                       tevent_req_nterror(req, status);
+                       return;
+               }
+               state->c->rdma.expected_event = RDMA_CM_EVENT_ROUTE_RESOLVED;
+               break;
+       case RDMA_CM_EVENT_ROUTE_RESOLVED:
+       errno = 0;
+       ret = 0;
+#if 0
+               state->c->ibv.pd = ibv_alloc_pd(state->c->rdma.cm_id->verbs);
+               if (state->c->ibv.pd == NULL) {
+                       status = map_nt_error_from_unix_common(errno);
+                       DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                               __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+                       tevent_req_nterror(req, status);
+                       return;
+               }
+
+               state->c->ibv.comp_channel = ibv_create_comp_channel(state->c->rdma.cm_id->verbs);
+               if (state->c->ibv.comp_channel == NULL) {
+                       status = map_nt_error_from_unix_common(errno);
+                       DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                               __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+                       tevent_req_nterror(req, status);
+                       return;
+               }
+
+               set_blocking(state->c->ibv.comp_channel->fd, false);
+               smb_set_close_on_exec(state->c->ibv.comp_channel->fd);
+
+               ZERO_STRUCT(init_attr);
+               init_attr.cap.max_send_wr = 2;
+               init_attr.cap.max_recv_wr = 2;
+               init_attr.cap.max_recv_sge = 2;
+               init_attr.cap.max_send_sge = 2;
+               init_attr.qp_type = IBV_QPT_RC;
+               init_attr.sq_sig_all = 1;
+
+               state->c->ibv.send_cq = ibv_create_cq(state->c->rdma.cm_id->verbs,
+                                                     init_attr.cap.max_send_wr,
+                                                     state->c,
+                                                     state->c->ibv.comp_channel,
+                                                     0);
+               if (state->c->ibv.send_cq == NULL) {
+                       status = map_nt_error_from_unix_common(errno);
+                       DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                               __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+                       tevent_req_nterror(req, status);
+                       return;
+               }
+               init_attr.send_cq = state->c->ibv.send_cq;
+               state->c->ibv.recv_cq = ibv_create_cq(state->c->rdma.cm_id->verbs,
+                                                     init_attr.cap.max_recv_wr,
+                                                     state->c,
+                                                     state->c->ibv.comp_channel,
+                                                     0);
+               if (state->c->ibv.recv_cq == NULL) {
+                       status = map_nt_error_from_unix_common(errno);
+                       DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                               __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+                       tevent_req_nterror(req, status);
+                       return;
+               }
+               init_attr.recv_cq = state->c->ibv.recv_cq;
+
+               errno = 0;
+               ret = ibv_req_notify_cq(state->c->ibv.send_cq, 0);
+               if (ret != 0) {
+                       status = map_nt_error_from_unix_common(errno);
+                       DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                               __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+                       tevent_req_nterror(req, status);
+                       return;
+               }
+
+               errno = 0;
+               ret = ibv_req_notify_cq(state->c->ibv.recv_cq, 0);
+               if (ret != 0) {
+                       status = map_nt_error_from_unix_common(errno);
+                       DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                               __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+                       tevent_req_nterror(req, status);
+                       return;
+               }
+
+               errno = 0;
+               ret = rdma_create_qp(state->c->rdma.cm_id, state->c->ibv.pd,
+                                    &init_attr);
+               if (ret != 0) {
+                       status = map_nt_error_from_unix_common(errno);
+                       DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                               __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+                       tevent_req_nterror(req, status);
+                       return;
+               }
+               state->c->ibv.qp = state->c->rdma.cm_id->qp;
+#endif
+               RSIVAL(ird_ord_hdr, 0, 16);
+               RSIVAL(ird_ord_hdr, 4, 0);
+
+               ZERO_STRUCT(conn_param);
+               conn_param.private_data = ird_ord_hdr;
+               conn_param.private_data_len = sizeof(ird_ord_hdr);
+               conn_param.responder_resources = 1;
+               conn_param.initiator_depth = 1;
+               conn_param.retry_count = 10;
+
+               errno = 0;
+               ret = rdma_connect(state->c->rdma.cm_id, &conn_param);
+               if (ret != 0) {
+                       status = map_nt_error_from_unix_common(errno);
+                       DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                               __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+                       tevent_req_nterror(req, status);
+                       return;
+               }
+               state->c->rdma.expected_event = RDMA_CM_EVENT_ESTABLISHED;
+               break;
+
+       case RDMA_CM_EVENT_ESTABLISHED:
+               errno = 0;
+               ret = 0;
+               DEBUG(0,("%s:%s: ret[%d] errno[%d]\n",
+                       __location__, __FUNCTION__, ret, errno));
+
+               state->c->rdma.expected_event = RDMA_CM_EVENT_DISCONNECTED;
+               TALLOC_FREE(state->c->rdma.fde_channel);
+               rdma_ack_cm_event(state->c->rdma.cm_event);
+               state->c->rdma.cm_event = NULL;
+               tevent_req_done(req);
+               return;
+
+       case RDMA_CM_EVENT_ADDR_ERROR:
+       case RDMA_CM_EVENT_ROUTE_ERROR:
+       case RDMA_CM_EVENT_CONNECT_REQUEST:
+       case RDMA_CM_EVENT_CONNECT_RESPONSE:
+       case RDMA_CM_EVENT_CONNECT_ERROR:
+       case RDMA_CM_EVENT_UNREACHABLE:
+       case RDMA_CM_EVENT_REJECTED:
+       case RDMA_CM_EVENT_DISCONNECTED:
+       case RDMA_CM_EVENT_DEVICE_REMOVAL:
+       case RDMA_CM_EVENT_MULTICAST_JOIN:
+       case RDMA_CM_EVENT_MULTICAST_ERROR:
+       case RDMA_CM_EVENT_ADDR_CHANGE:
+       case RDMA_CM_EVENT_TIMEWAIT_EXIT:
+               status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+               DEBUG(0,("%s:%s: event[%d] ret[%d] errno[%d] status[%s]\n",
+                       __location__, __FUNCTION__,
+                       state->c->rdma.cm_event->event, ret, errno, nt_errstr(status)));
+               tevent_req_nterror(req, status);
+               return;
+       }
+
+       rdma_ack_cm_event(state->c->rdma.cm_event);
+       state->c->rdma.cm_event = NULL;
+}
+
+static NTSTATUS smb_direct_connection_rdma_connect_recv(struct tevent_req *req)
+{
+       struct smb_direct_connection_rdma_connect_state *state =
+               tevent_req_data(req,
+               struct smb_direct_connection_rdma_connect_state);
+       NTSTATUS status;
+
+       // TODO: _cleanup_fn
+       TALLOC_FREE(state->c->rdma.fde_channel);
+
+       if (tevent_req_is_nterror(req, &status)) {
+               tevent_req_received(req);
+               return status;
+       }
+
+       tevent_req_received(req);
+       return NT_STATUS_OK;
+}
+
+struct smb_direct_connection_negotiate_connect_state {
+       struct smb_direct_connection *c;
+       struct {
+               struct ibv_sge sge[1];
+               struct ibv_send_wr wr;
+       } rdma_read;
+       struct {
+               uint8_t buffer[0x14];
+               struct ibv_mr *mr;
+               struct ibv_sge sge[1];
+               struct ibv_send_wr wr;
+       } req;
+       struct {
+               uint8_t buffer[512];//0x20];
+               struct ibv_mr *mr;
+               struct ibv_sge sge[1];
+               struct ibv_recv_wr wr;
+       } rep;
+};
+
+static int smb_direct_connection_negotiate_connect_destructor(
+              struct smb_direct_connection_negotiate_connect_state *state)
+{
+       TALLOC_FREE(state->c->ibv.fde_channel);
+       TALLOC_FREE(state->c->rdma.fde_channel);
+
+       if (state->req.mr != NULL) {
+               ibv_dereg_mr(state->req.mr);
+               state->req.mr = NULL;
+       }
+
+       return 0;
+}
+
+static void smb_direct_connection_negotiate_connect_rdma_handler(struct tevent_context *ev,
+                                             struct tevent_fd *fde,
+                                             uint16_t flags,
+                                             void *private_data);
+static void smb_direct_connection_negotiate_connect_ibv_handler(struct tevent_context *ev,
+                                            struct tevent_fd *fde,
+                                            uint16_t flags,
+                                            void *private_data);
+
+static struct tevent_req *smb_direct_connection_negotiate_connect_send(TALLOC_CTX *mem_ctx,
+                                                    struct tevent_context *ev,
+                                                    struct smb_direct_connection *c)
+{
+       struct tevent_req *req;
+       struct smb_direct_connection_negotiate_connect_state *state;
+       struct ibv_recv_wr *bad_recv_wr = NULL;
+       struct ibv_send_wr *bad_send_wr = NULL;
+       NTSTATUS status;
+       int ret;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct smb_direct_connection_negotiate_connect_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       state->c = c;
+
+       talloc_set_destructor(state, smb_direct_connection_negotiate_connect_destructor);
+
+       state->c->ibv.fde_channel = tevent_add_fd(ev, state->c,
+                                               state->c->ibv.comp_channel->fd,
+                                               TEVENT_FD_READ,
+                                               smb_direct_connection_negotiate_connect_ibv_handler,
+                                               req);
+       if (tevent_req_nomem(state->c->ibv.fde_channel, req)) {
+               return tevent_req_post(req, ev);
+       }
+       state->c->rdma.fde_channel = tevent_add_fd(ev, state->c,
+                                               state->c->rdma.cm_channel->fd,
+                                               TEVENT_FD_READ,
+                                               smb_direct_connection_negotiate_connect_rdma_handler,
+                                               req);
+       if (tevent_req_nomem(state->c->rdma.fde_channel, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       state->rdma_read.sge[0].addr = 1;
+       state->rdma_read.sge[0].length = 0;
+       state->rdma_read.sge[0].lkey = 1;
+       state->rdma_read.wr.opcode = IBV_WR_RDMA_READ;
+       state->rdma_read.wr.send_flags = IBV_SEND_SIGNALED;
+       state->rdma_read.wr.sg_list = state->rdma_read.sge;
+       state->rdma_read.wr.num_sge = ARRAY_SIZE(state->rdma_read.sge);
+       state->rdma_read.wr.wr.rdma.rkey = 1;
+       state->rdma_read.wr.wr.rdma.remote_addr = 1;
+
+       SSVAL(state->req.buffer, 0x00, 0x0100);
+       SSVAL(state->req.buffer, 0x02, 0x0100);
+       SSVAL(state->req.buffer, 0x04, 0x0000);
+       SSVAL(state->req.buffer, 0x06, state->c->state.send_credit_target);
+       SIVAL(state->req.buffer, 0x08, state->c->state.max_send_size);
+       SIVAL(state->req.buffer, 0x0C, state->c->state.max_receive_size);
+       SIVAL(state->req.buffer, 0x10, state->c->state.max_fragmented_size);
+
+       state->req.mr = ibv_reg_mr(state->c->ibv.pd,
+                                  state->req.buffer,
+                                  0x14,//sizeof(state->req.buffer),
+                                  0);
+       if (tevent_req_nomem(state->req.mr, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       state->req.sge[0].addr = (uint64_t) (uintptr_t) state->req.buffer;
+       state->req.sge[0].length = 0x14;//sizeof(state->req.buffer);
+       state->req.sge[0].lkey = state->req.mr->lkey;
+       state->req.wr.opcode = IBV_WR_SEND;
+       state->req.wr.send_flags = IBV_SEND_SIGNALED;
+       state->req.wr.sg_list = state->req.sge;
+       state->req.wr.num_sge = ARRAY_SIZE(state->req.sge);
+
+       state->rep.mr = ibv_reg_mr(state->c->ibv.pd,
+                                  state->rep.buffer,
+                                  sizeof(state->rep.buffer),
+                                  IBV_ACCESS_LOCAL_WRITE);
+       if (tevent_req_nomem(state->rep.mr, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       memset(state->rep.buffer, 0x1F, sizeof(state->rep.buffer));
+       state->rep.sge[0].addr = (uint64_t) (uintptr_t) state->rep.buffer;
+       state->rep.sge[0].length = sizeof(state->rep.buffer);
+       state->rep.sge[0].lkey = state->rep.mr->lkey;
+       state->rep.wr.sg_list = state->rep.sge;
+       state->rep.wr.num_sge = ARRAY_SIZE(state->rep.sge);
+
+       errno = 0;
+       ret = ibv_post_recv(state->c->ibv.qp, &state->rep.wr, &bad_recv_wr);
+       if (ret != 0) {
+               status = map_nt_error_from_unix_common(errno);
+               DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                       __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+               tevent_req_nterror(req, status);
+               return tevent_req_post(req, ev);
+       }
+
+       state->rdma_read.wr.next = &state->req.wr;
+       errno = 0;
+       ret = ibv_post_send(state->c->ibv.qp, &state->rdma_read.wr, &bad_send_wr);
+       if (ret != 0) {
+               status = map_nt_error_from_unix_common(errno);
+               DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                       __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+               tevent_req_nterror(req, status);
+               return tevent_req_post(req, ev);
+       }
+
+               DEBUG(0,("%s:%s: ret[%d] errno[%d]\n",
+                       __location__, __FUNCTION__, ret, errno));
+       return req;
+}
+
+static void smb_direct_connection_negotiate_connect_rdma_handler(struct tevent_context *ev,
+                                             struct tevent_fd *fde,
+                                             uint16_t flags,
+                                             void *private_data)
+{
+       struct tevent_req *req =
+               talloc_get_type_abort(private_data,
+               struct tevent_req);
+       struct smb_direct_connection_negotiate_connect_state *state =
+               tevent_req_data(req,
+               struct smb_direct_connection_negotiate_connect_state);
+       NTSTATUS status = NT_STATUS_INTERNAL_ERROR;
+       int ret;
+
+       errno = 0;
+
+       ret = rdma_get_cm_event(state->c->rdma.cm_channel,
+                               &state->c->rdma.cm_event);
+       if (ret != 0) {
+               status = map_nt_error_from_unix_common(errno);
+               DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                       __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+               tevent_req_nterror(req, status);
+               return;
+       }
+
+       if (state->c->rdma.cm_event->status != 0) {
+               errno = state->c->rdma.cm_event->status;
+               status = map_nt_error_from_unix_common(errno);
+               DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                       __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+               tevent_req_nterror(req, status);
+               return;
+       }
+
+       switch (state->c->rdma.cm_event->event) {
+       case RDMA_CM_EVENT_DISCONNECTED:
+               status = NT_STATUS_CONNECTION_DISCONNECTED;
+               DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                       __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+               tevent_req_nterror(req, status);
+               return;
+       case RDMA_CM_EVENT_ADDR_RESOLVED:
+       case RDMA_CM_EVENT_ADDR_ERROR:
+       case RDMA_CM_EVENT_ROUTE_RESOLVED:
+       case RDMA_CM_EVENT_ESTABLISHED:
+       case RDMA_CM_EVENT_ROUTE_ERROR:
+       case RDMA_CM_EVENT_CONNECT_REQUEST:
+       case RDMA_CM_EVENT_CONNECT_RESPONSE:
+       case RDMA_CM_EVENT_CONNECT_ERROR:
+       case RDMA_CM_EVENT_UNREACHABLE:
+       case RDMA_CM_EVENT_REJECTED:
+       case RDMA_CM_EVENT_DEVICE_REMOVAL:
+       case RDMA_CM_EVENT_MULTICAST_JOIN:
+       case RDMA_CM_EVENT_MULTICAST_ERROR:
+       case RDMA_CM_EVENT_ADDR_CHANGE:
+       case RDMA_CM_EVENT_TIMEWAIT_EXIT:
+               status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+               DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                       __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+               tevent_req_nterror(req, status);
+               return;
+       }
+
+       status = NT_STATUS_INTERNAL_ERROR;
+       DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+               __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+       tevent_req_nterror(req, status);
+}
+
+static void smb_direct_connection_negotiate_connect_ibv_handler(struct tevent_context *ev,
+                                            struct tevent_fd *fde,
+                                            uint16_t flags,
+                                            void *private_data)
+{
+       struct tevent_req *req =
+               talloc_get_type_abort(private_data,
+               struct tevent_req);
+       struct smb_direct_connection_negotiate_connect_state *state =
+               tevent_req_data(req,
+               struct smb_direct_connection_negotiate_connect_state);
+       struct ibv_cq *cq = NULL;
+       void *cq_context = NULL;
+       NTSTATUS status = NT_STATUS_INTERNAL_ERROR;
+       struct ibv_wc wc;
+       int ret;
+       uint16_t credits_requested;
+       uint16_t credits_granted;
+       uint32_t max_read_write_size;
+       uint32_t preferred_send_size;
+       uint32_t max_receive_size;
+       uint32_t max_fragmented_size;
+       uint32_t tmp;
+       uint8_t *ptr;
+       struct ibv_recv_wr *bad_recv_wr = NULL;
+
+       errno = 0;
+       ret = ibv_get_cq_event(state->c->ibv.comp_channel,
+                              &cq, &cq_context);
+       if (ret != 0) {
+               status = map_nt_error_from_unix_common(errno);
+               DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                       __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+               tevent_req_nterror(req, status);
+               return;
+       }
+
+       ibv_ack_cq_events(cq, 1);
+
+       if (cq_context != state->c) {
+               status = NT_STATUS_INTERNAL_ERROR;;
+               DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                       __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+               tevent_req_nterror(req, status);
+               return;
+       }
+
+       errno = 0;
+       ret = ibv_req_notify_cq(cq, 0);
+       if (ret != 0) {
+               status = map_nt_error_from_unix_common(errno);
+               DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                       __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+               tevent_req_nterror(req, status);
+               return;
+       }
+
+       errno = 0;
+       ZERO_STRUCT(wc);
+       ret = ibv_poll_cq(cq, 1, &wc);
+       if (ret != 1) {
+               status = map_nt_error_from_unix_common(errno);
+               DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                       __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+               tevent_req_nterror(req, status);
+               return;
+       }
+       ret = 0;
+
+       if (wc.status == IBV_WC_WR_FLUSH_ERR) {
+               //errno = wc.status;
+               status = map_nt_error_from_unix_common(wc.status);//errno);
+               DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                       __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+               TALLOC_FREE(state->c->ibv.fde_channel);
+               TALLOC_FREE(state->c->rdma.fde_channel);
+               smb_direct_connection_negotiate_connect_rdma_handler(ev, fde, flags, private_data);
+               return;
+       }
+       if (wc.status != IBV_WC_SUCCESS) {
+               errno = wc.status;
+               status = map_nt_error_from_unix_common(errno);
+               DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s] ibv[%s]\n",
+                       __location__, __FUNCTION__, ret, errno, nt_errstr(status),
+                       ibv_wc_status_str(wc.status)));
+               tevent_req_nterror(req, status);
+               return;
+       }
+
+       switch (wc.opcode) {
+       case IBV_WC_SEND:
+               DEBUG(0,("%s:%s: ret[%d] errno[%d]\n",
+                       __location__, __FUNCTION__, ret, errno));
+               break;
+       case IBV_WC_RDMA_READ:
+               DEBUG(0,("%s:%s: ret[%d] errno[%d]\n",
+                       __location__, __FUNCTION__, ret, errno));
+               break;
+       case IBV_WC_RECV:
+               DEBUG(0,("%s:%s: ret[%d] errno[%d]\n",
+                       __location__, __FUNCTION__, ret, errno));
+               dump_data(0, state->rep.buffer, wc.byte_len);
+               if (wc.byte_len < 0x20) {
+                       status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+                       DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                               __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+                       tevent_req_nterror(req, status);
+                       return;
+               }
+               if (SVAL(state->rep.buffer, 0x00) != 0x0100) {
+                       status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+                       DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                               __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+                       tevent_req_nterror(req, status);
+                       return;
+               }
+               if (SVAL(state->rep.buffer, 0x02) != 0x0100) {
+                       status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+                       DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                               __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+                       tevent_req_nterror(req, status);
+                       return;
+               }
+               if (SVAL(state->rep.buffer, 0x04) != 0x0100) {
+                       status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+                       DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                               __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+                       tevent_req_nterror(req, status);
+                       return;
+               }
+               credits_requested = SVAL(state->rep.buffer, 0x08);
+               if (credits_requested == 0) {
+                       status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+                       DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                               __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+                       tevent_req_nterror(req, status);
+                       return;
+               }
+               credits_granted = SVAL(state->rep.buffer, 0x0A);
+               if (credits_granted == 0) {
+                       status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+                       DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                               __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+                       tevent_req_nterror(req, status);
+                       return;
+               }
+               status = NT_STATUS(IVAL(state->rep.buffer, 0x0C));
+               if (!NT_STATUS_IS_OK(status)) {
+                       DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                               __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+                       tevent_req_nterror(req, status);
+                       return;
+               }
+               max_read_write_size = IVAL(state->rep.buffer, 0x10);
+               preferred_send_size = IVAL(state->rep.buffer, 0x14);
+               if (preferred_send_size > state->c->state.max_receive_size) {
+                       status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+                       DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                               __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+                       tevent_req_nterror(req, status);
+                       return;
+               }
+               max_receive_size = IVAL(state->rep.buffer, 0x18);
+               if (max_receive_size < 0x80) {
+                       status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+                       DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                               __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+                       tevent_req_nterror(req, status);
+                       return;
+               }
+               max_fragmented_size = IVAL(state->rep.buffer, 0x1C);
+               if (max_fragmented_size < 0x20000) {
+                       status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+                       DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                               __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+                       tevent_req_nterror(req, status);
+                       return;
+               }
+
+               state->c->state.receive_credit_target = credits_requested;
+
+               tmp = state->c->state.max_receive_size;
+               tmp = MIN(tmp, preferred_send_size);
+               tmp = MAX(tmp, 128);
+               state->c->state.max_receive_size = tmp;
+
+               tmp = state->c->state.max_send_size;
+               tmp = MIN(tmp, max_receive_size);
+               state->c->state.max_send_size = tmp;
+
+               tmp = MIN(1048576, max_read_write_size);
+               state->c->state.max_read_write_size = tmp;
+
+               tmp = state->c->state.max_fragmented_size;
+               tmp = MIN(tmp, max_fragmented_size);
+               state->c->state.max_fragmented_size = tmp;
+
+               state->c->state.send_credits = credits_granted;
+
+               TALLOC_FREE(state->c->ibv.fde_channel);
+               TALLOC_FREE(state->c->rdma.fde_channel);
+
+               DEBUG(0,("%s:%s: ret[%d] errno[%d]\n",
+                       __location__, __FUNCTION__, ret, errno));
+
+               state->c->inbuf.hdr_mr = ibv_reg_mr(state->c->ibv.pd,
+                                                   state->c->inbuf.hdr,
+                                                   sizeof(state->c->inbuf.hdr),
+                                                   IBV_ACCESS_LOCAL_WRITE);
+               if (tevent_req_nomem(state->c->inbuf.hdr_mr, req)) {
+                       return;
+               }
+
+               ptr = talloc_array(state->c, uint8_t,
+                                  state->c->state.max_receive_size - 0x14);
+               if (tevent_req_nomem(ptr, req)) {
+                       return;
+               }
+               state->c->inbuf.reassembly_buffer = ptr;
+
+               state->c->inbuf.cur_mr = ibv_reg_mr(state->c->ibv.pd,
+                                                   ptr,
+                                                   talloc_get_size(ptr),
+                                                   IBV_ACCESS_LOCAL_WRITE);
+               if (tevent_req_nomem(state->c->inbuf.cur_mr, req)) {
+                       return;
+               }
+
+               state->c->inbuf.sge[0].addr = (uint64_t) (uintptr_t) state->c->inbuf.hdr;
+               state->c->inbuf.sge[0].length = sizeof(state->c->inbuf.hdr);
+               state->c->inbuf.sge[0].lkey = state->c->inbuf.hdr_mr->lkey;
+               state->c->inbuf.sge[1].addr = (uint64_t) (uintptr_t) (ptr + 4);
+               state->c->inbuf.sge[1].length = talloc_get_size(ptr) - 4;
+               state->c->inbuf.sge[1].lkey = state->c->inbuf.cur_mr->lkey;
+               state->c->inbuf.wr.sg_list = state->c->inbuf.sge;
+               state->c->inbuf.wr.num_sge = ARRAY_SIZE(state->c->inbuf.sge);
+
+               errno = 0;
+               ret = ibv_post_recv(state->c->ibv.qp, &state->c->inbuf.wr, &bad_recv_wr);
+               if (ret != 0) {
+                       status = map_nt_error_from_unix_common(errno);
+                       DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                               __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+                       tevent_req_nterror(req, status);
+                       return;
+               }
+
+               tevent_req_done(req);
+               return;
+
+       case IBV_WC_RDMA_WRITE:
+       default:
+               status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+               DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                       __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+               tevent_req_nterror(req, status);
+               return;
+       }
+}
+
+static NTSTATUS smb_direct_connection_negotiate_connect_recv(struct tevent_req *req)
+{
+       NTSTATUS status;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               tevent_req_received(req);
+               return status;
+       }
+
+       tevent_req_received(req);
+       return NT_STATUS_OK;
+}
+
+static void smb_direct_connection_disconnect(struct smb_direct_connection *c,
+                                                NTSTATUS status)
+{
+       if (NT_STATUS_IS_OK(status)) {
+               status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+       }
+
+       if (c->reqs.read_pdu_req != NULL) {
+               tevent_req_defer_callback(c->reqs.read_pdu_req,
+                                         c->reqs.last_ev);
+               tevent_req_nterror(c->reqs.read_pdu_req, status);
+               c->reqs.read_pdu_req = NULL;
+       }
+
+       if (c->reqs.write_pdu_req != NULL) {
+               tevent_req_defer_callback(c->reqs.write_pdu_req,
+                                         c->reqs.last_ev);
+               tevent_req_nterror(c->reqs.write_pdu_req, status);
+               c->reqs.write_pdu_req = NULL;
+       }
+
+       smb_direct_connection_destructor(c);
+}
+
+static void smb_direct_connection_rdma_handler(struct tevent_context *ev,
+                                                  struct tevent_fd *fde,
+                                                  uint16_t flags,
+                                                  void *private_data)
+{
+       struct smb_direct_connection *c =
+               talloc_get_type_abort(private_data,
+               struct smb_direct_connection);
+       NTSTATUS status = NT_STATUS_INTERNAL_ERROR;
+       int ret;
+
+       errno = 0;
+
+       ret = rdma_get_cm_event(c->rdma.cm_channel,
+                               &c->rdma.cm_event);
+       if (ret != 0) {
+               status = map_nt_error_from_unix_common(errno);
+               DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                       __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+               smb_direct_connection_disconnect(c, status);
+               return;
+       }
+
+       if (c->rdma.cm_event->status != 0) {
+               errno = c->rdma.cm_event->status;
+               status = map_nt_error_from_unix_common(errno);
+               DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                       __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+               smb_direct_connection_disconnect(c, status);
+               return;
+       }
+
+       switch (c->rdma.cm_event->event) {
+       case RDMA_CM_EVENT_DISCONNECTED:
+               status = NT_STATUS_CONNECTION_DISCONNECTED;
+               DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                       __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+               smb_direct_connection_disconnect(c, status);
+               return;
+       case RDMA_CM_EVENT_ADDR_RESOLVED:
+       case RDMA_CM_EVENT_ADDR_ERROR:
+       case RDMA_CM_EVENT_ROUTE_RESOLVED:
+       case RDMA_CM_EVENT_ESTABLISHED:
+       case RDMA_CM_EVENT_ROUTE_ERROR:
+       case RDMA_CM_EVENT_CONNECT_REQUEST:
+       case RDMA_CM_EVENT_CONNECT_RESPONSE:
+       case RDMA_CM_EVENT_CONNECT_ERROR:
+       case RDMA_CM_EVENT_UNREACHABLE:
+       case RDMA_CM_EVENT_REJECTED:
+       case RDMA_CM_EVENT_DEVICE_REMOVAL:
+       case RDMA_CM_EVENT_MULTICAST_JOIN:
+       case RDMA_CM_EVENT_MULTICAST_ERROR:
+       case RDMA_CM_EVENT_ADDR_CHANGE:
+       case RDMA_CM_EVENT_TIMEWAIT_EXIT:
+               status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+               DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                       __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+               smb_direct_connection_disconnect(c, status);
+               return;
+       }
+
+       status = NT_STATUS_INTERNAL_ERROR;
+       DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+               __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+       smb_direct_connection_disconnect(c, status);
+}
+
+static void smb_direct_connection_ibv_handler(struct tevent_context *ev,
+                                                 struct tevent_fd *fde,
+                                                 uint16_t fde_flags,
+                                                 void *private_data)
+{
+       struct smb_direct_connection *c =
+               talloc_get_type_abort(private_data,
+               struct smb_direct_connection);
+       struct ibv_cq *cq = NULL;
+       void *cq_context = NULL;
+       NTSTATUS status = NT_STATUS_INTERNAL_ERROR;
+       struct ibv_wc wc;
+       int ret;
+       uint8_t *ptr;
+       uint16_t credits_requested;
+       uint16_t credits_granted;
+       uint16_t flags;
+       uint32_t remaining_length;
+       uint32_t data_offset;
+       uint32_t data_length;
+       struct ibv_recv_wr *bad_recv_wr = NULL;
+
+       errno = 0;
+       ret = ibv_get_cq_event(c->ibv.comp_channel,
+                              &cq, &cq_context);
+       if (ret != 0) {
+               status = map_nt_error_from_unix_common(errno);
+               DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                       __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+               smb_direct_connection_disconnect(c, status);
+               return;
+       }
+
+       ibv_ack_cq_events(cq, 1);
+
+       if (cq_context != c) {
+               status = NT_STATUS_INTERNAL_ERROR;
+               DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                       __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+               smb_direct_connection_disconnect(c, status);
+               return;
+       }
+
+       errno = 0;
+       ret = ibv_req_notify_cq(cq, 0);
+       if (ret != 0) {
+               status = map_nt_error_from_unix_common(errno);
+               DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                       __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+               smb_direct_connection_disconnect(c, status);
+               return;
+       }
+
+       errno = 0;
+       ZERO_STRUCT(wc);
+       ret = ibv_poll_cq(cq, 1, &wc);
+       if (ret != 1) {
+               status = map_nt_error_from_unix_common(errno);
+               DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                       __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+               smb_direct_connection_disconnect(c, status);
+               return;
+       }
+       ret = 0;
+
+       if (wc.status == IBV_WC_WR_FLUSH_ERR) {
+               errno = wc.status;
+               status = map_nt_error_from_unix_common(errno);
+               DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s] ibv[%s]\n",
+                       __location__, __FUNCTION__, ret, errno, nt_errstr(status),
+                       ibv_wc_status_str(wc.status)));
+               TALLOC_FREE(c->ibv.fde_channel);
+               TALLOC_FREE(c->rdma.fde_channel);
+               smb_direct_connection_rdma_handler(ev, fde, 0 /* flags */, private_data);
+               return;
+       }
+       if (wc.status != IBV_WC_SUCCESS) {
+               errno = wc.status;
+               status = map_nt_error_from_unix_common(errno);
+               DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s] ibv[%s]\n",
+                       __location__, __FUNCTION__, ret, errno, nt_errstr(status),
+                       ibv_wc_status_str(wc.status)));
+               smb_direct_connection_disconnect(c, status);
+               return;
+       }
+
+       switch (wc.opcode) {
+       case IBV_WC_SEND:
+               DEBUG(0,("%s:%s: ret[%d] errno[%d]\n",
+                       __location__, __FUNCTION__, ret, errno));
+               if (c->reqs.write_pdu_req == NULL) {
+                       status = NT_STATUS_INTERNAL_ERROR;
+                       smb_direct_connection_disconnect(c, status);
+                       return;
+               }
+               tevent_req_defer_callback(c->reqs.write_pdu_req,
+                                         c->reqs.last_ev);
+               tevent_req_done(c->reqs.write_pdu_req);
+               return;
+
+       case IBV_WC_RECV:
+               DEBUG(0,("%s:%s: GOT WC_RECV ret[%d] errno[%d]\n",
+                       __location__, __FUNCTION__, ret, errno));
+               if (wc.byte_len < 0x14) {
+                       status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+                       DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                               __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+                       smb_direct_connection_disconnect(c, status);
+                       return;
+               }
+               dump_data(0, c->inbuf.hdr, MIN(wc.byte_len, sizeof(c->inbuf.hdr)));
+               credits_requested = SVAL(c->inbuf.hdr, 0x00);
+               if (credits_requested == 0) {
+                       status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+                       DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                               __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+                       smb_direct_connection_disconnect(c, status);
+                       return;
+               }
+               credits_granted = SVAL(c->inbuf.hdr, 0x02);
+               if (credits_granted == 0) {
+                       status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+                       DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                               __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+                       smb_direct_connection_disconnect(c, status);
+                       return;
+               }
+               flags = SVAL(c->inbuf.hdr, 0x04);
+               remaining_length = IVAL(c->inbuf.hdr, 0x08);
+               data_offset = IVAL(c->inbuf.hdr, 0x0C);
+               data_length = IVAL(c->inbuf.hdr, 0x10);
+
+               c->state.receive_credits -= 1;
+               c->state.receive_credit_target = credits_requested;
+               c->state.send_credits += credits_granted;
+
+               if (data_offset == 0) {
+                       if (wc.byte_len != 0x14) {
+                               status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+                               DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                                       __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+                               smb_direct_connection_disconnect(c, status);
+                               return;
+                       }
+                       goto repost_receive;
+               } else if (data_offset == 0x18) {
+                       if (data_length >= (c->state.max_receive_size - data_offset)) {
+                               status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+                               DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                                       __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+                               smb_direct_connection_disconnect(c, status);
+                               return;
+                       }
+               } else {
+                       status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+                       DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                               __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+                       smb_direct_connection_disconnect(c, status);
+                       return;
+               }
+
+               if (remaining_length > 0) {
+                       // TODO: ...
+               }
+
+               if (flags) {
+                       // TODO: ...
+               }
+
+               if (c->reqs.read_pdu_req == NULL) {
+                       // TODO: is this correct???
+                       goto repost_receive;
+               }
+
+               if (c->reqs.read_pdu_req != NULL) {
+                       TALLOC_FREE(c->inbuf.full_buffer);
+                       if (c->inbuf.cur_mr != NULL) {
+                               ibv_dereg_mr(c->inbuf.cur_mr);
+                               c->inbuf.cur_mr = NULL;
+                       }
+                       ptr = c->inbuf.reassembly_buffer;
+                       ptr = talloc_realloc(c, ptr, uint8_t, 4 + data_length);
+                       if (ptr == NULL) {
+                               status = NT_STATUS_NO_MEMORY;
+                               DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                                       __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+                               smb_direct_connection_disconnect(c, status);
+                               return;
+                       }
+
+                       c->inbuf.full_buffer = ptr;
+                       c->inbuf.reassembly_buffer= NULL;
+                       tevent_req_defer_callback(c->reqs.read_pdu_req,
+                                                 c->reqs.last_ev);
+                       tevent_req_done(c->reqs.read_pdu_req);
+                       /* no return here */
+               }
+
+               ptr = talloc_array(c, uint8_t,
+                                  c->state.max_receive_size - 0x14);
+               if (ptr == NULL) {
+                       status = NT_STATUS_NO_MEMORY;
+                       DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                               __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+                       smb_direct_connection_disconnect(c, status);
+                       return;
+               }
+               c->inbuf.reassembly_buffer = ptr;
+
+               c->inbuf.cur_mr = ibv_reg_mr(c->ibv.pd,
+                                                   ptr,
+                                                   talloc_get_size(ptr),
+                                                   IBV_ACCESS_LOCAL_WRITE);
+               if (c->inbuf.cur_mr == NULL) {
+                       status = NT_STATUS_NO_MEMORY;
+                       DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                               __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+                       smb_direct_connection_disconnect(c, status);
+                       return;
+               }
+
+               c->inbuf.sge[1].addr = (uint64_t) (uintptr_t) (ptr + 4);
+               c->inbuf.sge[1].length = talloc_get_size(ptr) - 4;
+               c->inbuf.sge[1].lkey = c->inbuf.cur_mr->lkey;
+
+repost_receive:
+               errno = 0;
+                       DEBUG(0,("%s:%s: REPOST_RECV...\n",
+                               __location__, __FUNCTION__));
+               ret = ibv_post_recv(c->ibv.qp, &c->inbuf.wr, &bad_recv_wr);
+               if (ret != 0) {
+                       status = map_nt_error_from_unix_common(errno);
+                       DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                               __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+                       smb_direct_connection_disconnect(c, status);
+                       return;
+               }
+
+               return;
+
+       case IBV_WC_RDMA_READ:
+       case IBV_WC_RDMA_WRITE:
+       default:
+               status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+               DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                       __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+               smb_direct_connection_disconnect(c, status);
+               return;
+       }
+}
+
+static NTSTATUS smb_direct_connection_setup_events(struct smb_direct_connection *c,
+                                                      struct tevent_context *ev)
+{
+       if (tevent_fd_get_flags(c->ibv.fde_channel) == 0) {
+               c->reqs.last_ev = NULL;
+               TALLOC_FREE(c->ibv.fde_channel);
+               TALLOC_FREE(c->rdma.fde_channel);
+       }
+
+       if (tevent_fd_get_flags(c->rdma.fde_channel) == 0) {
+               c->reqs.last_ev = NULL;
+               TALLOC_FREE(c->ibv.fde_channel);
+               TALLOC_FREE(c->rdma.fde_channel);
+       }
+
+       if (c->reqs.read_pdu_req == NULL && c->reqs.write_pdu_req == NULL) {
+               c->reqs.last_ev = NULL;
+               TALLOC_FREE(c->ibv.fde_channel);
+               TALLOC_FREE(c->rdma.fde_channel);
+       }
+
+       if (ev == NULL) {
+               c->reqs.last_ev = NULL;
+               TALLOC_FREE(c->ibv.fde_channel);
+               TALLOC_FREE(c->rdma.fde_channel);
+       } else if (ev == c->reqs.last_ev) {
+               return NT_STATUS_OK;
+       } else if (c->reqs.last_ev == NULL) {
+               /* fallthrough */
+       } else {
+               return NT_STATUS_INVALID_PARAMETER_MIX;
+       }
+
+       c->ibv.fde_channel = tevent_add_fd(ev, c,
+                                          c->ibv.comp_channel->fd,
+                                          TEVENT_FD_READ,
+                                          smb_direct_connection_ibv_handler,
+                                          c);
+       if (c->ibv.fde_channel == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+       c->rdma.fde_channel = tevent_add_fd(ev, c,
+                                           c->rdma.cm_channel->fd,
+                                           TEVENT_FD_READ,
+                                           smb_direct_connection_rdma_handler,
+                                           c);
+       if (c->rdma.fde_channel == NULL) {
+               TALLOC_FREE(c->ibv.fde_channel);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       c->reqs.last_ev = ev;
+       return NT_STATUS_OK;
+}
+
+struct smb_direct_write_pdu_state {
+       struct smb_direct_connection *c;
+       uint8_t hdr[0x18];
+       uint8_t *buffer;
+       struct ibv_mr *hdr_mr;
+       struct ibv_mr *buffer_mr;
+       struct ibv_sge sge[2];
+       struct ibv_send_wr wr;
+};
+
+static int smb_direct_write_pdu_destructor(
+               struct smb_direct_write_pdu_state *state)
+{
+       if (state->c) {
+               state->c->reqs.write_pdu_req = NULL;
+       }
+
+       if (state->hdr_mr != NULL) {
+               ibv_dereg_mr(state->hdr_mr);
+               state->hdr_mr = NULL;
+       }
+       if (state->buffer_mr != NULL) {
+               ibv_dereg_mr(state->buffer_mr);
+               state->buffer_mr = NULL;
+       }
+
+       return 0;
+}
+
+static struct tevent_req *smb_direct_write_pdu_send(TALLOC_CTX *mem_ctx,
+                                      struct tevent_context *ev,
+                                      struct smb_transport *transport,
+                                      const struct iovec *vector,
+                                      size_t count)
+{
+       struct tevent_req *req;
+       struct smb_direct_write_pdu_state *state;
+       struct smb_direct_connection *c =
+               smb_transport_data(transport,
+               struct smb_direct_connection);
+       size_t to_write = 0;
+       size_t current_len = 0;
+       size_t remaining_len = 0;
+       uint8_t *ptr = NULL;
+       size_t buf_len = 0;
+       size_t i;
+       NTSTATUS status;
+       int ret;
+       struct ibv_send_wr *bad_send_wr = NULL;
+       uint16_t granted = 0;
+       uint16_t flags = 0;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct smb_direct_write_pdu_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       state->c = c;
+       talloc_set_destructor(state, smb_direct_write_pdu_destructor);
+
+       /* first check if the input is ok */
+#ifdef IOV_MAX
+       if (count > IOV_MAX) {
+               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+               return tevent_req_post(req, ev);
+       }
+#endif
+
+       for (i=0; i < count; i++) {
+               size_t tmp = to_write;
+               tmp += vector[i].iov_len;
+
+               if (tmp < to_write) {
+                       tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+                       return tevent_req_post(req, ev);
+               }
+
+               to_write = tmp;
+       }
+
+       if (to_write == 0) {
+               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+               return tevent_req_post(req, ev);
+       }
+
+       if (to_write > state->c->state.max_fragmented_size) {
+               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+               return tevent_req_post(req, ev);
+       }
+
+       if (state->c->state.send_credits == 0) {
+               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+               return tevent_req_post(req, ev);
+       }
+
+       current_len = MIN(state->c->state.max_send_size - 0x18, to_write);
+       remaining_len = to_write - current_len;
+
+       buf_len = current_len;
+       state->buffer = talloc_zero_array(state, uint8_t, buf_len);
+       if (tevent_req_nomem(state->buffer, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       granted = state->c->state.receive_credit_max;
+       granted -= state->c->state.receive_credits;
+       granted = MIN(granted, state->c->state.receive_credit_target);
+       state->c->state.receive_credits += granted;
+       state->c->state.send_credits -= 1;
+
+       if (state->c->state.send_credits == 0) {
+               flags |= 0x0001;
+       }
+
+       SSVAL(state->hdr, 0x00, state->c->state.send_credit_target);
+       SSVAL(state->hdr, 0x02, granted);
+       SSVAL(state->hdr, 0x04, flags);
+       SSVAL(state->hdr, 0x06, 0x0000);
+       SIVAL(state->hdr, 0x08, remaining_len);
+       SIVAL(state->hdr, 0x0C, 0x00000018);
+       SIVAL(state->hdr, 0x10, current_len);
+       SIVAL(state->hdr, 0x14, 0x00000000);
+
+       ptr = state->buffer;
+       for (i=0; i < count; i++) {
+               const uint8_t *this_buf = (const uint8_t *)vector[i].iov_base;
+               size_t this_len = MIN(current_len, vector[i].iov_len);
+
+               memcpy(ptr, this_buf, this_len);
+               ptr += this_len;
+               current_len -= this_len;
+               if (current_len == 0) {
+                       break;
+               }
+       }
+
+       status = smb_direct_connection_setup_events(state->c, ev);
+       if (!NT_STATUS_IS_OK(status)) {
+               smb_direct_connection_disconnect(state->c, status);
+               tevent_req_nterror(req, status);
+               return tevent_req_post(req, ev);
+       }
+       state->c->reqs.write_pdu_req = req;
+
+       state->hdr_mr = ibv_reg_mr(state->c->ibv.pd,
+                                  state->hdr,
+                                  sizeof(state->hdr),
+                                  0);
+       if (state->hdr_mr == NULL) {
+               smb_direct_connection_disconnect(state->c, NT_STATUS_NO_MEMORY);
+               return req;
+       }
+
+       state->buffer_mr = ibv_reg_mr(state->c->ibv.pd,
+                                     state->buffer,
+                                     state->c->state.max_send_size,
+                                     0);
+       if (state->buffer_mr == NULL) {
+               smb_direct_connection_disconnect(state->c, NT_STATUS_NO_MEMORY);
+               return req;
+       }
+
+       state->sge[0].addr = (uint64_t) (uintptr_t) state->hdr;
+       state->sge[0].length = sizeof(state->hdr);;
+       state->sge[0].lkey = state->hdr_mr->lkey;
+       state->sge[1].addr = (uint64_t) (uintptr_t) state->buffer;
+       state->sge[1].length = buf_len;
+       state->sge[1].lkey = state->buffer_mr->lkey;
+       state->wr.opcode = IBV_WR_SEND;
+       state->wr.send_flags = IBV_SEND_SIGNALED;
+       state->wr.sg_list = state->sge;
+       state->wr.num_sge = ARRAY_SIZE(state->sge);
+
+       errno = 0;
+       ret = ibv_post_send(state->c->ibv.qp, &state->wr, &bad_send_wr);
+       if (ret != 0) {
+               status = map_nt_error_from_unix_common(errno);
+               DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                       __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+               smb_direct_connection_disconnect(state->c, status);
+               return req;
+       }
+
+       return req;
+}
+
+static NTSTATUS smb_direct_write_pdu_recv(struct tevent_req *req)
+{
+       struct smb_direct_write_pdu_state *state =
+               tevent_req_data(req,
+               struct smb_direct_write_pdu_state);
+       NTSTATUS status;
+
+       state->c->reqs.write_pdu_req = NULL;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               tevent_req_received(req);
+               return status;
+       }
+
+       tevent_req_received(req);
+       return NT_STATUS_OK;
+}
+
+struct smb_direct_read_pdu_state {
+       struct smb_direct_connection *c;
+       uint8_t *inbuf;
+};
+
+static int smb_direct_read_pdu_destructor(
+               struct smb_direct_read_pdu_state *state)
+{
+       if (state->c != NULL) {
+               state->c->reqs.read_pdu_req = NULL;
+       }
+
+       return 0;
+}
+
+static struct tevent_req *smb_direct_read_pdu_send(TALLOC_CTX *mem_ctx,
+                                     struct tevent_context *ev,
+                                     struct smb_transport *transport)
+{
+       struct tevent_req *req;
+       struct smb_direct_read_pdu_state *state;
+       struct smb_direct_connection *c =
+               smb_transport_data(transport,
+               struct smb_direct_connection);
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct smb_direct_read_pdu_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       state->c = c;
+       talloc_set_destructor(state, smb_direct_read_pdu_destructor);
+
+       state->c->reqs.read_pdu_req = req;
+
+       return req;
+}
+
+static NTSTATUS smb_direct_read_pdu_recv(struct tevent_req *req,
+                                               TALLOC_CTX *mem_ctx,
+                                               struct iovec *vector)
+{
+       struct smb_direct_read_pdu_state *state =
+               tevent_req_data(req,
+               struct smb_direct_read_pdu_state);
+       NTSTATUS status;
+
+       state->c->reqs.read_pdu_req = NULL;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               tevent_req_received(req);
+               return status;
+       }
+
+       vector->iov_len = talloc_get_size(state->c->inbuf.full_buffer);
+       _smb_setlen_tcp(state->c->inbuf.full_buffer, vector->iov_len - 4);
+       vector->iov_base = talloc_move(mem_ctx, &state->c->inbuf.full_buffer);
+
+       tevent_req_received(req);
+       return NT_STATUS_OK;
+}
+
+static const struct smb_transport_ops smb_direct_ops = {
+       .name                   = "smbdirect",
+
+       .read_pdu_send          = smb_direct_read_pdu_send,
+       .read_pdu_recv          = smb_direct_read_pdu_recv,
+
+       .write_pdu_send         = smb_direct_write_pdu_send,
+       .write_pdu_recv         = smb_direct_write_pdu_recv,
+};
+
+#endif /* SMB_TRANSPORT_ENABLE_RDMA */
index 19023705c091e12b9a23eca0227eb3903128a8d6..07ed42b4248d22021d7608aebf04a3e83aa567c4 100644 (file)
 #ifndef _LIBCLI_SMB_SMB_DIRECT_H_
 #define _LIBCLI_SMB_SMB_DIRECT_H_
 
+struct smb_direct_connection;
+
+struct smb_direct_connection *smb_direct_connection_create(TALLOC_CTX *mem_ctx);
+
+struct tevent_req *smb_direct_connect_send(TALLOC_CTX *mem_ctx,
+                                          struct tevent_context *ev,
+                                          struct smb_direct_connection *c,
+                                          const struct sockaddr_storage *src,
+                                          const struct sockaddr_storage *dst);
+int smb_direct_connect_recv(struct tevent_req *req, int *fd);
+
 #define SMB_DIRECT_LISTEN_BACKLOG 100
 
 struct tevent_req *smb_direct_daemon_send(TALLOC_CTX *mem_ctx,
index 0d85a5f30984c4e280300c7741ca7d623465ee92..4a73013044bd94c1f3ec693d28f6fb41ea952732 100644 (file)
@@ -30,6 +30,7 @@ def configure(conf):
 def build(bld):
     bld.SAMBA_SUBSYSTEM('smb_direct',
                        source='''
+                            smb_direct.c
                             smb_direct_util.c
                             smb_direct_daemon.c
                             smb_direct_client.c