Home | History | Annotate | Download | only in iscsitgtd
      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 /*
     23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 #include <sys/types.h>
     28 #include <libdlpi.h>
     29 #include <ctype.h>
     30 #include <sys/sysmacros.h>
     31 #include <net/if_types.h>
     32 #include <net/if.h>
     33 #include <sys/types.h>
     34 #include <stdio.h>
     35 #include <stdlib.h>
     36 #include <net/if_dl.h>
     37 #include <sys/sockio.h>
     38 #include <sys/socket.h>
     39 #include <unistd.h>
     40 #include <strings.h>
     41 #include <stropts.h>
     42 #include <fcntl.h>
     43 #include <netinet/in.h>
     44 #include <arpa/inet.h>
     45 
     46 #include <iscsitgt_impl.h>
     47 #include "target.h"
     48 #include "queue.h"
     49 #include "utility.h"
     50 
     51 #define	LOCAL_LOOPBACK	"lo0"
     52 
     53 /*
     54  * This entire file is all about getting these two variables. To create a
     55  * unique iSCSI IQN string we need information that is unique. What Sun has
     56  * decided is to use a MAC address along with a timestamp. No other machine
     57  * at this given time will have the same MAC address and as time moves along
     58  * well, time will change.
     59  */
     60 uchar_t 	mac_addr[DLPI_PHYSADDR_MAX];
     61 size_t		mac_len;
     62 
     63 static struct lifreq *if_setup(int *n);
     64 static void dump_addr_to_ascii(struct sockaddr *addr, char *buf,
     65     size_t len);
     66 static Boolean_t grab_address(char *ifname, uchar_t *addrp, size_t *addrlenp);
     67 
     68 /*
     69  * []----
     70  * | if_find_mac -- Finds a valid MAC address to use for GUID & IQN creation
     71  * |
     72  * | To create both the GUID and the IQN string we need to make them unique
     73  * | and do so without requiring the user to have to register each target
     74  * | creation with Sun. Each machine that's using iSCSI will have a network
     75  * | interface from which we can obtain the MAC address. That guarantees
     76  * | uniqueness within the network, but doesn't guarantee uniqueness with
     77  * | the machine. So when creating the GUID/IQN we also use a timestamp.
     78  * []----
     79  */
     80 Boolean_t
     81 if_find_mac(target_queue_t *mgmt)
     82 {
     83 	struct lifreq	*lifrp, *first;
     84 	int		n;
     85 	char		*str;
     86 
     87 	mac_len = DLPI_PHYSADDR_MAX;
     88 
     89 	first = if_setup(&n);
     90 	for (lifrp = first; n > 0; n--, lifrp++) {
     91 		if (grab_address(lifrp->lifr_name, mac_addr,
     92 		    &mac_len) == True) {
     93 			str = _link_ntoa(mac_addr, NULL, mac_len, IFT_OTHER);
     94 			if ((str != NULL) && (mgmt != NULL)) {
     95 				queue_prt(mgmt, Q_GEN_DETAILS,
     96 				    "MAIN  %s: %s \n", lifrp->lifr_name, str);
     97 				free(str);
     98 			}
     99 			/* ---- grab the first valid MAC address ---- */
    100 			break;
    101 		}
    102 	}
    103 	if (first)
    104 		free(first);
    105 	return (mac_len == 0 ? False : True);
    106 }
    107 
    108 /*
    109  * []----
    110  * | if_target_address -- setup IP address for SendTargets
    111  * |
    112  * | This routine is called when the iSCSI target is returning SendTargets
    113  * | data during a discovery phase. The target name is returned along
    114  * | with all of the IP address that can access that target. There's one
    115  * | catch, the first address in the list will be the address used by
    116  * | the initiator if it doesn't support multiple connections per session.
    117  * | Therefore, whatever connection the initiator used is the first one
    118  * | that should be in our list. The ramificiations of not doing this are
    119  * | possible performance issues. Take for example a setup where both the
    120  * | initiator and target have 10GbE and 1GbE interfaces. The initiator wants
    121  * | to use the 10GbE interface because of it's speed. If the target returns
    122  * | a list of addresses with the 1GbE listed first, that's the one which
    123  * | the initiator would use. Not good.
    124  * []----
    125  */
    126 void
    127 if_target_address(char **text, int *text_length, struct sockaddr *sp)
    128 {
    129 	struct lifreq		*lp, *first;
    130 	int			n, i, s;
    131 	struct sockaddr_in	*sin4_cur, *sin4_pos;
    132 	struct sockaddr_in6	*sin6_cur, *sin6_pos;
    133 	char			ta[80], ip_buf[INET6_ADDRSTRLEN];
    134 	int			fromlen;
    135 
    136 	if (sp->sa_family == AF_INET) {
    137 		fromlen = sizeof (struct sockaddr_in);
    138 
    139 		if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
    140 			return;
    141 
    142 		/*LINTED*/
    143 		sin4_cur = (struct sockaddr_in *)sp;
    144 
    145 		/*
    146 		 * Ugh. Would you believe that even though this array
    147 		 * is defined as zero, we get back non-zero data from
    148 		 * getsockname().
    149 		 */
    150 		bzero(&sin4_cur->sin_zero[0], sizeof (sin4_cur->sin_zero));
    151 
    152 	} else if (sp->sa_family == AF_INET6) {
    153 		fromlen = sizeof (struct sockaddr_in6);
    154 
    155 		if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) == -1)
    156 			return;
    157 
    158 		/*LINTED*/
    159 		sin6_cur = (struct sockaddr_in6 *)sp;
    160 	} else
    161 		return;
    162 
    163 	first = if_setup(&n);
    164 	for (lp = first, i = 0; i < n; i++, lp++) {
    165 
    166 		if (sp->sa_family != lp->lifr_addr.ss_family)
    167 			continue;
    168 
    169 		/*
    170 		 * Change the possible incoming addresses port number,
    171 		 * which would be zero, to that of the current incoming
    172 		 * port number. Otherwise the comparison will not match.
    173 		 */
    174 		if (sp->sa_family == AF_INET) {
    175 			sin4_pos = (struct sockaddr_in *)&lp->lifr_addr;
    176 			sin4_pos->sin_port = sin4_cur->sin_port;
    177 		} else if (sp->sa_family == AF_INET6) {
    178 			sin6_pos = (struct sockaddr_in6 *)&lp->lifr_addr;
    179 			sin6_pos->sin6_port = sin6_cur->sin6_port;
    180 		} else
    181 			goto clean_up;
    182 	}
    183 
    184 	for (lp = first, i = 0; i < n; i++, lp++) {
    185 
    186 		if (bcmp(sp, &lp->lifr_addr, fromlen) == 0) {
    187 			dump_addr_to_ascii((struct sockaddr *)&lp->lifr_addr,
    188 			    ip_buf, sizeof (ip_buf));
    189 
    190 			if (sp->sa_family == AF_INET) {
    191 				(void) snprintf(ta, sizeof (ta), "%s,1",
    192 				    ip_buf);
    193 			} else if (sp->sa_family == AF_INET6) {
    194 				(void) snprintf(ta, sizeof (ta), "[%s],1",
    195 				    ip_buf);
    196 			} else
    197 				goto clean_up;
    198 
    199 			(void) add_text(text, text_length, "TargetAddress", ta);
    200 
    201 			/*
    202 			 * There is possiblity that both IPv4 & IPv6 enabled on
    203 			 * certain interface, then we will see that interface
    204 			 * twice identically in the list.
    205 			 * Of course we need only one of them, not both.
    206 			 */
    207 			break;
    208 		}
    209 	}
    210 
    211 	for (lp = first, i = 0; i < n; i++, lp++) {
    212 		/*
    213 		 * We allow for the loopback address to match the discovery
    214 		 * address above since it's entirely possible to create
    215 		 * a target on the same machine that you're running the
    216 		 * initiator. Now, when we provide the list of other
    217 		 * possible interfaces to use we don't want to include
    218 		 * the loopback because that's obviously not a valid I/F
    219 		 * for a remote node.
    220 		 */
    221 		if (strcmp(lp->lifr_name, LOCAL_LOOPBACK) == 0)
    222 			continue;
    223 
    224 		if (bcmp(sp, &lp->lifr_addr, fromlen) != 0) {
    225 			struct sockaddr *sp2;
    226 			sp2 = (struct sockaddr *)&lp->lifr_addr;
    227 			dump_addr_to_ascii(sp2, ip_buf, sizeof (ip_buf));
    228 
    229 			if (sp2->sa_family == AF_INET) {
    230 				(void) snprintf(ta, sizeof (ta), "%s,1",
    231 				    ip_buf);
    232 			} else if (sp2->sa_family == AF_INET6) {
    233 				(void) snprintf(ta, sizeof (ta), "[%s],1",
    234 				    ip_buf);
    235 			} else
    236 				goto clean_up;
    237 			(void) add_text(text, text_length, "TargetAddress", ta);
    238 		}
    239 	}
    240 
    241 clean_up:
    242 	(void) close(s);
    243 	if (first)
    244 		free(first);
    245 }
    246 
    247 /*
    248  * []----
    249  * | dump_addr_to_ascii -- Use appropriate translation routine
    250  * []----
    251  */
    252 static void
    253 dump_addr_to_ascii(struct sockaddr *addr, char *buf, size_t len)
    254 {
    255 	struct sockaddr_in	*sin4;
    256 	struct sockaddr_in6	*sin6;
    257 
    258 	if (addr->sa_family == AF_INET) {
    259 		/*LINTED*/
    260 		sin4 = (struct sockaddr_in *)addr;
    261 		(void) inet_ntop(AF_INET, &sin4->sin_addr, buf, len);
    262 	} else if (addr->sa_family == AF_INET6) {
    263 		/*LINTED*/
    264 		sin6 = (struct sockaddr_in6 *)addr;
    265 		(void) inet_ntop(AF_INET6, &sin6->sin6_addr, buf, len);
    266 	}
    267 }
    268 
    269 /*
    270  * []----
    271  * | if_setup -- Load up the interface names
    272  * |
    273  * | If this routine returns NULL, argument 'n' is also guaranteed to
    274  * | be set to 0.
    275  * []----
    276  */
    277 static struct lifreq *
    278 if_setup(int *n)
    279 {
    280 	struct lifnum	lifn;
    281 	struct lifconf	lifc;
    282 	int		numifs;
    283 	unsigned	bufsize;
    284 	char		*buf;
    285 	int		s;
    286 
    287 	*n = 0;
    288 	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) {
    289 		/*
    290 		 * If we failed to open an IPv6 socket
    291 		 * try IPv4 socket instead
    292 		 */
    293 		if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
    294 			return (NULL);
    295 	}
    296 
    297 	lifn.lifn_family = AF_UNSPEC;
    298 	lifn.lifn_flags = LIFC_ALLZONES | LIFC_EXTERNAL_SOURCE;
    299 	if (ioctl(s, SIOCGLIFNUM, (char *)&lifn) < 0)
    300 		return (NULL);
    301 
    302 	numifs = lifn.lifn_count;
    303 
    304 	bufsize = numifs * sizeof (struct lifreq);
    305 	if ((buf = malloc(bufsize)) == NULL) {
    306 		/*
    307 		 * This call is made so early on that if we're out of memory
    308 		 * here, just say goodbye.
    309 		 */
    310 		return (NULL);
    311 	}
    312 
    313 	lifc.lifc_family = AF_UNSPEC;
    314 	lifc.lifc_flags = LIFC_ALLZONES | LIFC_EXTERNAL_SOURCE;
    315 	lifc.lifc_len = bufsize;
    316 	lifc.lifc_buf = buf;
    317 
    318 	if (ioctl(s, SIOCGLIFCONF, (char *)&lifc) < 0) {
    319 		free(buf);
    320 		return (NULL);
    321 	}
    322 
    323 	(void) close(s);
    324 	*n = lifc.lifc_len / sizeof (struct lifreq);
    325 	return (lifc.lifc_req);
    326 }
    327 
    328 static Boolean_t
    329 grab_address(char *ifname, uchar_t *addrp, size_t *addrlenp)
    330 {
    331 	int		retval;
    332 	dlpi_handle_t	dh;
    333 
    334 	if (dlpi_open(ifname, &dh, 0) != DLPI_SUCCESS)
    335 		return (False);
    336 
    337 	retval = dlpi_get_physaddr(dh, DL_CURR_PHYS_ADDR, addrp, addrlenp);
    338 
    339 	dlpi_close(dh);
    340 
    341 	return (retval == DLPI_SUCCESS);
    342 }
    343