xref: /illumos-gate/usr/src/cmd/hal/addons/network-devices/common.c (revision edb348833aaacfa1176e502ad38875fd0b2717ab)
1 /*
2  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
3  *
4  * Licensed under the Academic Free License version 2.1
5  */
6 
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <unistd.h>
10 #include <signal.h>
11 #include <string.h>
12 #include <sys/types.h>
13 #include <sys/socket.h>
14 #include <sys/ioctl.h>
15 #include <sys/sockio.h>
16 #include <net/if.h>
17 #include <net/if_arp.h>
18 #include <netinet/in.h>
19 #include <arpa/inet.h>
20 #include <netdb.h>
21 
22 #include <libhal.h>
23 #include <logger.h>
24 
25 #include <glib.h>
26 
27 #include "network-discovery.h"
28 #define	NP(x)	(x?x:"NULL")
29 
30 extern int snmp_printer_info(char *hostname, char *community,
31 		char **manufacturer, char **model, char **description,
32 		char **serial_no, char ***command_set, char **uri);
33 
34 void
35 network_device_name_to_udi(char *udi, size_t size, ...)
36 {
37 	va_list ap;
38 	char *element;
39 	int i;
40 
41 	udi[0] = '\0';
42 	va_start(ap, size);
43 	while ((element = va_arg(ap, char *)) != NULL) {
44 		if (element[0] != '/')
45 			strlcat(udi, "/", size);
46 		strlcat(udi, element, size);
47 	}
48 	va_end(ap);
49 
50 	for (i = 0; udi[i] != NULL; i++)
51 		if (udi[i] == '.')
52 			udi[i] = '_';
53 }
54 
55 static void nop(int sig) {}
56 
57 static int
58 test_socket_access(struct in6_addr *addr, int port)
59 {
60 	int sd, rc;
61 	struct sockaddr_in6 sin6;
62 	void (*hndlr)(int);
63 
64 	memset(&sin6, 0, sizeof (sin6));
65 	sin6.sin6_family = AF_INET6;
66 	memcpy(&sin6.sin6_addr, addr, sizeof (*addr));
67 	sin6.sin6_port = htons(port);
68 
69 	sd = socket(AF_INET6, SOCK_STREAM, 0);
70 	hndlr = signal(SIGALRM, nop);
71 	alarm(1);
72 	rc = connect(sd, (struct sockaddr *)&sin6, sizeof (sin6));
73 	alarm(0);
74 	if (hndlr != NULL)
75 		signal(SIGALRM, hndlr);
76 	close(sd);
77 
78 	return ((rc < 0) ? 1 : 0);
79 }
80 
81 int
82 is_listening(char *hostname, int port)
83 {
84 	char *uri = NULL, addr_string[INET6_ADDRSTRLEN];
85 	struct in6_addr ipv6addr[1];
86 	int errnum;
87 	struct hostent *hp;
88 
89 	hp = getipnodebyname(hostname, AF_INET6,
90 			AI_ALL | AI_ADDRCONFIG | AI_V4MAPPED, &errnum);
91 	if (hp != NULL) {
92 		(void) memcpy(&ipv6addr, hp->h_addr_list[0], hp->h_length);
93 	} else
94 		return (-1);
95 
96 	return (test_socket_access(ipv6addr, port));
97 }
98 
99 static char *
100 addr_to_string(char *prefix, uchar_t *mac, int mac_len, char *buf, int buf_len)
101 {
102 	int i, n = 0;
103 
104 	buf[0] = '\0';
105 	if (prefix != NULL)
106 		n = sprintf(buf, prefix);
107 	for (i = 0; ((i < (mac_len)) && (n < buf_len)); i++)
108 		n += sprintf(buf + n, "%2.2X", *mac++);
109 
110 	return (buf);
111 }
112 
113 static char *
114 pseudo_serialno_from_addr(char *name)
115 {
116 	int sd, rc, errnum;
117 	char buf[128];
118 	struct hostent *hp;
119 	struct xarpreq ar;
120 
121 	if (name == NULL)
122 		return (NULL);
123 
124 	memset(&ar, 0, sizeof (ar));
125 
126 	hp = getipnodebyname(name, AF_INET6, AI_ADDRCONFIG, &errnum);
127 	if (hp != NULL) {
128 		struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&ar.xarp_pa;
129 
130 		sin6->sin6_family = AF_INET6;
131 		(void) memcpy(&sin6->sin6_addr, hp->h_addr_list[0],
132 				hp->h_length);
133 	} else {
134 		struct sockaddr_in *sin = (struct sockaddr_in *)&ar.xarp_pa;
135 
136 		sin->sin_family = AF_INET;
137 		sin->sin_addr.s_addr = inet_addr(name);
138 	}
139 
140 	sd = socket(AF_INET, SOCK_DGRAM, 0);
141 
142 	ar.xarp_ha.sdl_family = AF_LINK;
143 	rc = ioctl(sd, SIOCGXARP, (caddr_t)&ar);
144 
145 	close(sd);
146 
147 	if (ar.xarp_flags & ATF_COM) {  /* use the MAC address */
148 		uchar_t *ea = (uchar_t *)LLADDR(&ar.xarp_ha);
149 
150 		addr_to_string("LLADDR-", ea, ar.xarp_ha.sdl_alen,
151 					buf, sizeof (buf));
152 
153 	} else if (hp != NULL) {	  /* use the IPv6 address */
154 		addr_to_string("IPV6ADDR-", (uchar_t *)&hp->h_addr_list[0],
155 					hp->h_length, buf, sizeof (buf));
156 	} else {			  /* use the IPv4 address */
157 		struct sockaddr_in *sin = (struct sockaddr_in *)&ar.xarp_pa;
158 
159 		addr_to_string("IPV4ADDR-", (uchar_t *)&sin->sin_addr.s_addr, 4,
160 					buf, sizeof (buf));
161 	}
162 
163 	return (strdup(buf));
164 }
165 
166 int
167 add_network_printer(LibHalContext *ctx, char *base, char *hostaddr,
168 		char *device, char *community)
169 {
170 	DBusError error;
171 	int rc = -1;
172 	char udi[128];
173 	char *tmp_udi = NULL;
174 	static char *parent = NULL;
175 	char *manufacturer = NULL, *model = NULL, *description = NULL,
176 	     *uri = NULL, *sn, *serial;
177 
178 	sn = serial = pseudo_serialno_from_addr(hostaddr);
179 
180 	if (parent == NULL)
181 		parent = getenv("UDI");
182 
183 	dbus_error_init(&error);
184 
185 	network_device_name_to_udi(udi, sizeof (udi), base, serial, NULL);
186 
187 	if (libhal_device_exists(ctx, udi, &error) == TRUE)
188 		goto out;
189 
190 	if ((tmp_udi = libhal_new_device(ctx, &error)) == NULL)
191 		goto out;
192 
193 	snmp_printer_info(hostaddr, community, &manufacturer, &model,
194 			&description, &serial, NULL, &uri);
195 
196 	libhal_device_set_property_string(ctx, tmp_udi,
197 			"info.parent", parent, &error);
198 
199 	libhal_device_set_property_string(ctx, tmp_udi,
200 			"info.category", "printer", &error);
201 
202 	libhal_device_property_strlist_append(ctx, tmp_udi,
203 				"info.capabilities", "printer", &error);
204 	libhal_device_property_strlist_append(ctx, tmp_udi,
205 				"info.capabilities", "network_device", &error);
206 
207 	libhal_device_set_property_string(ctx, tmp_udi,
208 			"network_device.address", hostaddr, &error);
209 
210 	if ((community != NULL) && (strcasecmp(community, "public") != 0))
211 		libhal_device_set_property_string(ctx, tmp_udi,
212 			"network_device.snmp_community", community, &error);
213 
214 	if ((uri != NULL) || (device != NULL))
215 		libhal_device_set_property_string(ctx, tmp_udi,
216 			"printer.device", (uri ? uri : device), &error);
217 
218 	if (serial != NULL)
219 		libhal_device_set_property_string(ctx, tmp_udi,
220 			"printer.serial", serial, &error);
221 
222 	if (manufacturer != NULL)
223 		libhal_device_set_property_string(ctx, tmp_udi,
224 			"printer.vendor", manufacturer, &error);
225 
226 	if (model != NULL)
227 		libhal_device_set_property_string(ctx, tmp_udi,
228 			"printer.product", model, &error);
229 
230 	if (description != NULL)
231 		libhal_device_set_property_string(ctx, tmp_udi,
232 			"printer.description", description, &error);
233 
234 	/* commit the changes to the new UDI */
235 	rc = libhal_device_commit_to_gdl(ctx, tmp_udi, udi, &error);
236 
237 out:
238 	HAL_DEBUG(("result: %s (%s): %s, %s, %s, %s, %s", hostaddr, udi,
239 		NP(manufacturer), NP(model), NP(description), NP(serial),
240 		NP(uri)));
241 
242 	if (tmp_udi != NULL)
243 		free(tmp_udi);
244 	if (manufacturer != NULL)
245 		free(manufacturer);
246 	if (model != NULL)
247 		free(model);
248 	if (description != NULL)
249 		free(description);
250 	if (uri != NULL)
251 		free(uri);
252 	if (sn != NULL)
253 		free(sn);
254 
255 	if (dbus_error_is_set(&error)) {
256 		HAL_WARNING(("%s: %s", error.name, error.message));
257 		dbus_error_free(&error);
258 	}
259 
260 	HAL_DEBUG(("add: %s (%s)", hostaddr, udi));
261 
262 	return (rc);
263 }
264 
265 static int
266 number_of_interfaces(int s)
267 {
268 	int rc = -1;
269 	struct lifnum n;
270 
271 	memset(&n, 0 , sizeof (n));
272 	n.lifn_family = AF_INET;
273 	if (ioctl(s, SIOCGLIFNUM, (char *)&n) == 0)
274 		rc = n.lifn_count;
275 
276 	return (rc);
277 }
278 
279 static char *
280 broadcast_address(int s, char *ifname)
281 {
282 	char *result = NULL;
283 	struct lifreq r;
284 
285 	memset(&r, 0, sizeof (r));
286 	strlcpy(r.lifr_name, ifname, sizeof (r.lifr_name));
287 	if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&r) < 0) {
288 		HAL_DEBUG(("broadcast_address: ioctl(SIOCGLIFFLAGS) failed."));
289 		return (NULL);
290 	}
291 	if ((r.lifr_flags & (IFF_UP | IFF_LOOPBACK)) != IFF_UP) {
292 		return (NULL);
293 	}
294 	if (ioctl(s, SIOCGLIFBRDADDR, (char *)&r) >= 0) {
295 		char buf[INET_ADDRSTRLEN];
296 		struct sockaddr_in *s =
297 		    (struct sockaddr_in *)&r.lifr_broadaddr;
298 		result = (char *)inet_ntop(AF_INET,
299 		    &s->sin_addr, buf, sizeof (buf));
300 		if (result != NULL)
301 			result = strdup(result);
302 	}
303 
304 	return (result);
305 }
306 
307 GList *
308 broadcast_addresses()
309 {
310 	GList *result = NULL;
311 	int s;
312 	struct lifconf c;
313 	int count;
314 
315 	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
316 		return (NULL);
317 
318 	count = number_of_interfaces(s);
319 
320 	memset(&c, 0, sizeof (c));
321 	c.lifc_family = AF_INET;
322 	c.lifc_flags = 0;
323 	c.lifc_buf = calloc(count, sizeof (struct lifreq));
324 	c.lifc_len = (count * sizeof (struct lifreq));
325 
326 	if (ioctl(s, SIOCGLIFCONF, (char *)&c) == 0) {
327 		struct lifreq *r = c.lifc_req;
328 
329 		for (count = c.lifc_len / sizeof (struct lifreq);
330 		     count > 0; count--, r++) {
331 			char *address = broadcast_address(s, r->lifr_name);
332 
333 			if (address != NULL) /* add it to the list */
334 				result = g_list_append(result, address);
335 		}
336 	}
337 	free(c.lifc_buf);
338 	close(s);
339 
340 	return (result);
341 }
342