util_ifname.c revision c7e4935f5b755b4bbeaec416f1ad24337aeac7a4
/*
* 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 "%Z%%M% %I% %E% SMI"
#include <sys/types.h>
#include <libdlpi.h>
#include <ctype.h>
#include <sys/sysmacros.h>
#include <net/if_types.h>
#include <net/if.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <net/if_dl.h>
#include <sys/sockio.h>
#include <sys/socket.h>
#include <unistd.h>
#include <strings.h>
#include <stropts.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <iscsitgt_impl.h>
#include "target.h"
#include "queue.h"
#include "utility.h"
#define LOCAL_LOOPBACK "lo0"
/*
* This entire file is all about getting these two variables. To create a
* unique iSCSI IQN string we need information that is unique. What Sun has
* decided is to use a MAC address along with a timestamp. No other machine
* at this given time will have the same MAC address and as time moves along
* well, time will change.
*/
uchar_t mac_addr[DLPI_PHYSADDR_MAX];
size_t mac_len;
static struct lifreq *if_setup(int *n);
static void dump_addr_to_ascii(struct sockaddr *addr, char *buf,
size_t len);
static Boolean_t grab_address(char *ifname, uchar_t *addrp, size_t *addrlenp);
/*
* []----
* | if_find_mac -- Finds a valid MAC address to use for GUID & IQN creation
* |
* | To create both the GUID and the IQN string we need to make them unique
* | and do so without requiring the user to have to register each target
* | creation with Sun. Each machine that's using iSCSI will have a network
* | interface from which we can obtain the MAC address. That guarantees
* | uniqueness within the network, but doesn't guarantee uniqueness with
* | the machine. So when creating the GUID/IQN we also use a timestamp.
* []----
*/
Boolean_t
if_find_mac(target_queue_t *mgmt)
{
struct lifreq *lifrp, *first;
int n;
char *str;
mac_len = DLPI_PHYSADDR_MAX;
first = if_setup(&n);
for (lifrp = first; n > 0; n--, lifrp++) {
if (grab_address(lifrp->lifr_name, mac_addr,
&mac_len) == True) {
str = _link_ntoa(mac_addr, NULL, mac_len, IFT_OTHER);
if ((str != NULL) && (mgmt != NULL)) {
queue_prt(mgmt, Q_GEN_DETAILS,
"MAIN %s: %s \n", lifrp->lifr_name, str);
free(str);
}
/* ---- grab the first valid MAC address ---- */
break;
}
}
if (first)
free(first);
return (mac_len == 0 ? False : True);
}
/*
* []----
* | if_target_address -- setup IP address for SendTargets
* |
* | This routine is called when the iSCSI target is returning SendTargets
* | data during a discovery phase. The target name is returned along
* | with all of the IP address that can access that target. There's one
* | catch, the first address in the list will be the address used by
* | the initiator if it doesn't support multiple connections per session.
* | Therefore, whatever connection the initiator used is the first one
* | that should be in our list. The ramificiations of not doing this are
* | possible performance issues. Take for example a setup where both the
* | initiator and target have 10GbE and 1GbE interfaces. The initiator wants
* | to use the 10GbE interface because of it's speed. If the target returns
* | a list of addresses with the 1GbE listed first, that's the one which
* | the initiator would use. Not good.
* []----
*/
void
if_target_address(char **text, int *text_length, struct sockaddr *sp)
{
struct lifreq *lp,
*first;
int n,
i,
s;
struct sockaddr_in *sin4_cur,
*sin4_pos;
struct sockaddr_in6 *sin6_cur,
*sin6_pos;
char ta[80],
ip_buf[INET6_ADDRSTRLEN];
int fromlen;
if (sp->sa_family == AF_INET) {
fromlen = sizeof (struct sockaddr_in);
if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
return;
/*LINTED*/
sin4_cur = (struct sockaddr_in *)sp;
/*
* Ugh. Would you believe that even though this array
* is defined as zero, we get back non-zero data from
* getsockname().
*/
bzero(&sin4_cur->sin_zero[0], sizeof (sin4_cur->sin_zero));
} else if (sp->sa_family == AF_INET6) {
fromlen = sizeof (struct sockaddr_in6);
if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) == -1)
return;
/*LINTED*/
sin6_cur = (struct sockaddr_in6 *)sp;
} else
return;
first = if_setup(&n);
for (lp = first, i = 0; i < n; i++, lp++) {
if (ioctl(s, SIOCGLIFADDR, lp) < 0)
continue;
if (sp->sa_family != lp->lifr_addr.ss_family)
continue;
/*
* Change the possible incoming addresses port number,
* which would be zero, to that of the current incoming
* port number. Otherwise the comparison will not match.
*/
if (sp->sa_family == AF_INET) {
sin4_pos = (struct sockaddr_in *)&lp->lifr_addr;
sin4_pos->sin_port = sin4_cur->sin_port;
} else if (sp->sa_family == AF_INET6) {
sin6_pos = (struct sockaddr_in6 *)&lp->lifr_addr;
sin6_pos->sin6_port = sin6_cur->sin6_port;
} else
goto clean_up;
}
for (lp = first, i = 0; i < n; i++, lp++) {
if (bcmp(sp, &lp->lifr_addr, fromlen) == 0) {
dump_addr_to_ascii((struct sockaddr *)&lp->lifr_addr,
ip_buf, sizeof (ip_buf));
if (sp->sa_family == AF_INET) {
(void) snprintf(ta, sizeof (ta), "%s,1",
ip_buf);
} else if (sp->sa_family == AF_INET6) {
(void) snprintf(ta, sizeof (ta), "[%s],1",
ip_buf);
} else
goto clean_up;
(void) add_text(text, text_length, "TargetAddress", ta);
/*
* There is possiblity that both IPv4 & IPv6 enabled on
* certain interface, then we will see that interface
* twice identically in the list.
* Of course we need only one of them, not both.
*/
break;
}
}
for (lp = first, i = 0; i < n; i++, lp++) {
/*
* We allow for the loopback address to match the discovery
* address above since it's entirely possible to create
* a target on the same machine that you're running the
* initiator. Now, when we provide the list of other
* possible interfaces to use we don't want to include
* the loopback because that's obviously not a valid I/F
* for a remote node.
*/
if (strcmp(lp->lifr_name, LOCAL_LOOPBACK) == 0)
continue;
if (bcmp(sp, &lp->lifr_addr, fromlen) != 0) {
dump_addr_to_ascii((struct sockaddr *)&lp->lifr_addr,
ip_buf, sizeof (ip_buf));
if (sp->sa_family == AF_INET) {
(void) snprintf(ta, sizeof (ta), "%s,1",
ip_buf);
} else if (sp->sa_family == AF_INET6) {
(void) snprintf(ta, sizeof (ta), "[%s],1",
ip_buf);
} else
goto clean_up;
(void) add_text(text, text_length, "TargetAddress", ta);
}
}
clean_up:
(void) close(s);
if (first)
free(first);
}
/*
* []----
* | dump_addr_to_ascii -- Use appropriate translation routine
* []----
*/
static void
dump_addr_to_ascii(struct sockaddr *addr, char *buf, size_t len)
{
struct sockaddr_in *sin4;
struct sockaddr_in6 *sin6;
if (addr->sa_family == AF_INET) {
/*LINTED*/
sin4 = (struct sockaddr_in *)addr;
(void) inet_ntop(AF_INET, &sin4->sin_addr, buf, len);
} else if (addr->sa_family == AF_INET6) {
/*LINTED*/
sin6 = (struct sockaddr_in6 *)addr;
(void) inet_ntop(AF_INET6, &sin6->sin6_addr, buf, len);
}
}
/*
* []----
* | if_setup -- Load up the interface names
* |
* | If this routine returns NULL, argument 'n' is also guaranteed to
* | be set to 0.
* []----
*/
static struct lifreq *
if_setup(int *n)
{
struct lifnum lifn;
struct lifconf lifc;
int numifs;
unsigned bufsize;
char *buf;
int s;
*n = 0;
if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) {
/*
* If we failed to open an IPv6 socket
* try IPv4 socket instead
*/
if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
return (NULL);
}
lifn.lifn_family = AF_UNSPEC;
lifn.lifn_flags = LIFC_NOXMIT | LIFC_TEMPORARY | LIFC_ALLZONES;
if (ioctl(s, SIOCGLIFNUM, (char *)&lifn) < 0)
return (NULL);
numifs = lifn.lifn_count;
bufsize = numifs * sizeof (struct lifreq);
if ((buf = malloc(bufsize)) == NULL) {
/*
* This call is made so early on that if we're out of memory
* here, just say goodbye.
*/
return (NULL);
}
lifc.lifc_family = AF_UNSPEC;
lifc.lifc_flags = LIFC_NOXMIT | LIFC_TEMPORARY | LIFC_ALLZONES;
lifc.lifc_len = bufsize;
lifc.lifc_buf = buf;
if (ioctl(s, SIOCGLIFCONF, (char *)&lifc) < 0) {
free(buf);
return (NULL);
}
(void) close(s);
*n = lifc.lifc_len / sizeof (struct lifreq);
return (lifc.lifc_req);
}
static Boolean_t
grab_address(char *ifname, uchar_t *addrp, size_t *addrlenp)
{
int retval;
dlpi_handle_t dh;
if (dlpi_open(ifname, &dh, 0) != DLPI_SUCCESS)
return (False);
retval = dlpi_get_physaddr(dh, DL_CURR_PHYS_ADDR, addrp, addrlenp);
dlpi_close(dh);
return (retval == DLPI_SUCCESS);
}