etheraddr.c revision 948f2876ce2a3010558f4f6937e16086ebcd36f2
/*
* 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 <stdlib.h>
#include <stdio.h>
#include <strings.h>
#include <stropts.h>
#include <unistd.h>
#include <uuid/uuid.h>
#include <sys/sockio.h>
#include <libdlpi.h>
#include <libdllink.h>
#include <sys/utsname.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "etheraddr.h"
/*
* debugging flag
*/
static int debug = 0;
static void get_etheraddr(void *arg, const char *linkname);
/*
* get an individual arp entry
*/
int
arp_get(uuid_node_t *node)
{
struct utsname name;
struct arpreq ar;
struct hostent *hp;
struct sockaddr_in *sin;
int s;
if (uname(&name) == -1) {
return (-1);
}
(void) memset(&ar, 0, sizeof (ar));
ar.arp_pa.sa_family = AF_INET;
/* LINTED pointer */
sin = (struct sockaddr_in *)&ar.arp_pa;
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = inet_addr(name.nodename);
if (sin->sin_addr.s_addr == (in_addr_t)-1) {
hp = gethostbyname(name.nodename);
if (hp == NULL) {
return (-1);
}
(void) memcpy(&sin->sin_addr, hp->h_addr,
sizeof (sin->sin_addr));
}
s = socket(AF_INET, SOCK_DGRAM, 0);
if (s < 0) {
return (-1);
}
if (ioctl(s, SIOCGARP, (caddr_t)&ar) < 0) {
(void) close(s);
return (-1);
}
(void) close(s);
if (ar.arp_flags & ATF_COM) {
bcopy(&ar.arp_ha.sa_data, node, 6);
} else
return (-1);
return (0);
}
/*
* Name: get_ethernet_address
*
* Description: Obtains the system ethernet address.
*
* Returns: 0 on success, non-zero otherwise. The system ethernet
* address is copied into the passed-in variable.
*/
int
get_ethernet_address(uuid_node_t *node)
{
walker_arg_t state;
if (arp_get(node) == 0)
return (0);
/*
* Try to get physical (ethernet) address from network interfaces.
*/
state.wa_addrvalid = B_FALSE;
if (dladm_walk(get_etheraddr, &state) == 0 && state.wa_addrvalid) {
bcopy(state.wa_etheraddr, node, state.wa_etheraddrlen);
}
return (state.wa_addrvalid ? 0 : -1);
}
/*
* Get the physical address via dlpi and update the flag to true upon success.
*/
static void
get_etheraddr(void *arg, const char *linkname)
{
int retval;
dlpi_handle_t dh;
walker_arg_t *statep = arg;
if (!(statep->wa_addrvalid)) {
if (debug)
(void) printf("get_etheraddr: opening %s\n", linkname);
if ((retval = dlpi_open(linkname, &dh, 0)) != DLPI_SUCCESS) {
if (debug) {
(void) fprintf(stderr, "get_etheraddr: "
"cannot open link: \"%s\" %s\n",
linkname, retval);
}
return;
}
if (debug) {
(void) printf("get_etheraddr: getting ethernet address"
" from link: %s\n", linkname);
}
statep->wa_etheraddrlen = DLPI_PHYSADDR_MAX;
retval = dlpi_get_physaddr(dh, DL_CURR_PHYS_ADDR,
statep->wa_etheraddr, &(statep->wa_etheraddrlen));
if (debug) {
(void) fprintf(stderr, "get_etheraddr: "
"dlpi_get_physaddr: \"%s\" %s\n", linkname, retval);
}
if (retval == DLPI_SUCCESS)
statep->wa_addrvalid = B_TRUE;
dlpi_close(dh);
}
}