discovery.c revision 267606
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: head/usr.sbin/ctld/discovery.c 267606 2014-06-18 12:26:02Z mav $ 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#include <netdb.h> 39#include <sys/socket.h> 40 41#include "ctld.h" 42#include "iscsi_proto.h" 43 44static struct pdu * 45text_receive(struct connection *conn) 46{ 47 struct pdu *request; 48 struct iscsi_bhs_text_request *bhstr; 49 50 request = pdu_new(conn); 51 pdu_receive(request); 52 if ((request->pdu_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) != 53 ISCSI_BHS_OPCODE_TEXT_REQUEST) 54 log_errx(1, "protocol error: received invalid opcode 0x%x", 55 request->pdu_bhs->bhs_opcode); 56 bhstr = (struct iscsi_bhs_text_request *)request->pdu_bhs; 57#if 0 58 if ((bhstr->bhstr_flags & ISCSI_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_cmdsn) < conn->conn_cmdsn) { 67 log_errx(1, "received Text PDU with decreasing CmdSN: " 68 "was %d, is %d", conn->conn_cmdsn, ntohl(bhstr->bhstr_cmdsn)); 69 } 70 if (ntohl(bhstr->bhstr_expstatsn) != conn->conn_statsn) { 71 log_errx(1, "received Text PDU with wrong StatSN: " 72 "is %d, should be %d", ntohl(bhstr->bhstr_expstatsn), 73 conn->conn_statsn); 74 } 75 conn->conn_cmdsn = ntohl(bhstr->bhstr_cmdsn); 76 77 return (request); 78} 79 80static struct pdu * 81text_new_response(struct pdu *request) 82{ 83 struct pdu *response; 84 struct connection *conn; 85 struct iscsi_bhs_text_request *bhstr; 86 struct iscsi_bhs_text_response *bhstr2; 87 88 bhstr = (struct iscsi_bhs_text_request *)request->pdu_bhs; 89 conn = request->pdu_connection; 90 91 response = pdu_new_response(request); 92 bhstr2 = (struct iscsi_bhs_text_response *)response->pdu_bhs; 93 bhstr2->bhstr_opcode = ISCSI_BHS_OPCODE_TEXT_RESPONSE; 94 bhstr2->bhstr_flags = BHSTR_FLAGS_FINAL; 95 bhstr2->bhstr_lun = bhstr->bhstr_lun; 96 bhstr2->bhstr_initiator_task_tag = bhstr->bhstr_initiator_task_tag; 97 bhstr2->bhstr_target_transfer_tag = bhstr->bhstr_target_transfer_tag; 98 bhstr2->bhstr_statsn = htonl(conn->conn_statsn++); 99 bhstr2->bhstr_expcmdsn = htonl(conn->conn_cmdsn); 100 bhstr2->bhstr_maxcmdsn = htonl(conn->conn_cmdsn); 101 102 return (response); 103} 104 105static struct pdu * 106logout_receive(struct connection *conn) 107{ 108 struct pdu *request; 109 struct iscsi_bhs_logout_request *bhslr; 110 111 request = pdu_new(conn); 112 pdu_receive(request); 113 if ((request->pdu_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) != 114 ISCSI_BHS_OPCODE_LOGOUT_REQUEST) 115 log_errx(1, "protocol error: received invalid opcode 0x%x", 116 request->pdu_bhs->bhs_opcode); 117 bhslr = (struct iscsi_bhs_logout_request *)request->pdu_bhs; 118 if ((bhslr->bhslr_reason & 0x7f) != BHSLR_REASON_CLOSE_SESSION) 119 log_debugx("received Logout PDU with invalid reason 0x%x; " 120 "continuing anyway", bhslr->bhslr_reason & 0x7f); 121 if (ntohl(bhslr->bhslr_cmdsn) < conn->conn_cmdsn) { 122 log_errx(1, "received Logout PDU with decreasing CmdSN: " 123 "was %d, is %d", conn->conn_cmdsn, 124 ntohl(bhslr->bhslr_cmdsn)); 125 } 126 if (ntohl(bhslr->bhslr_expstatsn) != conn->conn_statsn) { 127 log_errx(1, "received Logout PDU with wrong StatSN: " 128 "is %d, should be %d", ntohl(bhslr->bhslr_expstatsn), 129 conn->conn_statsn); 130 } 131 conn->conn_cmdsn = ntohl(bhslr->bhslr_cmdsn); 132 133 return (request); 134} 135 136static struct pdu * 137logout_new_response(struct pdu *request) 138{ 139 struct pdu *response; 140 struct connection *conn; 141 struct iscsi_bhs_logout_request *bhslr; 142 struct iscsi_bhs_logout_response *bhslr2; 143 144 bhslr = (struct iscsi_bhs_logout_request *)request->pdu_bhs; 145 conn = request->pdu_connection; 146 147 response = pdu_new_response(request); 148 bhslr2 = (struct iscsi_bhs_logout_response *)response->pdu_bhs; 149 bhslr2->bhslr_opcode = ISCSI_BHS_OPCODE_LOGOUT_RESPONSE; 150 bhslr2->bhslr_flags = 0x80; 151 bhslr2->bhslr_response = BHSLR_RESPONSE_CLOSED_SUCCESSFULLY; 152 bhslr2->bhslr_initiator_task_tag = bhslr->bhslr_initiator_task_tag; 153 bhslr2->bhslr_statsn = htonl(conn->conn_statsn++); 154 bhslr2->bhslr_expcmdsn = htonl(conn->conn_cmdsn); 155 bhslr2->bhslr_maxcmdsn = htonl(conn->conn_cmdsn); 156 157 return (response); 158} 159 160static void 161discovery_add_target(struct keys *response_keys, struct target *targ) 162{ 163 struct portal *portal; 164 char *buf; 165 char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; 166 struct addrinfo *ai; 167 int ret; 168 169 keys_add(response_keys, "TargetName", targ->t_name); 170 TAILQ_FOREACH(portal, &targ->t_portal_group->pg_portals, p_next) { 171 ai = portal->p_ai; 172 ret = getnameinfo(ai->ai_addr, ai->ai_addrlen, 173 hbuf, sizeof(hbuf), sbuf, sizeof(sbuf), 174 NI_NUMERICHOST | NI_NUMERICSERV); 175 if (ret != 0) { 176 log_warnx("getnameinfo: %s", gai_strerror(ret)); 177 continue; 178 } 179 switch (ai->ai_addr->sa_family) { 180 case AF_INET: 181 if (strcmp(hbuf, "0.0.0.0") == 0) 182 continue; 183 ret = asprintf(&buf, "%s:%s,%d", hbuf, sbuf, 184 targ->t_portal_group->pg_tag); 185 break; 186 case AF_INET6: 187 if (strcmp(hbuf, "::") == 0) 188 continue; 189 ret = asprintf(&buf, "[%s]:%s,%d", hbuf, sbuf, 190 targ->t_portal_group->pg_tag); 191 break; 192 default: 193 continue; 194 } 195 if (ret <= 0) 196 log_err(1, "asprintf"); 197 keys_add(response_keys, "TargetAddress", buf); 198 free(buf); 199 } 200} 201 202void 203discovery(struct connection *conn) 204{ 205 struct pdu *request, *response; 206 struct keys *request_keys, *response_keys; 207 struct target *targ; 208 const char *send_targets; 209 210 log_debugx("beginning discovery session; waiting for Text PDU"); 211 request = text_receive(conn); 212 request_keys = keys_new(); 213 keys_load(request_keys, request); 214 215 send_targets = keys_find(request_keys, "SendTargets"); 216 if (send_targets == NULL) 217 log_errx(1, "received Text PDU without SendTargets"); 218 219 response = text_new_response(request); 220 response_keys = keys_new(); 221 222 if (strcmp(send_targets, "All") == 0) { 223 TAILQ_FOREACH(targ, 224 &conn->conn_portal->p_portal_group->pg_conf->conf_targets, 225 t_next) { 226 if (targ->t_portal_group != 227 conn->conn_portal->p_portal_group) { 228 log_debugx("not returning target \"%s\"; " 229 "belongs to a different portal group", 230 targ->t_name); 231 continue; 232 } 233 discovery_add_target(response_keys, targ); 234 } 235 } else { 236 targ = target_find(conn->conn_portal->p_portal_group->pg_conf, 237 send_targets); 238 if (targ == NULL) { 239 log_debugx("initiator requested information on unknown " 240 "target \"%s\"; returning nothing", send_targets); 241 } else 242 discovery_add_target(response_keys, targ); 243 } 244 keys_save(response_keys, response); 245 246 pdu_send(response); 247 pdu_delete(response); 248 keys_delete(response_keys); 249 pdu_delete(request); 250 keys_delete(request_keys); 251 252 log_debugx("done sending targets; waiting for Logout PDU"); 253 request = logout_receive(conn); 254 response = logout_new_response(request); 255 256 pdu_send(response); 257 pdu_delete(response); 258 pdu_delete(request); 259 260 log_debugx("discovery session done"); 261} 262