diff --git a/lib/libiscsiutil/Makefile b/lib/libiscsiutil/Makefile index 9ec625970eae..c99fb6366536 100644 --- a/lib/libiscsiutil/Makefile +++ b/lib/libiscsiutil/Makefile @@ -1,10 +1,10 @@ LIB= iscsiutil INTERNALLIB= PACKAGE= iscsi INCS= libiscsiutil.h -SRCS= chap.c connection.c keys.c log.c pdu.c utils.c +SRCS= chap.c connection.c keys.c log.c pdu.c text.c utils.c CFLAGS+= -I${SRCTOP}/sys/dev/iscsi .include diff --git a/lib/libiscsiutil/libiscsiutil.h b/lib/libiscsiutil/libiscsiutil.h index 59682a7f6c74..97d699c6c4b9 100644 --- a/lib/libiscsiutil/libiscsiutil.h +++ b/lib/libiscsiutil/libiscsiutil.h @@ -1,160 +1,168 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2012 The FreeBSD Foundation * * This software was developed by Edward Tomasz Napierala under sponsorship * from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef __LIBISCSIUTIL_H__ #define __LIBISCSIUTIL_H__ #include #include struct connection_ops; #define CONN_DIGEST_NONE 0 #define CONN_DIGEST_CRC32C 1 struct connection { const struct connection_ops *conn_ops; int conn_socket; uint8_t conn_isid[6]; uint16_t conn_tsih; uint32_t conn_cmdsn; uint32_t conn_statsn; int conn_header_digest; int conn_data_digest; bool conn_immediate_data; bool conn_use_proxy; int conn_max_recv_data_segment_length; int conn_max_send_data_segment_length; int conn_max_burst_length; int conn_first_burst_length; }; struct pdu { struct connection *pdu_connection; struct iscsi_bhs *pdu_bhs; char *pdu_data; size_t pdu_data_len; }; struct connection_ops { bool (*timed_out)(void); void (*pdu_receive_proxy)(struct pdu *); void (*pdu_send_proxy)(struct pdu *); void (*fail)(const struct connection *, const char *); }; #define KEYS_MAX 1024 struct keys { char *keys_names[KEYS_MAX]; char *keys_values[KEYS_MAX]; }; #define CHAP_CHALLENGE_LEN 1024 #define CHAP_DIGEST_LEN 16 /* Equal to MD5 digest size. */ struct chap { unsigned char chap_id; char chap_challenge[CHAP_CHALLENGE_LEN]; char chap_response[CHAP_DIGEST_LEN]; }; struct rchap { char *rchap_secret; unsigned char rchap_id; void *rchap_challenge; size_t rchap_challenge_len; }; struct chap *chap_new(void); char *chap_get_id(const struct chap *chap); char *chap_get_challenge(const struct chap *chap); int chap_receive(struct chap *chap, const char *response); int chap_authenticate(struct chap *chap, const char *secret); void chap_delete(struct chap *chap); struct rchap *rchap_new(const char *secret); int rchap_receive(struct rchap *rchap, const char *id, const char *challenge); char *rchap_get_response(struct rchap *rchap); void rchap_delete(struct rchap *rchap); struct keys *keys_new(void); void keys_delete(struct keys *key); void keys_load(struct keys *keys, const char *data, size_t len); void keys_save(struct keys *keys, char **datap, size_t *lenp); const char *keys_find(struct keys *keys, const char *name); void keys_add(struct keys *keys, const char *name, const char *value); void keys_add_int(struct keys *keys, const char *name, int value); static __inline void keys_load_pdu(struct keys *keys, const struct pdu *pdu) { keys_load(keys, pdu->pdu_data, pdu->pdu_data_len); } static __inline void keys_save_pdu(struct keys *keys, struct pdu *pdu) { keys_save(keys, &pdu->pdu_data, &pdu->pdu_data_len); } struct pdu *pdu_new(struct connection *ic); struct pdu *pdu_new_response(struct pdu *request); int pdu_ahs_length(const struct pdu *pdu); int pdu_data_segment_length(const struct pdu *pdu); void pdu_set_data_segment_length(struct pdu *pdu, uint32_t len); void pdu_receive(struct pdu *request); void pdu_send(struct pdu *response); void pdu_delete(struct pdu *ip); +void text_send_request(struct connection *conn, + struct keys *request_keys); +struct keys * text_read_response(struct connection *conn); +struct keys * text_read_request(struct connection *conn, + struct pdu **requestp); +void text_send_response(struct pdu *request, + struct keys *response_keys); + void connection_init(struct connection *conn, const struct connection_ops *ops, bool use_proxy); void log_init(int level); void log_set_peer_name(const char *name); void log_set_peer_addr(const char *addr); void log_err(int, const char *, ...) __dead2 __printflike(2, 3); void log_errx(int, const char *, ...) __dead2 __printflike(2, 3); void log_warn(const char *, ...) __printflike(1, 2); void log_warnx(const char *, ...) __printflike(1, 2); void log_debugx(const char *, ...) __printflike(1, 2); char *checked_strdup(const char *); #endif /* !__LIBISCSIUTIL_H__ */ diff --git a/lib/libiscsiutil/text.c b/lib/libiscsiutil/text.c new file mode 100644 index 000000000000..f8b88e4f70be --- /dev/null +++ b/lib/libiscsiutil/text.c @@ -0,0 +1,333 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2012 The FreeBSD Foundation + * + * This software was developed by Edward Tomasz Napierala under sponsorship + * from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include + +#include +#include + +#include +#include "libiscsiutil.h" + +/* Construct a new TextRequest PDU. */ +static struct pdu * +text_new_request(struct connection *conn, uint32_t ttt) +{ + struct pdu *request; + struct iscsi_bhs_text_request *bhstr; + + request = pdu_new(conn); + bhstr = (struct iscsi_bhs_text_request *)request->pdu_bhs; + bhstr->bhstr_opcode = ISCSI_BHS_OPCODE_TEXT_REQUEST | + ISCSI_BHS_OPCODE_IMMEDIATE; + bhstr->bhstr_flags = BHSTR_FLAGS_FINAL; + bhstr->bhstr_initiator_task_tag = 0; + bhstr->bhstr_target_transfer_tag = ttt; + + bhstr->bhstr_cmdsn = conn->conn_cmdsn; + bhstr->bhstr_expstatsn = htonl(conn->conn_statsn + 1); + + return (request); +} + +/* Receive a TextRequest PDU from a connection. */ +static struct pdu * +text_receive_request(struct connection *conn) +{ + struct pdu *request; + struct iscsi_bhs_text_request *bhstr; + + request = pdu_new(conn); + pdu_receive(request); + if ((request->pdu_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) != + ISCSI_BHS_OPCODE_TEXT_REQUEST) + log_errx(1, "protocol error: received invalid opcode 0x%x", + request->pdu_bhs->bhs_opcode); + bhstr = (struct iscsi_bhs_text_request *)request->pdu_bhs; + + /* + * XXX: Implement the C flag some day. + */ + if ((bhstr->bhstr_flags & (BHSTR_FLAGS_FINAL | BHSTR_FLAGS_CONTINUE)) != + BHSTR_FLAGS_FINAL) + log_errx(1, "received TextRequest PDU with invalid " + "flags: %u", bhstr->bhstr_flags); + if (ISCSI_SNLT(ntohl(bhstr->bhstr_cmdsn), conn->conn_cmdsn)) { + log_errx(1, "received TextRequest PDU with decreasing CmdSN: " + "was %u, is %u", conn->conn_cmdsn, ntohl(bhstr->bhstr_cmdsn)); + } + conn->conn_cmdsn = ntohl(bhstr->bhstr_cmdsn); + if ((bhstr->bhstr_opcode & ISCSI_BHS_OPCODE_IMMEDIATE) == 0) + conn->conn_cmdsn++; + + return (request); +} + +/* Construct a new TextResponse PDU in reply to a request. */ +static struct pdu * +text_new_response(struct pdu *request, uint32_t ttt, bool final) +{ + struct pdu *response; + struct connection *conn; + struct iscsi_bhs_text_request *bhstr; + struct iscsi_bhs_text_response *bhstr2; + + bhstr = (struct iscsi_bhs_text_request *)request->pdu_bhs; + conn = request->pdu_connection; + + response = pdu_new_response(request); + bhstr2 = (struct iscsi_bhs_text_response *)response->pdu_bhs; + bhstr2->bhstr_opcode = ISCSI_BHS_OPCODE_TEXT_RESPONSE; + if (final) + bhstr2->bhstr_flags = BHSTR_FLAGS_FINAL; + else + bhstr2->bhstr_flags = BHSTR_FLAGS_CONTINUE; + bhstr2->bhstr_lun = bhstr->bhstr_lun; + bhstr2->bhstr_initiator_task_tag = bhstr->bhstr_initiator_task_tag; + bhstr2->bhstr_target_transfer_tag = ttt; + bhstr2->bhstr_statsn = htonl(conn->conn_statsn++); + bhstr2->bhstr_expcmdsn = htonl(conn->conn_cmdsn); + bhstr2->bhstr_maxcmdsn = htonl(conn->conn_cmdsn); + + return (response); +} + +/* Receive a TextResponse PDU from a connection. */ +static struct pdu * +text_receive_response(struct connection *conn) +{ + struct pdu *response; + struct iscsi_bhs_text_response *bhstr; + uint8_t flags; + + response = pdu_new(conn); + pdu_receive(response); + if (response->pdu_bhs->bhs_opcode != ISCSI_BHS_OPCODE_TEXT_RESPONSE) + log_errx(1, "protocol error: received invalid opcode 0x%x", + response->pdu_bhs->bhs_opcode); + bhstr = (struct iscsi_bhs_text_response *)response->pdu_bhs; + flags = bhstr->bhstr_flags & (BHSTR_FLAGS_FINAL | BHSTR_FLAGS_CONTINUE); + switch (flags) { + case BHSTR_FLAGS_CONTINUE: + if (bhstr->bhstr_target_transfer_tag == 0xffffffff) + log_errx(1, "received continue TextResponse PDU with " + "invalid TTT 0x%x", + bhstr->bhstr_target_transfer_tag); + break; + case BHSTR_FLAGS_FINAL: + if (bhstr->bhstr_target_transfer_tag != 0xffffffff) + log_errx(1, "received final TextResponse PDU with " + "invalid TTT 0x%x", + bhstr->bhstr_target_transfer_tag); + break; + default: + log_errx(1, "received TextResponse PDU with invalid " + "flags: %u", bhstr->bhstr_flags); + } + if (ntohl(bhstr->bhstr_statsn) != conn->conn_statsn + 1) { + log_errx(1, "received TextResponse PDU with wrong StatSN: " + "is %u, should be %u", ntohl(bhstr->bhstr_statsn), + conn->conn_statsn + 1); + } + conn->conn_statsn = ntohl(bhstr->bhstr_statsn); + + return (response); +} + +/* + * Send a list of keys from the initiator to the target in a + * TextRequest PDU. + */ +void +text_send_request(struct connection *conn, struct keys *request_keys) +{ + struct pdu *request; + + request = text_new_request(conn, 0xffffffff); + keys_save_pdu(request_keys, request); + if (request->pdu_data_len == 0) + log_errx(1, "No keys to send in a TextRequest"); + if (request->pdu_data_len > + (size_t)conn->conn_max_send_data_segment_length) + log_errx(1, "Keys to send in TextRequest are too long"); + + pdu_send(request); + pdu_delete(request); +} + +/* + * Read a list of keys from the target in a series of TextResponse + * PDUs. + */ +struct keys * +text_read_response(struct connection *conn) +{ + struct keys *response_keys; + char *keys_data; + size_t keys_len; + uint32_t ttt; + + keys_data = NULL; + keys_len = 0; + ttt = 0xffffffff; + for (;;) { + struct pdu *request, *response; + struct iscsi_bhs_text_response *bhstr; + + response = text_receive_response(conn); + bhstr = (struct iscsi_bhs_text_response *)response->pdu_bhs; + if (keys_data == NULL) { + ttt = bhstr->bhstr_target_transfer_tag; + keys_data = response->pdu_data; + keys_len = response->pdu_data_len; + response->pdu_data = NULL; + } else { + keys_data = realloc(keys_data, + keys_len + response->pdu_data_len); + if (keys_data == NULL) + log_err(1, "failed to grow keys block"); + memcpy(keys_data + keys_len, response->pdu_data, + response->pdu_data_len); + keys_len += response->pdu_data_len; + } + if ((bhstr->bhstr_flags & BHSTR_FLAGS_FINAL) != 0) { + pdu_delete(response); + break; + } + if (bhstr->bhstr_target_transfer_tag != ttt) + log_errx(1, "received non-final TextRequest PDU with " + "invalid TTT 0x%x", + bhstr->bhstr_target_transfer_tag); + pdu_delete(response); + + /* Send an empty request. */ + request = text_new_request(conn, ttt); + pdu_send(request); + pdu_delete(request); + } + + response_keys = keys_new(); + keys_load(response_keys, keys_data, keys_len); + free(keys_data); + return (response_keys); +} + +/* + * Read a list of keys from the initiator in a TextRequest PDU. + */ +struct keys * +text_read_request(struct connection *conn, struct pdu **requestp) +{ + struct iscsi_bhs_text_request *bhstr; + struct pdu *request; + struct keys *request_keys; + + request = text_receive_request(conn); + bhstr = (struct iscsi_bhs_text_request *)request->pdu_bhs; + if (bhstr->bhstr_target_transfer_tag != 0xffffffff) + log_errx(1, "received TextRequest PDU with invalid TTT 0x%x", + bhstr->bhstr_target_transfer_tag); + if (ntohl(bhstr->bhstr_expstatsn) != conn->conn_statsn) { + log_errx(1, "received TextRequest PDU with wrong ExpStatSN: " + "is %u, should be %u", ntohl(bhstr->bhstr_expstatsn), + conn->conn_statsn); + } + + request_keys = keys_new(); + keys_load_pdu(request_keys, request); + *requestp = request; + return (request_keys); +} + +/* + * Send a response back to the initiator as a series of TextResponse + * PDUs. + */ +void +text_send_response(struct pdu *request, struct keys *response_keys) +{ + struct connection *conn = request->pdu_connection; + char *keys_data; + size_t keys_len; + size_t keys_offset; + uint32_t ttt; + + keys_save(response_keys, &keys_data, &keys_len); + keys_offset = 0; + ttt = keys_len; + for (;;) { + struct pdu *request2, *response; + struct iscsi_bhs_text_request *bhstr; + size_t todo; + bool final; + + todo = keys_len - keys_offset; + if (todo > (size_t)conn->conn_max_send_data_segment_length) { + final = false; + todo = conn->conn_max_send_data_segment_length; + } else { + final = true; + ttt = 0xffffffff; + } + + response = text_new_response(request, ttt, final); + response->pdu_data = keys_data + keys_offset; + response->pdu_data_len = todo; + keys_offset += todo; + + pdu_send(response); + response->pdu_data = NULL; + pdu_delete(response); + + if (final) + break; + + /* + * Wait for an empty request. + * + * XXX: Linux's Open-iSCSI initiator doesn't update + * ExpStatSN when receiving a TextResponse PDU. + */ + request2 = text_receive_request(conn); + bhstr = (struct iscsi_bhs_text_request *)request2->pdu_bhs; + if ((bhstr->bhstr_flags & BHSTR_FLAGS_FINAL) == 0) + log_errx(1, "received continuation TextRequest PDU " + "without F set"); + if (pdu_data_segment_length(request2) != 0) + log_errx(1, "received non-empty continuation " + "TextRequest PDU"); + if (bhstr->bhstr_target_transfer_tag != ttt) + log_errx(1, "received TextRequest PDU with invalid " + "TTT 0x%x", bhstr->bhstr_target_transfer_tag); + pdu_delete(request2); + } + free(keys_data); +} diff --git a/usr.sbin/ctld/discovery.c b/usr.sbin/ctld/discovery.c index 244b08a7f63c..3a713c1afac0 100644 --- a/usr.sbin/ctld/discovery.c +++ b/usr.sbin/ctld/discovery.c @@ -1,337 +1,269 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2012 The FreeBSD Foundation * * This software was developed by Edward Tomasz Napierala under sponsorship * from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include "ctld.h" #include "iscsi_proto.h" -static struct pdu * -text_receive(struct connection *conn) -{ - struct pdu *request; - struct iscsi_bhs_text_request *bhstr; - - request = pdu_new(conn); - pdu_receive(request); - if ((request->pdu_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) != - ISCSI_BHS_OPCODE_TEXT_REQUEST) - log_errx(1, "protocol error: received invalid opcode 0x%x", - request->pdu_bhs->bhs_opcode); - bhstr = (struct iscsi_bhs_text_request *)request->pdu_bhs; -#if 0 - if ((bhstr->bhstr_flags & ISCSI_BHSTR_FLAGS_FINAL) == 0) - log_errx(1, "received Text PDU without the \"F\" flag"); -#endif - /* - * XXX: Implement the C flag some day. - */ - if ((bhstr->bhstr_flags & BHSTR_FLAGS_CONTINUE) != 0) - log_errx(1, "received Text PDU with unsupported \"C\" flag"); - if (ISCSI_SNLT(ntohl(bhstr->bhstr_cmdsn), conn->conn_cmdsn)) { - log_errx(1, "received Text PDU with decreasing CmdSN: " - "was %u, is %u", conn->conn_cmdsn, ntohl(bhstr->bhstr_cmdsn)); - } - if (ntohl(bhstr->bhstr_expstatsn) != conn->conn_statsn) { - log_errx(1, "received Text PDU with wrong ExpStatSN: " - "is %u, should be %u", ntohl(bhstr->bhstr_expstatsn), - conn->conn_statsn); - } - conn->conn_cmdsn = ntohl(bhstr->bhstr_cmdsn); - if ((bhstr->bhstr_opcode & ISCSI_BHS_OPCODE_IMMEDIATE) == 0) - conn->conn_cmdsn++; - - return (request); -} - -static struct pdu * -text_new_response(struct pdu *request) -{ - struct pdu *response; - struct connection *conn; - struct iscsi_bhs_text_request *bhstr; - struct iscsi_bhs_text_response *bhstr2; - - bhstr = (struct iscsi_bhs_text_request *)request->pdu_bhs; - conn = request->pdu_connection; - - response = pdu_new_response(request); - bhstr2 = (struct iscsi_bhs_text_response *)response->pdu_bhs; - bhstr2->bhstr_opcode = ISCSI_BHS_OPCODE_TEXT_RESPONSE; - bhstr2->bhstr_flags = BHSTR_FLAGS_FINAL; - bhstr2->bhstr_lun = bhstr->bhstr_lun; - bhstr2->bhstr_initiator_task_tag = bhstr->bhstr_initiator_task_tag; - bhstr2->bhstr_target_transfer_tag = bhstr->bhstr_target_transfer_tag; - bhstr2->bhstr_statsn = htonl(conn->conn_statsn++); - bhstr2->bhstr_expcmdsn = htonl(conn->conn_cmdsn); - bhstr2->bhstr_maxcmdsn = htonl(conn->conn_cmdsn); - - return (response); -} - static struct pdu * logout_receive(struct connection *conn) { struct pdu *request; struct iscsi_bhs_logout_request *bhslr; request = pdu_new(conn); pdu_receive(request); if ((request->pdu_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) != ISCSI_BHS_OPCODE_LOGOUT_REQUEST) log_errx(1, "protocol error: received invalid opcode 0x%x", request->pdu_bhs->bhs_opcode); bhslr = (struct iscsi_bhs_logout_request *)request->pdu_bhs; if ((bhslr->bhslr_reason & 0x7f) != BHSLR_REASON_CLOSE_SESSION) log_debugx("received Logout PDU with invalid reason 0x%x; " "continuing anyway", bhslr->bhslr_reason & 0x7f); if (ISCSI_SNLT(ntohl(bhslr->bhslr_cmdsn), conn->conn_cmdsn)) { log_errx(1, "received Logout PDU with decreasing CmdSN: " "was %u, is %u", conn->conn_cmdsn, ntohl(bhslr->bhslr_cmdsn)); } if (ntohl(bhslr->bhslr_expstatsn) != conn->conn_statsn) { log_errx(1, "received Logout PDU with wrong ExpStatSN: " "is %u, should be %u", ntohl(bhslr->bhslr_expstatsn), conn->conn_statsn); } conn->conn_cmdsn = ntohl(bhslr->bhslr_cmdsn); if ((bhslr->bhslr_opcode & ISCSI_BHS_OPCODE_IMMEDIATE) == 0) conn->conn_cmdsn++; return (request); } static struct pdu * logout_new_response(struct pdu *request) { struct pdu *response; struct connection *conn; struct iscsi_bhs_logout_request *bhslr; struct iscsi_bhs_logout_response *bhslr2; bhslr = (struct iscsi_bhs_logout_request *)request->pdu_bhs; conn = request->pdu_connection; response = pdu_new_response(request); bhslr2 = (struct iscsi_bhs_logout_response *)response->pdu_bhs; bhslr2->bhslr_opcode = ISCSI_BHS_OPCODE_LOGOUT_RESPONSE; bhslr2->bhslr_flags = 0x80; bhslr2->bhslr_response = BHSLR_RESPONSE_CLOSED_SUCCESSFULLY; bhslr2->bhslr_initiator_task_tag = bhslr->bhslr_initiator_task_tag; bhslr2->bhslr_statsn = htonl(conn->conn_statsn++); bhslr2->bhslr_expcmdsn = htonl(conn->conn_cmdsn); bhslr2->bhslr_maxcmdsn = htonl(conn->conn_cmdsn); return (response); } static void discovery_add_target(struct keys *response_keys, const struct target *targ) { struct port *port; struct portal *portal; char *buf; char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; struct addrinfo *ai; int ret; keys_add(response_keys, "TargetName", targ->t_name); TAILQ_FOREACH(port, &targ->t_ports, p_ts) { if (port->p_portal_group == NULL) continue; TAILQ_FOREACH(portal, &port->p_portal_group->pg_portals, p_next) { ai = portal->p_ai; ret = getnameinfo(ai->ai_addr, ai->ai_addrlen, hbuf, sizeof(hbuf), sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV); if (ret != 0) { log_warnx("getnameinfo: %s", gai_strerror(ret)); continue; } switch (ai->ai_addr->sa_family) { case AF_INET: if (strcmp(hbuf, "0.0.0.0") == 0) continue; ret = asprintf(&buf, "%s:%s,%d", hbuf, sbuf, port->p_portal_group->pg_tag); break; case AF_INET6: if (strcmp(hbuf, "::") == 0) continue; ret = asprintf(&buf, "[%s]:%s,%d", hbuf, sbuf, port->p_portal_group->pg_tag); break; default: continue; } if (ret <= 0) log_err(1, "asprintf"); keys_add(response_keys, "TargetAddress", buf); free(buf); } } } static bool discovery_target_filtered_out(const struct ctld_connection *conn, const struct port *port) { const struct auth_group *ag; const struct portal_group *pg; const struct target *targ; const struct auth *auth; int error; targ = port->p_target; ag = port->p_auth_group; if (ag == NULL) ag = targ->t_auth_group; pg = conn->conn_portal->p_portal_group; assert(pg->pg_discovery_auth_group != PG_FILTER_UNKNOWN); if (pg->pg_discovery_filter >= PG_FILTER_PORTAL && auth_portal_check(ag, &conn->conn_initiator_sa) != 0) { log_debugx("initiator does not match initiator portals " "allowed for target \"%s\"; skipping", targ->t_name); return (true); } if (pg->pg_discovery_filter >= PG_FILTER_PORTAL_NAME && auth_name_check(ag, conn->conn_initiator_name) != 0) { log_debugx("initiator does not match initiator names " "allowed for target \"%s\"; skipping", targ->t_name); return (true); } if (pg->pg_discovery_filter >= PG_FILTER_PORTAL_NAME_AUTH && ag->ag_type != AG_TYPE_NO_AUTHENTICATION) { if (conn->conn_chap == NULL) { assert(pg->pg_discovery_auth_group->ag_type == AG_TYPE_NO_AUTHENTICATION); log_debugx("initiator didn't authenticate, but target " "\"%s\" requires CHAP; skipping", targ->t_name); return (true); } assert(conn->conn_user != NULL); auth = auth_find(ag, conn->conn_user); if (auth == NULL) { log_debugx("CHAP user \"%s\" doesn't match target " "\"%s\"; skipping", conn->conn_user, targ->t_name); return (true); } error = chap_authenticate(conn->conn_chap, auth->a_secret); if (error != 0) { log_debugx("password for CHAP user \"%s\" doesn't " "match target \"%s\"; skipping", conn->conn_user, targ->t_name); return (true); } } return (false); } void discovery(struct ctld_connection *conn) { struct pdu *request, *response; struct keys *request_keys, *response_keys; const struct port *port; const struct portal_group *pg; const char *send_targets; pg = conn->conn_portal->p_portal_group; - log_debugx("beginning discovery session; waiting for Text PDU"); - request = text_receive(&conn->conn); - request_keys = keys_new(); - keys_load_pdu(request_keys, request); + log_debugx("beginning discovery session; waiting for TextRequest PDU"); + request_keys = text_read_request(&conn->conn, &request); send_targets = keys_find(request_keys, "SendTargets"); if (send_targets == NULL) - log_errx(1, "received Text PDU without SendTargets"); + log_errx(1, "received TextRequest PDU without SendTargets"); - response = text_new_response(request); response_keys = keys_new(); if (strcmp(send_targets, "All") == 0) { TAILQ_FOREACH(port, &pg->pg_ports, p_pgs) { if (discovery_target_filtered_out(conn, port)) { /* Ignore this target. */ continue; } discovery_add_target(response_keys, port->p_target); } } else { port = port_find_in_pg(pg, send_targets); if (port == NULL) { log_debugx("initiator requested information on unknown " "target \"%s\"; returning nothing", send_targets); } else { if (discovery_target_filtered_out(conn, port)) { /* Ignore this target. */ } else { discovery_add_target(response_keys, port->p_target); } } } - keys_save_pdu(response_keys, response); - pdu_send(response); - pdu_delete(response); + text_send_response(request, response_keys); keys_delete(response_keys); pdu_delete(request); keys_delete(request_keys); log_debugx("done sending targets; waiting for Logout PDU"); request = logout_receive(&conn->conn); response = logout_new_response(request); pdu_send(response); pdu_delete(response); pdu_delete(request); log_debugx("discovery session done"); } diff --git a/usr.sbin/iscsid/discovery.c b/usr.sbin/iscsid/discovery.c index 60aff28c1b03..942d4a4bc408 100644 --- a/usr.sbin/iscsid/discovery.c +++ b/usr.sbin/iscsid/discovery.c @@ -1,233 +1,174 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2012 The FreeBSD Foundation * * This software was developed by Edward Tomasz Napierala under sponsorship * from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include "iscsid.h" #include "iscsi_proto.h" -static struct pdu * -text_receive(struct connection *conn) -{ - struct pdu *response; - struct iscsi_bhs_text_response *bhstr; - - response = pdu_new(conn); - pdu_receive(response); - if (response->pdu_bhs->bhs_opcode != ISCSI_BHS_OPCODE_TEXT_RESPONSE) - log_errx(1, "protocol error: received invalid opcode 0x%x", - response->pdu_bhs->bhs_opcode); - bhstr = (struct iscsi_bhs_text_response *)response->pdu_bhs; -#if 0 - if ((bhstr->bhstr_flags & BHSTR_FLAGS_FINAL) == 0) - log_errx(1, "received Text PDU without the \"F\" flag"); -#endif - /* - * XXX: Implement the C flag some day. - */ - if ((bhstr->bhstr_flags & BHSTR_FLAGS_CONTINUE) != 0) - log_errx(1, "received Text PDU with unsupported \"C\" flag"); - if (ntohl(bhstr->bhstr_statsn) != conn->conn_statsn + 1) { - log_errx(1, "received Text PDU with wrong StatSN: " - "is %u, should be %u", ntohl(bhstr->bhstr_statsn), - conn->conn_statsn + 1); - } - conn->conn_statsn = ntohl(bhstr->bhstr_statsn); - - return (response); -} - -static struct pdu * -text_new_request(struct connection *conn) -{ - struct pdu *request; - struct iscsi_bhs_text_request *bhstr; - - request = pdu_new(conn); - bhstr = (struct iscsi_bhs_text_request *)request->pdu_bhs; - bhstr->bhstr_opcode = ISCSI_BHS_OPCODE_TEXT_REQUEST | - ISCSI_BHS_OPCODE_IMMEDIATE; - bhstr->bhstr_flags = BHSTR_FLAGS_FINAL; - bhstr->bhstr_initiator_task_tag = 0; - bhstr->bhstr_target_transfer_tag = 0xffffffff; - - bhstr->bhstr_initiator_task_tag = 0; /* XXX */ - bhstr->bhstr_cmdsn = 0; /* XXX */ - bhstr->bhstr_expstatsn = htonl(conn->conn_statsn + 1); - - return (request); -} - static struct pdu * logout_receive(struct connection *conn) { struct pdu *response; struct iscsi_bhs_logout_response *bhslr; response = pdu_new(conn); pdu_receive(response); if (response->pdu_bhs->bhs_opcode != ISCSI_BHS_OPCODE_LOGOUT_RESPONSE) log_errx(1, "protocol error: received invalid opcode 0x%x", response->pdu_bhs->bhs_opcode); bhslr = (struct iscsi_bhs_logout_response *)response->pdu_bhs; if (ntohs(bhslr->bhslr_response) != BHSLR_RESPONSE_CLOSED_SUCCESSFULLY) log_warnx("received Logout Response with reason %d", ntohs(bhslr->bhslr_response)); if (ntohl(bhslr->bhslr_statsn) != conn->conn_statsn + 1) { log_errx(1, "received Logout PDU with wrong StatSN: " "is %u, should be %u", ntohl(bhslr->bhslr_statsn), conn->conn_statsn + 1); } conn->conn_statsn = ntohl(bhslr->bhslr_statsn); return (response); } static struct pdu * logout_new_request(struct connection *conn) { struct pdu *request; struct iscsi_bhs_logout_request *bhslr; request = pdu_new(conn); bhslr = (struct iscsi_bhs_logout_request *)request->pdu_bhs; bhslr->bhslr_opcode = ISCSI_BHS_OPCODE_LOGOUT_REQUEST | ISCSI_BHS_OPCODE_IMMEDIATE; bhslr->bhslr_reason = BHSLR_REASON_CLOSE_SESSION; bhslr->bhslr_reason |= 0x80; bhslr->bhslr_initiator_task_tag = 0; /* XXX */ bhslr->bhslr_cmdsn = 0; /* XXX */ bhslr->bhslr_expstatsn = htonl(conn->conn_statsn + 1); return (request); } static void kernel_add(const struct iscsid_connection *conn, const char *target) { struct iscsi_session_add isa; int error; memset(&isa, 0, sizeof(isa)); memcpy(&isa.isa_conf, &conn->conn_conf, sizeof(isa.isa_conf)); strlcpy(isa.isa_conf.isc_target, target, sizeof(isa.isa_conf.isc_target)); isa.isa_conf.isc_discovery = 0; error = ioctl(conn->conn_iscsi_fd, ISCSISADD, &isa); if (error != 0) log_warn("failed to add %s: ISCSISADD", target); } static void kernel_remove(const struct iscsid_connection *conn) { struct iscsi_session_remove isr; int error; memset(&isr, 0, sizeof(isr)); isr.isr_session_id = conn->conn_session_id; error = ioctl(conn->conn_iscsi_fd, ISCSISREMOVE, &isr); if (error != 0) log_warn("ISCSISREMOVE"); } void discovery(struct iscsid_connection *conn) { struct pdu *request, *response; struct keys *request_keys, *response_keys; int i; log_debugx("beginning discovery session"); - request = text_new_request(&conn->conn); request_keys = keys_new(); keys_add(request_keys, "SendTargets", "All"); - keys_save_pdu(request_keys, request); + text_send_request(&conn->conn, request_keys); keys_delete(request_keys); request_keys = NULL; - pdu_send(request); - pdu_delete(request); - request = NULL; log_debugx("waiting for Text Response"); - response = text_receive(&conn->conn); - response_keys = keys_new(); - keys_load_pdu(response_keys, response); + response_keys = text_read_response(&conn->conn); for (i = 0; i < KEYS_MAX; i++) { if (response_keys->keys_names[i] == NULL) break; if (strcmp(response_keys->keys_names[i], "TargetName") != 0) continue; log_debugx("adding target %s", response_keys->keys_values[i]); /* * XXX: Validate the target name? */ kernel_add(conn, response_keys->keys_values[i]); } keys_delete(response_keys); - pdu_delete(response); log_debugx("removing temporary discovery session"); kernel_remove(conn); #ifdef ICL_KERNEL_PROXY if (conn->conn_conf.isc_iser == 1) { /* * If we're going through the proxy, the kernel already * sent Logout PDU for us and destroyed the session, * so we can't send anything anymore. */ log_debugx("discovery session done"); return; } #endif log_debugx("discovery done; logging out"); request = logout_new_request(&conn->conn); pdu_send(request); pdu_delete(request); request = NULL; log_debugx("waiting for Logout Response"); response = logout_receive(&conn->conn); pdu_delete(response); log_debugx("discovery session done"); }