1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <sys/types.h> 27 #include <sys/socket.h> 28 29 30 #ifdef _KERNEL 31 #include <sys/sunddi.h> 32 #else 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <strings.h> 36 #include <ctype.h> 37 #include <netinet/in.h> 38 #include <sys/utsname.h> 39 40 /* 41 * NOTE: This routine is found in libnsl. There's apparently no prototype to 42 * be found in any of the header files in /usr/include so defining a prototype 43 * here to keep the compiler happy. 44 */ 45 int getdomainname(char *, int); 46 47 static const char *iqn_template = "iqn.2004-02.%s"; 48 #endif 49 50 #include <sys/scsi/adapters/iscsi_if.h> 51 52 typedef struct utils_val_name { 53 int u_val; 54 char *u_name; 55 } utils_val_name_t; 56 57 utils_val_name_t param_names[] = { 58 { ISCSI_LOGIN_PARAM_DATA_SEQUENCE_IN_ORDER, "Sequence In Order"}, 59 { ISCSI_LOGIN_PARAM_IMMEDIATE_DATA, "Immediate Data"}, 60 { ISCSI_LOGIN_PARAM_INITIAL_R2T, "Inital R2T"}, 61 { ISCSI_LOGIN_PARAM_DATA_PDU_IN_ORDER, "Data PDU In Order"}, 62 { ISCSI_LOGIN_PARAM_HEADER_DIGEST, "Header Digest"}, 63 { ISCSI_LOGIN_PARAM_DATA_DIGEST, "Data Digest"}, 64 { ISCSI_LOGIN_PARAM_DEFAULT_TIME_2_RETAIN, "Default Time To Retain"}, 65 { ISCSI_LOGIN_PARAM_DEFAULT_TIME_2_WAIT, "Default Time To Wait"}, 66 { ISCSI_LOGIN_PARAM_MAX_RECV_DATA_SEGMENT_LENGTH, 67 "Max Recv Data Segment Length"}, 68 { ISCSI_LOGIN_PARAM_FIRST_BURST_LENGTH, "First Burst Length"}, 69 { ISCSI_LOGIN_PARAM_MAX_BURST_LENGTH, "Max Burst Length"}, 70 { ISCSI_LOGIN_PARAM_MAX_CONNECTIONS, "Max Connections"}, 71 { ISCSI_LOGIN_PARAM_OUTSTANDING_R2T, "Outstanding R2T"}, 72 { ISCSI_LOGIN_PARAM_ERROR_RECOVERY_LEVEL, "Error Recovery Level"}, 73 { 0, NULL } 74 }; 75 76 /* 77 * utils_map_param -- Given a parameter return it's ascii name 78 * 79 * This routine was created because previously an array contained in order 80 * the parameter names. Once or twice the parameters value changed which 81 * changed the order, but not the array. To avoid further confusion we'll 82 * do a simple lookup. This code is rarely called so it shouldn't be an 83 * issue. 84 */ 85 char * 86 utils_map_param(int p) 87 { 88 utils_val_name_t *pn; 89 90 for (pn = param_names; pn->u_name != NULL; pn++) 91 if (pn->u_val == p) 92 return (pn->u_name); 93 return (NULL); 94 } 95 96 /* 97 * prt_bitmap -- print out ascii strings associated with bit numbers. 98 */ 99 char * 100 prt_bitmap(int bitmap, char *str, char *buf, int size) 101 { 102 char *p = NULL; 103 char *start = buf; 104 int do_put = 0; 105 106 /* 107 * The maximum space required will if the bitmap was all 1's which 108 * would cause the octal characters to be replaced by '|'. So make 109 * sure the buffer has enough space. 110 */ 111 if (size < strlen(str)) 112 return ("No room"); 113 114 for (p = str; size--; p++) { 115 if (*p < 0x20) { 116 117 /* 118 * if we have been putting out stuff add separator 119 */ 120 if (do_put) 121 *buf++ = '|'; 122 123 do_put = ((1 << *p) & bitmap); 124 bitmap &= ~(1 << *p); 125 126 } else if (do_put) 127 *buf++ = *p; 128 } 129 130 /* ---- remove the last separator if it was added ---- */ 131 if ((buf > start) && (*(buf - 1) == '|')) 132 buf--; 133 *buf = '\0'; 134 return (start); 135 } 136 137 /* 138 * parse_addr_port_tpgt - Used to parse addr, port and tpgt from string 139 * 140 * This function is used to parse addr, port and tpgt from a string. Callers 141 * of this function are the sendtargets and login redirection code. The 142 * caller must be aware that this function will modify the callers string 143 * to insert NULL terminators if required. Port and TPGT are optional. 144 */ 145 boolean_t 146 parse_addr_port_tpgt(char *in, char **addr, int *type, char **port, char **tpgt) 147 { 148 char *t_port, *t_tpgt; 149 150 /* default return values if requested */ 151 if (addr == NULL) { 152 return (B_FALSE); 153 } else { 154 *addr = NULL; 155 } 156 if (port != NULL) { 157 *port = NULL; 158 } 159 if (tpgt != NULL) { 160 *tpgt = NULL; 161 } 162 163 /* extract ip or domain name */ 164 if (*in == '[') { 165 /* IPV6 */ 166 *type = AF_INET6; 167 *addr = ++in; 168 in = strchr(*addr, ']'); 169 if (in == NULL) 170 return (B_FALSE); 171 *in++ = '\0'; 172 } else { 173 /* IPV4 or domainname */ 174 *type = AF_INET; 175 *addr = in; 176 } 177 178 /* extract port */ 179 if (port != NULL) { 180 t_port = strchr(in, ':'); 181 if (t_port != NULL) { 182 *t_port++ = '\0'; 183 *port = in = t_port; 184 } 185 } 186 187 /* exact tpgt */ 188 if (tpgt != NULL) { 189 t_tpgt = strchr(in, ','); 190 if (t_tpgt != NULL) { 191 *t_tpgt++ = '\0'; 192 *tpgt = in = t_tpgt; 193 } 194 } 195 196 return (B_TRUE); 197 } 198 199 #ifndef _KERNEL 200 /* 201 * []--------------------------------------------------------------[] 202 * | reverse_fqdn -- given a fully qualified domain name reverse it | 203 * | | 204 * | The routine has the obvious problem that it can only handle a | 205 * | name with 5 or less dots. This needs to be fixed by counting | 206 * | the number of dots in the incoming name, calloc'ing an array | 207 * | of the appropriate size and then handling the pointers. | 208 * []--------------------------------------------------------------[] 209 */ 210 static boolean_t 211 /* LINTED E_FUNC_ARG_UNUSED for 3rd arg size */ 212 reverse_fqdn(const char *domain, char *buf, int size) 213 { 214 char *ptrs[5]; 215 char *dp; 216 char *dp1; 217 char *p; 218 int v = 4; 219 220 if ((dp = dp1 = malloc(strlen(domain) + 1)) == NULL) 221 return (B_FALSE); 222 (void) strcpy(dp, domain); 223 while ((p = (char *)strchr(dp, '.')) != NULL) { 224 *p = '\0'; 225 if (v < 0) { 226 free(dp1); 227 return (B_FALSE); 228 } 229 ptrs[v--] = dp; 230 dp = p + 1; 231 } 232 (void) strcpy(buf, dp); 233 for (v++; v < 5; v++) { 234 (void) strcat(buf, "."); 235 (void) strcat(buf, ptrs[v]); 236 } 237 free(dp1); 238 return (B_TRUE); 239 } 240 241 /* 242 * []------------------------------------------------------------------[] 243 * | utils_iqn_create -- returns an iqn name for the machine | 244 * | | 245 * | The information found in the iqn is not correct. The year and | 246 * | date should be flexible. Currently this is hardwired to the | 247 * | current year and month of this project. | 248 * []------------------------------------------------------------------[] 249 */ 250 boolean_t 251 utils_iqn_create(char *iqn_buf, int size) 252 { 253 struct utsname uts_info; 254 char domainname[256]; 255 char *temp = NULL; 256 char *p; 257 char *pmet = NULL; /* temp reversed .. get it */ 258 int len; 259 boolean_t rval = B_FALSE; /* Default */ 260 261 if (uname(&uts_info) == -1) { 262 goto out; 263 } 264 265 if (getdomainname(domainname, sizeof (domainname))) { 266 goto out; 267 } 268 269 if ((temp = malloc(strlen(uts_info.nodename) + 270 strlen(domainname) + 2)) == NULL) { 271 goto out; 272 } 273 274 /* 275 * getdomainname always returns something in the order of 276 * host.domainname so we need to skip over that portion of the 277 * host name because we don't care about it. 278 */ 279 if ((p = strchr(domainname, '.')) == NULL) 280 p = domainname; 281 else 282 p++; 283 284 /* ---- Create Fully Qualified Domain Name ---- */ 285 (void) snprintf(temp, strlen(p), "%s.%s", uts_info.nodename, p); 286 287 /* ---- According to the spec, names must be lower case ---- */ 288 for (p = temp; *p; p++) 289 if (isupper(*p)) 290 *p = tolower(*p); 291 292 len = strlen(temp) + 1; 293 if ((pmet = malloc(len)) == NULL) { 294 goto out; 295 } 296 297 if (reverse_fqdn(temp, pmet, len) == B_FALSE) { 298 goto out; 299 } 300 301 /* 302 * Now use the template with the reversed domainname to create 303 * an iSCSI name using the IQN format. Only count it a success 304 * if the number of characters formated is less than the buffer 305 * size. 306 */ 307 if (snprintf(iqn_buf, size, iqn_template, pmet) <= size) 308 rval = B_TRUE; 309 out: 310 if (temp) 311 free(temp); 312 if (pmet) 313 free(pmet); 314 315 return (rval); 316 } 317 #endif /* !_KERNEL */ 318