diff options
Diffstat (limited to 'usr/src/common/iscsi/utils.c')
-rw-r--r-- | usr/src/common/iscsi/utils.c | 317 |
1 files changed, 317 insertions, 0 deletions
diff --git a/usr/src/common/iscsi/utils.c b/usr/src/common/iscsi/utils.c new file mode 100644 index 0000000000..e1106be9fc --- /dev/null +++ b/usr/src/common/iscsi/utils.c @@ -0,0 +1,317 @@ +/* + * 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. + */ + +#include <sys/types.h> +#include <sys/socket.h> + + +#ifdef _KERNEL +#include <sys/sunddi.h> +#else +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> +#include <ctype.h> +#include <netinet/in.h> +#include <sys/utsname.h> + +/* + * NOTE: This routine is found in libnsl. There's apparently no prototype to + * be found in any of the header files in /usr/include so defining a prototype + * here to keep the compiler happy. + */ +int getdomainname(char *, int); + +static const char *iqn_template = "iqn.2004-02.%s"; +#endif + +#include <sys/scsi/adapters/iscsi_if.h> + +typedef struct utils_val_name { + int u_val; + char *u_name; +} utils_val_name_t; + +utils_val_name_t param_names[] = { + { ISCSI_LOGIN_PARAM_DATA_SEQUENCE_IN_ORDER, "Sequence In Order"}, + { ISCSI_LOGIN_PARAM_IMMEDIATE_DATA, "Immediate Data"}, + { ISCSI_LOGIN_PARAM_INITIAL_R2T, "Inital R2T"}, + { ISCSI_LOGIN_PARAM_DATA_PDU_IN_ORDER, "Data PDU In Order"}, + { ISCSI_LOGIN_PARAM_HEADER_DIGEST, "Header Digest"}, + { ISCSI_LOGIN_PARAM_DATA_DIGEST, "Data Digest"}, + { ISCSI_LOGIN_PARAM_DEFAULT_TIME_2_RETAIN, "Default Time To Retain"}, + { ISCSI_LOGIN_PARAM_DEFAULT_TIME_2_WAIT, "Default Time To Wait"}, + { ISCSI_LOGIN_PARAM_MAX_RECV_DATA_SEGMENT_LENGTH, + "Max Recv Data Segment Length"}, + { ISCSI_LOGIN_PARAM_FIRST_BURST_LENGTH, "First Burst Length"}, + { ISCSI_LOGIN_PARAM_MAX_BURST_LENGTH, "Max Burst Length"}, + { ISCSI_LOGIN_PARAM_MAX_CONNECTIONS, "Max Connections"}, + { ISCSI_LOGIN_PARAM_OUTSTANDING_R2T, "Outstanding R2T"}, + { ISCSI_LOGIN_PARAM_ERROR_RECOVERY_LEVEL, "Error Recovery Level"}, + { 0, NULL } +}; + +/* + * utils_map_param -- Given a parameter return it's ascii name + * + * This routine was created because previously an array contained in order + * the parameter names. Once or twice the parameters value changed which + * changed the order, but not the array. To avoid further confusion we'll + * do a simple lookup. This code is rarely called so it shouldn't be an + * issue. + */ +char * +utils_map_param(int p) +{ + utils_val_name_t *pn; + + for (pn = param_names; pn->u_name != NULL; pn++) + if (pn->u_val == p) + return (pn->u_name); + return (NULL); +} + +/* + * prt_bitmap -- print out ascii strings associated with bit numbers. + */ +char * +prt_bitmap(int bitmap, char *str, char *buf, int size) +{ + char *p = NULL; + char *start = buf; + int do_put = 0; + + /* + * The maximum space required will if the bitmap was all 1's which + * would cause the octal characters to be replaced by '|'. So make + * sure the buffer has enough space. + */ + if (size < strlen(str)) + return ("No room"); + + for (p = str; size--; p++) { + if (*p < 0x20) { + + /* + * if we have been putting out stuff add separator + */ + if (do_put) + *buf++ = '|'; + + do_put = ((1 << *p) & bitmap); + bitmap &= ~(1 << *p); + + } else if (do_put) + *buf++ = *p; + } + + /* ---- remove the last separator if it was added ---- */ + if ((buf > start) && (*(buf - 1) == '|')) + buf--; + *buf = '\0'; + return (start); +} + +/* + * parse_addr_port_tpgt - Used to parse addr, port and tpgt from string + * + * This function is used to parse addr, port and tpgt from a string. Callers + * of this function are the sendtargets and login redirection code. The + * caller must be aware that this function will modify the callers string + * to insert NULL terminators if required. Port and TPGT are optional. + */ +boolean_t +parse_addr_port_tpgt(char *in, char **addr, int *type, char **port, char **tpgt) +{ + char *t_port, *t_tpgt; + + /* default return values if requested */ + if (addr == NULL) { + return (B_FALSE); + } else { + *addr = NULL; + } + if (port != NULL) { + *port = NULL; + } + if (tpgt != NULL) { + *tpgt = NULL; + } + + /* extract ip or domain name */ + if (*in == '[') { + /* IPV6 */ + *type = AF_INET6; + *addr = ++in; + in = strchr(*addr, ']'); + if (in == NULL) + return (B_FALSE); + *in++ = '\0'; + } else { + /* IPV4 or domainname */ + *type = AF_INET; + *addr = in; + } + + /* extract port */ + if (port != NULL) { + t_port = strchr(in, ':'); + if (t_port != NULL) { + *t_port++ = '\0'; + *port = in = t_port; + } + } + + /* exact tpgt */ + if (tpgt != NULL) { + t_tpgt = strchr(in, ','); + if (t_tpgt != NULL) { + *t_tpgt++ = '\0'; + *tpgt = in = t_tpgt; + } + } + + return (B_TRUE); +} + +#ifndef _KERNEL +/* + * []--------------------------------------------------------------[] + * | reverse_fqdn -- given a fully qualified domain name reverse it | + * | | + * | The routine has the obvious problem that it can only handle a | + * | name with 5 or less dots. This needs to be fixed by counting | + * | the number of dots in the incoming name, calloc'ing an array | + * | of the appropriate size and then handling the pointers. | + * []--------------------------------------------------------------[] + */ +static boolean_t +/* LINTED E_FUNC_ARG_UNUSED for 3rd arg size */ +reverse_fqdn(const char *domain, char *buf, int size) +{ + char *ptrs[5]; + char *dp; + char *dp1; + char *p; + int v = 4; + + if ((dp = dp1 = malloc(strlen(domain) + 1)) == NULL) + return (B_FALSE); + (void) strcpy(dp, domain); + while ((p = (char *)strchr(dp, '.')) != NULL) { + *p = '\0'; + if (v < 0) { + free(dp1); + return (B_FALSE); + } + ptrs[v--] = dp; + dp = p + 1; + } + (void) strcpy(buf, dp); + for (v++; v < 5; v++) { + (void) strcat(buf, "."); + (void) strcat(buf, ptrs[v]); + } + free(dp1); + return (B_TRUE); +} + +/* + * []------------------------------------------------------------------[] + * | utils_iqn_create -- returns an iqn name for the machine | + * | | + * | The information found in the iqn is not correct. The year and | + * | date should be flexible. Currently this is hardwired to the | + * | current year and month of this project. | + * []------------------------------------------------------------------[] + */ +boolean_t +utils_iqn_create(char *iqn_buf, int size) +{ + struct utsname uts_info; + char domainname[256]; + char *temp = NULL; + char *p; + char *pmet = NULL; /* temp reversed .. get it */ + int len; + boolean_t rval = B_FALSE; /* Default */ + + if (uname(&uts_info) == -1) { + goto out; + } + + if (getdomainname(domainname, sizeof (domainname))) { + goto out; + } + + if ((temp = malloc(strlen(uts_info.nodename) + + strlen(domainname) + 2)) == NULL) { + goto out; + } + + /* + * getdomainname always returns something in the order of + * host.domainname so we need to skip over that portion of the + * host name because we don't care about it. + */ + if ((p = strchr(domainname, '.')) == NULL) + p = domainname; + else + p++; + + /* ---- Create Fully Qualified Domain Name ---- */ + (void) snprintf(temp, strlen(p), "%s.%s", uts_info.nodename, p); + + /* ---- According to the spec, names must be lower case ---- */ + for (p = temp; *p; p++) + if (isupper(*p)) + *p = tolower(*p); + + len = strlen(temp) + 1; + if ((pmet = malloc(len)) == NULL) { + goto out; + } + + if (reverse_fqdn(temp, pmet, len) == B_FALSE) { + goto out; + } + + /* + * Now use the template with the reversed domainname to create + * an iSCSI name using the IQN format. Only count it a success + * if the number of characters formated is less than the buffer + * size. + */ + if (snprintf(iqn_buf, size, iqn_template, pmet) <= size) + rval = B_TRUE; +out: + if (temp) + free(temp); + if (pmet) + free(pmet); + + return (rval); +} +#endif /* !_KERNEL */ |