/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
*/
#include <sys/stmf_ioctl.h>
#include "iscsit.h"
#include "iscsit_isns.h"
/*
* iscsit_isns.c -- isns client that is part of the iscsit server
*
* The COMSTAR iSCSI target uses four pieces of iSNS functionality:
* - DevAttrReg to notify the iSNS server of our targets and portals.
* - DeregDev to notify when a target goes away or we shut down
* - DevAttrQry (self-query) to see if iSNS server still knows us.
* - Request ESI probes from iSNS server as a keepalive mechanism
*
* We send only two kinds of DevAttrReg messages.
*
* REPLACE-ALL the info the iSNS server knows about us:
* Set Flag in PDU header to ISNS_FLAG_REPLACE_REG
* Set "source" to same iSCSI target each time
* EID (Entity Identifier) == our DNS name
* "Delimiter"
* Object operated on = EID
* "Entity Portals" owned by this "network entity"
* List of targets
* (Targets with TPGT are followed by PGT and PG portal info)
*
* Flag for replace reg not set
* Source and EID and Delimiter and Object Operated On as above
* Single Target
* (Targets with TPGT are followed by PGT and PG portal info)
*
* Interfaces to iscsit
*
* iscsit_isns_register -- a new target comes online
* iscsit_isns_deregister -- target goes offline
* iscsit_isns_target_update -- called when a target is modified
* iscsit_isns_portal_online -- called when defining a new portal
* iscsit_isns_portal_offline -- no longer using a portal
*
* Copying Data Structures
*
* The above routines copy all the data they need, so iscsit can
* proceed without interfering with us. This is moving in the
* direction of having this isns_client be a standalone user-mode
* program. Specifically, we copy the target name, alias, and
* tpgt+portal information.
*
* The iscsit_isns_mutex protects the shadow copies of target and portal
* information. The ISNS_GLOBAL_LOCK protects the iSNS run time structures
* that the monitor thread uses. The routine isnst_copy_global_status_changes
* has to acquire both locks and copy all the required information from the
* global structs to the per-server structs. Once it completes, the monitor
* thread should run completely off the per-server copies.
*
* Global State vs Per-Server state
* There is a global list of targets and portals that is kept updated
* by iscsit. Each svr keeps its own list of targets that have been
* announced to the iSNS server.
*
* Invariants
*
* 1) If svr->svr_registered, then there is some itarget with
* itarget->target_registered.
* 2) If itarget->target_delete_needed, then also itarget->target_registered.
* (Corollary: Any time you remove the last registered target, you have
* to send an unregister-all message.)
* 3) If a target has a non-default portal, then the portal goes online
* before the target goes online, and comes offline afterwards.
* (This is enforced by the iscsit state machines.)
*/
/* local defines */
/* The ISNS_GLOBAL_LOCK protects the per-server data structures */
#define ISNS_GLOBAL_LOCK() \
#define ISNS_GLOBAL_LOCK_HELD() \
#define ISNS_GLOBAL_UNLOCK() \
/*
*/
/*
* If fail this many times to send an update to the server, then
* declare the server non-responsive and reregister everything with
* the server when we next connect.
*/
/*
* The use of ESI probes to all active portals is not appropriate in
* all network environments, since the iSNS server may not have
* connectivity to all portals, so we turn it off by default.
*/
/*
* Interval to request ESI probes at, in seconds. The server is free
* to specify a different frequency in its response.
*/
/*
* Registration Period -- we guarantee to check in with iSNS server at
* least this often. Used when ESI probes are turned off.
*/
/*
* Socket connect, PDU receive, and PDU send must complete
* within this number of microseconds.
*/
/*
* iSNS Message size -- we start with the max that can fit into one PDU.
* If the message doesn't fit, we will expand at run time to a higher
* installation knows it always goes over the standard limit.
*/
/*
* Number of seconds to wait after isnst_monitor thread starts up
* before sending first DevAttrReg message.
*/
/*
* Because of a bug in the Solaris isns server (c 2009), we cannot send a
* modify operation that changes the target's TPGTs. So just replace all.
* If the iSNS server does not have this bug, clear this flag.
* Changes take effect on each modify_target operation
*/
/* If PDU sizes ever go over the following, we need to rearchitect */
/*
* iSNS ESI thread state
*/
/*
* Our list of targets. Kept in lock-step synch with iscsit.
* The iscsit_isns_mutex protects the global data structures that are
* kept in lock-step with iscsit.
* NOTE: Now that isnst runs independently of iscsit, we could remove the
* shadow copies of iscsit structures, such as isns_target_list and
* isns_tpg_portals, and have isnst_copy_global_status_changes reconcile
* isnst directly with the iscsit data structures.
*/
/*
* List of portals from TPGs. Protected by iscsit_isns_mutex.
*/
/* List of all portals. Protected by ISNS_GLOBAL_LOCK */
static int num_default_portals;
static int num_tpg_portals;
/*
* Our entity identifier (fully-qualified hostname). Passed in from libiscsit.
*/
/*
* in6addr_any is currently all zeroes, but use the macro in case this
* ever changes.
*/
static void
isnst_start();
static void
isnst_stop();
static void
static void
static void
static void
static iscsit_isns_svr_t *
static void
isnst_monitor(void *arg);
static int
static void
isnst_monitor_awaken(void);
static boolean_t
static void
static void
static int
static size_t
static size_t
static isns_target_t *
static isns_target_t *
static int
static uint16_t
static size_t
static int
static int
static int
static int
static int
static int
static size_t
static int
void *attr_data,
static int
static size_t
static void *
static void
isnst_close_so(void *);
static void
isnst_esi_thread(void *arg);
static void
static void isnst_esi_start(void);
static void isnst_esi_stop(void);
avl_tree_t *list);
static void isnst_monitor_start(void);
static void isnst_monitor_stop(void);
static void
static int
static int
static void
isnst_clear_default_portals(void);
static void
static void
static isns_portal_t *
static isns_portal_t *
static void
static int
{
/*
* Determine whether iSNS is enabled in the new config.
* Isns property may not be set up yet.
*/
/* Delete iSNS servers that are no longer part of the config */
isns_svr = next_isns_svr) {
}
/* Add new iSNS servers */
cfg_isns_svr != NULL;
} else if (isns_svr->svr_delete_needed) {
/*
* If reactivating a server that was being
* deleted, turn it into a reset.
*/
}
}
/*
* There is no "modify case" since the user specifies a complete
* server list each time. A modify is the same as a remove+add.
*/
/* Wake up the monitor thread to complete the state change */
return (0);
}
int
{
num_default_portals = 0;
/* initialize isns client */
xid = 0;
return (0);
}
void
{
/*
* The following call to iscsit_set_isns waits until all the
* iSNS servers have been fully deactivated and the monitor and esi
* threads have stopped.
*/
/* Clean up data structures */
/*
* Free our EID and target list.
*/
if (isns_eid) {
}
num_default_portals = 0;
}
static void
{
/*
* Update state and isns stop flag
*/
/* reset retry count for all servers */
svr)) {
svr->svr_retry_count = 0;
}
if (state) {
isnst_start();
} else {
isnst_stop();
}
}
}
void
{
sizeof (struct sockaddr_storage));
/* put it on the global isns server list */
}
void
{
/* If monitor thread not running, finish delete here */
} else {
}
}
void
{
/* free the memory */
}
static iscsit_isns_svr_t *
{
return (svr);
}
return (NULL);
}
static isns_target_info_t *
{
char *str;
/* Cannot hold the iscsit_isns_mutex here! */
&str) == 0) {
}
do {
/*
* Only need portal list for non-default portal.
*/
do {
KM_SLEEP);
sizeof (tip->portal_addr));
}
return (ti);
}
static void
{
}
}
}
/*
* iscsit_isns_register
* called by iscsit when a target goes online
*/
int
{
/* Create TI struct outside of isns_mutex */
} else {
ASSERT(0);
}
/* Copy the target info so it will last beyond deregister */
return (0);
}
/*
* iscsit_isns_deregister
* called by iscsit when a target goes offline
*/
int
{
/*
* The main thread is done with the target_info object.
* Make sure the delete callback is called when
* all the svrs are done with it.
*/
return (0);
}
/*
* iscsit_isns_target_update
* This function is called by iscsit when a target's configuration
* has changed.
*/
void
{
/* Create new TI struct outside of isns_mutex */
/*
* If iscsit calls us to modify a target, that target should
* already exist in the isns_svr_list.
*/
/*
* If target-update gets called while the target is still
* offline, then there is nothing to do. The target will be
* completely registered when it comes online.
*/
/* Remove the target_info struct -- not needed */
return;
}
/* Remove the old target_info struct */
/* Link to new target_info struct */
}
static void
{
/*
* Start ESI thread(s)
*/
/*
* Create a thread for monitoring server communications
*/
}
static void
{
}
static void
isnst_monitor_start(void)
{
while (!isns_monitor_thr_running)
}
static void
isnst_monitor_stop(void)
{
if (isns_monitor_thr_running) {
return;
}
}
/*
* isnst_update_server_timestamp
*
* When we receive an ESI request, update the timestamp for the server.
* If we don't receive one for the specified period of time, we'll attempt
* to re-register.
*
*/
static boolean_t
{
/*
* Find the server and update the timestamp
*/
/*
* Note that the port number in incoming probe will be
* different than the iSNS server's port number.
*/
B_TRUE /* v4_mapped_as_v4 */,
B_FALSE /* don't compare_ports */) == 0) {
break;
}
}
/* Update the timestamp we keep for this server */
/*
* If we receive ESI probe from a server we are not
* registered to, then cause a re-reg attempt.
*/
if (!svr->svr_registered) {
}
return (B_TRUE);
}
return (B_FALSE);
}
/*
* isnst_monitor_all_servers -- loop through all servers
*/
static void
{
int rc;
/*
* isnst_monitor_one_server can release ISNS_GLOBAL_LOCK
* internally. This allows isnst_config_merge to run
* even when messages to iSNS servers are pending.
*/
if (rc != 0) {
svr->svr_retry_count++;
if (svr->svr_registered &&
if (! svr->svr_reset_needed) {
"isnst: iSNS server %s"
" not responding (rc=%d).",
server_buf, sizeof (server_buf)),
rc);
}
}
} else {
svr->svr_retry_count = 0;
}
/*
* If we have finished unregistering this server,
* it is now OK to delete it.
*/
}
}
}
static void
isnst_monitor_awaken(void)
{
if (isns_monitor_thr_running) {
}
}
/*
* isnst_monitor -- the monitor thread for iSNS
*/
/*ARGSUSED*/
static void
{
/*
* Start with a short pause (5 sec) to allow all targets
* to be registered before we send register-all. This is
* purely an optimization to cut down on the number of
* messages we send to the iSNS server.
*/
/* Force an initialization of isns_all_portals */
while (isns_monitor_thr_running) {
/* Update servers */
/* If something needs attention, go right to the top */
if (isns_targets_changed || isns_portals_changed) {
/* isns_monitor_mutex still held */
continue;
}
/*
* Keep running until isns_monitor_thr_running is set to
* B_FALSE.
*/
if (! isns_monitor_thr_running)
break;
}
/* Update the servers one last time for deregistration */
/* Clean up the all-portals list */
/* terminate the thread at the last */
thread_exit();
}
static int
{
int rc = 0;
/*
* First, take care of the case where iSNS is no longer enabled.
*
*/
/*
* Just try one time to deregister all from server.
* Doesn't matter if this fails. We're disabled.
*/
return (0);
}
/*
* If the server needs replace-all, check if it should
* be a DevDereg (i.e. if the last target is gone.)
*/
/* Send DevDereg if last registered target */
if (!jtarget->target_delete_needed) {
break;
}
}
/*
* jtarget is null IFF all tgts need deletion,
* and there are no new targets to register.
*/
if (rc != 0) {
return (rc);
}
return (0);
}
}
/*
* If the server is not yet registered, do the registration
*/
/* If no targets, nothing to register */
return (0);
}
ISNS_REGISTER_ALL)) != 0) {
/* Registration failed */
return (rc);
}
}
/* The following checks are expensive, so only do them if needed */
if (svr->svr_targets_changed) {
/*
* If there is a target to be deleted, send the
* deletion request for one target at a time.
*/
itarget = next_target) {
if (itarget->target_delete_needed) {
/* See if last non-deleted target */
for (jtarget =
jtarget)) {
if (jtarget->target_registered &&
break;
}
}
/* jtarget is null if last registered tgt */
/*
* Removing last tgt -- deregister all.
* Doesn't matter if this fails.
* We're disabled.
*/
if (rc != 0) {
return (rc);
}
return (0);
}
/* Retryable code => try replace-all */
goto retry_replace_all;
}
if (rc != 0) {
return (rc);
}
&svr->svr_target_list);
}
}
/* If any target needs a register or an update, do so */
while (itarget) {
if (!itarget->target_registered ||
/*
* Because of a bug in the isns
* server, we cannot send a modify
* operation that changes the target's
* TPGTs. So just replace all.
*/
if (isns_modify_must_replace) {
goto retry_replace_all;
}
/* Try to update existing info for one tgt */
/* Retryable code => try replace-all */
goto retry_replace_all;
}
if (rc != 0) {
return (rc);
}
}
itarget);
}
/*
* We have gone through all the cases -- this server
* is now up to date.
*/
}
if (isns_use_esi) {
/*
* If using ESI, and no ESI request is received within
* MAX_ESI_INTERVALS (3) number of intervals, we'll
* try to re-register with the server. The server will
* delete our information if we fail to respond for 2
* ESI intervals.
*/
MAX_ESI_INTERVALS))) {
/* re-register everything */
goto retry_replace_all;
}
} else {
/*
* If not using ESI, make sure to ping server during
* each registration period. Do this at half the
* registration interval, so we won't get timed out.
*/
/* Send a self-query as a keepalive. */
/* Retryable code => try replace-all */
goto retry_replace_all;
}
if (rc != 0) {
return (rc);
}
}
}
return (0);
}
/*
* isnst_mark_deleted_target -- find tgt in svr list but not global list
*/
static void
{
itarget = nxt_target) {
if (itarget->target_registered) {
} else {
&svr->svr_target_list);
}
}
}
}
static isns_target_t *
{
/*
* Make sure this target isn't already in our list.
*/
} else {
ASSERT(0);
}
return (itarget);
}
static void
{
&svr->svr_target_list);
}
}
static void
{
!= NULL) {
} else {
ASSERT(0);
}
}
/*
* isnst_copy_global_status_changes -- update svrs to match iscsit
*
* At the end of this routine svr->svr_target_list has all the entries
* in the current isns_target_list plus any targets that are marked
* for deletion.
*/
static void
{
/*
* Copy info about recent transitions from global state to
* per-server state. We use the global state so that iscsit
* functions can proceed without blocking on slow-to-release
* iSNS locks.
*/
/*
* Periodically check for changed IP addresses. This function
* sets isns_all_portals to the current set, and sets
* isns_portals_changed if a portal is added or removed.
*/
/* Initialize the per-server structs to some basic values */
svr)) {
/*
* Cause re-register, for now, when portals change.
* Eventually, we should add new portals one by one
*/
}
if (!svr->svr_registered) {
/* To re-register, start with empty target list */
/* And set flag to add all current targets, below */
/* Mark to look for target changes */
}
}
/*
* If any target has been modified, tell all the svrs to
* update that target.
*/
if (isns_targets_changed) {
while (ttarget) {
svr)) {
/* Add a new target */
(void) isnst_latch_to_target_list(
} else if (ttarget->target_update_needed) {
/* Modify existing target */
/* Remove link to old target_info */
/* Link to new target_info struct */
}
}
}
}
/*
* Now we have updated the per-server state for all servers.
* Clear the global state flags
*/
}
/*
* isnst_update_one_server releases ISNS_GLOBAL_LOCK internally and
* acquires it again as needed. This allows isnst_config_merge and
* isnst_esi_thread to run even while waiting for a response from the
* iSNS server (or a dead iSNS server).
*/
static int
{
int rc = 0;
switch (reg) {
case ISNS_DEREGISTER_TARGET:
break;
case ISNS_DEREGISTER_ALL:
break;
case ISNS_MODIFY_TARGET:
case ISNS_REGISTER_TARGET:
break;
case ISNS_REGISTER_ALL:
break;
default:
ASSERT(0);
/* NOTREACHED */
}
return (rc);
}
/*
* isnst_retry_registration
*
* This function checks the return value from a registration pdu and
* determines whether or not we should retry this request. If the
* request is retried, it will do so as an "update", which means we
* re-register everything.
*/
static boolean_t
{
/*
* The following are the error codes that indicate isns-client
* and isns-server are out of synch. E.g. No-Such-Entry can
* occur on a keepalive if the server has timed out our
* connection. If we get one of these messages, we replace-all
* right away to get back in synch faster.
*/
switch (rsp_status_code) {
case ISNS_RSP_INVALID_REGIS:
case ISNS_RSP_BUSY:
case ISNS_RSP_INVALID_UPDATE:
case ISNS_RSP_NO_SUCH_ENTRY:
break;
default:
break;
}
return (retry);
}
static int
{
int rc = 0;
/* create TCP connection to the isns server */
return (-1);
}
if (pdu_size == 0) {
return (-1);
}
if (rc != 0) {
return (rc);
}
if (rsp_size == 0) {
return (-1);
}
return (rc);
}
/*
* isnst_make_reg_pdu:
* Cases:
* initial registration of all targets (replace-all)
* initial registration of a single target (update-existing)
* modify an existing target (update-existing)
*/
static size_t
{
char *str;
int len;
/*
* svr could have an empty target list if svr was added
* by isnst_config_merge sometime after the last call to
* copy_global_status_changes. Just skip this chance
* to reregister. The next call to copy_global_status_changes
* will sort things out.
*/
/* If no targets, nothing to register */
return (0);
}
/*
* Find a source attribute for this registration.
*
* If updating a specific target for the first time, use that
* target.
* If already registered, use a registered target
* Otherwise, use the first target we are going to register.
*/
} else if (svr->svr_registered) {
} else {
/*
* When registering to a server, and we don't know which
* of our targets the server might already know,
* cycle through each of our targets as source. The server
* does source validation. If the server knows any of our
* targets, it will eventually accept one of our registrations.
*/
int i;
if (svr->svr_last_target_index >=
svr->svr_last_target_index = 0;
} else {
}
i < svr->svr_last_target_index;
}
}
/*
* Null target means we're replacing everything.
*/
/* Reset itarget to the beginning of our list */
} else if (regtype == ISNS_REGISTER_TARGET) {
}
if (pdu_size == 0) {
return (0);
}
/* Source Attribute */
goto pdu_error;
}
/*
* Message Key Attributes - EID
*/
goto pdu_error;
}
/* Delimiter */
0, 0, 0) != 0) {
goto pdu_error;
}
/*
* Operating Attributes
*/
isns_eid, 0) != 0) {
goto pdu_error;
}
/* ENTITY Protocol - Section 6.2.2 */
4, 0, ISNS_ENTITY_PROTOCOL_ISCSI) != 0) {
goto pdu_error;
}
if (reg_all) {
/* Registration Period -- use if not using ESI */
if (!isns_use_esi &&
0, isns_registration_period) != 0) {
goto pdu_error;
}
/*
* Network entity portal information - only when
* replacing all. Since targets are only registered
* to iSNS when their portals are already registered
* to iSNS, we can assume entity portals exist.
*/
goto pdu_error;
}
/*
* Skip over delete-pending tgts. There must be at
* least one non-deleted tgt, or it is an error.
*/
while (itarget->target_delete_needed) {
itarget);
}
}
/* Add information about each target or one target */
do {
/* iSCSI Name - Section 6.4.1 */
goto pdu_error;
}
/* iSCSI Node Type */
ISNS_ISCSI_NODE_TYPE_ATTR_ID, 4, 0,
ISNS_TARGET_NODE_TYPE) != 0) {
goto pdu_error;
}
/* iSCSI Alias */
if (len) {
/* Found alias in property list */
goto pdu_error;
}
}
goto pdu_error;
}
/* If registering one target, then we are done. */
if (!reg_all) {
break;
}
/* Skip over delete-pending tgts */
do {
return (pdu_size);
/* packet too large, no memory (or other error) */
/* Increase the PDU size we will ask for next time */
isns_message_buf_size *= 2;
"Increasing isns_message_buf_size to %d",
} else {
" to send required PDU");
}
}
return (0);
}
static int
{
int rc = 0;
/* Do not include ESI port if not using ESI */
isns_use_esi /* ESI info */) != 0) {
rc = -1;
break;
}
}
return (rc);
}
/*
* isnst_reg_pdu_add_pg -- add the PG and PGT entries for one target.
*/
static int
{
int rval = 0;
/*
* If all registered targets only use the default TPGT, then
* we can skip sending PG info to the iSNS server.
*/
if (num_tpg_portals == 0)
return (0);
/*
* For each target, we start with the full portal list,
* and then remove portals as we add them to TPGTs for this target.
* At the end, all the remaining portals go into the "null pg".
* We use the "null_portals" list to track this.
*/
/* Add portal info from list of default portals */
&null_portals) != 0) {
rval = 1;
break;
}
} else {
/* Add portal info from this TPGT's entries */
&null_portals) != 0) {
rval = 1;
break;
}
}
}
/* Add the remaining portals (if any) to the null PG */
if (rval == 0 &&
rval = 1;
}
return (rval);
}
/* Write one TPGT's info into the PDU */
static int
{
int rval = 0;
/* Portal Group Tag */
rval = 1;
goto pg_done;
}
do {
/* PG Portal Addr and PG Portal Port */
rval = 1;
goto pg_done;
}
return (rval);
}
static int
{
if (num_default_portals == 0) {
/*
* It is OK for a target with default-portals to be
* online from an STMF perspective and yet all
* default portals are down. if other (non-default)
* portals do exist, we will still announce the target
* to the isns server. In this case, we will specify
* all the active non-default portals as NULL portals.
* This is an OK state.
*
* There is a corner case if non-default portals have
* been marked online but the targets that use them
* are not fully online yet, AND all the default portals
* are down. In this case, the iSNS server will receive
* a DevAttrReg pdu that announces both non-default
* portals and default-portal-only targets. In other
* words, there may be no target that has an active
* portal. The iSNS spec does not forbid this case.
*
* Both of the above cases are somewhat theoretical.
* If the default portals are down we probably cannot
* get any messages through to the iSNS server anyway.
*/
return (0);
}
/* Portal Group Tag */
return (1);
}
if (iportal->portal_default) {
/* PG Portal Addr and PG Portal Port */
return (1);
}
}
}
return (0);
}
static int
{
/* If all portals accounted for, no NULL PG needed */
if (avl_numnodes(null_portal_list) == 0) {
return (0);
}
/* NULL Portal Group Tag means no access via these portals. */
ISNS_PG_TAG_ATTR_ID, 0, 0, 0) != 0) {
return (1);
}
return (1);
}
}
return (0);
}
static int
{
void *inaddrp;
attr_numeric_data = sizeof (in_addr_t);
attr_numeric_data = sizeof (in6_addr_t);
}
/* Portal Group Portal IP Address */
return (1);
}
/* Portal Group Portal Port */
return (1);
}
/* ESI interval and port */
NULL, isns_default_esi_interval) != 0) {
return (1);
}
return (1);
}
}
return (0);
}
static int
{
int rc;
return (-1);
}
if (pdu_size == 0) {
return (-1);
}
if (rc != 0) {
return (rc);
}
if (rsp_size == 0) {
return (-1);
}
return (rc);
}
static int
{
int rc;
return (-1);
}
if (pdu_size == 0) {
return (-1);
}
if (rc != 0) {
return (rc);
}
if (rsp_size == 0) {
return (-1);
}
return (rc);
}
static size_t
{
int len;
/*
* create DevDereg Message with all of target nodes
*/
if (pdu_size == 0) {
return (0);
}
/*
* Source attribute - Must be a storage node in the same
* network entity.
*/
if (svr->svr_registered) {
} else {
goto dereg_pdu_error;
}
goto dereg_pdu_error;
}
/* Delimiter */
0, 0, 0) != 0) {
goto dereg_pdu_error;
}
/*
* Operating attributes
*/
/* dereg everything */
goto dereg_pdu_error;
}
} else {
/* dereg one target only */
goto dereg_pdu_error;
}
}
return (pdu_size);
return (0);
}
static size_t
{
int len;
/*
* create DevAttrQuery Message
*/
if (pdu_size == 0) {
return (0);
}
/*
* Source attribute - Must be a iscsi target in the same
* network entity.
*/
goto keepalive_pdu_error;
}
/* EID */
goto keepalive_pdu_error;
}
/* Delimiter */
0, 0, 0) != 0) {
goto keepalive_pdu_error;
}
/* Values to Fetch -- EID */
0, 0, 0) != 0) {
goto keepalive_pdu_error;
}
return (pdu_size);
return (0);
}
static isns_target_t *
{
/*
* If svr is registered, then there must be at least one
* target that is registered to that svr.
*/
return (itarget);
}
static isns_target_t *
{
do {
break;
return (itarget);
}
static int
{
int status;
/*
* Ensure we have at least a valid header (don't count the
* "payload" field.
*/
return (-1);
}
/* Make sure we have the amount of data that the header specifies */
(int)rsp_size,
return (-1);
}
/* Find the start of all operational parameters */
/*
* Make sure isnst_pdu_get_op didn't encounter an error
* in the attributes.
*/
return (-1);
}
/* verify response transaction id */
return (-1);
}
/* check the error code */
/* validate response function id */
case ISNS_DEV_ATTR_REG:
if (func_id != ISNS_DEV_ATTR_REG_RSP) {
return (-1);
}
/* Only look through response if msg status says OK */
if (status != 0) {
break;
}
/*
* Get the ESI interval returned by the server. It could
* be different than what we asked for. We never know which
* portal a request may come in on, and any server could demand
* any interval. We'll simply keep track of the largest
* interval for use in monitoring.
*/
while (rsp_payload_len >= 8) {
if (attr_id == ISNS_ESI_INTERVAL_ATTR_ID) {
if (attr_len != 4 ||
/* Mal-formed packet */
return (-1);
}
((void *)(&attr->attr_value))));
break;
}
attr = (isns_tlv_t *)
}
break;
case ISNS_DEV_DEREG:
if (func_id != ISNS_DEV_DEREG_RSP) {
return (-1);
}
break;
case ISNS_DEV_ATTR_QRY:
/* Keepalive Response */
if (func_id != ISNS_DEV_ATTR_QRY_RSP) {
return (-1);
}
if (status == 0) {
/* Scan the operational parameters */
while (rsp_payload_len >= 8) {
if (attr_id == ISNS_EID_ATTR_ID &&
attr_len > 0 &&
/*
* If the isns server knows us, the
* response will include our EID in
* the operational parameters, i.e.
* after the delimiter.
* Just receiving this pattern
* is good enough to tell the isns
* server still knows us.
*/
break;
}
attr = (isns_tlv_t *)
}
if (! found_eid) {
}
}
if (status == ISNS_RSP_NO_SUCH_ENTRY) {
/*
* The iSNS server has forgotten about us.
* We will re-register everything.
* This can happen e.g. if ESI probes time out,
* or if the iSNS server does a factory reset.
*/
" forgot about us and has to be reminded.",
server_buf, sizeof (server_buf)));
/* isnst_retry_registration will trigger the reset */
}
break;
default:
ASSERT(0);
break;
}
/* Update the last time we heard from this server */
if (status == 0) {
}
return (status);
}
static uint16_t
{
/* get payload */
/* find the operating attributes */
return (0);
}
if (payload_len >= tlv_len) {
payload_len -= tlv_len;
if (attr_id == ISNS_DELIMITER_ATTR_ID) {
break;
}
} else {
/* mal-formed packet */
payload_len = 0;
}
}
return (payload_len);
}
static size_t
{
} else {
pdu_size = 0;
}
return (pdu_size);
}
static int
void *attr_data,
{
/* The attribute length must be 4-byte aligned. Section 5.1.3. */
/* Check if we are going to exceed the maximum PDU length. */
return (1);
}
switch (attr_id) {
case ISNS_DELIMITER_ATTR_ID:
break;
if (attr_numeric_data == sizeof (in_addr_t)) {
/* IPv4 */
sizeof (in_addr_t));
} else if (attr_numeric_data == sizeof (in6_addr_t)) {
/* IPv6 */
sizeof (in6_addr_t));
} else if (attr_numeric_data == 0) {
/* EMPTY */
/* Do nothing */
} else {
return (1);
}
break;
case ISNS_EID_ATTR_ID:
case ISNS_ISCSI_NAME_ATTR_ID:
case ISNS_ISCSI_ALIAS_ATTR_ID:
}
break;
default:
if (attr_len == 8) {
} else if (attr_len == 4) {
}
break;
}
/*
* Convert the network byte ordered payload length to host byte
* ordered for local address calculation.
*/
/*
* Convert the host byte ordered payload length back to network
* byte ordered - it's now ready to be sent on the wire.
*/
return (0);
}
static void
{
/* Wake up any sosend or sorecv blocked on this socket */
}
static int
{
int rc;
ASSERT(! ISNS_GLOBAL_LOCK_HELD());
/* update pdu flags */
/* initalize sequence number */
seq = 0;
/* total payload length */
/* fill in the pdu header */
do {
/* split the payload accordingly */
if (total_len > ISNSP_MAX_PAYLOAD_SIZE) {
} else {
/* set the last pdu flag */
}
/* set back the pdu flags */
/* set the sequence number */
/* set the payload length */
/* fill in the payload */
/* send the pdu */
(void) untimeout(send_timer);
flags &= ~ISNS_FLAG_FIRST_PDU;
payload += payload_len;
total_len -= payload_len;
/* increase the sequence number */
seq ++;
return (rc);
}
static size_t
{
ASSERT(! ISNS_GLOBAL_LOCK_HELD());
total_pdu_len = total_payload_len = 0;
seq = 0;
do {
/* receive the pdu header */
goto rcv_error;
}
/* receive the payload */
if (payload_len > ISNST_MAX_MSG_SIZE) {
goto rcv_error;
}
goto rcv_error;
}
goto rcv_error;
}
/* combine the pdu if it is not the first one */
if (total_pdu_len > 0) {
if (combined_pdu == NULL) {
goto rcv_error;
}
*pdu = combined_pdu;
} else {
goto rcv_error;
}
}
/* the flags of pdu which is just received */
/* increase sequence number by one */
seq ++;
} while ((flags & ISNS_FLAG_LAST_PDU) == 0);
return (total_pdu_len);
}
}
return (0);
}
static void *
{
int sa_sz;
ASSERT(! ISNS_GLOBAL_LOCK_HELD());
/* determine local IP address */
/* IPv4 */
sa_sz = sizeof (struct sockaddr_in);
/* Create socket */
} else {
/* IPv6 */
sa_sz = sizeof (struct sockaddr_in6);
/* Create socket */
}
isns_timeout_usec) != 0) {
/* not calling isnst_close_so() to */
/* make dtrace output look clear */
}
}
sizeof (server_buf)));
struct sockaddr_storage *, sa);
}
return (so);
}
static void
{
}
/*
* ESI handling
*/
static void
isnst_esi_start(void)
{
if (isns_use_esi == B_FALSE) {
return;
}
/*
* Wait for the thread to start
*/
while (!esi.esi_thread_running) {
}
}
static void
{
/* Shutdown ESI listening socket, wait for thread to terminate */
if (esi.esi_enabled) {
}
if (need_offline) {
}
} else {
}
}
/*
* isnst_esi_thread
*
* This function listens on a socket for incoming connections from an
* iSNS server until told to stop.
*/
/*ARGSUSED*/
static void
{
int rc;
sin_addrlen = sizeof (struct sockaddr_in6);
/*
* Mark the thread as running and the portal as no longer new.
*/
while (esi.esi_enabled) {
/*
* Create a socket to listen for requests from the iSNS server.
*/
NULL) {
"isnst_esi_thread: Unable to create socket");
continue;
}
/*
* Set options, bind, and listen until we're told to stop
*/
continue;
}
/*
* Get the port (sin6 is meaningless at this point)
*/
"failure 0x%x", rc);
continue;
}
while (esi.esi_enabled) {
struct sockaddr_in6, &sin6);
/*
* If we were interrupted with EINTR
* it's not really a failure.
*/
"accept failure (0x%x)", rc);
continue;
} else {
break;
}
}
if (pl_size == 0) {
"rcv_pdu failure");
continue;
}
}
/*
* If we're going to try to re-establish the listener then
* destroy this socket. Otherwise isnst_esi_stop already
* destroyed it.
*/
if (esi.esi_enabled)
}
thread_exit();
}
/*
* Handle an incoming ESI request
*/
static void
{
return;
}
"payload exceeds PDU size (%d > %d)",
(int)pdu_size);
/* Not all data is present -- ignore */
return;
}
"isnst_handle_esi_req: PDU payload exceeds max (%ld bytes)",
req_pl_len + sizeof (uint32_t));
return;
}
/*
* Check portal in ESI request and make sure it is valid. Return
* esi_response of ISNS_RSP_SUCCESSFUL if valid, otherwise don't
* respond at all. Get IP addr and port. Format of ESI
* is:
*
* ISNS_TIMESTAMP_ATTR_ID,
* ISNS_EID_ATTR_ID,
* ISNS_PORTAL_IP_ADDR_ATTR_ID,
* ISNS_PORTAL_PORT_ATTR_ID
*/
while (tlv_len <= req_pl_len) {
switch (attr_id) {
case ISNS_TIMESTAMP_ATTR_ID:
break;
case ISNS_EID_ATTR_ID:
break;
/* Bad attribute format */
} else {
sizeof (portal_addr6->sin6_addr));
}
break;
case ISNS_PORTAL_PORT_ATTR_ID:
/* Bad attribute format */
} else {
}
break;
default:
/* Bad request format */
break;
}
/* If we've set an error then stop processing */
if (esi_response != ISNS_RSP_SUCCESSFUL) {
break;
}
/* Get next attribute */
req_pl_len -= tlv_len;
}
if (!portal_port_valid)
if (!portal_addr_valid) {
}
/*
* If we've detected an error or if the portal does not
* exist then drop the request. The server will eventually
* timeout the portal and eliminate it from the list.
*/
if (esi_response != ISNS_RSP_SUCCESSFUL) {
return;
}
/* Get the remote peer's IP address */
CRED())) {
return;
}
if (iscsit_isns_logging) {
sizeof (server_buf)),
sizeof (portal_buf)));
}
return;
}
/*
* Update the server timestamp of how recently we have
* received an ESI request from this iSNS server.
* We ignore requests from servers we don't know.
*/
if (! isnst_update_server_timestamp(&server_ss)) {
return;
}
/*
* Update ESI timestamps for all portals with same IP address.
*/
}
}
/*
* Build response validating the portal
*/
if (rsp_size == 0) {
return;
}
/* Use xid from the request pdu */
/* Copy original data */
"isnst_handle_esi_req: Send response failed");
}
}
static int
{
/*
* Sort by target (pointer to iscsit_tgt_t).
*/
return (-1);
return (1);
}
return (0);
}
static void
{
if (registered == B_TRUE) {
while (itarget) {
if (itarget->target_delete_needed) {
/* All deleted tgts removed */
&svr->svr_target_list);
} else {
/* Other tgts marked registered */
/* No updates needed -- clean slate */
}
}
} else {
}
}
static void
{
if (default_portal_online) {
}
/*
* We compute a new list of portals if
* a) Something in itadm has changed a portal
* b) there are new default portals
* c) the default portal has gone offline
*/
if (isns_portals_changed ||
((new_portal_list_size != 0) &&
num_default_portals)) ||
((new_portal_list_size == 0) && (num_default_portals > 0))) {
if (new_portal_list_size != 0) {
}
}
/* Catch any case where we miss an update to TPG portals */
if (new_portal_list != NULL) {
}
}
static int
{
int aidx;
int num_portals_found = 0;
/* Found a non-matching default portal */
return (-1);
}
if (iportal->portal_default) {
}
}
return (num_portals_found);
}
static void
{
num_tpg_portals = 0;
num_default_portals = 0;
}
static int
{
int aidx;
}
return (alist->al_out_cnt);
}
static int
{
}
static void
{
}
}
static void
{
while (iportal) {
}
}
static isns_portal_t *
{
return (iportal);
}
static isns_portal_t *
{
/*
* Make sure this portal isn't already in our list.
*/
}
return (iportal);
}
static void
{
!= NULL) {
}
}
/*
* These functions are called by iscsit proper when a portal comes online
* or goes offline.
*/
void
{
if (portal->portal_default) {
/* Portals should only be onlined once */
} else {
}
}
void
{
if (portal->portal_default) {
/* Portals should only be offlined once */
} else {
}
}