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 31270888Strasz#include <sys/cdefs.h> 32270888Strasz__FBSDID("$FreeBSD$"); 33270888Strasz 34255570Strasz#include <sys/types.h> 35255570Strasz#include <sys/ioctl.h> 36255570Strasz#include <assert.h> 37255570Strasz#include <stdbool.h> 38255570Strasz#include <stdio.h> 39255570Strasz#include <stdlib.h> 40255570Strasz#include <string.h> 41255570Strasz#include <netinet/in.h> 42255570Strasz 43255570Strasz#include "iscsid.h" 44255570Strasz#include "iscsi_proto.h" 45255570Strasz 46255570Straszstatic struct pdu * 47255570Strasztext_receive(struct connection *conn) 48255570Strasz{ 49255570Strasz struct pdu *response; 50255570Strasz struct iscsi_bhs_text_response *bhstr; 51255570Strasz 52255570Strasz response = pdu_new(conn); 53255570Strasz pdu_receive(response); 54255570Strasz if (response->pdu_bhs->bhs_opcode != ISCSI_BHS_OPCODE_TEXT_RESPONSE) 55255570Strasz log_errx(1, "protocol error: received invalid opcode 0x%x", 56255570Strasz response->pdu_bhs->bhs_opcode); 57255570Strasz bhstr = (struct iscsi_bhs_text_response *)response->pdu_bhs; 58255570Strasz#if 0 59255570Strasz if ((bhstr->bhstr_flags & 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"); 67255570Strasz if (ntohl(bhstr->bhstr_statsn) != conn->conn_statsn + 1) { 68255570Strasz log_errx(1, "received Text PDU with wrong StatSN: " 69255570Strasz "is %d, should be %d", ntohl(bhstr->bhstr_statsn), 70255570Strasz conn->conn_statsn + 1); 71255570Strasz } 72255570Strasz conn->conn_statsn = ntohl(bhstr->bhstr_statsn); 73255570Strasz 74255570Strasz return (response); 75255570Strasz} 76255570Strasz 77255570Straszstatic struct pdu * 78255570Strasztext_new_request(struct connection *conn) 79255570Strasz{ 80255570Strasz struct pdu *request; 81255570Strasz struct iscsi_bhs_text_request *bhstr; 82255570Strasz 83255570Strasz request = pdu_new(conn); 84255570Strasz bhstr = (struct iscsi_bhs_text_request *)request->pdu_bhs; 85255570Strasz bhstr->bhstr_opcode = ISCSI_BHS_OPCODE_TEXT_REQUEST | 86255570Strasz ISCSI_BHS_OPCODE_IMMEDIATE; 87255570Strasz bhstr->bhstr_flags = BHSTR_FLAGS_FINAL; 88255570Strasz bhstr->bhstr_initiator_task_tag = 0; 89255570Strasz bhstr->bhstr_target_transfer_tag = 0xffffffff; 90255570Strasz 91255570Strasz bhstr->bhstr_initiator_task_tag = 0; /* XXX */ 92255570Strasz bhstr->bhstr_cmdsn = 0; /* XXX */ 93255570Strasz bhstr->bhstr_expstatsn = htonl(conn->conn_statsn + 1); 94255570Strasz 95255570Strasz return (request); 96255570Strasz} 97255570Strasz 98255570Straszstatic struct pdu * 99255570Straszlogout_receive(struct connection *conn) 100255570Strasz{ 101255570Strasz struct pdu *response; 102255570Strasz struct iscsi_bhs_logout_response *bhslr; 103255570Strasz 104255570Strasz response = pdu_new(conn); 105255570Strasz pdu_receive(response); 106255570Strasz if (response->pdu_bhs->bhs_opcode != ISCSI_BHS_OPCODE_LOGOUT_RESPONSE) 107255570Strasz log_errx(1, "protocol error: received invalid opcode 0x%x", 108255570Strasz response->pdu_bhs->bhs_opcode); 109255570Strasz bhslr = (struct iscsi_bhs_logout_response *)response->pdu_bhs; 110255570Strasz if (ntohs(bhslr->bhslr_response) != BHSLR_RESPONSE_CLOSED_SUCCESSFULLY) 111255570Strasz log_warnx("received Logout Response with reason %d", 112255570Strasz ntohs(bhslr->bhslr_response)); 113255570Strasz if (ntohl(bhslr->bhslr_statsn) != conn->conn_statsn + 1) { 114255570Strasz log_errx(1, "received Logout PDU with wrong StatSN: " 115255570Strasz "is %d, should be %d", ntohl(bhslr->bhslr_statsn), 116255570Strasz conn->conn_statsn + 1); 117255570Strasz } 118255570Strasz conn->conn_statsn = ntohl(bhslr->bhslr_statsn); 119255570Strasz 120255570Strasz return (response); 121255570Strasz} 122255570Strasz 123255570Straszstatic struct pdu * 124255570Straszlogout_new_request(struct connection *conn) 125255570Strasz{ 126255570Strasz struct pdu *request; 127255570Strasz struct iscsi_bhs_logout_request *bhslr; 128255570Strasz 129255570Strasz request = pdu_new(conn); 130255570Strasz bhslr = (struct iscsi_bhs_logout_request *)request->pdu_bhs; 131255570Strasz bhslr->bhslr_opcode = ISCSI_BHS_OPCODE_LOGOUT_REQUEST | 132255570Strasz ISCSI_BHS_OPCODE_IMMEDIATE; 133255570Strasz bhslr->bhslr_reason = BHSLR_REASON_CLOSE_SESSION; 134255570Strasz bhslr->bhslr_reason |= 0x80; 135255570Strasz bhslr->bhslr_initiator_task_tag = 0; /* XXX */ 136255570Strasz bhslr->bhslr_cmdsn = 0; /* XXX */ 137255570Strasz bhslr->bhslr_expstatsn = htonl(conn->conn_statsn + 1); 138255570Strasz 139255570Strasz return (request); 140255570Strasz} 141255570Strasz 142255570Straszstatic void 143255570Straszkernel_add(const struct connection *conn, const char *target) 144255570Strasz{ 145255570Strasz struct iscsi_session_add isa; 146255570Strasz int error; 147255570Strasz 148255570Strasz memset(&isa, 0, sizeof(isa)); 149256193Strasz memcpy(&isa.isa_conf, &conn->conn_conf, sizeof(isa.isa_conf)); 150255570Strasz strlcpy(isa.isa_conf.isc_target, target, 151255570Strasz sizeof(isa.isa_conf.isc_target)); 152255570Strasz isa.isa_conf.isc_discovery = 0; 153255570Strasz error = ioctl(conn->conn_iscsi_fd, ISCSISADD, &isa); 154255570Strasz if (error != 0) 155255570Strasz log_warn("failed to add %s: ISCSISADD", target); 156255570Strasz} 157255570Strasz 158255570Straszstatic void 159255570Straszkernel_remove(const struct connection *conn) 160255570Strasz{ 161255570Strasz struct iscsi_session_remove isr; 162255570Strasz int error; 163255570Strasz 164255570Strasz memset(&isr, 0, sizeof(isr)); 165255570Strasz isr.isr_session_id = conn->conn_session_id; 166255570Strasz error = ioctl(conn->conn_iscsi_fd, ISCSISREMOVE, &isr); 167255570Strasz if (error != 0) 168255570Strasz log_warn("ISCSISREMOVE"); 169255570Strasz} 170255570Strasz 171255570Straszvoid 172255570Straszdiscovery(struct connection *conn) 173255570Strasz{ 174255570Strasz struct pdu *request, *response; 175255570Strasz struct keys *request_keys, *response_keys; 176255570Strasz int i; 177255570Strasz 178255570Strasz log_debugx("beginning discovery session"); 179255570Strasz request = text_new_request(conn); 180255570Strasz request_keys = keys_new(); 181255570Strasz keys_add(request_keys, "SendTargets", "All"); 182255570Strasz keys_save(request_keys, request); 183255570Strasz keys_delete(request_keys); 184255570Strasz request_keys = NULL; 185255570Strasz pdu_send(request); 186255570Strasz pdu_delete(request); 187255570Strasz request = NULL; 188255570Strasz 189255570Strasz log_debugx("waiting for Text Response"); 190255570Strasz response = text_receive(conn); 191255570Strasz response_keys = keys_new(); 192255570Strasz keys_load(response_keys, response); 193255570Strasz for (i = 0; i < KEYS_MAX; i++) { 194255570Strasz if (response_keys->keys_names[i] == NULL) 195255570Strasz break; 196255570Strasz 197255570Strasz if (strcmp(response_keys->keys_names[i], "TargetName") != 0) 198255570Strasz continue; 199255570Strasz 200255570Strasz log_debugx("adding target %s", response_keys->keys_values[i]); 201255570Strasz /* 202255570Strasz * XXX: Validate the target name? 203255570Strasz */ 204255570Strasz kernel_add(conn, response_keys->keys_values[i]); 205255570Strasz } 206255570Strasz keys_delete(response_keys); 207255570Strasz pdu_delete(response); 208255570Strasz 209255570Strasz log_debugx("removing temporary discovery session"); 210255570Strasz kernel_remove(conn); 211255570Strasz 212255570Strasz log_debugx("discovery done; logging out"); 213255570Strasz request = logout_new_request(conn); 214255570Strasz pdu_send(request); 215256194Strasz pdu_delete(request); 216255570Strasz request = NULL; 217255570Strasz 218255570Strasz log_debugx("waiting for Logout Response"); 219255570Strasz response = logout_receive(conn); 220255570Strasz pdu_delete(response); 221255570Strasz 222255570Strasz log_debugx("discovery session done"); 223255570Strasz} 224