discovery.c revision 263733
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: stable/10/usr.sbin/ctld/discovery.c 263733 2014-03-25 12:33:16Z trasz $ 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 42static struct pdu * 43text_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 (ntohl(bhstr->bhstr_cmdsn) < conn->conn_cmdsn) { 65 log_errx(1, "received Text PDU with decreasing CmdSN: " 66 "was %d, is %d", conn->conn_cmdsn, ntohl(bhstr->bhstr_cmdsn)); 67 } 68 if (ntohl(bhstr->bhstr_expstatsn) != conn->conn_statsn) { 69 log_errx(1, "received Text PDU with wrong StatSN: " 70 "is %d, should be %d", ntohl(bhstr->bhstr_expstatsn), 71 conn->conn_statsn); 72 } 73 conn->conn_cmdsn = ntohl(bhstr->bhstr_cmdsn); 74 75 return (request); 76} 77 78static struct pdu * 79text_new_response(struct pdu *request) 80{ 81 struct pdu *response; 82 struct connection *conn; 83 struct iscsi_bhs_text_request *bhstr; 84 struct iscsi_bhs_text_response *bhstr2; 85 86 bhstr = (struct iscsi_bhs_text_request *)request->pdu_bhs; 87 conn = request->pdu_connection; 88 89 response = pdu_new_response(request); 90 bhstr2 = (struct iscsi_bhs_text_response *)response->pdu_bhs; 91 bhstr2->bhstr_opcode = ISCSI_BHS_OPCODE_TEXT_RESPONSE; 92 bhstr2->bhstr_flags = BHSTR_FLAGS_FINAL; 93 bhstr2->bhstr_lun = bhstr->bhstr_lun; 94 bhstr2->bhstr_initiator_task_tag = bhstr->bhstr_initiator_task_tag; 95 bhstr2->bhstr_target_transfer_tag = bhstr->bhstr_target_transfer_tag; 96 bhstr2->bhstr_statsn = htonl(conn->conn_statsn++); 97 bhstr2->bhstr_expcmdsn = htonl(conn->conn_cmdsn); 98 bhstr2->bhstr_maxcmdsn = htonl(conn->conn_cmdsn); 99 100 return (response); 101} 102 103static struct pdu * 104logout_receive(struct connection *conn) 105{ 106 struct pdu *request; 107 struct iscsi_bhs_logout_request *bhslr; 108 109 request = pdu_new(conn); 110 pdu_receive(request); 111 if ((request->pdu_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) != 112 ISCSI_BHS_OPCODE_LOGOUT_REQUEST) 113 log_errx(1, "protocol error: received invalid opcode 0x%x", 114 request->pdu_bhs->bhs_opcode); 115 bhslr = (struct iscsi_bhs_logout_request *)request->pdu_bhs; 116 if ((bhslr->bhslr_reason & 0x7f) != BHSLR_REASON_CLOSE_SESSION) 117 log_debugx("received Logout PDU with invalid reason 0x%x; " 118 "continuing anyway", bhslr->bhslr_reason & 0x7f); 119 if (ntohl(bhslr->bhslr_cmdsn) < conn->conn_cmdsn) { 120 log_errx(1, "received Logout PDU with decreasing CmdSN: " 121 "was %d, is %d", conn->conn_cmdsn, 122 ntohl(bhslr->bhslr_cmdsn)); 123 } 124 if (ntohl(bhslr->bhslr_expstatsn) != conn->conn_statsn) { 125 log_errx(1, "received Logout PDU with wrong StatSN: " 126 "is %d, should be %d", ntohl(bhslr->bhslr_expstatsn), 127 conn->conn_statsn); 128 } 129 conn->conn_cmdsn = ntohl(bhslr->bhslr_cmdsn); 130 131 return (request); 132} 133 134static struct pdu * 135logout_new_response(struct pdu *request) 136{ 137 struct pdu *response; 138 struct connection *conn; 139 struct iscsi_bhs_logout_request *bhslr; 140 struct iscsi_bhs_logout_response *bhslr2; 141 142 bhslr = (struct iscsi_bhs_logout_request *)request->pdu_bhs; 143 conn = request->pdu_connection; 144 145 response = pdu_new_response(request); 146 bhslr2 = (struct iscsi_bhs_logout_response *)response->pdu_bhs; 147 bhslr2->bhslr_opcode = ISCSI_BHS_OPCODE_LOGOUT_RESPONSE; 148 bhslr2->bhslr_flags = 0x80; 149 bhslr2->bhslr_response = BHSLR_RESPONSE_CLOSED_SUCCESSFULLY; 150 bhslr2->bhslr_initiator_task_tag = bhslr->bhslr_initiator_task_tag; 151 bhslr2->bhslr_statsn = htonl(conn->conn_statsn++); 152 bhslr2->bhslr_expcmdsn = htonl(conn->conn_cmdsn); 153 bhslr2->bhslr_maxcmdsn = htonl(conn->conn_cmdsn); 154 155 return (response); 156} 157 158void 159discovery(struct connection *conn) 160{ 161 struct pdu *request, *response; 162 struct keys *request_keys, *response_keys; 163 struct target *targ; 164 const char *send_targets; 165 166 log_debugx("beginning discovery session; waiting for Text PDU"); 167 request = text_receive(conn); 168 request_keys = keys_new(); 169 keys_load(request_keys, request); 170 171 send_targets = keys_find(request_keys, "SendTargets"); 172 if (send_targets == NULL) 173 log_errx(1, "received Text PDU without SendTargets"); 174 175 response = text_new_response(request); 176 response_keys = keys_new(); 177 178 if (strcmp(send_targets, "All") == 0) { 179 TAILQ_FOREACH(targ, 180 &conn->conn_portal->p_portal_group->pg_conf->conf_targets, 181 t_next) { 182 if (targ->t_portal_group != 183 conn->conn_portal->p_portal_group) { 184 log_debugx("not returning target \"%s\"; " 185 "belongs to a different portal group", 186 targ->t_name); 187 continue; 188 } 189 keys_add(response_keys, "TargetName", targ->t_name); 190 } 191 } else { 192 targ = target_find(conn->conn_portal->p_portal_group->pg_conf, 193 send_targets); 194 if (targ == NULL) { 195 log_debugx("initiator requested information on unknown " 196 "target \"%s\"; returning nothing", send_targets); 197 } else { 198 keys_add(response_keys, "TargetName", targ->t_name); 199 } 200 } 201 keys_save(response_keys, response); 202 203 pdu_send(response); 204 pdu_delete(response); 205 keys_delete(response_keys); 206 pdu_delete(request); 207 keys_delete(request_keys); 208 209 log_debugx("done sending targets; waiting for Logout PDU"); 210 request = logout_receive(conn); 211 response = logout_new_response(request); 212 213 pdu_send(response); 214 pdu_delete(response); 215 pdu_delete(request); 216 217 log_debugx("discovery session done"); 218} 219