#ifndef __NVMF_TCP_H__
#define __NVMF_TCP_H__
#ifndef _KERNEL
#define MPASS assert
#endif
#define NVME_SGL_TYPE_ICD \
NVME_SGL_TYPE(NVME_SGL_TYPE_DATA_BLOCK, NVME_SGL_SUBTYPE_OFFSET)
#define NVME_SGL_TYPE_COMMAND_BUFFER \
NVME_SGL_TYPE(NVME_SGL_TYPE_TRANSPORT_DATA_BLOCK, \
NVME_SGL_SUBTYPE_TRANSPORT)
static __inline int
nvmf_tcp_validate_pdu_header(const struct nvme_tcp_common_pdu_hdr *ch,
bool controller, bool header_digests, bool data_digests, uint8_t rxpda,
uint32_t *data_lenp, uint16_t *fes, uint32_t *fei)
{
uint32_t data_len, plen;
u_int expected_hlen, full_hlen;
uint8_t digest_flags, valid_flags;
plen = le32toh(ch->plen);
full_hlen = ch->hlen;
if ((ch->flags & NVME_TCP_CH_FLAGS_HDGSTF) != 0)
full_hlen += sizeof(uint32_t);
if (plen == full_hlen)
data_len = 0;
else
data_len = plen - ch->pdo;
if (controller != ((ch->pdu_type & 0x01) == 0)) {
printf("NVMe/TCP: Invalid PDU type %u\n", ch->pdu_type);
*fes = NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD;
*fei = offsetof(struct nvme_tcp_common_pdu_hdr, pdu_type);
return (EBADMSG);
}
switch (ch->pdu_type) {
case NVME_TCP_PDU_TYPE_IC_REQ:
case NVME_TCP_PDU_TYPE_IC_RESP:
printf("NVMe/TCP: Received Initialize Connection PDU\n");
*fes = NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD;
*fei = offsetof(struct nvme_tcp_common_pdu_hdr, pdu_type);
return (EBADMSG);
case NVME_TCP_PDU_TYPE_H2C_TERM_REQ:
case NVME_TCP_PDU_TYPE_C2H_TERM_REQ:
if (plen < sizeof(struct nvme_tcp_term_req_hdr) ||
plen > NVME_TCP_TERM_REQ_PDU_MAX_SIZE) {
printf("NVMe/TCP: Received invalid termination request\n");
return (ECONNRESET);
}
break;
case NVME_TCP_PDU_TYPE_CAPSULE_CMD:
case NVME_TCP_PDU_TYPE_CAPSULE_RESP:
case NVME_TCP_PDU_TYPE_H2C_DATA:
case NVME_TCP_PDU_TYPE_C2H_DATA:
case NVME_TCP_PDU_TYPE_R2T:
break;
default:
printf("NVMe/TCP: Invalid PDU type %u\n", ch->pdu_type);
*fes = NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD;
*fei = offsetof(struct nvme_tcp_common_pdu_hdr, pdu_type);
return (EBADMSG);
}
switch (ch->pdu_type) {
default:
__assert_unreachable();
break;
case NVME_TCP_PDU_TYPE_H2C_TERM_REQ:
case NVME_TCP_PDU_TYPE_C2H_TERM_REQ:
valid_flags = 0;
break;
case NVME_TCP_PDU_TYPE_CAPSULE_CMD:
valid_flags = NVME_TCP_CH_FLAGS_HDGSTF |
NVME_TCP_CH_FLAGS_DDGSTF;
break;
case NVME_TCP_PDU_TYPE_CAPSULE_RESP:
case NVME_TCP_PDU_TYPE_R2T:
valid_flags = NVME_TCP_CH_FLAGS_HDGSTF;
break;
case NVME_TCP_PDU_TYPE_H2C_DATA:
valid_flags = NVME_TCP_CH_FLAGS_HDGSTF |
NVME_TCP_CH_FLAGS_DDGSTF | NVME_TCP_H2C_DATA_FLAGS_LAST_PDU;
break;
case NVME_TCP_PDU_TYPE_C2H_DATA:
valid_flags = NVME_TCP_CH_FLAGS_HDGSTF |
NVME_TCP_CH_FLAGS_DDGSTF | NVME_TCP_C2H_DATA_FLAGS_LAST_PDU |
NVME_TCP_C2H_DATA_FLAGS_SUCCESS;
break;
}
if ((ch->flags & ~valid_flags) != 0) {
printf("NVMe/TCP: Invalid PDU header flags %#x\n", ch->flags);
*fes = NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD;
*fei = offsetof(struct nvme_tcp_common_pdu_hdr, flags);
return (EBADMSG);
}
digest_flags = 0;
if (header_digests)
digest_flags |= NVME_TCP_CH_FLAGS_HDGSTF;
if (data_digests && data_len != 0)
digest_flags |= NVME_TCP_CH_FLAGS_DDGSTF;
if ((digest_flags & valid_flags) !=
(ch->flags & (NVME_TCP_CH_FLAGS_HDGSTF |
NVME_TCP_CH_FLAGS_DDGSTF))) {
printf("NVMe/TCP: Invalid PDU header flags %#x\n", ch->flags);
*fes = NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD;
*fei = offsetof(struct nvme_tcp_common_pdu_hdr, flags);
return (EBADMSG);
}
if (ch->pdu_type == NVME_TCP_PDU_TYPE_C2H_DATA &&
(ch->flags & (NVME_TCP_C2H_DATA_FLAGS_LAST_PDU |
NVME_TCP_C2H_DATA_FLAGS_SUCCESS)) ==
NVME_TCP_C2H_DATA_FLAGS_SUCCESS) {
printf("NVMe/TCP: Invalid PDU header flags %#x\n", ch->flags);
*fes = NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD;
*fei = offsetof(struct nvme_tcp_common_pdu_hdr, flags);
return (EBADMSG);
}
switch (ch->pdu_type) {
default:
__assert_unreachable();
break;
case NVME_TCP_PDU_TYPE_H2C_TERM_REQ:
case NVME_TCP_PDU_TYPE_C2H_TERM_REQ:
expected_hlen = sizeof(struct nvme_tcp_term_req_hdr);
break;
case NVME_TCP_PDU_TYPE_CAPSULE_CMD:
expected_hlen = sizeof(struct nvme_tcp_cmd);
break;
case NVME_TCP_PDU_TYPE_CAPSULE_RESP:
expected_hlen = sizeof(struct nvme_tcp_rsp);
break;
case NVME_TCP_PDU_TYPE_H2C_DATA:
expected_hlen = sizeof(struct nvme_tcp_h2c_data_hdr);
break;
case NVME_TCP_PDU_TYPE_C2H_DATA:
expected_hlen = sizeof(struct nvme_tcp_c2h_data_hdr);
break;
case NVME_TCP_PDU_TYPE_R2T:
expected_hlen = sizeof(struct nvme_tcp_r2t_hdr);
break;
}
if (ch->hlen != expected_hlen) {
printf("NVMe/TCP: Invalid PDU header length %u\n", ch->hlen);
*fes = NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD;
*fei = offsetof(struct nvme_tcp_common_pdu_hdr, hlen);
return (EBADMSG);
}
switch (ch->pdu_type) {
default:
__assert_unreachable();
break;
case NVME_TCP_PDU_TYPE_H2C_TERM_REQ:
case NVME_TCP_PDU_TYPE_C2H_TERM_REQ:
case NVME_TCP_PDU_TYPE_CAPSULE_RESP:
case NVME_TCP_PDU_TYPE_R2T:
if (ch->pdo != 0) {
printf("NVMe/TCP: Invalid PDU data offset %u\n",
ch->pdo);
*fes = NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD;
*fei = offsetof(struct nvme_tcp_common_pdu_hdr, pdo);
return (EBADMSG);
}
break;
case NVME_TCP_PDU_TYPE_CAPSULE_CMD:
case NVME_TCP_PDU_TYPE_H2C_DATA:
case NVME_TCP_PDU_TYPE_C2H_DATA:
if (data_len == 0 && ch->pdo == 0)
break;
if (ch->pdo < full_hlen || ch->pdo > plen ||
ch->pdo % rxpda != 0) {
printf("NVMe/TCP: Invalid PDU data offset %u\n",
ch->pdo);
*fes = NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD;
*fei = offsetof(struct nvme_tcp_common_pdu_hdr, pdo);
return (EBADMSG);
}
break;
}
if (plen < ch->hlen) {
printf("NVMe/TCP: Invalid PDU length %u\n", plen);
*fes = NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD;
*fei = offsetof(struct nvme_tcp_common_pdu_hdr, plen);
return (EBADMSG);
}
switch (ch->pdu_type) {
default:
__assert_unreachable();
break;
case NVME_TCP_PDU_TYPE_H2C_TERM_REQ:
case NVME_TCP_PDU_TYPE_C2H_TERM_REQ:
MPASS(plen <= NVME_TCP_TERM_REQ_PDU_MAX_SIZE);
break;
case NVME_TCP_PDU_TYPE_CAPSULE_CMD:
case NVME_TCP_PDU_TYPE_H2C_DATA:
case NVME_TCP_PDU_TYPE_C2H_DATA:
if ((ch->flags & NVME_TCP_CH_FLAGS_DDGSTF) != 0 &&
data_len <= sizeof(uint32_t)) {
printf("NVMe/TCP: PDU %u too short for digest\n",
ch->pdu_type);
*fes = NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD;
*fei = offsetof(struct nvme_tcp_common_pdu_hdr, plen);
return (EBADMSG);
}
break;
case NVME_TCP_PDU_TYPE_R2T:
case NVME_TCP_PDU_TYPE_CAPSULE_RESP:
if (data_len != 0) {
printf("NVMe/TCP: PDU %u with data length %u\n",
ch->pdu_type, data_len);
*fes = NVME_TCP_TERM_REQ_FES_INVALID_HEADER_FIELD;
*fei = offsetof(struct nvme_tcp_common_pdu_hdr, plen);
return (EBADMSG);
}
break;
}
if ((ch->flags & NVME_TCP_CH_FLAGS_DDGSTF) != 0)
data_len -= sizeof(uint32_t);
*data_lenp = data_len;
return (0);
}
#endif