discovery.c revision 273543
1255570Strasz/*- 2255570Strasz * Copyright (c) 2012 The FreeBSD Foundation 3255570Strasz * All rights reserved. 4255570Strasz * 5255570Strasz * This software was developed by Edward Tomasz Napierala under sponsorship 6255570Strasz * from the FreeBSD Foundation. 7255570Strasz * 8255570Strasz * Redistribution and use in source and binary forms, with or without 9255570Strasz * modification, are permitted provided that the following conditions 10255570Strasz * are met: 11255570Strasz * 1. Redistributions of source code must retain the above copyright 12255570Strasz * notice, this list of conditions and the following disclaimer. 13255570Strasz * 2. Redistributions in binary form must reproduce the above copyright 14255570Strasz * notice, this list of conditions and the following disclaimer in the 15255570Strasz * documentation and/or other materials provided with the distribution. 16255570Strasz * 17255570Strasz * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18255570Strasz * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19255570Strasz * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20255570Strasz * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21255570Strasz * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22255570Strasz * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23255570Strasz * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24255570Strasz * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25255570Strasz * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26255570Strasz * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27255570Strasz * SUCH DAMAGE. 28255570Strasz * 29255570Strasz */ 30255570Strasz 31270279Strasz#include <sys/cdefs.h> 32270279Strasz__FBSDID("$FreeBSD: head/usr.sbin/ctld/discovery.c 273543 2014-10-23 12:02:27Z trasz $"); 33270279Strasz 34255570Strasz#include <assert.h> 35255570Strasz#include <stdint.h> 36255570Strasz#include <stdio.h> 37255570Strasz#include <stdlib.h> 38255570Strasz#include <string.h> 39255570Strasz#include <netinet/in.h> 40267606Smav#include <netdb.h> 41267606Smav#include <sys/socket.h> 42255570Strasz 43255570Strasz#include "ctld.h" 44255570Strasz#include "iscsi_proto.h" 45255570Strasz 46255570Straszstatic struct pdu * 47255570Strasztext_receive(struct connection *conn) 48255570Strasz{ 49255570Strasz struct pdu *request; 50255570Strasz struct iscsi_bhs_text_request *bhstr; 51255570Strasz 52255570Strasz request = pdu_new(conn); 53255570Strasz pdu_receive(request); 54255570Strasz if ((request->pdu_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) != 55255570Strasz ISCSI_BHS_OPCODE_TEXT_REQUEST) 56255570Strasz log_errx(1, "protocol error: received invalid opcode 0x%x", 57255570Strasz request->pdu_bhs->bhs_opcode); 58255570Strasz bhstr = (struct iscsi_bhs_text_request *)request->pdu_bhs; 59255570Strasz#if 0 60255570Strasz if ((bhstr->bhstr_flags & ISCSI_BHSTR_FLAGS_FINAL) == 0) 61255570Strasz log_errx(1, "received Text PDU without the \"F\" flag"); 62255570Strasz#endif 63255570Strasz /* 64255570Strasz * XXX: Implement the C flag some day. 65255570Strasz */ 66255570Strasz if ((bhstr->bhstr_flags & BHSTR_FLAGS_CONTINUE) != 0) 67255570Strasz log_errx(1, "received Text PDU with unsupported \"C\" flag"); 68255570Strasz if (ntohl(bhstr->bhstr_cmdsn) < conn->conn_cmdsn) { 69255570Strasz log_errx(1, "received Text PDU with decreasing CmdSN: " 70255570Strasz "was %d, is %d", conn->conn_cmdsn, ntohl(bhstr->bhstr_cmdsn)); 71255570Strasz } 72255570Strasz if (ntohl(bhstr->bhstr_expstatsn) != conn->conn_statsn) { 73255570Strasz log_errx(1, "received Text PDU with wrong StatSN: " 74255570Strasz "is %d, should be %d", ntohl(bhstr->bhstr_expstatsn), 75255570Strasz conn->conn_statsn); 76255570Strasz } 77255570Strasz conn->conn_cmdsn = ntohl(bhstr->bhstr_cmdsn); 78255570Strasz 79255570Strasz return (request); 80255570Strasz} 81255570Strasz 82255570Straszstatic struct pdu * 83255570Strasztext_new_response(struct pdu *request) 84255570Strasz{ 85255570Strasz struct pdu *response; 86255570Strasz struct connection *conn; 87255570Strasz struct iscsi_bhs_text_request *bhstr; 88255570Strasz struct iscsi_bhs_text_response *bhstr2; 89255570Strasz 90255570Strasz bhstr = (struct iscsi_bhs_text_request *)request->pdu_bhs; 91255570Strasz conn = request->pdu_connection; 92255570Strasz 93255570Strasz response = pdu_new_response(request); 94255570Strasz bhstr2 = (struct iscsi_bhs_text_response *)response->pdu_bhs; 95255570Strasz bhstr2->bhstr_opcode = ISCSI_BHS_OPCODE_TEXT_RESPONSE; 96255570Strasz bhstr2->bhstr_flags = BHSTR_FLAGS_FINAL; 97255570Strasz bhstr2->bhstr_lun = bhstr->bhstr_lun; 98255570Strasz bhstr2->bhstr_initiator_task_tag = bhstr->bhstr_initiator_task_tag; 99255570Strasz bhstr2->bhstr_target_transfer_tag = bhstr->bhstr_target_transfer_tag; 100255570Strasz bhstr2->bhstr_statsn = htonl(conn->conn_statsn++); 101255570Strasz bhstr2->bhstr_expcmdsn = htonl(conn->conn_cmdsn); 102255570Strasz bhstr2->bhstr_maxcmdsn = htonl(conn->conn_cmdsn); 103255570Strasz 104255570Strasz return (response); 105255570Strasz} 106255570Strasz 107255570Straszstatic struct pdu * 108255570Straszlogout_receive(struct connection *conn) 109255570Strasz{ 110255570Strasz struct pdu *request; 111255570Strasz struct iscsi_bhs_logout_request *bhslr; 112255570Strasz 113255570Strasz request = pdu_new(conn); 114255570Strasz pdu_receive(request); 115255570Strasz if ((request->pdu_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) != 116255570Strasz ISCSI_BHS_OPCODE_LOGOUT_REQUEST) 117255570Strasz log_errx(1, "protocol error: received invalid opcode 0x%x", 118255570Strasz request->pdu_bhs->bhs_opcode); 119255570Strasz bhslr = (struct iscsi_bhs_logout_request *)request->pdu_bhs; 120255570Strasz if ((bhslr->bhslr_reason & 0x7f) != BHSLR_REASON_CLOSE_SESSION) 121255570Strasz log_debugx("received Logout PDU with invalid reason 0x%x; " 122255570Strasz "continuing anyway", bhslr->bhslr_reason & 0x7f); 123255570Strasz if (ntohl(bhslr->bhslr_cmdsn) < conn->conn_cmdsn) { 124255570Strasz log_errx(1, "received Logout PDU with decreasing CmdSN: " 125255570Strasz "was %d, is %d", conn->conn_cmdsn, 126255570Strasz ntohl(bhslr->bhslr_cmdsn)); 127255570Strasz } 128255570Strasz if (ntohl(bhslr->bhslr_expstatsn) != conn->conn_statsn) { 129255570Strasz log_errx(1, "received Logout PDU with wrong StatSN: " 130255570Strasz "is %d, should be %d", ntohl(bhslr->bhslr_expstatsn), 131255570Strasz conn->conn_statsn); 132255570Strasz } 133255570Strasz conn->conn_cmdsn = ntohl(bhslr->bhslr_cmdsn); 134255570Strasz 135255570Strasz return (request); 136255570Strasz} 137255570Strasz 138255570Straszstatic struct pdu * 139255570Straszlogout_new_response(struct pdu *request) 140255570Strasz{ 141255570Strasz struct pdu *response; 142255570Strasz struct connection *conn; 143255570Strasz struct iscsi_bhs_logout_request *bhslr; 144255570Strasz struct iscsi_bhs_logout_response *bhslr2; 145255570Strasz 146255570Strasz bhslr = (struct iscsi_bhs_logout_request *)request->pdu_bhs; 147255570Strasz conn = request->pdu_connection; 148255570Strasz 149255570Strasz response = pdu_new_response(request); 150255570Strasz bhslr2 = (struct iscsi_bhs_logout_response *)response->pdu_bhs; 151255570Strasz bhslr2->bhslr_opcode = ISCSI_BHS_OPCODE_LOGOUT_RESPONSE; 152255570Strasz bhslr2->bhslr_flags = 0x80; 153255570Strasz bhslr2->bhslr_response = BHSLR_RESPONSE_CLOSED_SUCCESSFULLY; 154255570Strasz bhslr2->bhslr_initiator_task_tag = bhslr->bhslr_initiator_task_tag; 155255570Strasz bhslr2->bhslr_statsn = htonl(conn->conn_statsn++); 156255570Strasz bhslr2->bhslr_expcmdsn = htonl(conn->conn_cmdsn); 157255570Strasz bhslr2->bhslr_maxcmdsn = htonl(conn->conn_cmdsn); 158255570Strasz 159255570Strasz return (response); 160255570Strasz} 161255570Strasz 162267606Smavstatic void 163273543Straszdiscovery_add_target(struct keys *response_keys, const struct target *targ) 164267606Smav{ 165267606Smav struct portal *portal; 166267606Smav char *buf; 167267606Smav char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; 168267606Smav struct addrinfo *ai; 169267606Smav int ret; 170267606Smav 171267606Smav keys_add(response_keys, "TargetName", targ->t_name); 172267606Smav TAILQ_FOREACH(portal, &targ->t_portal_group->pg_portals, p_next) { 173267606Smav ai = portal->p_ai; 174267606Smav ret = getnameinfo(ai->ai_addr, ai->ai_addrlen, 175267606Smav hbuf, sizeof(hbuf), sbuf, sizeof(sbuf), 176267606Smav NI_NUMERICHOST | NI_NUMERICSERV); 177267606Smav if (ret != 0) { 178267606Smav log_warnx("getnameinfo: %s", gai_strerror(ret)); 179267606Smav continue; 180267606Smav } 181267606Smav switch (ai->ai_addr->sa_family) { 182267606Smav case AF_INET: 183267606Smav if (strcmp(hbuf, "0.0.0.0") == 0) 184267606Smav continue; 185267606Smav ret = asprintf(&buf, "%s:%s,%d", hbuf, sbuf, 186267606Smav targ->t_portal_group->pg_tag); 187267606Smav break; 188267606Smav case AF_INET6: 189267606Smav if (strcmp(hbuf, "::") == 0) 190267606Smav continue; 191267606Smav ret = asprintf(&buf, "[%s]:%s,%d", hbuf, sbuf, 192267606Smav targ->t_portal_group->pg_tag); 193267606Smav break; 194267606Smav default: 195267606Smav continue; 196267606Smav } 197267606Smav if (ret <= 0) 198267606Smav log_err(1, "asprintf"); 199267606Smav keys_add(response_keys, "TargetAddress", buf); 200267606Smav free(buf); 201267606Smav } 202267606Smav} 203267606Smav 204255570Straszvoid 205255570Straszdiscovery(struct connection *conn) 206255570Strasz{ 207255570Strasz struct pdu *request, *response; 208255570Strasz struct keys *request_keys, *response_keys; 209273543Strasz const struct portal_group *pg; 210273543Strasz const struct target *targ; 211255570Strasz const char *send_targets; 212255570Strasz 213273543Strasz pg = conn->conn_portal->p_portal_group; 214273543Strasz 215255570Strasz log_debugx("beginning discovery session; waiting for Text PDU"); 216255570Strasz request = text_receive(conn); 217255570Strasz request_keys = keys_new(); 218255570Strasz keys_load(request_keys, request); 219255570Strasz 220255570Strasz send_targets = keys_find(request_keys, "SendTargets"); 221255570Strasz if (send_targets == NULL) 222255570Strasz log_errx(1, "received Text PDU without SendTargets"); 223255570Strasz 224255570Strasz response = text_new_response(request); 225255570Strasz response_keys = keys_new(); 226255570Strasz 227255570Strasz if (strcmp(send_targets, "All") == 0) { 228273543Strasz TAILQ_FOREACH(targ, &pg->pg_conf->conf_targets, t_next) { 229273543Strasz if (targ->t_portal_group != pg) { 230255570Strasz log_debugx("not returning target \"%s\"; " 231255570Strasz "belongs to a different portal group", 232261757Strasz targ->t_name); 233255570Strasz continue; 234255570Strasz } 235267606Smav discovery_add_target(response_keys, targ); 236255570Strasz } 237255570Strasz } else { 238273543Strasz targ = target_find(pg->pg_conf, send_targets); 239255570Strasz if (targ == NULL) { 240255570Strasz log_debugx("initiator requested information on unknown " 241255570Strasz "target \"%s\"; returning nothing", send_targets); 242267606Smav } else 243267606Smav discovery_add_target(response_keys, targ); 244255570Strasz } 245255570Strasz keys_save(response_keys, response); 246255570Strasz 247255570Strasz pdu_send(response); 248255570Strasz pdu_delete(response); 249255570Strasz keys_delete(response_keys); 250255570Strasz pdu_delete(request); 251255570Strasz keys_delete(request_keys); 252255570Strasz 253255570Strasz log_debugx("done sending targets; waiting for Logout PDU"); 254255570Strasz request = logout_receive(conn); 255255570Strasz response = logout_new_response(request); 256255570Strasz 257255570Strasz pdu_send(response); 258255570Strasz pdu_delete(response); 259255570Strasz pdu_delete(request); 260255570Strasz 261255570Strasz log_debugx("discovery session done"); 262255570Strasz} 263