discovery.c revision 279589
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 279589 2015-03-04 12:12:46Z mav $"); 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"); 68275864Smav if (ISCSI_SNLT(ntohl(bhstr->bhstr_cmdsn), conn->conn_cmdsn)) { 69255570Strasz log_errx(1, "received Text PDU with decreasing CmdSN: " 70275864Smav "was %u, is %u", 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: " 74275864Smav "is %u, should be %u", ntohl(bhstr->bhstr_expstatsn), 75255570Strasz conn->conn_statsn); 76255570Strasz } 77255570Strasz conn->conn_cmdsn = ntohl(bhstr->bhstr_cmdsn); 78279589Smav if ((bhstr->bhstr_opcode & ISCSI_BHS_OPCODE_IMMEDIATE) == 0) 79279589Smav conn->conn_cmdsn++; 80255570Strasz 81255570Strasz return (request); 82255570Strasz} 83255570Strasz 84255570Straszstatic struct pdu * 85255570Strasztext_new_response(struct pdu *request) 86255570Strasz{ 87255570Strasz struct pdu *response; 88255570Strasz struct connection *conn; 89255570Strasz struct iscsi_bhs_text_request *bhstr; 90255570Strasz struct iscsi_bhs_text_response *bhstr2; 91255570Strasz 92255570Strasz bhstr = (struct iscsi_bhs_text_request *)request->pdu_bhs; 93255570Strasz conn = request->pdu_connection; 94255570Strasz 95255570Strasz response = pdu_new_response(request); 96255570Strasz bhstr2 = (struct iscsi_bhs_text_response *)response->pdu_bhs; 97255570Strasz bhstr2->bhstr_opcode = ISCSI_BHS_OPCODE_TEXT_RESPONSE; 98255570Strasz bhstr2->bhstr_flags = BHSTR_FLAGS_FINAL; 99255570Strasz bhstr2->bhstr_lun = bhstr->bhstr_lun; 100255570Strasz bhstr2->bhstr_initiator_task_tag = bhstr->bhstr_initiator_task_tag; 101255570Strasz bhstr2->bhstr_target_transfer_tag = bhstr->bhstr_target_transfer_tag; 102255570Strasz bhstr2->bhstr_statsn = htonl(conn->conn_statsn++); 103255570Strasz bhstr2->bhstr_expcmdsn = htonl(conn->conn_cmdsn); 104255570Strasz bhstr2->bhstr_maxcmdsn = htonl(conn->conn_cmdsn); 105255570Strasz 106255570Strasz return (response); 107255570Strasz} 108255570Strasz 109255570Straszstatic struct pdu * 110255570Straszlogout_receive(struct connection *conn) 111255570Strasz{ 112255570Strasz struct pdu *request; 113255570Strasz struct iscsi_bhs_logout_request *bhslr; 114255570Strasz 115255570Strasz request = pdu_new(conn); 116255570Strasz pdu_receive(request); 117255570Strasz if ((request->pdu_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) != 118255570Strasz ISCSI_BHS_OPCODE_LOGOUT_REQUEST) 119255570Strasz log_errx(1, "protocol error: received invalid opcode 0x%x", 120255570Strasz request->pdu_bhs->bhs_opcode); 121255570Strasz bhslr = (struct iscsi_bhs_logout_request *)request->pdu_bhs; 122255570Strasz if ((bhslr->bhslr_reason & 0x7f) != BHSLR_REASON_CLOSE_SESSION) 123255570Strasz log_debugx("received Logout PDU with invalid reason 0x%x; " 124255570Strasz "continuing anyway", bhslr->bhslr_reason & 0x7f); 125275864Smav if (ISCSI_SNLT(ntohl(bhslr->bhslr_cmdsn), conn->conn_cmdsn)) { 126255570Strasz log_errx(1, "received Logout PDU with decreasing CmdSN: " 127275864Smav "was %u, is %u", conn->conn_cmdsn, 128255570Strasz ntohl(bhslr->bhslr_cmdsn)); 129255570Strasz } 130255570Strasz if (ntohl(bhslr->bhslr_expstatsn) != conn->conn_statsn) { 131255570Strasz log_errx(1, "received Logout PDU with wrong StatSN: " 132275864Smav "is %u, should be %u", ntohl(bhslr->bhslr_expstatsn), 133255570Strasz conn->conn_statsn); 134255570Strasz } 135255570Strasz conn->conn_cmdsn = ntohl(bhslr->bhslr_cmdsn); 136279589Smav if ((bhslr->bhslr_opcode & ISCSI_BHS_OPCODE_IMMEDIATE) == 0) 137279589Smav conn->conn_cmdsn++; 138255570Strasz 139255570Strasz return (request); 140255570Strasz} 141255570Strasz 142255570Straszstatic struct pdu * 143255570Straszlogout_new_response(struct pdu *request) 144255570Strasz{ 145255570Strasz struct pdu *response; 146255570Strasz struct connection *conn; 147255570Strasz struct iscsi_bhs_logout_request *bhslr; 148255570Strasz struct iscsi_bhs_logout_response *bhslr2; 149255570Strasz 150255570Strasz bhslr = (struct iscsi_bhs_logout_request *)request->pdu_bhs; 151255570Strasz conn = request->pdu_connection; 152255570Strasz 153255570Strasz response = pdu_new_response(request); 154255570Strasz bhslr2 = (struct iscsi_bhs_logout_response *)response->pdu_bhs; 155255570Strasz bhslr2->bhslr_opcode = ISCSI_BHS_OPCODE_LOGOUT_RESPONSE; 156255570Strasz bhslr2->bhslr_flags = 0x80; 157255570Strasz bhslr2->bhslr_response = BHSLR_RESPONSE_CLOSED_SUCCESSFULLY; 158255570Strasz bhslr2->bhslr_initiator_task_tag = bhslr->bhslr_initiator_task_tag; 159255570Strasz bhslr2->bhslr_statsn = htonl(conn->conn_statsn++); 160255570Strasz bhslr2->bhslr_expcmdsn = htonl(conn->conn_cmdsn); 161255570Strasz bhslr2->bhslr_maxcmdsn = htonl(conn->conn_cmdsn); 162255570Strasz 163255570Strasz return (response); 164255570Strasz} 165255570Strasz 166267606Smavstatic void 167273543Straszdiscovery_add_target(struct keys *response_keys, const struct target *targ) 168267606Smav{ 169278322Smav struct port *port; 170267606Smav struct portal *portal; 171267606Smav char *buf; 172267606Smav char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; 173267606Smav struct addrinfo *ai; 174267606Smav int ret; 175267606Smav 176267606Smav keys_add(response_keys, "TargetName", targ->t_name); 177278322Smav TAILQ_FOREACH(port, &targ->t_ports, p_ts) { 178278322Smav if (port->p_portal_group == NULL) 179278322Smav continue; 180278322Smav TAILQ_FOREACH(portal, &port->p_portal_group->pg_portals, p_next) { 181267606Smav ai = portal->p_ai; 182267606Smav ret = getnameinfo(ai->ai_addr, ai->ai_addrlen, 183267606Smav hbuf, sizeof(hbuf), sbuf, sizeof(sbuf), 184267606Smav NI_NUMERICHOST | NI_NUMERICSERV); 185267606Smav if (ret != 0) { 186267606Smav log_warnx("getnameinfo: %s", gai_strerror(ret)); 187267606Smav continue; 188267606Smav } 189267606Smav switch (ai->ai_addr->sa_family) { 190267606Smav case AF_INET: 191267606Smav if (strcmp(hbuf, "0.0.0.0") == 0) 192267606Smav continue; 193267606Smav ret = asprintf(&buf, "%s:%s,%d", hbuf, sbuf, 194278322Smav port->p_portal_group->pg_tag); 195267606Smav break; 196267606Smav case AF_INET6: 197267606Smav if (strcmp(hbuf, "::") == 0) 198267606Smav continue; 199267606Smav ret = asprintf(&buf, "[%s]:%s,%d", hbuf, sbuf, 200278322Smav port->p_portal_group->pg_tag); 201267606Smav break; 202267606Smav default: 203267606Smav continue; 204267606Smav } 205267606Smav if (ret <= 0) 206267606Smav log_err(1, "asprintf"); 207267606Smav keys_add(response_keys, "TargetAddress", buf); 208267606Smav free(buf); 209278322Smav } 210267606Smav } 211267606Smav} 212267606Smav 213273813Straszstatic bool 214273813Straszdiscovery_target_filtered_out(const struct connection *conn, 215278322Smav const struct port *port) 216273813Strasz{ 217273813Strasz const struct auth_group *ag; 218273813Strasz const struct portal_group *pg; 219278322Smav const struct target *targ; 220273813Strasz const struct auth *auth; 221273813Strasz int error; 222273813Strasz 223278322Smav targ = port->p_target; 224278322Smav ag = port->p_auth_group; 225278322Smav if (ag == NULL) 226278322Smav ag = targ->t_auth_group; 227273813Strasz pg = conn->conn_portal->p_portal_group; 228273813Strasz 229273813Strasz assert(pg->pg_discovery_auth_group != PG_FILTER_UNKNOWN); 230273813Strasz 231273813Strasz if (pg->pg_discovery_filter >= PG_FILTER_PORTAL && 232273813Strasz auth_portal_check(ag, &conn->conn_initiator_sa) != 0) { 233273813Strasz log_debugx("initiator does not match initiator portals " 234273813Strasz "allowed for target \"%s\"; skipping", targ->t_name); 235273813Strasz return (true); 236273813Strasz } 237273813Strasz 238273813Strasz if (pg->pg_discovery_filter >= PG_FILTER_PORTAL_NAME && 239273813Strasz auth_name_check(ag, conn->conn_initiator_name) != 0) { 240273813Strasz log_debugx("initiator does not match initiator names " 241273813Strasz "allowed for target \"%s\"; skipping", targ->t_name); 242273813Strasz return (true); 243273813Strasz } 244273813Strasz 245273813Strasz if (pg->pg_discovery_filter >= PG_FILTER_PORTAL_NAME_AUTH && 246273813Strasz ag->ag_type != AG_TYPE_NO_AUTHENTICATION) { 247273813Strasz if (conn->conn_chap == NULL) { 248273813Strasz assert(pg->pg_discovery_auth_group->ag_type == 249273813Strasz AG_TYPE_NO_AUTHENTICATION); 250273813Strasz 251273813Strasz log_debugx("initiator didn't authenticate, but target " 252273813Strasz "\"%s\" requires CHAP; skipping", targ->t_name); 253273813Strasz return (true); 254273813Strasz } 255273813Strasz 256273813Strasz assert(conn->conn_user != NULL); 257273813Strasz auth = auth_find(ag, conn->conn_user); 258273813Strasz if (auth == NULL) { 259273813Strasz log_debugx("CHAP user \"%s\" doesn't match target " 260273813Strasz "\"%s\"; skipping", conn->conn_user, targ->t_name); 261273813Strasz return (true); 262273813Strasz } 263273813Strasz 264273813Strasz error = chap_authenticate(conn->conn_chap, auth->a_secret); 265273813Strasz if (error != 0) { 266273813Strasz log_debugx("password for CHAP user \"%s\" doesn't " 267273813Strasz "match target \"%s\"; skipping", 268273813Strasz conn->conn_user, targ->t_name); 269273813Strasz return (true); 270273813Strasz } 271273813Strasz } 272273813Strasz 273273813Strasz return (false); 274273813Strasz} 275273813Strasz 276255570Straszvoid 277255570Straszdiscovery(struct connection *conn) 278255570Strasz{ 279255570Strasz struct pdu *request, *response; 280255570Strasz struct keys *request_keys, *response_keys; 281278322Smav const struct port *port; 282273543Strasz const struct portal_group *pg; 283255570Strasz const char *send_targets; 284255570Strasz 285273543Strasz pg = conn->conn_portal->p_portal_group; 286273543Strasz 287255570Strasz log_debugx("beginning discovery session; waiting for Text PDU"); 288255570Strasz request = text_receive(conn); 289255570Strasz request_keys = keys_new(); 290255570Strasz keys_load(request_keys, request); 291255570Strasz 292255570Strasz send_targets = keys_find(request_keys, "SendTargets"); 293255570Strasz if (send_targets == NULL) 294255570Strasz log_errx(1, "received Text PDU without SendTargets"); 295255570Strasz 296255570Strasz response = text_new_response(request); 297255570Strasz response_keys = keys_new(); 298255570Strasz 299255570Strasz if (strcmp(send_targets, "All") == 0) { 300278322Smav TAILQ_FOREACH(port, &pg->pg_ports, p_pgs) { 301278322Smav if (discovery_target_filtered_out(conn, port)) { 302273813Strasz /* Ignore this target. */ 303273813Strasz continue; 304273813Strasz } 305278322Smav discovery_add_target(response_keys, port->p_target); 306255570Strasz } 307255570Strasz } else { 308278322Smav port = port_find_in_pg(pg, send_targets); 309278322Smav if (port == NULL) { 310255570Strasz log_debugx("initiator requested information on unknown " 311255570Strasz "target \"%s\"; returning nothing", send_targets); 312273813Strasz } else { 313278322Smav if (discovery_target_filtered_out(conn, port)) { 314273813Strasz /* Ignore this target. */ 315273813Strasz } else { 316278322Smav discovery_add_target(response_keys, port->p_target); 317273813Strasz } 318273813Strasz } 319255570Strasz } 320255570Strasz keys_save(response_keys, response); 321255570Strasz 322255570Strasz pdu_send(response); 323255570Strasz pdu_delete(response); 324255570Strasz keys_delete(response_keys); 325255570Strasz pdu_delete(request); 326255570Strasz keys_delete(request_keys); 327255570Strasz 328255570Strasz log_debugx("done sending targets; waiting for Logout PDU"); 329255570Strasz request = logout_receive(conn); 330255570Strasz response = logout_new_response(request); 331255570Strasz 332255570Strasz pdu_send(response); 333255570Strasz pdu_delete(response); 334255570Strasz pdu_delete(request); 335255570Strasz 336255570Strasz log_debugx("discovery session done"); 337255570Strasz} 338