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 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <strings.h>
31 #include <stropts.h>
32 #include <unistd.h>
33 #include <uuid/uuid.h>
34 #include <sys/sockio.h>
35 #include <libdlpi.h>
36 #include <libdllink.h>
37 #include <sys/utsname.h>
38 
39 #include <netdb.h>
40 #include <netinet/in.h>
41 #include <arpa/inet.h>
42 
43 #include "etheraddr.h"
44 
45 /*
46  * debugging flag
47  */
48 static	int	debug = 0;
49 
50 static void get_etheraddr(void *arg, const char *linkname);
51 
52 /*
53  * get an individual arp entry
54  */
55 int
56 arp_get(uuid_node_t *node)
57 {
58 	struct utsname name;
59 	struct arpreq ar;
60 	struct hostent *hp;
61 	struct sockaddr_in *sin;
62 	int s;
63 
64 	if (uname(&name) == -1) {
65 		return (-1);
66 	}
67 	(void) memset(&ar, 0, sizeof (ar));
68 	ar.arp_pa.sa_family = AF_INET;
69 	/* LINTED pointer */
70 	sin = (struct sockaddr_in *)&ar.arp_pa;
71 	sin->sin_family = AF_INET;
72 	sin->sin_addr.s_addr = inet_addr(name.nodename);
73 	if (sin->sin_addr.s_addr == (in_addr_t)-1) {
74 		hp = gethostbyname(name.nodename);
75 		if (hp == NULL) {
76 			return (-1);
77 		}
78 		(void) memcpy(&sin->sin_addr, hp->h_addr,
79 		    sizeof (sin->sin_addr));
80 	}
81 	s = socket(AF_INET, SOCK_DGRAM, 0);
82 	if (s < 0) {
83 		return (-1);
84 	}
85 	if (ioctl(s, SIOCGARP, (caddr_t)&ar) < 0) {
86 		(void) close(s);
87 		return (-1);
88 	}
89 	(void) close(s);
90 	if (ar.arp_flags & ATF_COM) {
91 		bcopy(&ar.arp_ha.sa_data, node, 6);
92 	} else
93 		return (-1);
94 	return (0);
95 }
96 
97 /*
98  * Name:	get_ethernet_address
99  *
100  * Description:	Obtains the system ethernet address.
101  *
102  * Returns:	0 on success, non-zero otherwise.  The system ethernet
103  *		address is copied into the passed-in variable.
104  */
105 int
106 get_ethernet_address(uuid_node_t *node)
107 {
108 	walker_arg_t	state;
109 
110 	if (arp_get(node) == 0)
111 		return (0);
112 
113 	/*
114 	 * Try to get physical (ethernet) address from network interfaces.
115 	 */
116 	state.wa_addrvalid = B_FALSE;
117 	if (dladm_walk(get_etheraddr, &state) == 0 && state.wa_addrvalid) {
118 		bcopy(state.wa_etheraddr, node, state.wa_etheraddrlen);
119 	}
120 
121 	return (state.wa_addrvalid ? 0 : -1);
122 }
123 
124 /*
125  * Get the physical address via dlpi and update the flag to true upon success.
126  */
127 static void
128 get_etheraddr(void *arg, const char *linkname)
129 {
130 	int		retval;
131 	dlpi_handle_t	dh;
132 	walker_arg_t	*statep = arg;
133 
134 	if (!(statep->wa_addrvalid)) {
135 		if (debug)
136 			(void) printf("get_etheraddr: opening %s\n", linkname);
137 		if ((retval = dlpi_open(linkname, &dh, 0)) != DLPI_SUCCESS) {
138 			if (debug) {
139 				(void) fprintf(stderr, "get_etheraddr: "
140 				    "cannot open link: \"%s\" %s\n",
141 				    linkname, retval);
142 			}
143 			return;
144 		}
145 
146 		if (debug) {
147 			(void) printf("get_etheraddr: getting ethernet address"
148 			    " from link: %s\n", linkname);
149 		}
150 		statep->wa_etheraddrlen = DLPI_PHYSADDR_MAX;
151 		retval = dlpi_get_physaddr(dh, DL_CURR_PHYS_ADDR,
152 		    statep->wa_etheraddr, &(statep->wa_etheraddrlen));
153 		if (debug) {
154 			(void) fprintf(stderr, "get_etheraddr: "
155 			    "dlpi_get_physaddr: \"%s\" %s\n", linkname, retval);
156 		}
157 
158 		if (retval == DLPI_SUCCESS)
159 			statep->wa_addrvalid = B_TRUE;
160 
161 		dlpi_close(dh);
162 	}
163 }
164