Mercurial > illumos > onarm
diff usr/src/cmd/iscsi/iscsitgtd/isns_client.c @ 0:c9caec207d52 b86
Initial porting based on b86
author | Koji Uno <koji.uno@sun.com> |
---|---|
date | Tue, 02 Jun 2009 18:56:50 +0900 |
parents | |
children | 1a15d5aaf794 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/iscsi/iscsitgtd/isns_client.c Tue Jun 02 18:56:50 2009 +0900 @@ -0,0 +1,1632 @@ +/* + * 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 + * or http://www.opensolaris.org/os/licensing. + * 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 + */ + +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "@(#)isns_client.c 1.5 08/02/11 SMI" + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <strings.h> +#include <syslog.h> +#include <netdb.h> +#include <pthread.h> +#include <signal.h> +#include <errno.h> +#include <netinet/in.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> + +#include <iscsitgt_impl.h> + +#include "isns_protocol.h" +#include "isns_client.h" +#include "target.h" +#include "queue.h" + + +typedef struct { + uint32_t pf_family; + uint32_t ip_len; + uint32_t ai_addrlen; + union { + in_addr_t in; + in6_addr_t in6; + } ip_adr; +} ip_t; + +#define ISNS_TGT_LOGOUT 54321 + +extern target_queue_t *mgmtq; + +/* + * Global + * Parameters for ESI/SCN processing. + * scn_port: ESI/SCN port to receive ISNS_ESI & ISNS_SCN messages + * isns_args: + * eid_ip: Entity IP info + */ +static int scn_port = 0; +static esi_scn_arg_t isns_args; +static ip_t eid_ip; +static int num_reg = 0; +static pthread_t scn_tid = 0; +static Boolean_t isns_shutdown = False; + +Boolean_t isns_initialized = False; +sema_t isns_sema; +target_queue_t *mgmtq = NULL; + +static int get_ip_addr(char *node, ip_t *sa); +static int isns_op_all(uint16_t); +static int append_tpgt(tgt_node_t *, isns_pdu_t *); +static void process_esi(int, isns_pdu_t *); +static void process_scn(int, isns_pdu_t *); +static void *esi_scn_thr(void *); +static int process_rsp(isns_pdu_t *, isns_rsp_t *); +static int isns_dev_attr_reg(int, tgt_node_t *, char *, char *); +static int isns_dev_attr_dereg(int, char *); +static int isns_scn_reg(int, char *); +static int isns_scn_dereg(int so, char *node); +static tgt_node_t *find_tgt_by_name(char *, char **); +static tgt_node_t *find_next_tgt(tgt_node_t *, char **); + +/* + * find_tgt_by_name searches DB by iscsi name or local name, if found + * returns tgt_node_t. iname needs to be free by caller. + */ +static tgt_node_t * +find_tgt_by_name(char *targ, char **iname) +{ + tgt_node_t *tgt = NULL; + + while ((tgt = tgt_node_next(targets_config, XML_ELEMENT_TARG, tgt)) + != NULL) { + if (tgt_find_value_str(tgt, XML_ELEMENT_INAME, iname) + == FALSE) { + syslog(LOG_ALERT, "ISNS: Missing iscsi name\n"); + break; + } + /* match either iscsi name or local name */ + if (strcmp(targ, tgt->x_value) == 0 || + strcmp(targ, *iname) == 0) { + return (tgt); + } + free(*iname); + } + return (NULL); +} + +static tgt_node_t * +find_next_tgt(tgt_node_t *tgt, char **iname) +{ + while ((tgt = tgt_node_next(targets_config, XML_ELEMENT_TARG, tgt)) + != NULL) { + if (tgt_find_value_str(tgt, XML_ELEMENT_INAME, iname) + == FALSE) { + continue; + } + return (tgt); + } + return (NULL); +} + +/* + * Find ip-addr associated with TPGT, don't send if no ip-addr is + * found for a TPGT + */ +static int +append_tpgt(tgt_node_t *tgt, isns_pdu_t *cmd) +{ + tgt_node_t *t, *x; + tgt_node_t *pgt = NULL; + tgt_node_t *ip = NULL; + tgt_node_t *tpgt = NULL; + ip_t eid; + + /* Always add the default TPGT (1) */ + isns_append_attr(cmd, ISNS_PG_TAG_ATTR_ID, ISNS_PG_TAG_SZ, NULL, 1); + if (isns_append_attr(cmd, ISNS_PG_PORTAL_IP_ADDR_ATTR_ID, + eid_ip.ai_addrlen, (void *)&eid_ip.ip_adr, + eid_ip.ip_len) != 0) { + return (-1); + } + if (isns_append_attr(cmd, ISNS_PG_PORTAL_PORT_ATTR_ID, + ISNS_PORT_SZ, NULL, iscsi_port) != 0) { + return (-1); + } + + /* Get the remainning TPGT-LIST */ + if ((t = tgt_node_next(tgt, XML_ELEMENT_TPGTLIST, NULL)) + != NULL) { + /* find tgpt from tpgt-list */ + while ((pgt = tgt_node_next(t, XML_ELEMENT_TPGT, pgt)) + != NULL) { + /* update isns only if TPGT contains ip_addr */ + while ((tpgt = tgt_node_next(main_config, + XML_ELEMENT_TPGT, tpgt)) != NULL) { + if (strcmp(pgt->x_value, tpgt->x_value) != 0) + continue; + if ((ip = tgt_node_next(tpgt, + XML_ELEMENT_IPADDR, NULL)) != NULL) + break; + } + if (tpgt == NULL || ip == NULL) + continue; + if (isns_append_attr(cmd, ISNS_PG_TAG_ATTR_ID, + ISNS_PG_TAG_SZ, NULL, + strtol(pgt->x_value, NULL, 0)) != 0) { + return (-1); + } + + /* get ip-addr & port */ + for (x = tpgt->x_child; x; x = x->x_sibling) { + get_ip_addr(x->x_value, &eid); + if (isns_append_attr(cmd, + ISNS_PG_PORTAL_IP_ADDR_ATTR_ID, + eid.ai_addrlen, (void *)&eid.ip_adr, + eid.ip_len) != 0) { + return (-1); + } + if (isns_append_attr(cmd, + ISNS_PG_PORTAL_PORT_ATTR_ID, + ISNS_PORT_SZ, NULL, iscsi_port) != 0) { + return (-1); + } + } + } + } + + return (0); +} + +/* + * process_scn() + * -Added/Updated object: nop, initiator is verified during connect + * + * -Removed object: logout_targ if still connected + * + * RFC 4171 section 5.6.5.9 + * destination attribute is always the 1st attribute in the SCN message, + * then follows by SCN_BITMAP(35) & Source_Attribute(32) + */ +static void +process_scn(int so, isns_pdu_t *scn) +{ + uint8_t *ptr = scn->payload; + isns_tlv_t *tlv; + uint16_t cnt = 0; + uint32_t got_dest = 0; + uint32_t got_source = 0; + uint32_t bitmap = 0; + uint32_t got_bitmap = 0; + char dest[MAXNAMELEN]; + char source[MAXNAMELEN]; + + queue_prt(mgmtq, Q_ISNS_DBG, "PROCESS_SCN %u\n", + scn->payload_len); + + if (scn->payload_len < TAG_LEN_SZ) { + syslog(LOG_ALERT, "ISNS SCN message error\n"); + return; + } + + while (cnt < scn->payload_len) { + /* LINTED */ + tlv = (isns_tlv_t *)ptr; + tlv->attr_id = ntohl(tlv->attr_id); + tlv->attr_len = ntohl(tlv->attr_len); + queue_prt(mgmtq, Q_ISNS_DBG, "PROCESS_SCN %u %u\n", + tlv->attr_id, tlv->attr_len); + /* + * devAttrQry the source attribute, process if node_type + * is initiator + */ + switch (tlv->attr_id) { + case ISNS_ISCSI_NAME_ATTR_ID: + if (got_dest == 0) { + bcopy(tlv->attr_value, dest, + tlv->attr_len); + queue_prt(mgmtq, Q_ISNS_DBG, + "PROCESS_SCN dest %s\n", dest); + got_dest = 1; + } else { + bcopy(tlv->attr_value, source, + tlv->attr_len); + queue_prt(mgmtq, Q_ISNS_DBG, + "PROCESS_SCN source %s\n", source); + got_source = 1; + } + break; + case ISNS_ISCSI_SCN_BITMAP_ATTR_ID: + bcopy(tlv->attr_value, &bitmap, tlv->attr_len); + bitmap = ntohl(bitmap); + queue_prt(mgmtq, Q_ISNS_DBG, + "PROCESS_SCN bitmap %u\n", bitmap); + got_bitmap = 1; + break; + default: + queue_prt(mgmtq, Q_ISNS_DBG, + "PROCESS_SCN DEFAULT\n"); + break; + } + + if (got_source && !got_bitmap) { + queue_prt(mgmtq, Q_ISNS_DBG, + "process_scn: message out-of-order\n"); + return; + } + + if (got_source && got_bitmap) { + switch (bitmap) { + case ISNS_OBJ_ADDED: + case ISNS_OBJ_UPDATED: + queue_prt(mgmtq, Q_ISNS_DBG, + "PROCESS_SCN OBJ ADDED"); + isns_update(); + break; + case ISNS_OBJ_REMOVED: + queue_prt(mgmtq, Q_ISNS_DBG, + "PROCESS_SCN OBJ REMOVED"); + /* logout target */ + if (got_dest == 0) { + syslog(LOG_ALERT, + "ISNS protocol error\n"); + continue; + } + logout_targ(dest); + break; + default: + break; + } + + /* clear got_xxx */ + got_source = 0; + got_bitmap = 1; + } + + /* next attribute */ + cnt += ISNS_ATTR_SZ(tlv->attr_len); + ptr += ISNS_ATTR_SZ(tlv->attr_len); + } + queue_prt(mgmtq, Q_ISNS_DBG, "DONE PROCESS_SCN\n"); +} + +/* + * Process ESI requires a success response only + */ +static void +process_esi(int so, isns_pdu_t *esi) +{ + isns_rsp_t *cmd; + int pl_len; + + if (isns_create_pdu(ISNS_ESI_RSP, 0, (isns_pdu_t **)&cmd) != 0) { + return; + } + + pl_len = esi->payload_len + ISNS_STATUS_SZ; + if (pl_len > MAX_PDU_PAYLOAD_SZ) { + syslog(LOG_ALERT, "process_esi: payload size exceeded"); + isns_free_pdu(cmd); + return; + } + + /* change the xid to the request xid */ + cmd->xid = htons(esi->xid); + cmd->status = htonl(ISNS_RSP_SUCCESSFUL); + + /* copy original data */ + bcopy(esi->payload, cmd->data, esi->payload_len); + cmd->pdu_len = htons(pl_len); + + if (isns_send(so, (isns_pdu_t *)cmd) < 0) { + syslog(LOG_ALERT, "process_esi failed to isns_send"); + } + + isns_free_pdu(cmd); +} + +/* + * esi_scn_thr() is the thread creates an end point to receive and process + * ESI & SCN messages. This thread is created when isns_access is enabled + * and for the duration of the iscsi daemon + */ +static void +*esi_scn_thr(void *arg) +{ + struct sockaddr sa, *ai; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; + int so, fd; + socklen_t len; + char strport[NI_MAXSERV]; + int pf; + esi_scn_arg_t *args = (esi_scn_arg_t *)arg; + isns_pdu_t *scn = NULL; + + /* + * open isns server connect and determine which PF_INET + * to use + */ + if ((so = isns_open(args->server)) < 0) { + syslog(LOG_ERR, + "esi_scn_thr: isns server %s not found", args->server); + return (NULL); + } + len = sizeof (sa); + if (getsockname(so, &sa, &len) < 0) { + isns_close(so); + syslog(LOG_ALERT, "getsockname failed"); + return (NULL); + } + pf = sa.sa_family; + isns_close(so); + + if (pf != PF_INET && pf != PF_INET6) { + syslog(LOG_ERR, "esi_scn_thr: unknown domain type"); + return (NULL); + } + + /* + * create and bind SCN socket + * save the scn port info + */ + if ((so = socket(pf, SOCK_STREAM, 0)) == -1) { + syslog(LOG_ALERT, "socket failed"); + return (NULL); + } + + if (pf == PF_INET) { + bzero(&sin, sizeof (sin)); + sin.sin_family = PF_INET; + sin.sin_port = htons(0); + sin.sin_addr.s_addr = INADDR_ANY; + ai = (struct sockaddr *)&sin; + len = sizeof (sin); + } else { + bzero(&sin6, sizeof (sin6)); + sin6.sin6_family = PF_INET6; + sin6.sin6_port = htons(0); + sin6.sin6_addr = in6addr_any; + ai = (struct sockaddr *)&sin6; + len = sizeof (sin6); + } + + (void) setsockopt(so, SOL_SOCKET, SO_REUSEADDR, 0, 0); + + if (bind(so, ai, len) < 0) { + syslog(LOG_ALERT, "esi_scn_thr: bind failed"); + return (NULL); + } + + /* get scn port info */ + len = sizeof (sa); + if (getsockname(so, &sa, &len) < 0) { + syslog(LOG_ALERT, "getsockname failed"); + return (NULL); + } + if (getnameinfo(&sa, len, NULL, 0, strport, NI_MAXSERV, + NI_NUMERICSERV) != 0) { + syslog(LOG_ALERT, "getnameinfo failed"); + return (NULL); + } + scn_port = atoi(strport); + + sema_post(&isns_sema); + + if (listen(so, 5) < 0) { + syslog(LOG_ALERT, "esi_scn_thr: failed listen"); + return (NULL); + } + + /* listen for esi or scn messages */ + while (isns_shutdown == False) { + if ((fd = accept(so, &sa, &len)) < 0) { + syslog(LOG_ALERT, "esi_scn_thr: failed accept"); + continue; + } + + if (isns_recv(fd, (isns_rsp_t **)&scn) == 0) { + /* Just return success for ESI */ + switch (scn->func_id) { + case ISNS_ESI: + process_esi(fd, scn); + break; + case ISNS_SCN: + /* call the SCN process function */ + process_scn(fd, scn); + break; + default: + syslog(LOG_ERR, + "esi_scn_thr: Invalid funcid %d\n", + scn->func_id); + break; + } + /* free response resource */ + isns_free_pdu(scn); + } else { + syslog(LOG_ALERT, "esi_scn_thr fails isns_recv "); + } + + close(fd); + } + return (NULL); +} + +/* + * Perform operation on all targets + */ +static int +isns_op_all(uint16_t op) +{ + int so; + tgt_node_t *tgt = NULL; + char *iname; + + if ((so = isns_open(isns_args.server)) == -1) { + syslog(LOG_ERR, "isns_reg failed"); + return (-1); + } + + while ((tgt = tgt_node_next(targets_config, XML_ELEMENT_TARG, tgt)) + != NULL) { + if (tgt_find_value_str(tgt, XML_ELEMENT_INAME, &iname) + == FALSE) { + continue; + } + + switch (op) { + case ISNS_DEV_DEREG: + if (isns_dev_attr_dereg(so, iname) == -1) { + syslog(LOG_ERR, + "ISNS de-register failed\n"); + } + num_reg = 0; + break; + case ISNS_SCN_DEREG: + if (isns_scn_dereg(so, iname) == -1) { + syslog(LOG_ERR, + "ISNS SCN de-register failed\n"); + } + break; + case ISNS_SCN_REG: + if (isns_scn_reg(so, iname) == -1) { + syslog(LOG_ERR, + "ISNS SCN register failed\n"); + } + break; + case ISNS_TGT_LOGOUT: + logout_targ(iname); + break; + default: + break; + } + + free(iname); + } + + isns_close(so); + return (0); +} + +/* + * isns_init() needs to be call before all ISNS operations. + * Save the isns_server & entity name. + * Start esi_scn_thr to receive ESI & SCN messages + */ +int +isns_init(target_queue_t *q) +{ + char *isns_srv, *isns_port; + + if (q != NULL) + mgmtq = q; + + if (isns_enabled() == False) + return (0); + + /* initialize */ + sema_init(&isns_sema, 0, USYNC_THREAD, NULL); + + /* get isns server info */ + tgt_find_value_str(main_config, XML_ELEMENT_ISNS_SERV, &isns_srv); + isns_port = strchr(isns_srv, ':'); + if (isns_port == NULL) { + isns_args.isns_port = ISNS_DEFAULT_SERVER_PORT; + } else { + isns_args.isns_port = strtoul(isns_port + 1, NULL, 0); + if (isns_args.isns_port == 0) { + isns_args.isns_port = ISNS_DEFAULT_SERVER_PORT; + } + *isns_port = '\0'; + } + bcopy(isns_srv, isns_args.server, MAXHOSTNAMELEN); + free(isns_srv); + + /* get local hostname for entity usage */ + if ((gethostname(isns_args.entity, MAXHOSTNAMELEN) < 0) || + (get_ip_addr(isns_args.entity, &eid_ip) < 0)) { + syslog(LOG_ERR, "ISNS fails to get ENTITY properties"); + return (-1); + } + + if (pthread_create(&scn_tid, NULL, esi_scn_thr, (void *)&isns_args) != + 0) { + syslog(LOG_ALERT, "isns_init failed to pthread_create"); + return (-1); + } + + if (sema_wait(&isns_sema) != 0) { + syslog(LOG_ERR, "sema_wait error\n"); + } + + isns_initialized = True; + + /* + * register all targets, what happens if no targets are created yet? + * this should not be a failure, when new target gets created, update + * gets call. + * what if SCN register fails? + */ + if (isns_reg_all() == 0) { + /* scn register all targets */ + if (isns_op_all(ISNS_SCN_REG) != 0) { + syslog(LOG_ERR, "SCN registrations failed\n"); + isns_op_all(ISNS_DEV_DEREG); + return (-1); + } + } + + return (0); +} + +/* + * isns_update gets call on modify_admin, this is changes to + * isns access and/or isns server + */ +int +isns_update() +{ + char *isns_srv = NULL, *isns_port; + + if (isns_initialized == False) { + /* isns enabled after iscsi started */ + if (isns_enabled() == True) { + isns_init(NULL); + } + return (0); + } + + /* + * isns is disabled after enabled, + * log off all targets and fini isns service + */ + if (isns_initialized == True && isns_enabled() == False) { + isns_fini(); + return (0); + } + + /* + * isns is already initialized and isns_access is still enabled, + * let's update the isns_server name + */ + tgt_find_value_str(main_config, XML_ELEMENT_ISNS_SERV, &isns_srv); + isns_port = strchr(isns_srv, ':'); + if (isns_port == NULL) { + isns_args.isns_port = ISNS_DEFAULT_SERVER_PORT; + } else { + isns_args.isns_port = strtoul(isns_port + 1, NULL, 0); + if (isns_args.isns_port == 0) { + isns_args.isns_port = ISNS_DEFAULT_SERVER_PORT; + } + *isns_port = '\0'; + } + + /* + * If the iSNS server is different, then dregister all from + * the old iSNS server, and register all to the new iSNS server + */ + + if (strcmp(isns_srv, isns_args.server) != 0) { + /* de-reg from old iSNS server */ + (void) isns_dereg_all(); + + bcopy(isns_srv, isns_args.server, MAXHOSTNAMELEN); + + if (isns_reg_all() == 0) { + /* scn register all targets */ + if (isns_op_all(ISNS_SCN_REG) != 0) { + syslog(LOG_ERR, "SCN registrations failed\n"); + isns_op_all(ISNS_DEV_DEREG); + return (-1); + } + } + } + if (isns_srv != NULL) + free(isns_srv); + + return (0); +} + +/* + * isns_fini is called when isns access is disable + */ +void +isns_fini() +{ + /* + * de-register all targets 1st, this prevents initiator from + * logging back in + */ + isns_op_all(ISNS_SCN_DEREG); + isns_op_all(ISNS_DEV_DEREG); + + /* log off all targets */ + isns_op_all(ISNS_TGT_LOGOUT); + + isns_initialized = False; +} + +static int +get_ip_addr(char *node, ip_t *sa) +{ + struct addrinfo *ai = NULL, *aip; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + + if (getaddrinfo(node, NULL, NULL, &ai) != 0) { + syslog(LOG_ALERT, "ISNS server not found"); + return (-1); + } + + bzero(sa, sizeof (ip_t)); + aip = ai; + do { + sa->ai_addrlen = aip->ai_addrlen; + sa->pf_family = aip->ai_family; + switch (aip->ai_family) { + case PF_INET: + /* LINTED */ + sin = (struct sockaddr_in *)aip->ai_addr; + sa->ip_len = sizeof (in_addr_t); + bcopy(&sin->sin_addr, (void *)&sa->ip_adr.in, + sa->ip_len); + freeaddrinfo(ai); + return (0); + case PF_INET6: + /* LINTED */ + sin6 = (struct sockaddr_in6 *)aip->ai_addr; + sa->ip_len = sizeof (in6_addr_t); + bcopy(&sin6->sin6_addr, &sa->ip_adr.in6, + sa->ip_len); + freeaddrinfo(ai); + return (0); + default: + continue; + } + } while ((aip = aip->ai_next) != NULL); + + freeaddrinfo(ai); + return (-1); +} + +/* + * Process isns response, need to verify same transaction id, func_id + * as the isns command, the isns command is in network byte order, + * the isns response is in host byte order + */ +static int +process_rsp(isns_pdu_t *cmd, isns_rsp_t *rsp) +{ + queue_prt(mgmtq, Q_ISNS_DBG, "PROCESS_RSP"); + /* + * Process responses: + * -verify sucessful response + * -verify match xid + * -process operating attributes + * For DevAttrReg & DevAttrQry and most isns command, + * the response func_id is command_func_id | 0x8000. + */ + rsp->status = ntohl(rsp->status); + if (rsp->status != ISNS_RSP_SUCCESSFUL || + rsp->xid != ntohs(cmd->xid) || + rsp->func_id != (ntohs(cmd->func_id) | 0x8000)) { + queue_prt(mgmtq, Q_ISNS_DBG, + "cmd failed with: status= %d xid= %d %d "\ + "response attribute %x\n", rsp->status, rsp->xid,\ + ntohs(cmd->xid), rsp->func_id); + return (-1); + } + + return (0); +} + +/* + * DevAttrDereg + */ +static int +isns_dev_attr_dereg(int so, char *node) +{ + isns_pdu_t *cmd = NULL; + isns_rsp_t *rsp = NULL; + uint32_t flags = 0; + int ret = -1; + + queue_prt(mgmtq, Q_ISNS_DBG, "ISNS_DEV_ATTR_DEREG"); + + if (isns_create_pdu(ISNS_DEV_DEREG, flags, &cmd) != 0) { + return (-1); + } + + /* add source attribute */ + if (isns_append_attr(cmd, ISNS_ISCSI_NAME_ATTR_ID, + STRLEN(node), node, 0) != 0) { + goto error; + } + + /* add delimiter */ + if (isns_append_attr(cmd, ISNS_DELIMITER_ATTR_ID, 0, NULL, 0) != 0) { + goto error; + } + + /* add operation attributes */ + if (isns_append_attr(cmd, ISNS_ISCSI_NAME_ATTR_ID, + STRLEN(node), node, 0) != 0) { + goto error; + } + + /* send pdu */ + if (isns_send(so, cmd) == -1) { + syslog(LOG_ERR, "isns_dev_attr_dereg fails isns_send"); + goto error; + } + + /* get isns response */ + if (isns_recv(so, &rsp) == -1) { + syslog(LOG_ERR, "isns_dev_attr_dereg fails isns_recv "); + goto error; + } + + /* process response */ + if (process_rsp(cmd, rsp) == 0) { + num_reg--; + ret = 0; + } + +error: + /* Free all resouces here */ + if (cmd) + isns_free_pdu(cmd); + if (rsp) + isns_free_pdu(rsp); + return (ret); +} + +/* + * Register a new node, need to find another node that is already registered + * DevAttrReg + * RFC 4171 Section 5.6.5.5 indicated SCN-port-tag (23) needed to be + * included in the registration + * Also need to register ESI-port-tag (20) see Section 6.3.5 + */ +static int +isns_dev_attr_reg(int so, tgt_node_t *tgt, char *node, char *alias) +{ + isns_pdu_t *cmd = NULL; + isns_rsp_t *rsp = NULL; + uint32_t flags = 0; + int ret = 0; + Boolean_t found = False; + tgt_node_t *src = NULL; + char *src_nm = NULL; + + queue_prt(mgmtq, Q_ISNS_DBG, "ISNS_DEV_ATTR_REG"); + + if ((so = isns_open(isns_args.server)) == -1) { + return (-1); + } + + if (num_reg == 0) { + flags |= ISNS_FLAG_REPLACE_REG; + } + + if (isns_create_pdu(ISNS_DEV_ATTR_REG, flags, &cmd) != 0) { + return (-1); + } + + if (num_reg == 0) { + /* add new node to source attribute */ + if (isns_append_attr(cmd, ISNS_ISCSI_NAME_ATTR_ID, + STRLEN(node), node, 0) != 0) { + goto error; + } + } else { + /* find a registered node to use */ + do { + src = find_next_tgt(src, &src_nm); + if (src == NULL) { + syslog(LOG_ALERT, "ISNS out of sync\n"); + goto error; + } + if (tgt == src) { + free(src_nm); + continue; + } else { + found = True; + } + } while (found == False); + + if (isns_append_attr(cmd, ISNS_ISCSI_NAME_ATTR_ID, + STRLEN(src_nm), src_nm, 0) != 0) { + goto error; + } + } + + /* add message key attribute */ + if (isns_append_attr(cmd, ISNS_EID_ATTR_ID, + STRLEN(isns_args.entity), isns_args.entity, 0) != 0) { + goto error; + } + + /* add delimiter */ + if (isns_append_attr(cmd, ISNS_DELIMITER_ATTR_ID, 0, NULL, 0) != 0) { + goto error; + } + + /* add operation attributes */ + + /* entity id */ + if (isns_append_attr(cmd, ISNS_EID_ATTR_ID, + STRLEN(isns_args.entity), isns_args.entity, 0) != 0) { + goto error; + } + + /* entity type */ + if (isns_append_attr(cmd, ISNS_ENTITY_PROTOCOL_ATTR_ID, + ISNS_ENTITY_TYP_SZ, NULL, ISNS_ENTITY_PROTOCOL_ISCSI) != 0) { + goto error; + } + + /* + * Register entity portal properties the 1st time + */ + if (num_reg == 0) { + /* portal ip-addr */ + if (isns_append_attr(cmd, ISNS_PORTAL_IP_ADDR_ATTR_ID, + eid_ip.ai_addrlen, (void *)&eid_ip.ip_adr, + eid_ip.ip_len) != 0) { + goto error; + } + + /* portal port */ + if (isns_append_attr(cmd, ISNS_PORTAL_PORT_ATTR_ID, + ISNS_PORT_SZ, NULL, iscsi_port) != 0) { + goto error; + } + + /* ESI interval */ + if (isns_append_attr(cmd, ISNS_ESI_INTERVAL_ATTR_ID, + ISNS_ESI_TICK_SZ, NULL, 10) != 0) { + goto error; + } + + /* scn port */ + if (isns_append_attr(cmd, ISNS_SCN_PORT_ATTR_ID, + ISNS_PORT_SZ, NULL, scn_port) != 0) { + goto error; + } + + /* esi port */ + if (isns_append_attr(cmd, ISNS_ESI_PORT_ATTR_ID, + ISNS_PORT_SZ, NULL, scn_port) != 0) { + goto error; + } + } + + /* iscsi node name */ + if (isns_append_attr(cmd, ISNS_ISCSI_NAME_ATTR_ID, + STRLEN(node), node, 0) != 0) { + goto error; + } + + /* iscsi node type */ + if (isns_append_attr(cmd, ISNS_ISCSI_NODE_TYPE_ATTR_ID, + ISNS_NODE_TYP_SZ, NULL, ISNS_TARGET_NODE_TYPE) != 0) { + goto error; + } + + /* iscsi node alias */ + if (isns_append_attr(cmd, ISNS_ISCSI_ALIAS_ATTR_ID, + STRLEN(alias), alias, 0) != 0) { + goto error; + } + + /* PGT */ + if (append_tpgt(tgt, cmd) != 0) { + goto error; + } + + /* send pdu */ + if (isns_send(so, cmd) == -1) { + goto error; + } + + /* get isns response */ + if (isns_recv(so, &rsp) == -1) { + goto error; + } + + /* process response */ + if ((ret = process_rsp(cmd, rsp)) == 0) { + num_reg++; + } + +error: + /* Free all resouces here */ + if (cmd) + isns_free_pdu(cmd); + if (rsp) + isns_free_pdu(rsp); + if (src_nm) + free(src_nm); + return (ret); +} + +/* + * DevAttrQry for iscsi initiator + * See RFC 4171 Sect. 5.6.5.2 for query detail + */ +static int +isns_dev_attr_qry(int so, char *target, char *initiator) +{ + isns_pdu_t *cmd; + isns_rsp_t *rsp; + uint32_t flags = 0; + int ret = -1; + size_t remain; + isns_tlv_t *tlv; + uint8_t *ptr; + + queue_prt(mgmtq, Q_ISNS_DBG, "ISNS_DEV_ATTR_QRY"); + + if (isns_create_pdu(ISNS_DEV_ATTR_QRY, flags, &cmd) != 0) { + return (-1); + } + + /* source attribute */ + if (isns_append_attr(cmd, ISNS_ISCSI_NAME_ATTR_ID, + STRLEN(target), target, 0) == -1) { + goto error; + } + + /* message key attribute */ + /* iscsi initiator node type */ + if (isns_append_attr(cmd, ISNS_ISCSI_NODE_TYPE_ATTR_ID, + ISNS_NODE_TYP_SZ, NULL, ISNS_INITIATOR_NODE_TYPE) == -1) { + goto error; + } + + /* delimiter */ + if (isns_append_attr(cmd, ISNS_DELIMITER_ATTR_ID, 0, NULL, 0) == -1) { + goto error; + } + + /* + * operating attributes + * Query Iscsi initiator with zero length TLV operating + * attribute + */ + + /* iscsi name */ + if (isns_append_attr(cmd, ISNS_ISCSI_NAME_ATTR_ID, + 0, NULL, 0) != 0) { + goto error; + } + + if (isns_send(so, cmd) == -1) { + syslog(LOG_ERR, "isns_dev_attr_qry fails isns_send"); + goto error; + } + + /* recv response */ + if (isns_recv(so, &rsp) == -1) { + syslog(LOG_ERR, "isns_dev_attr_qry fails isns_recv "); + goto error; + } + + /* process response */ + if ((ret = process_rsp(cmd, rsp)) == 0) { + /* compare initiator name to the response, success if found */ + /* subtract out status word */ + remain = rsp->pdu_len - ISNS_STATUS_SZ; + ptr = rsp->data; + + while (remain > 0) { + /* LINTED */ + tlv = (isns_tlv_t *)ptr; + + /* debug only */ + print_ntoh_tlv(tlv); + + /* process tag-len-value */ + ntoh_tlv(tlv); + /* + * let's process the data, only interested + * in iscsi name, skip everything else for + * now. + */ + if (tlv->attr_id == ISNS_ISCSI_NAME_ATTR_ID) { + if (strncmp((char *)tlv->attr_value, initiator, + tlv->attr_len) == 0) { + break; + } + } + /* next tlv */ + remain -= ISNS_ATTR_SZ(tlv->attr_len); + ptr += ISNS_ATTR_SZ(tlv->attr_len); + } + ret = (remain > 0) ? 1 : 0; + } + +error: + if (cmd) + isns_free_pdu(cmd); + if (rsp) + isns_free_pdu(rsp); + return (ret); +} + +/* + * SCNReg + * See RFC 4171 Section 5.6.5.5 + */ +static int +isns_scn_reg(int so, char *node) +{ + isns_pdu_t *cmd; + isns_rsp_t *rsp; + uint32_t flags = 0; + uint32_t bitmap = 0; + int ret = -1; + + queue_prt(mgmtq, Q_ISNS_DBG, "ISNS_SCN_REG"); + + if (isns_create_pdu(ISNS_SCN_REG, flags, &cmd) != 0) { + return (-1); + } + + /* source attribute */ + if (isns_append_attr(cmd, ISNS_ISCSI_NAME_ATTR_ID, + STRLEN(node), node, 0) == -1) { + goto error; + } + + /* message key attribute */ + /* iscsi initiator node name */ + if (isns_append_attr(cmd, ISNS_ISCSI_NAME_ATTR_ID, + STRLEN(node), node, 0) != 0) { + goto error; + } + + /* delimiter */ + if (isns_append_attr(cmd, ISNS_DELIMITER_ATTR_ID, 0, NULL, 0) == -1) { + goto error; + } + + /* SCN bitmap */ + bitmap = ISNS_INIT_SELF_INFO_ONLY | ISNS_OBJ_REMOVED | + ISNS_OBJ_ADDED | ISNS_OBJ_UPDATED; + if (isns_append_attr(cmd, ISNS_ISCSI_SCN_BITMAP_ATTR_ID, + ISNS_SCN_BITMAP_SZ, NULL, bitmap) == -1) { + goto error; + } + + if (isns_send(so, cmd) == -1) { + syslog(LOG_ERR, "isns_scn_reg fails isns_send"); + goto error; + } + + if (isns_recv(so, &rsp) == -1) { + syslog(LOG_ERR, "isns_scn_reg fails isns_recv "); + goto error; + } + + /* process response */ + if (process_rsp(cmd, rsp) == 0) { + ret = 0; + } + +error: + if (cmd) + isns_free_pdu(cmd); + if (rsp) + isns_free_pdu(rsp); + return (ret); +} + + +/* + * SCNDereg + */ +static int +isns_scn_dereg(int so, char *node) +{ + isns_pdu_t *cmd = NULL; + isns_rsp_t *rsp = NULL; + uint32_t flags = 0; + int ret = -1; + + queue_prt(mgmtq, Q_ISNS_DBG, "ISNS_SCN_DEREG"); + + if (isns_create_pdu(ISNS_SCN_DEREG, flags, &cmd) != 0) { + return (-1); + } + + /* source attribute */ + if (isns_append_attr(cmd, ISNS_ISCSI_NAME_ATTR_ID, + STRLEN(node), node, 0) == -1) { + goto error; + } + + /* message key attribute */ + /* iscsi initiator node name */ + if (isns_append_attr(cmd, ISNS_ISCSI_NAME_ATTR_ID, + STRLEN(node), node, 0) != 0) { + goto error; + } + + if (isns_append_attr(cmd, ISNS_DELIMITER_ATTR_ID, 0, NULL, 0) == -1) { + goto error; + } + + if (isns_send(so, cmd) == -1) { + syslog(LOG_ERR, "isns_scn_reg fails isns_send"); + goto error; + } + + if (isns_recv(so, &rsp) == -1) { + syslog(LOG_ERR, "isns_scn_reg fails isns_recv "); + goto error; + } + + /* process response */ + if (process_rsp(cmd, rsp) == 0) { + ret = 0; + } + +error: + if (cmd) + isns_free_pdu(cmd); + if (rsp) + isns_free_pdu(rsp); + return (ret); +} + +/* + * isns_reg is called to register new target + */ +int +isns_reg(char *targ) +{ + int so; + tgt_node_t *tgt; + char *iqn; + + if ((so = isns_open(isns_args.server)) == -1) { + syslog(LOG_ERR, "isns_reg failed"); + return (-1); + } + + /* + * Open targets_config and devAttrReg all nodes + */ + if ((tgt = find_tgt_by_name(targ, &iqn)) != NULL) { + if (isns_dev_attr_reg(so, tgt, iqn, tgt->x_value) != 0) { + syslog(LOG_ALERT, "ISNS registration failed %s\n", + tgt->x_value); + } + if (isns_scn_reg(so, iqn) == -1) { + syslog(LOG_ERR, "ISNS SCN register failed\n"); + } + free(iqn); + } + + isns_close(so); + return (0); +} + + +/* + * Register all iscsi target nodes from the XML database + * Alway use the ISNS_FLAG_REPLACE_REG flag + */ +int +isns_reg_all() +{ + int so; + uint32_t flags = ISNS_FLAG_REPLACE_REG; + isns_pdu_t *cmd; + isns_rsp_t *rsp; + char *n = NULL; + char *a = NULL; + char alias[MAXNAMELEN]; + char iname[MAXNAMELEN]; + tgt_node_t *tgt = NULL; + int ret = -1; + int tgt_cnt = 0; + + /* + * get the 1st target and use it for the source attribute + */ + if ((tgt = tgt_node_next(targets_config, XML_ELEMENT_TARG, tgt)) + == NULL) { + return (0); + } + if (tgt->x_value == NULL) { + syslog(LOG_ALERT, "ISNS: target with NULL local name\n"); + return (-1); + } + if (tgt_find_value_str(tgt, XML_ELEMENT_INAME, &n) + == FALSE) { + syslog(LOG_ALERT, "ISNS: no XML_ELEMENT_INAME found\n"); + return (-1); + } + strcpy(iname, n); + free(n); + + if ((so = isns_open(isns_args.server)) == -1) { + syslog(LOG_ALERT, "ISNS: fails to connect to %s\n", + isns_args.server); + return (-1); + } + + if (isns_create_pdu(ISNS_DEV_ATTR_REG, flags, &cmd) != 0) { + goto error; + } + + /* source attribute */ + if (isns_append_attr(cmd, ISNS_ISCSI_NAME_ATTR_ID, + STRLEN(iname), iname, 0) != 0) { + goto error; + } + + /* add message key attribute */ + if (isns_append_attr(cmd, ISNS_EID_ATTR_ID, + STRLEN(isns_args.entity), isns_args.entity, 0) != 0) { + goto error; + } + + /* add delimiter */ + if (isns_append_attr(cmd, ISNS_DELIMITER_ATTR_ID, 0, NULL, 0) != 0) { + goto error; + } + + /* entity id */ + if (isns_append_attr(cmd, ISNS_EID_ATTR_ID, + STRLEN(isns_args.entity), isns_args.entity, 0) != 0) { + goto error; + } + + /* entity type */ + if (isns_append_attr(cmd, ISNS_ENTITY_PROTOCOL_ATTR_ID, + ISNS_ENTITY_TYP_SZ, NULL, ISNS_ENTITY_PROTOCOL_ISCSI) != 0) { + goto error; + } + + /* portal ip-addr */ + if (isns_append_attr(cmd, ISNS_PORTAL_IP_ADDR_ATTR_ID, + eid_ip.ai_addrlen, (void *)&eid_ip.ip_adr, + eid_ip.ip_len) != 0) { + goto error; + } + + /* portal port */ + if (isns_append_attr(cmd, ISNS_PORTAL_PORT_ATTR_ID, + ISNS_PORT_SZ, NULL, iscsi_port) != 0) { + goto error; + } + + /* ESI interval */ + if (isns_append_attr(cmd, ISNS_ESI_INTERVAL_ATTR_ID, + ISNS_ESI_TICK_SZ, NULL, 10) != 0) { + goto error; + } + + + /* scn port */ + if (isns_append_attr(cmd, ISNS_SCN_PORT_ATTR_ID, + ISNS_PORT_SZ, NULL, scn_port) != 0) { + goto error; + } + + /* esi port */ + if (isns_append_attr(cmd, ISNS_ESI_PORT_ATTR_ID, + ISNS_PORT_SZ, NULL, scn_port) != 0) { + goto error; + } + + /* + * Open targets_config and devAttrReg all nodes + */ + tgt = NULL; + while ((tgt = tgt_node_next(targets_config, XML_ELEMENT_TARG, tgt)) + != NULL) { + if (tgt->x_value == NULL) { + syslog(LOG_ALERT, "ISNS: target with NULL name\n"); + continue; + } + /* use this value as alias if alias is not set */ + strcpy(alias, tgt->x_value); + + if (tgt_find_value_str(tgt, XML_ELEMENT_INAME, &n) + == FALSE) { + continue; + } + strcpy(iname, n); + free(n); + + /* find alias */ + if (tgt_find_value_str(tgt, XML_ELEMENT_ALIAS, &a) + == TRUE) { + strcpy(alias, a); + free(a); + } + + tgt_cnt++; /* increment target count */ + + /* operation attributes */ + if (isns_append_attr(cmd, ISNS_ISCSI_NAME_ATTR_ID, + STRLEN(iname), iname, 0) != 0) { + goto error; + } + if (isns_append_attr(cmd, ISNS_ISCSI_NODE_TYPE_ATTR_ID, + 4, NULL, ISNS_TARGET_NODE_TYPE) != 0) { + goto error; + } + if (isns_append_attr(cmd, ISNS_ISCSI_ALIAS_ATTR_ID, + STRLEN(alias), alias, 0) != 0) { + goto error; + } + + if (append_tpgt(tgt, cmd) != 0) { + goto error; + } + + } + + /* send pdu */ + if (isns_send(so, cmd) == -1) { + goto error; + } + + /* get isns response */ + if (isns_recv(so, &rsp) == -1) { + goto error; + } + + /* process response */ + if (process_rsp(cmd, rsp) == 0) { + ret = 0; + num_reg = tgt_cnt; + queue_prt(mgmtq, Q_ISNS_DBG, "DevAttrRegAll successful"); + } else { + syslog(LOG_ALERT, "DevAttrReg failed"); + } + +error: + if (cmd) + isns_free_pdu(cmd); + if (rsp) + isns_free_pdu(rsp); + isns_close(so); + return (ret); +} + +/* + * Deregister an iscsi target node + */ +int +isns_dereg(char *name) +{ + int so; + int ret; + + if ((so = isns_open(isns_args.server)) == -1) { + return (-1); + } + + ret = isns_dev_attr_dereg(so, name); + + isns_close(so); + return (ret); +} + +/* + * Update an existing iscsi target property + */ +int +isns_dev_update(char *targ, uint32_t mods) +{ + int so; + int flags = 0; /* update only */ + char *iname = NULL; + char *dummy = NULL; + char alias[MAXNAMELEN]; + tgt_node_t *tgt = NULL; + isns_pdu_t *cmd; + isns_rsp_t *rsp; + int ret = -1; + + if (mods == 0) + return (0); + + if ((tgt = find_tgt_by_name(targ, &iname)) != NULL) { + if (tgt_find_value_str(tgt, XML_ELEMENT_ALIAS, &dummy) == + True) { + strcpy(alias, dummy); + free(dummy); + } else + strcpy(alias, tgt->x_value); + + if ((so = isns_open(isns_args.server)) < 0) { + goto error; + } + + if (isns_create_pdu(ISNS_DEV_ATTR_REG, flags, &cmd)) { + goto error; + } + /* source attr, msg key, delimiter */ + if (isns_append_attr(cmd, ISNS_ISCSI_NAME_ATTR_ID, + STRLEN(iname), iname, 0) != 0) { + goto error; + } + if (isns_append_attr(cmd, ISNS_EID_ATTR_ID, + STRLEN(isns_args.entity), isns_args.entity, 0) != 0) { + goto error; + } + if (isns_append_attr(cmd, ISNS_DELIMITER_ATTR_ID, 0, NULL, 0) + != 0) { + goto error; + } + + /* + * get current operating attributes, alias & portal group + * objects, these should be the only things that get change + */ + isns_append_attr(cmd, ISNS_ISCSI_NAME_ATTR_ID, STRLEN(iname), + iname, 0); + isns_append_attr(cmd, ISNS_ISCSI_NODE_TYPE_ATTR_ID, + ISNS_NODE_TYP_SZ, NULL, ISNS_TARGET_NODE_TYPE); + + if (mods & ISNS_MOD_ALIAS) + if (isns_append_attr(cmd, ISNS_ISCSI_ALIAS_ATTR_ID, + STRLEN(alias), alias, 0) != 0) { + goto error; + } + + if (mods & ISNS_MOD_TPGT) + if (append_tpgt(tgt, cmd) != 0) { + goto error; + } + + if (isns_send(so, (isns_pdu_t *)cmd) < 0) { + goto error; + } + + if (isns_recv(so, &rsp) == -1) { + goto error; + } + + /* process response, if failed do a isns_reg_all */ + if ((ret = process_rsp(cmd, rsp)) == -1) { + if (isns_reg_all() != 0 || isns_scn_reg_all() != 0) { + syslog(LOG_ALERT, "ISNS register failed\n"); + goto error; + } + ret = 0; + } else { + if (isns_scn_reg(so, iname) == -1) { + syslog(LOG_ERR, "ISNS SCN register failed\n"); + goto error; + } + ret = 0; + } + } else { + syslog(LOG_ERR, "ISNS: fails to update target %s\n", alias); + } + +error: + if (cmd) + isns_free_pdu(cmd); + if (rsp) + isns_free_pdu(rsp); + if (iname) + free(iname); + isns_close(so); + return (ret); +} + + +/* + * Deregister all iscsi target nodes from the XML database + */ +int +isns_dereg_all() +{ + return (isns_op_all(ISNS_DEV_DEREG)); +} + +int +isns_scn_reg_all() +{ + return (isns_op_all(ISNS_SCN_REG)); +} + +int +isns_scn_dereg_all() +{ + return (isns_op_all(ISNS_SCN_DEREG)); +} + +/* + * Query an iscsi initiator node + */ +Boolean_t +isns_qry_initiator(char *target, char *initiator) +{ + int so; + int ret; + + if ((so = isns_open(isns_args.server)) == -1) { + syslog(LOG_ERR, "isns_qry failed"); + return (-1); + } + + ret = isns_dev_attr_qry(so, target, initiator); + + isns_close(so); + return (ret == 1 ? True : False); +} + +Boolean_t +isns_enabled() +{ + Boolean_t isns_access = False; + char *isns_srv = NULL; + + (void) tgt_find_value_boolean(main_config, XML_ELEMENT_ISNS_ACCESS, + &isns_access); + /* get isns server info */ + if (isns_access == True) { + if (tgt_find_value_str(main_config, XML_ELEMENT_ISNS_SERV, + &isns_srv) == True) { + free(isns_srv); + return (True); + } + } + return (False); +}