Mercurial > illumos > onarm
view usr/src/cmd/iscsi/iscsitgtd/isns.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 source
/* * 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 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "@(#)isns.c 1.1 07/05/29 SMI" #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <strings.h> #include <syslog.h> #include <ctype.h> #include <pthread.h> #include <netdb.h> #include <libintl.h> #include <errno.h> #include <assert.h> #include <sys/byteorder.h> #include "isns_protocol.h" #include "isns_client.h" #include "queue.h" extern target_queue_t *mgmtq; static uint16_t xid = 0; static pthread_mutex_t xid_lock = PTHREAD_MUTEX_INITIALIZER; /* * more than 1 processes can accessing get_xid */ static uint16_t get_xid() { uint16_t tmp; pthread_mutex_lock(&xid_lock); tmp = xid++; pthread_mutex_unlock(&xid_lock); return (tmp); } void ntoh_tlv(isns_tlv_t *tlv) { uint32_t val; tlv->attr_id = ntohl(tlv->attr_id); tlv->attr_len = ntohl(tlv->attr_len); switch (tlv->attr_id) { case ISNS_DELIMITER_ATTR_ID: break; case ISNS_ISCSI_NAME_ATTR_ID: case ISNS_EID_ATTR_ID: case ISNS_ISCSI_ALIAS_ATTR_ID: case ISNS_PORTAL_NAME_ATTR_ID: case ISNS_PG_ISCSI_NAME_ATTR_ID: break; case ISNS_PORTAL_IP_ADDR_ATTR_ID: case ISNS_PG_PORTAL_IP_ADDR_ATTR_ID: bcopy(tlv->attr_value, &val, 4); *tlv->attr_value = ntohl(val); break; default: switch (tlv->attr_len) { case 4: val = ntohl( (uint32_t)(*tlv->attr_value)); bcopy(&val, tlv->attr_value, 4); break; default: break; } break; } } /* * print_ntoh_tlv print network byte order tag-length-value attribute */ void print_ntoh_tlv(isns_tlv_t *tlv) { uint32_t tag, len, val, pf_type; char buf[256]; struct sockaddr_in6 sin6; tag = ntohl(tlv->attr_id); len = ntohl(tlv->attr_len); if (len == 0) { queue_prt(mgmtq, Q_ISNS_DBG, "Zero length tag: %d\n", tag); return; } switch (tag) { case ISNS_DELIMITER_ATTR_ID: break; case ISNS_ISCSI_NAME_ATTR_ID: case ISNS_EID_ATTR_ID: case ISNS_ISCSI_ALIAS_ATTR_ID: case ISNS_PORTAL_NAME_ATTR_ID: case ISNS_PG_ISCSI_NAME_ATTR_ID: queue_prt(mgmtq, Q_ISNS_DBG, "Tag %d: Value: %s\n", tag, tlv->attr_value); break; case ISNS_PORTAL_IP_ADDR_ATTR_ID: case ISNS_PG_PORTAL_IP_ADDR_ATTR_ID: bcopy(tlv->attr_value, &pf_type, 4); pf_type = ntohl(pf_type); pf_type = (pf_type == sizeof (in6_addr_t)) ? PF_INET6 : PF_INET; switch (pf_type) { case PF_INET: /* RFC2372 IPv4 mapped IPv6 address */ if (inet_ntop(pf_type, (void *)&tlv->attr_value[12], buf, 256) == NULL) { syslog(LOG_ERR, "inet_ntop failed"); break; } queue_prt(mgmtq, Q_ISNS_DBG, "IP_ADDR %s\n", buf); break; case PF_INET6: bcopy(tlv->attr_value, &sin6, sizeof (struct sockaddr_in6)); inet_ntop(pf_type, (void *)&sin6.sin6_addr, buf, 256); break; default: queue_prt(mgmtq, Q_ISNS_DBG, "unknown pf_type\n"); break; } break; default: switch (len) { case 4: bcopy(tlv->attr_value, &val, 4); val = ntohl(val); queue_prt(mgmtq, Q_ISNS_DBG, "Tag: %d Value: %ld\n", tag, val); break; default: break; } break; } } void print_attr(isns_tlv_t *attr, void *pval, uint32_t ival) { uint32_t tag = ntohl(attr->attr_id); uint32_t len = ntohl(attr->attr_len); uint32_t pf_type; char buf[256]; queue_prt(mgmtq, Q_ISNS_DBG, "Tag: %d Length: %d\n", tag, len); switch (tag) { case ISNS_DELIMITER_ATTR_ID: break; case ISNS_ISCSI_NAME_ATTR_ID: case ISNS_EID_ATTR_ID: case ISNS_ISCSI_ALIAS_ATTR_ID: case ISNS_PORTAL_NAME_ATTR_ID: case ISNS_PG_ISCSI_NAME_ATTR_ID: if (len && pval != NULL) { queue_prt(mgmtq, Q_ISNS_DBG, "Value: %s\n", attr->attr_value); } break; case ISNS_PORTAL_IP_ADDR_ATTR_ID: case ISNS_PG_PORTAL_IP_ADDR_ATTR_ID: if (len) { pf_type = (ival == sizeof (in6_addr_t)) ? PF_INET6 : PF_INET; inet_ntop(pf_type, pval, buf, 256); queue_prt(mgmtq, Q_ISNS_DBG, "IP_ADDR %s\n", buf); } break; default: switch (len) { case 4: queue_prt(mgmtq, Q_ISNS_DBG, "Value: %d\n", ntohl(*attr->attr_value)); break; default: break; } break; } } void print_isns_hdr(isns_hdr_t *hdr) { queue_prt(mgmtq, Q_ISNS_DBG, "hdr->version %d\n", hdr->version); queue_prt(mgmtq, Q_ISNS_DBG, "hdr->func_id %x\n", hdr->func_id); queue_prt(mgmtq, Q_ISNS_DBG, "hdr->pdu_len %d\n", hdr->pdu_len); queue_prt(mgmtq, Q_ISNS_DBG, "hdr->flags %x\n", hdr->flags); queue_prt(mgmtq, Q_ISNS_DBG, "hdr->xid %d\n", hdr->xid); queue_prt(mgmtq, Q_ISNS_DBG, "hdr->seqid %d\n", hdr->seqid); } void ntoh_isns_hdr(isns_hdr_t *hdr) { hdr->version = ntohs(hdr->version); hdr->func_id = ntohs(hdr->func_id); hdr->pdu_len = ntohs(hdr->pdu_len); hdr->flags = ntohs(hdr->flags); hdr->xid = ntohs(hdr->xid); hdr->seqid = ntohs(hdr->seqid); } int isns_append_attr(isns_pdu_t *pdu, uint32_t tag, uint32_t len, void *pval, uint32_t ival) { uint32_t val; uint32_t pad_len; uint16_t pdu_len; isns_tlv_t *attr; char *tlv; if (pdu == NULL) { syslog(LOG_ALERT, "NULL PDU\n"); return (-1); } /* get current pdu payload length */ pdu_len = ntohs(pdu->payload_len); /* pad 4 bytes alignment */ pad_len = PAD4(len); if ((pdu_len + pad_len) > MAX_PDU_PAYLOAD_SZ) { syslog(LOG_ALERT, "Exceeded PDU size\n"); return (-1); } if ((attr = (isns_tlv_t *)malloc(ISNS_ATTR_SZ(pad_len))) == NULL) { syslog(LOG_ALERT, "Malloc error"); return (-1); } bzero(attr, ISNS_ATTR_SZ(pad_len)); attr->attr_id = htonl(tag); attr->attr_len = htonl(pad_len); switch (tag) { case ISNS_DELIMITER_ATTR_ID: break; case ISNS_ISCSI_NAME_ATTR_ID: case ISNS_EID_ATTR_ID: case ISNS_ISCSI_ALIAS_ATTR_ID: case ISNS_PORTAL_NAME_ATTR_ID: case ISNS_PG_ISCSI_NAME_ATTR_ID: if (len && pval != NULL) { bcopy(pval, attr->attr_value, len); } break; case ISNS_PORTAL_IP_ADDR_ATTR_ID: case ISNS_PG_PORTAL_IP_ADDR_ATTR_ID: if (len && ival == sizeof (in_addr_t)) { /* IPv4 */ attr->attr_value[10] = 0xFF; attr->attr_value[11] = 0xFF; bcopy(pval, ((attr->attr_value) + 12), ival); } else if (len && ival == sizeof (in6_addr_t)) { /* IPv6 */ bcopy(pval, attr->attr_value, ival); } break; default: switch (len) { case 4: val = htonl(ival); bcopy(&val, attr->attr_value, 4); break; default: break; } break; } /* copy attribute to pdu */ tlv = (char *)pdu + ISNSP_HEADER_SIZE + pdu_len; bcopy(attr, tlv, ISNS_ATTR_SZ(pad_len)); pdu->payload_len = htons(pdu_len + ISNS_ATTR_SZ(pad_len)); /* debug only */ print_ntoh_tlv(attr); free(attr); return (0); } int isns_create_pdu(uint16_t func_id, uint32_t flags, isns_pdu_t **pdu) { size_t pdu_sz = MAX_PDU_SZ; if ((*pdu = (isns_pdu_t *)malloc(pdu_sz)) == NULL) { syslog(LOG_ERR, "isns_create_pdu malloc failure"); return (-1); } bzero(*pdu, pdu_sz); (*pdu)->payload_len = 0; (*pdu)->seq = 0; (*pdu)->xid = htons(get_xid()); (*pdu)->version = htons((uint16_t)ISNSP_VERSION); (*pdu)->func_id = htons((uint16_t)(func_id)); (*pdu)->flags = htons((uint16_t)(flags | ISNS_FLAG_CLIENT | ISNS_FLAG_FIRST_PDU | ISNS_FLAG_LAST_PDU)); return (0); } void isns_free_pdu(void *pdu) { free(pdu); } /* * Desc: Open connection to the isns server * Args: isns server name or isns server ip-addr * Return: -1 if open failed, descriptor to socket if open succeeded */ int isns_open(char *server) { struct addrinfo hints, *ai, *aip; struct sockaddr *sa; struct sockaddr_in sin; struct sockaddr_in6 sin6; size_t sa_len; int so; int ret; if (server == NULL) { syslog(LOG_ERR, "ISNS server ID required"); return (-1); } bzero(&hints, sizeof (struct addrinfo)); hints.ai_family = AF_UNSPEC; if (getaddrinfo(server, NULL, NULL, &ai) != 0) { syslog(LOG_ALERT, "ISNS server %s not found", server); return (-1); } aip = ai; do { so = socket(aip->ai_family, SOCK_STREAM, 0); if (so != -1) { sa_len = aip->ai_addrlen; switch (aip->ai_family) { case PF_INET: bzero(&sin, sizeof (struct sockaddr_in)); sa = (struct sockaddr *)&sin; bcopy(aip->ai_addr, sa, sa_len); sin.sin_port = htons( ISNS_DEFAULT_SERVER_PORT); break; case PF_INET6: bzero(&sin6, sizeof (struct sockaddr_in6)); sa = (struct sockaddr *)&sin6; bcopy(aip->ai_addr, sa, sa_len); sin6.sin6_port = htons(ISNS_DEFAULT_SERVER_PORT); break; default: continue; } while (((ret = connect(so, sa, sa_len)) == -1) && (errno == EINTR)) ; if (ret == 0) { freeaddrinfo(ai); return (so); } else { syslog(LOG_ALERT, "Connect failed"); close(so); } } } while ((aip = aip->ai_next) != NULL); if (ai != NULL) freeaddrinfo(ai); return (-1); } void isns_close(int so) { if (so) { close(so); } } /* * isns_send allocated pdu, caller needs to free pdu when done processing */ int isns_send(int so, isns_pdu_t *pdu) { size_t len; assert(pdu != NULL); len = ISNSP_HEADER_SIZE + ntohs(pdu->payload_len); if (send(so, pdu, len, 0) == -1) { syslog(LOG_ALERT, "isns_send failure"); return (-1); } return (0); } /* * Desc: isns_recv malloc memory for the isns response message, user needs * to isns_free() memory after process the response message. The * isns header is converted to host byte order, the remaining TLV * attributes can be converted using ntoh_tlv() */ int isns_recv(int so, isns_rsp_t **pdu) { isns_hdr_t hdr, hdr1; isns_rsp_t *rsp; uint8_t *ptr; size_t total_pdu_len; int len; uint16_t xid_x, func_id_x, seqid_x; boolean_t done; *pdu = NULL; total_pdu_len = 0; seqid_x = 0; done = FALSE; do { /* read pdu header 1st */ do { len = recv(so, &hdr1, ISNSP_HEADER_SIZE, MSG_WAITALL); } while ((len == -1) && (errno == EINTR)); if (len != ISNSP_HEADER_SIZE) { syslog(LOG_ALERT, "isns_recv fail to read header"); return (-1); } /* normalize the pdu header for processing */ bcopy(&hdr1, &hdr, sizeof (isns_hdr_t)); ntoh_isns_hdr(&hdr); if (IS_1ST_PDU(hdr.flags)) { if (hdr.seqid != 0) { syslog(LOG_ALERT, "ISNS out of sequence"); return (-1); } xid_x = hdr.xid; func_id_x = hdr.func_id; } if (IS_LAST_PDU(hdr.flags)) { done = TRUE; } /* verify seq, xid, func_id */ if (seqid_x != hdr.seqid) { syslog(LOG_ALERT, "ISNS out of sequence"); return (-1); } if (xid_x != hdr.xid || func_id_x != hdr.func_id) { syslog(LOG_ALERT, "Non matching xid or func_id"); return (-1); } ++seqid_x; /* next expected seqid */ /* malloc size + previous payload length */ if ((ptr = malloc(ISNSP_HEADER_SIZE + hdr.pdu_len + total_pdu_len)) == NULL) { syslog(LOG_ALERT, "Malloc failure"); return (-1); } bzero(ptr, ISNSP_HEADER_SIZE + hdr.pdu_len); if (hdr.seqid == 0) { *pdu = (void *)ptr; bcopy(&hdr1, ptr, ISNSP_HEADER_SIZE); ptr += ISNSP_HEADER_SIZE; if ((len = recv(so, ptr, hdr.pdu_len, MSG_WAITALL)) != hdr.pdu_len) { syslog(LOG_ERR, "isns_recv fail to read 1st payload"); free(*pdu); *pdu = NULL; return (-1); } } else { /* merge the pdu */ bcopy(*pdu, ptr, ISNSP_HEADER_SIZE + total_pdu_len); free(*pdu); *pdu = (void *)ptr; ptr += (ISNSP_HEADER_SIZE + total_pdu_len); if (recv(so, ptr, hdr.pdu_len, MSG_WAITALL) != hdr.pdu_len) { syslog(LOG_ERR, "isns_recv fail to read payload"); free(*pdu); *pdu = NULL; return (-1); } } total_pdu_len += hdr.pdu_len; } while (done == FALSE); /* normalize the response status */ rsp = (isns_rsp_t *)*pdu; rsp->pdu_len = htons(total_pdu_len); ntoh_isns_hdr((isns_hdr_t *)rsp); return (0); }