1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2012 The FreeBSD Foundation 5 * All rights reserved. 6 * 7 * This software was developed by Edward Tomasz Napierala under sponsorship 8 * from the FreeBSD Foundation. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 * 31 */ 32 33 #include <sys/cdefs.h> 34 __FBSDID("$FreeBSD$"); 35 36 #include <sys/types.h> 37 #include <sys/ioctl.h> 38 #include <stdbool.h> 39 #include <string.h> 40 #include <netinet/in.h> 41 42 #include "iscsid.h" 43 #include "iscsi_proto.h" 44 45 static struct pdu * 46 text_receive(struct connection *conn) 47 { 48 struct pdu *response; 49 struct iscsi_bhs_text_response *bhstr; 50 51 response = pdu_new(conn); 52 pdu_receive(response); 53 if (response->pdu_bhs->bhs_opcode != ISCSI_BHS_OPCODE_TEXT_RESPONSE) 54 log_errx(1, "protocol error: received invalid opcode 0x%x", 55 response->pdu_bhs->bhs_opcode); 56 bhstr = (struct iscsi_bhs_text_response *)response->pdu_bhs; 57 #if 0 58 if ((bhstr->bhstr_flags & BHSTR_FLAGS_FINAL) == 0) 59 log_errx(1, "received Text PDU without the \"F\" flag"); 60 #endif 61 /* 62 * XXX: Implement the C flag some day. 63 */ 64 if ((bhstr->bhstr_flags & BHSTR_FLAGS_CONTINUE) != 0) 65 log_errx(1, "received Text PDU with unsupported \"C\" flag"); 66 if (ntohl(bhstr->bhstr_statsn) != conn->conn_statsn + 1) { 67 log_errx(1, "received Text PDU with wrong StatSN: " 68 "is %u, should be %u", ntohl(bhstr->bhstr_statsn), 69 conn->conn_statsn + 1); 70 } 71 conn->conn_statsn = ntohl(bhstr->bhstr_statsn); 72 73 return (response); 74 } 75 76 static struct pdu * 77 text_new_request(struct connection *conn) 78 { 79 struct pdu *request; 80 struct iscsi_bhs_text_request *bhstr; 81 82 request = pdu_new(conn); 83 bhstr = (struct iscsi_bhs_text_request *)request->pdu_bhs; 84 bhstr->bhstr_opcode = ISCSI_BHS_OPCODE_TEXT_REQUEST | 85 ISCSI_BHS_OPCODE_IMMEDIATE; 86 bhstr->bhstr_flags = BHSTR_FLAGS_FINAL; 87 bhstr->bhstr_initiator_task_tag = 0; 88 bhstr->bhstr_target_transfer_tag = 0xffffffff; 89 90 bhstr->bhstr_initiator_task_tag = 0; /* XXX */ 91 bhstr->bhstr_cmdsn = 0; /* XXX */ 92 bhstr->bhstr_expstatsn = htonl(conn->conn_statsn + 1); 93 94 return (request); 95 } 96 97 static struct pdu * 98 logout_receive(struct connection *conn) 99 { 100 struct pdu *response; 101 struct iscsi_bhs_logout_response *bhslr; 102 103 response = pdu_new(conn); 104 pdu_receive(response); 105 if (response->pdu_bhs->bhs_opcode != ISCSI_BHS_OPCODE_LOGOUT_RESPONSE) 106 log_errx(1, "protocol error: received invalid opcode 0x%x", 107 response->pdu_bhs->bhs_opcode); 108 bhslr = (struct iscsi_bhs_logout_response *)response->pdu_bhs; 109 if (ntohs(bhslr->bhslr_response) != BHSLR_RESPONSE_CLOSED_SUCCESSFULLY) 110 log_warnx("received Logout Response with reason %d", 111 ntohs(bhslr->bhslr_response)); 112 if (ntohl(bhslr->bhslr_statsn) != conn->conn_statsn + 1) { 113 log_errx(1, "received Logout PDU with wrong StatSN: " 114 "is %u, should be %u", ntohl(bhslr->bhslr_statsn), 115 conn->conn_statsn + 1); 116 } 117 conn->conn_statsn = ntohl(bhslr->bhslr_statsn); 118 119 return (response); 120 } 121 122 static struct pdu * 123 logout_new_request(struct connection *conn) 124 { 125 struct pdu *request; 126 struct iscsi_bhs_logout_request *bhslr; 127 128 request = pdu_new(conn); 129 bhslr = (struct iscsi_bhs_logout_request *)request->pdu_bhs; 130 bhslr->bhslr_opcode = ISCSI_BHS_OPCODE_LOGOUT_REQUEST | 131 ISCSI_BHS_OPCODE_IMMEDIATE; 132 bhslr->bhslr_reason = BHSLR_REASON_CLOSE_SESSION; 133 bhslr->bhslr_reason |= 0x80; 134 bhslr->bhslr_initiator_task_tag = 0; /* XXX */ 135 bhslr->bhslr_cmdsn = 0; /* XXX */ 136 bhslr->bhslr_expstatsn = htonl(conn->conn_statsn + 1); 137 138 return (request); 139 } 140 141 static void 142 kernel_add(const struct connection *conn, const char *target) 143 { 144 struct iscsi_session_add isa; 145 int error; 146 147 memset(&isa, 0, sizeof(isa)); 148 memcpy(&isa.isa_conf, &conn->conn_conf, sizeof(isa.isa_conf)); 149 strlcpy(isa.isa_conf.isc_target, target, 150 sizeof(isa.isa_conf.isc_target)); 151 isa.isa_conf.isc_discovery = 0; 152 error = ioctl(conn->conn_iscsi_fd, ISCSISADD, &isa); 153 if (error != 0) 154 log_warn("failed to add %s: ISCSISADD", target); 155 } 156 157 static void 158 kernel_remove(const struct connection *conn) 159 { 160 struct iscsi_session_remove isr; 161 int error; 162 163 memset(&isr, 0, sizeof(isr)); 164 isr.isr_session_id = conn->conn_session_id; 165 error = ioctl(conn->conn_iscsi_fd, ISCSISREMOVE, &isr); 166 if (error != 0) 167 log_warn("ISCSISREMOVE"); 168 } 169 170 void 171 discovery(struct connection *conn) 172 { 173 struct pdu *request, *response; 174 struct keys *request_keys, *response_keys; 175 int i; 176 177 log_debugx("beginning discovery session"); 178 request = text_new_request(conn); 179 request_keys = keys_new(); 180 keys_add(request_keys, "SendTargets", "All"); 181 keys_save(request_keys, request); 182 keys_delete(request_keys); 183 request_keys = NULL; 184 pdu_send(request); 185 pdu_delete(request); 186 request = NULL; 187 188 log_debugx("waiting for Text Response"); 189 response = text_receive(conn); 190 response_keys = keys_new(); 191 keys_load(response_keys, response); 192 for (i = 0; i < KEYS_MAX; i++) { 193 if (response_keys->keys_names[i] == NULL) 194 break; 195 196 if (strcmp(response_keys->keys_names[i], "TargetName") != 0) 197 continue; 198 199 log_debugx("adding target %s", response_keys->keys_values[i]); 200 /* 201 * XXX: Validate the target name? 202 */ 203 kernel_add(conn, response_keys->keys_values[i]); 204 } 205 keys_delete(response_keys); 206 pdu_delete(response); 207 208 log_debugx("removing temporary discovery session"); 209 kernel_remove(conn); 210 211 #ifdef ICL_KERNEL_PROXY 212 if (conn->conn_conf.isc_iser == 1) { 213 /* 214 * If we're going through the proxy, the kernel already 215 * sent Logout PDU for us and destroyed the session, 216 * so we can't send anything anymore. 217 */ 218 log_debugx("discovery session done"); 219 return; 220 } 221 #endif 222 223 log_debugx("discovery done; logging out"); 224 request = logout_new_request(conn); 225 pdu_send(request); 226 pdu_delete(request); 227 request = NULL; 228 229 log_debugx("waiting for Logout Response"); 230 response = logout_receive(conn); 231 pdu_delete(response); 232 233 log_debugx("discovery session done"); 234 } 235