discovery.c revision 300210
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 300210 2016-05-19 14:45:34Z trasz $"); 33270279Strasz 34255570Strasz#include <assert.h> 35255570Strasz#include <stdio.h> 36255570Strasz#include <stdlib.h> 37255570Strasz#include <string.h> 38255570Strasz#include <netinet/in.h> 39267606Smav#include <netdb.h> 40267606Smav#include <sys/socket.h> 41255570Strasz 42255570Strasz#include "ctld.h" 43255570Strasz#include "iscsi_proto.h" 44255570Strasz 45255570Straszstatic struct pdu * 46255570Strasztext_receive(struct connection *conn) 47255570Strasz{ 48255570Strasz struct pdu *request; 49255570Strasz struct iscsi_bhs_text_request *bhstr; 50255570Strasz 51255570Strasz request = pdu_new(conn); 52255570Strasz pdu_receive(request); 53255570Strasz if ((request->pdu_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) != 54255570Strasz ISCSI_BHS_OPCODE_TEXT_REQUEST) 55255570Strasz log_errx(1, "protocol error: received invalid opcode 0x%x", 56255570Strasz request->pdu_bhs->bhs_opcode); 57255570Strasz bhstr = (struct iscsi_bhs_text_request *)request->pdu_bhs; 58255570Strasz#if 0 59255570Strasz if ((bhstr->bhstr_flags & ISCSI_BHSTR_FLAGS_FINAL) == 0) 60255570Strasz log_errx(1, "received Text PDU without the \"F\" flag"); 61255570Strasz#endif 62255570Strasz /* 63255570Strasz * XXX: Implement the C flag some day. 64255570Strasz */ 65255570Strasz if ((bhstr->bhstr_flags & BHSTR_FLAGS_CONTINUE) != 0) 66255570Strasz log_errx(1, "received Text PDU with unsupported \"C\" flag"); 67275864Smav if (ISCSI_SNLT(ntohl(bhstr->bhstr_cmdsn), conn->conn_cmdsn)) { 68255570Strasz log_errx(1, "received Text PDU with decreasing CmdSN: " 69275864Smav "was %u, is %u", conn->conn_cmdsn, ntohl(bhstr->bhstr_cmdsn)); 70255570Strasz } 71255570Strasz if (ntohl(bhstr->bhstr_expstatsn) != conn->conn_statsn) { 72300210Strasz log_errx(1, "received Text PDU with wrong ExpStatSN: " 73275864Smav "is %u, should be %u", ntohl(bhstr->bhstr_expstatsn), 74255570Strasz conn->conn_statsn); 75255570Strasz } 76255570Strasz conn->conn_cmdsn = ntohl(bhstr->bhstr_cmdsn); 77279589Smav if ((bhstr->bhstr_opcode & ISCSI_BHS_OPCODE_IMMEDIATE) == 0) 78279589Smav conn->conn_cmdsn++; 79255570Strasz 80255570Strasz return (request); 81255570Strasz} 82255570Strasz 83255570Straszstatic struct pdu * 84255570Strasztext_new_response(struct pdu *request) 85255570Strasz{ 86255570Strasz struct pdu *response; 87255570Strasz struct connection *conn; 88255570Strasz struct iscsi_bhs_text_request *bhstr; 89255570Strasz struct iscsi_bhs_text_response *bhstr2; 90255570Strasz 91255570Strasz bhstr = (struct iscsi_bhs_text_request *)request->pdu_bhs; 92255570Strasz conn = request->pdu_connection; 93255570Strasz 94255570Strasz response = pdu_new_response(request); 95255570Strasz bhstr2 = (struct iscsi_bhs_text_response *)response->pdu_bhs; 96255570Strasz bhstr2->bhstr_opcode = ISCSI_BHS_OPCODE_TEXT_RESPONSE; 97255570Strasz bhstr2->bhstr_flags = BHSTR_FLAGS_FINAL; 98255570Strasz bhstr2->bhstr_lun = bhstr->bhstr_lun; 99255570Strasz bhstr2->bhstr_initiator_task_tag = bhstr->bhstr_initiator_task_tag; 100255570Strasz bhstr2->bhstr_target_transfer_tag = bhstr->bhstr_target_transfer_tag; 101255570Strasz bhstr2->bhstr_statsn = htonl(conn->conn_statsn++); 102255570Strasz bhstr2->bhstr_expcmdsn = htonl(conn->conn_cmdsn); 103255570Strasz bhstr2->bhstr_maxcmdsn = htonl(conn->conn_cmdsn); 104255570Strasz 105255570Strasz return (response); 106255570Strasz} 107255570Strasz 108255570Straszstatic struct pdu * 109255570Straszlogout_receive(struct connection *conn) 110255570Strasz{ 111255570Strasz struct pdu *request; 112255570Strasz struct iscsi_bhs_logout_request *bhslr; 113255570Strasz 114255570Strasz request = pdu_new(conn); 115255570Strasz pdu_receive(request); 116255570Strasz if ((request->pdu_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) != 117255570Strasz ISCSI_BHS_OPCODE_LOGOUT_REQUEST) 118255570Strasz log_errx(1, "protocol error: received invalid opcode 0x%x", 119255570Strasz request->pdu_bhs->bhs_opcode); 120255570Strasz bhslr = (struct iscsi_bhs_logout_request *)request->pdu_bhs; 121255570Strasz if ((bhslr->bhslr_reason & 0x7f) != BHSLR_REASON_CLOSE_SESSION) 122255570Strasz log_debugx("received Logout PDU with invalid reason 0x%x; " 123255570Strasz "continuing anyway", bhslr->bhslr_reason & 0x7f); 124275864Smav if (ISCSI_SNLT(ntohl(bhslr->bhslr_cmdsn), conn->conn_cmdsn)) { 125255570Strasz log_errx(1, "received Logout PDU with decreasing CmdSN: " 126275864Smav "was %u, is %u", conn->conn_cmdsn, 127255570Strasz ntohl(bhslr->bhslr_cmdsn)); 128255570Strasz } 129255570Strasz if (ntohl(bhslr->bhslr_expstatsn) != conn->conn_statsn) { 130300210Strasz log_errx(1, "received Logout PDU with wrong ExpStatSN: " 131275864Smav "is %u, should be %u", ntohl(bhslr->bhslr_expstatsn), 132255570Strasz conn->conn_statsn); 133255570Strasz } 134255570Strasz conn->conn_cmdsn = ntohl(bhslr->bhslr_cmdsn); 135279589Smav if ((bhslr->bhslr_opcode & ISCSI_BHS_OPCODE_IMMEDIATE) == 0) 136279589Smav conn->conn_cmdsn++; 137255570Strasz 138255570Strasz return (request); 139255570Strasz} 140255570Strasz 141255570Straszstatic struct pdu * 142255570Straszlogout_new_response(struct pdu *request) 143255570Strasz{ 144255570Strasz struct pdu *response; 145255570Strasz struct connection *conn; 146255570Strasz struct iscsi_bhs_logout_request *bhslr; 147255570Strasz struct iscsi_bhs_logout_response *bhslr2; 148255570Strasz 149255570Strasz bhslr = (struct iscsi_bhs_logout_request *)request->pdu_bhs; 150255570Strasz conn = request->pdu_connection; 151255570Strasz 152255570Strasz response = pdu_new_response(request); 153255570Strasz bhslr2 = (struct iscsi_bhs_logout_response *)response->pdu_bhs; 154255570Strasz bhslr2->bhslr_opcode = ISCSI_BHS_OPCODE_LOGOUT_RESPONSE; 155255570Strasz bhslr2->bhslr_flags = 0x80; 156255570Strasz bhslr2->bhslr_response = BHSLR_RESPONSE_CLOSED_SUCCESSFULLY; 157255570Strasz bhslr2->bhslr_initiator_task_tag = bhslr->bhslr_initiator_task_tag; 158255570Strasz bhslr2->bhslr_statsn = htonl(conn->conn_statsn++); 159255570Strasz bhslr2->bhslr_expcmdsn = htonl(conn->conn_cmdsn); 160255570Strasz bhslr2->bhslr_maxcmdsn = htonl(conn->conn_cmdsn); 161255570Strasz 162255570Strasz return (response); 163255570Strasz} 164255570Strasz 165267606Smavstatic void 166273543Straszdiscovery_add_target(struct keys *response_keys, const struct target *targ) 167267606Smav{ 168278322Smav struct port *port; 169267606Smav struct portal *portal; 170267606Smav char *buf; 171267606Smav char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; 172267606Smav struct addrinfo *ai; 173267606Smav int ret; 174267606Smav 175267606Smav keys_add(response_keys, "TargetName", targ->t_name); 176278322Smav TAILQ_FOREACH(port, &targ->t_ports, p_ts) { 177278322Smav if (port->p_portal_group == NULL) 178278322Smav continue; 179278322Smav TAILQ_FOREACH(portal, &port->p_portal_group->pg_portals, p_next) { 180267606Smav ai = portal->p_ai; 181267606Smav ret = getnameinfo(ai->ai_addr, ai->ai_addrlen, 182267606Smav hbuf, sizeof(hbuf), sbuf, sizeof(sbuf), 183267606Smav NI_NUMERICHOST | NI_NUMERICSERV); 184267606Smav if (ret != 0) { 185267606Smav log_warnx("getnameinfo: %s", gai_strerror(ret)); 186267606Smav continue; 187267606Smav } 188267606Smav switch (ai->ai_addr->sa_family) { 189267606Smav case AF_INET: 190267606Smav if (strcmp(hbuf, "0.0.0.0") == 0) 191267606Smav continue; 192267606Smav ret = asprintf(&buf, "%s:%s,%d", hbuf, sbuf, 193278322Smav port->p_portal_group->pg_tag); 194267606Smav break; 195267606Smav case AF_INET6: 196267606Smav if (strcmp(hbuf, "::") == 0) 197267606Smav continue; 198267606Smav ret = asprintf(&buf, "[%s]:%s,%d", hbuf, sbuf, 199278322Smav port->p_portal_group->pg_tag); 200267606Smav break; 201267606Smav default: 202267606Smav continue; 203267606Smav } 204267606Smav if (ret <= 0) 205267606Smav log_err(1, "asprintf"); 206267606Smav keys_add(response_keys, "TargetAddress", buf); 207267606Smav free(buf); 208278322Smav } 209267606Smav } 210267606Smav} 211267606Smav 212273813Straszstatic bool 213273813Straszdiscovery_target_filtered_out(const struct connection *conn, 214278322Smav const struct port *port) 215273813Strasz{ 216273813Strasz const struct auth_group *ag; 217273813Strasz const struct portal_group *pg; 218278322Smav const struct target *targ; 219273813Strasz const struct auth *auth; 220273813Strasz int error; 221273813Strasz 222278322Smav targ = port->p_target; 223278322Smav ag = port->p_auth_group; 224278322Smav if (ag == NULL) 225278322Smav ag = targ->t_auth_group; 226273813Strasz pg = conn->conn_portal->p_portal_group; 227273813Strasz 228273813Strasz assert(pg->pg_discovery_auth_group != PG_FILTER_UNKNOWN); 229273813Strasz 230273813Strasz if (pg->pg_discovery_filter >= PG_FILTER_PORTAL && 231273813Strasz auth_portal_check(ag, &conn->conn_initiator_sa) != 0) { 232273813Strasz log_debugx("initiator does not match initiator portals " 233273813Strasz "allowed for target \"%s\"; skipping", targ->t_name); 234273813Strasz return (true); 235273813Strasz } 236273813Strasz 237273813Strasz if (pg->pg_discovery_filter >= PG_FILTER_PORTAL_NAME && 238273813Strasz auth_name_check(ag, conn->conn_initiator_name) != 0) { 239273813Strasz log_debugx("initiator does not match initiator names " 240273813Strasz "allowed for target \"%s\"; skipping", targ->t_name); 241273813Strasz return (true); 242273813Strasz } 243273813Strasz 244273813Strasz if (pg->pg_discovery_filter >= PG_FILTER_PORTAL_NAME_AUTH && 245273813Strasz ag->ag_type != AG_TYPE_NO_AUTHENTICATION) { 246273813Strasz if (conn->conn_chap == NULL) { 247273813Strasz assert(pg->pg_discovery_auth_group->ag_type == 248273813Strasz AG_TYPE_NO_AUTHENTICATION); 249273813Strasz 250273813Strasz log_debugx("initiator didn't authenticate, but target " 251273813Strasz "\"%s\" requires CHAP; skipping", targ->t_name); 252273813Strasz return (true); 253273813Strasz } 254273813Strasz 255273813Strasz assert(conn->conn_user != NULL); 256273813Strasz auth = auth_find(ag, conn->conn_user); 257273813Strasz if (auth == NULL) { 258273813Strasz log_debugx("CHAP user \"%s\" doesn't match target " 259273813Strasz "\"%s\"; skipping", conn->conn_user, targ->t_name); 260273813Strasz return (true); 261273813Strasz } 262273813Strasz 263273813Strasz error = chap_authenticate(conn->conn_chap, auth->a_secret); 264273813Strasz if (error != 0) { 265273813Strasz log_debugx("password for CHAP user \"%s\" doesn't " 266273813Strasz "match target \"%s\"; skipping", 267273813Strasz conn->conn_user, targ->t_name); 268273813Strasz return (true); 269273813Strasz } 270273813Strasz } 271273813Strasz 272273813Strasz return (false); 273273813Strasz} 274273813Strasz 275255570Straszvoid 276255570Straszdiscovery(struct connection *conn) 277255570Strasz{ 278255570Strasz struct pdu *request, *response; 279255570Strasz struct keys *request_keys, *response_keys; 280278322Smav const struct port *port; 281273543Strasz const struct portal_group *pg; 282255570Strasz const char *send_targets; 283255570Strasz 284273543Strasz pg = conn->conn_portal->p_portal_group; 285273543Strasz 286255570Strasz log_debugx("beginning discovery session; waiting for Text PDU"); 287255570Strasz request = text_receive(conn); 288255570Strasz request_keys = keys_new(); 289255570Strasz keys_load(request_keys, request); 290255570Strasz 291255570Strasz send_targets = keys_find(request_keys, "SendTargets"); 292255570Strasz if (send_targets == NULL) 293255570Strasz log_errx(1, "received Text PDU without SendTargets"); 294255570Strasz 295255570Strasz response = text_new_response(request); 296255570Strasz response_keys = keys_new(); 297255570Strasz 298255570Strasz if (strcmp(send_targets, "All") == 0) { 299278322Smav TAILQ_FOREACH(port, &pg->pg_ports, p_pgs) { 300278322Smav if (discovery_target_filtered_out(conn, port)) { 301273813Strasz /* Ignore this target. */ 302273813Strasz continue; 303273813Strasz } 304278322Smav discovery_add_target(response_keys, port->p_target); 305255570Strasz } 306255570Strasz } else { 307278322Smav port = port_find_in_pg(pg, send_targets); 308278322Smav if (port == NULL) { 309255570Strasz log_debugx("initiator requested information on unknown " 310255570Strasz "target \"%s\"; returning nothing", send_targets); 311273813Strasz } else { 312278322Smav if (discovery_target_filtered_out(conn, port)) { 313273813Strasz /* Ignore this target. */ 314273813Strasz } else { 315278322Smav discovery_add_target(response_keys, port->p_target); 316273813Strasz } 317273813Strasz } 318255570Strasz } 319255570Strasz keys_save(response_keys, response); 320255570Strasz 321255570Strasz pdu_send(response); 322255570Strasz pdu_delete(response); 323255570Strasz keys_delete(response_keys); 324255570Strasz pdu_delete(request); 325255570Strasz keys_delete(request_keys); 326255570Strasz 327255570Strasz log_debugx("done sending targets; waiting for Logout PDU"); 328255570Strasz request = logout_receive(conn); 329255570Strasz response = logout_new_response(request); 330255570Strasz 331255570Strasz pdu_send(response); 332255570Strasz pdu_delete(response); 333255570Strasz pdu_delete(request); 334255570Strasz 335255570Strasz log_debugx("discovery session done"); 336255570Strasz} 337