1 /*- 2 * Copyright (c) 2012 The FreeBSD Foundation 3 * All rights reserved. 4 * 5 * This software was developed by Edward Tomasz Napierala under sponsorship 6 * from the FreeBSD Foundation. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 * $FreeBSD$ 30 */ 31 32 #include <assert.h> 33 #include <stdint.h> 34 #include <stdio.h> 35 #include <stdlib.h> 36 #include <string.h> 37 #include <netinet/in.h> 38 39 #include "ctld.h" 40 #include "iscsi_proto.h" 41 42 static struct pdu * 43 text_receive(struct connection *conn) 44 { 45 struct pdu *request; 46 struct iscsi_bhs_text_request *bhstr; 47 48 request = pdu_new(conn); 49 pdu_receive(request); 50 if ((request->pdu_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) != 51 ISCSI_BHS_OPCODE_TEXT_REQUEST) 52 log_errx(1, "protocol error: received invalid opcode 0x%x", 53 request->pdu_bhs->bhs_opcode); 54 bhstr = (struct iscsi_bhs_text_request *)request->pdu_bhs; 55 #if 0 56 if ((bhstr->bhstr_flags & ISCSI_BHSTR_FLAGS_FINAL) == 0) 57 log_errx(1, "received Text PDU without the \"F\" flag"); 58 #endif 59 /* 60 * XXX: Implement the C flag some day. 61 */ 62 if ((bhstr->bhstr_flags & BHSTR_FLAGS_CONTINUE) != 0) 63 log_errx(1, "received Text PDU with unsupported \"C\" flag"); 64 if (request->pdu_data_len == 0) 65 log_errx(1, "received Text PDU with empty data segment"); 66 67 if (ntohl(bhstr->bhstr_cmdsn) < conn->conn_cmdsn) { 68 log_errx(1, "received Text PDU with decreasing CmdSN: " 69 "was %d, is %d", conn->conn_cmdsn, ntohl(bhstr->bhstr_cmdsn)); 70 } 71 if (ntohl(bhstr->bhstr_expstatsn) != conn->conn_statsn) { 72 log_errx(1, "received Text PDU with wrong StatSN: " 73 "is %d, should be %d", ntohl(bhstr->bhstr_expstatsn), 74 conn->conn_statsn); 75 } 76 conn->conn_cmdsn = ntohl(bhstr->bhstr_cmdsn); 77 78 return (request); 79 } 80 81 static struct pdu * 82 text_new_response(struct pdu *request) 83 { 84 struct pdu *response; 85 struct connection *conn; 86 struct iscsi_bhs_text_request *bhstr; 87 struct iscsi_bhs_text_response *bhstr2; 88 89 bhstr = (struct iscsi_bhs_text_request *)request->pdu_bhs; 90 conn = request->pdu_connection; 91 92 response = pdu_new_response(request); 93 bhstr2 = (struct iscsi_bhs_text_response *)response->pdu_bhs; 94 bhstr2->bhstr_opcode = ISCSI_BHS_OPCODE_TEXT_RESPONSE; 95 bhstr2->bhstr_flags = BHSTR_FLAGS_FINAL; 96 bhstr2->bhstr_lun = bhstr->bhstr_lun; 97 bhstr2->bhstr_initiator_task_tag = bhstr->bhstr_initiator_task_tag; 98 bhstr2->bhstr_target_transfer_tag = bhstr->bhstr_target_transfer_tag; 99 bhstr2->bhstr_statsn = htonl(conn->conn_statsn++); 100 bhstr2->bhstr_expcmdsn = htonl(conn->conn_cmdsn); 101 bhstr2->bhstr_maxcmdsn = htonl(conn->conn_cmdsn); 102 103 return (response); 104 } 105 106 static struct pdu * 107 logout_receive(struct connection *conn) 108 { 109 struct pdu *request; 110 struct iscsi_bhs_logout_request *bhslr; 111 112 request = pdu_new(conn); 113 pdu_receive(request); 114 if ((request->pdu_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) != 115 ISCSI_BHS_OPCODE_LOGOUT_REQUEST) 116 log_errx(1, "protocol error: received invalid opcode 0x%x", 117 request->pdu_bhs->bhs_opcode); 118 bhslr = (struct iscsi_bhs_logout_request *)request->pdu_bhs; 119 if ((bhslr->bhslr_reason & 0x7f) != BHSLR_REASON_CLOSE_SESSION) 120 log_debugx("received Logout PDU with invalid reason 0x%x; " 121 "continuing anyway", bhslr->bhslr_reason & 0x7f); 122 if (ntohl(bhslr->bhslr_cmdsn) < conn->conn_cmdsn) { 123 log_errx(1, "received Logout PDU with decreasing CmdSN: " 124 "was %d, is %d", conn->conn_cmdsn, 125 ntohl(bhslr->bhslr_cmdsn)); 126 } 127 if (ntohl(bhslr->bhslr_expstatsn) != conn->conn_statsn) { 128 log_errx(1, "received Logout PDU with wrong StatSN: " 129 "is %d, should be %d", ntohl(bhslr->bhslr_expstatsn), 130 conn->conn_statsn); 131 } 132 conn->conn_cmdsn = ntohl(bhslr->bhslr_cmdsn); 133 134 return (request); 135 } 136 137 static struct pdu * 138 logout_new_response(struct pdu *request) 139 { 140 struct pdu *response; 141 struct connection *conn; 142 struct iscsi_bhs_logout_request *bhslr; 143 struct iscsi_bhs_logout_response *bhslr2; 144 145 bhslr = (struct iscsi_bhs_logout_request *)request->pdu_bhs; 146 conn = request->pdu_connection; 147 148 response = pdu_new_response(request); 149 bhslr2 = (struct iscsi_bhs_logout_response *)response->pdu_bhs; 150 bhslr2->bhslr_opcode = ISCSI_BHS_OPCODE_LOGOUT_RESPONSE; 151 bhslr2->bhslr_flags = 0x80; 152 bhslr2->bhslr_response = BHSLR_RESPONSE_CLOSED_SUCCESSFULLY; 153 bhslr2->bhslr_initiator_task_tag = bhslr->bhslr_initiator_task_tag; 154 bhslr2->bhslr_statsn = htonl(conn->conn_statsn++); 155 bhslr2->bhslr_expcmdsn = htonl(conn->conn_cmdsn); 156 bhslr2->bhslr_maxcmdsn = htonl(conn->conn_cmdsn); 157 158 return (response); 159 } 160 161 void 162 discovery(struct connection *conn) 163 { 164 struct pdu *request, *response; 165 struct keys *request_keys, *response_keys; 166 struct target *targ; 167 const char *send_targets; 168 169 log_debugx("beginning discovery session; waiting for Text PDU"); 170 request = text_receive(conn); 171 request_keys = keys_new(); 172 keys_load(request_keys, request); 173 174 send_targets = keys_find(request_keys, "SendTargets"); 175 if (send_targets == NULL) 176 log_errx(1, "received Text PDU without SendTargets"); 177 178 response = text_new_response(request); 179 response_keys = keys_new(); 180 181 if (strcmp(send_targets, "All") == 0) { 182 TAILQ_FOREACH(targ, 183 &conn->conn_portal->p_portal_group->pg_conf->conf_targets, 184 t_next) { 185 if (targ->t_portal_group != 186 conn->conn_portal->p_portal_group) { 187 log_debugx("not returning target \"%s\"; " 188 "belongs to a different portal group", 189 targ->t_iqn); 190 continue; 191 } 192 keys_add(response_keys, "TargetName", targ->t_iqn); 193 } 194 } else { 195 targ = target_find(conn->conn_portal->p_portal_group->pg_conf, 196 send_targets); 197 if (targ == NULL) { 198 log_debugx("initiator requested information on unknown " 199 "target \"%s\"; returning nothing", send_targets); 200 } else { 201 keys_add(response_keys, "TargetName", targ->t_iqn); 202 } 203 } 204 keys_save(response_keys, response); 205 206 pdu_send(response); 207 pdu_delete(response); 208 keys_delete(response_keys); 209 pdu_delete(request); 210 keys_delete(request_keys); 211 212 log_debugx("done sending targets; waiting for Logout PDU"); 213 request = logout_receive(conn); 214 response = logout_new_response(request); 215 216 pdu_send(response); 217 pdu_delete(response); 218 pdu_delete(request); 219 220 log_debugx("discovery session done"); 221 } 222