xref: /netbsd-src/external/mpl/dhcp/dist/common/socket.c (revision f407d9293b6650aa8c33d6a995f797bb6aaefd90)
1 /*	$NetBSD: socket.c,v 1.5 2022/04/03 01:10:58 christos Exp $	*/
2 
3 /* socket.c
4 
5    BSD socket interface code... */
6 
7 /*
8  * Copyright (C) 2004-2022 Internet Systems Consortium, Inc. ("ISC")
9  * Copyright (c) 1995-2003 by Internet Software Consortium
10  *
11  * This Source Code Form is subject to the terms of the Mozilla Public
12  * License, v. 2.0. If a copy of the MPL was not distributed with this
13  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
16  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
17  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
18  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
20  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
21  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  *
23  *   Internet Systems Consortium, Inc.
24  *   PO Box 360
25  *   Newmarket, NH 03857 USA
26  *   <info@isc.org>
27  *   https://www.isc.org/
28  *
29  */
30 
31 #include <sys/cdefs.h>
32 __RCSID("$NetBSD: socket.c,v 1.5 2022/04/03 01:10:58 christos Exp $");
33 
34 /* SO_BINDTODEVICE support added by Elliot Poger (poger@leland.stanford.edu).
35  * This sockopt allows a socket to be bound to a particular interface,
36  * thus enabling the use of DHCPD on a multihomed host.
37  * If SO_BINDTODEVICE is defined in your system header files, the use of
38  * this sockopt will be automatically enabled.
39  * I have implemented it under Linux; other systems should be doable also.
40  */
41 
42 #include "dhcpd.h"
43 #include <isc/util.h>
44 #include <errno.h>
45 #include <sys/ioctl.h>
46 #include <sys/uio.h>
47 #include <sys/uio.h>
48 
49 #if defined(sun) && defined(USE_V4_PKTINFO)
50 #include <sys/sysmacros.h>
51 #include <net/if.h>
52 #include <sys/sockio.h>
53 #include <net/if_dl.h>
54 #include <sys/dlpi.h>
55 #endif
56 
57 #ifdef USE_SOCKET_FALLBACK
58 # if !defined (USE_SOCKET_SEND)
59 #  define if_register_send if_register_fallback
60 #  define send_packet send_fallback
61 #  define if_reinitialize_send if_reinitialize_fallback
62 # endif
63 #endif
64 
65 #if defined(DHCPv6)
66 /*
67  * XXX: this is gross.  we need to go back and overhaul the API for socket
68  * handling.
69  */
70 static int no_global_v6_socket = 0;
71 static unsigned int global_v6_socket_references = 0;
72 static int global_v6_socket = -1;
73 #if defined(RELAY_PORT)
74 static unsigned int relay_port_v6_socket_references = 0;
75 static int relay_port_v6_socket = -1;
76 #endif
77 
78 static void if_register_multicast(struct interface_info *info);
79 #endif
80 
81 /*
82  * We can use a single socket for AF_INET (similar to AF_INET6) on all
83  * interfaces configured for DHCP if the system has support for IP_PKTINFO
84  * and IP_RECVPKTINFO (for example Solaris 11).
85  */
86 #if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO)
87 static unsigned int global_v4_socket_references = 0;
88 static int global_v4_socket = -1;
89 #endif
90 
91 /*
92  * If we can't bind() to a specific interface, then we can only have
93  * a single socket. This variable insures that we don't try to listen
94  * on two sockets.
95  */
96 #if !defined(SO_BINDTODEVICE) && !defined(USE_FALLBACK)
97 static int once = 0;
98 #endif /* !defined(SO_BINDTODEVICE) && !defined(USE_FALLBACK) */
99 
100 /* Reinitializes the specified interface after an address change.   This
101    is not required for packet-filter APIs. */
102 
103 #if defined (USE_SOCKET_SEND) || defined (USE_SOCKET_FALLBACK)
if_reinitialize_send(info)104 void if_reinitialize_send (info)
105 	struct interface_info *info;
106 {
107 #if 0
108 #ifndef USE_SOCKET_RECEIVE
109 	once = 0;
110 	close (info -> wfdesc);
111 #endif
112 	if_register_send (info);
113 #endif
114 }
115 #endif
116 
117 #ifdef USE_SOCKET_RECEIVE
if_reinitialize_receive(info)118 void if_reinitialize_receive (info)
119 	struct interface_info *info;
120 {
121 #if 0
122 	once = 0;
123 	close (info -> rfdesc);
124 	if_register_receive (info);
125 #endif
126 }
127 #endif
128 
129 #if defined (USE_SOCKET_SEND) || \
130 	defined (USE_SOCKET_RECEIVE) || \
131 		defined (USE_SOCKET_FALLBACK)
132 /* Generic interface registration routine... */
133 int
if_register_socket(struct interface_info * info,int family,int * do_multicast,struct in6_addr * linklocal6)134 if_register_socket(struct interface_info *info, int family,
135 		   int *do_multicast, struct in6_addr *linklocal6)
136 {
137 	struct sockaddr_storage name;
138 	int name_len;
139 	int sock;
140 	int flag;
141 	int domain;
142 #ifdef DHCPv6
143 	struct sockaddr_in6 *addr6;
144 #endif
145 	struct sockaddr_in *addr;
146 
147 	/* INSIST((family == AF_INET) || (family == AF_INET6)); */
148 
149 #if !defined(SO_BINDTODEVICE) && !defined(USE_FALLBACK)
150 	/* Make sure only one interface is registered. */
151 	if (once) {
152 		log_fatal ("The standard socket API can only support %s",
153 		       "hosts with a single network interface.");
154 	}
155 	once = 1;
156 #endif
157 
158 	/*
159 	 * Set up the address we're going to bind to, depending on the
160 	 * address family.
161 	 */
162 	memset(&name, 0, sizeof(name));
163 	switch (family) {
164 #ifdef DHCPv6
165 	case AF_INET6:
166 		addr6 = (struct sockaddr_in6 *)&name;
167 		addr6->sin6_family = AF_INET6;
168 		addr6->sin6_port = *libdhcp_callbacks.local_port;
169 #if defined(RELAY_PORT)
170 		if (relay_port &&
171 		    ((info->flags & INTERFACE_STREAMS) == INTERFACE_UPSTREAM))
172 			addr6->sin6_port = relay_port;
173 #endif
174 		/* A server feature */
175 		if (bind_local_address6) {
176 			memcpy(&addr6->sin6_addr,
177 			       &local_address6,
178 			       sizeof(addr6->sin6_addr));
179 		}
180 		/* A client feature */
181 		if (linklocal6) {
182 			memcpy(&addr6->sin6_addr,
183 			       linklocal6,
184 			       sizeof(addr6->sin6_addr));
185 		}
186 		if (IN6_IS_ADDR_LINKLOCAL(&addr6->sin6_addr)) {
187 			addr6->sin6_scope_id = if_nametoindex(info->name);
188 		}
189 #ifdef HAVE_SA_LEN
190 		addr6->sin6_len = sizeof(*addr6);
191 #endif
192 		name_len = sizeof(*addr6);
193 		domain = PF_INET6;
194 		if ((info->flags & INTERFACE_STREAMS) == INTERFACE_UPSTREAM) {
195 			*do_multicast = 0;
196 		}
197 		break;
198 #endif /* DHCPv6 */
199 
200 	case AF_INET:
201 	default:
202 		addr = (struct sockaddr_in *)&name;
203 		addr->sin_family = AF_INET;
204 		addr->sin_port = relay_port ? relay_port : *libdhcp_callbacks.local_port;
205 		memcpy(&addr->sin_addr,
206 		       &local_address,
207 		       sizeof(addr->sin_addr));
208 #ifdef HAVE_SA_LEN
209 		addr->sin_len = sizeof(*addr);
210 #endif
211 		name_len = sizeof(*addr);
212 		domain = PF_INET;
213 		break;
214 	}
215 
216 	/* Make a socket... */
217 	sock = socket(domain, SOCK_DGRAM, IPPROTO_UDP);
218 	if (sock < 0) {
219 		log_fatal("Can't create dhcp socket for %s: %m", info->name);
220 	}
221 
222 	/* Set the REUSEADDR option so that we don't fail to start if
223 	   we're being restarted. */
224 	flag = 1;
225 	if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
226 			(char *)&flag, sizeof(flag)) < 0) {
227 		log_fatal("Can't set SO_REUSEADDR on dhcp socket for"
228 			  " %s: %m", info->name);
229 	}
230 
231 	/* Set the BROADCAST option so that we can broadcast DHCP responses.
232 	   We shouldn't do this for fallback devices, and we can detect that
233 	   a device is a fallback because it has no ifp structure. */
234 	if (info->ifp &&
235 	    (setsockopt(sock, SOL_SOCKET, SO_BROADCAST,
236 			 (char *)&flag, sizeof(flag)) < 0)) {
237 		log_fatal("Can't set SO_BROADCAST on dhcp socket for"
238 			  " %s: %m", info->name);
239 	}
240 
241 #if defined(DHCPv6) && defined(SO_REUSEPORT)
242 	/*
243 	 * We only set SO_REUSEPORT on AF_INET6 sockets, so that multiple
244 	 * daemons can bind to their own sockets and get data for their
245 	 * respective interfaces.  This does not (and should not) affect
246 	 * DHCPv4 sockets; we can't yet support BSD sockets well, much
247 	 * less multiple sockets. Make sense only with multicast.
248 	 * RedHat defines SO_REUSEPORT with a kernel which does not support
249 	 * it and returns ENOPROTOOPT so in this case ignore the error.
250 	 */
251 	if ((local_family == AF_INET6) && *do_multicast) {
252 		flag = 1;
253 		if ((setsockopt(sock, SOL_SOCKET, SO_REUSEPORT,
254 			        (char *)&flag, sizeof(flag)) < 0) &&
255 		    (errno != ENOPROTOOPT)) {
256 			log_fatal("Can't set SO_REUSEPORT on dhcp socket for"
257 				  " %s: %m", info->name);
258 		}
259 	}
260 #endif
261 
262 	/* Bind the socket to this interface's IP address. */
263 	if (bind(sock, (struct sockaddr *)&name, name_len) < 0) {
264 		log_error("Can't bind to dhcp address: %m");
265 		log_error("Please make sure there is no other dhcp server");
266 		log_error("running and that there's no entry for dhcp or");
267 		log_error("bootp in /etc/inetd.conf.   Also make sure you");
268 		log_error("are not running HP JetAdmin software, which");
269 		log_fatal("includes a bootp server.");
270 	}
271 
272 #if defined(SO_BINDTODEVICE)
273 	/* Bind this socket to this interface. */
274 	if ((local_family != AF_INET6) && (info->ifp != NULL) &&
275 	    setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE,
276 		       (char *)(info -> ifp), sizeof(*(info -> ifp))) < 0) {
277 		log_fatal("Can't set SO_BINDTODEVICE on dhcp socket for"
278 			  " %s : %m", (char *)(info->ifp));
279 	}
280 #endif
281 
282 	/* IP_BROADCAST_IF instructs the kernel which interface to send
283 	 * IP packets whose destination address is 255.255.255.255.  These
284 	 * will be treated as subnet broadcasts on the interface identified
285 	 * by ip address (info -> primary_address).  This is only known to
286 	 * be defined in SCO system headers, and may not be defined in all
287 	 * releases.
288 	 */
289 #if defined(SCO) && defined(IP_BROADCAST_IF)
290         if (info->address_count &&
291 	    setsockopt(sock, IPPROTO_IP, IP_BROADCAST_IF, &info->addresses[0],
292 		       sizeof(info->addresses[0])) < 0)
293 		log_fatal("Can't set IP_BROADCAST_IF on dhcp socket for"
294 			  " %s: %m", info->name);
295 #endif
296 
297 #if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO)  && defined(USE_V4_PKTINFO)
298 	/*
299 	 * If we turn on IP_RECVPKTINFO we will be able to receive
300 	 * the interface index information of the received packet.
301 	 */
302 	if (family == AF_INET) {
303 		int on = 1;
304 		if (setsockopt(sock, IPPROTO_IP, IP_RECVPKTINFO,
305 		               &on, sizeof(on)) != 0) {
306 			log_fatal("Can't set IP_RECVPTKINFO on dhcp socket for"
307 				  " %s: %m", info->name);
308 		}
309 	}
310 #endif
311 
312 #ifdef DHCPv6
313 	/*
314 	 * If we turn on IPV6_PKTINFO, we will be able to receive
315 	 * additional information, such as the destination IP address.
316 	 * We need this to spot unicast packets.
317 	 */
318 	if (family == AF_INET6) {
319 		int on = 1;
320 #ifdef IPV6_RECVPKTINFO
321 		/* RFC3542 */
322 		if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO,
323 		               &on, sizeof(on)) != 0) {
324 			log_fatal("setsockopt: IPV6_RECVPKTINFO for %s: %m",
325 				  info->name);
326 		}
327 #else
328 		/* RFC2292 */
329 		if (setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO,
330 		               &on, sizeof(on)) != 0) {
331 			log_fatal("setsockopt: IPV6_PKTINFO for %s: %m",
332 				  info->name);
333 		}
334 #endif
335 	}
336 
337 #endif /* DHCPv6 */
338 
339 	return sock;
340 }
341 
342 #ifdef DHCPv6
set_multicast_hop_limit(struct interface_info * info,int hop_limit)343 void set_multicast_hop_limit(struct interface_info* info, int hop_limit) {
344 	if (setsockopt(info->wfdesc, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
345 		       &hop_limit, sizeof(int)) < 0) {
346 		log_fatal("setsockopt: IPV6_MULTICAST_HOPS for %s: %m",
347 			  info->name);
348 	}
349 
350 	log_debug("Setting hop count limit to %d for interface %s",
351 		  hop_limit, info->name);
352 
353 }
354 #endif /* DHCPv6 */
355 
356 #endif /* USE_SOCKET_SEND || USE_SOCKET_RECEIVE || USE_SOCKET_FALLBACK */
357 
358 #if defined (USE_SOCKET_SEND) || defined (USE_SOCKET_FALLBACK)
if_register_send(info)359 void if_register_send (info)
360 	struct interface_info *info;
361 {
362 #ifndef USE_SOCKET_RECEIVE
363 	info->wfdesc = if_register_socket(info, AF_INET, 0, NULL);
364 	/* If this is a normal IPv4 address, get the hardware address. */
365 	if (strcmp(info->name, "fallback") != 0)
366 		get_hw_addr(info->name, &info->hw_address);
367 #if defined (USE_SOCKET_FALLBACK)
368 	/* Fallback only registers for send, but may need to receive as
369 	   well. */
370 	info->rfdesc = info->wfdesc;
371 #endif
372 #else
373 	info->wfdesc = info->rfdesc;
374 #endif
375 	if (!quiet_interface_discovery)
376 		log_info ("Sending on   Socket/%s%s%s",
377 		      info->name,
378 		      (info->shared_network ? "/" : ""),
379 		      (info->shared_network ?
380 		       info->shared_network->name : ""));
381 }
382 
383 #if defined (USE_SOCKET_SEND)
if_deregister_send(info)384 void if_deregister_send (info)
385 	struct interface_info *info;
386 {
387 #ifndef USE_SOCKET_RECEIVE
388 	close (info -> wfdesc);
389 #endif
390 	info -> wfdesc = -1;
391 
392 	if (!quiet_interface_discovery)
393 		log_info ("Disabling output on Socket/%s%s%s",
394 		      info -> name,
395 		      (info -> shared_network ? "/" : ""),
396 		      (info -> shared_network ?
397 		       info -> shared_network -> name : ""));
398 }
399 #endif /* USE_SOCKET_SEND */
400 #endif /* USE_SOCKET_SEND || USE_SOCKET_FALLBACK */
401 
402 #ifdef USE_SOCKET_RECEIVE
if_register_receive(info)403 void if_register_receive (info)
404 	struct interface_info *info;
405 {
406 
407 #if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO)
408 	if (global_v4_socket_references == 0) {
409 		global_v4_socket = if_register_socket(info, AF_INET, 0, NULL);
410 		if (global_v4_socket < 0) {
411 			/*
412 			 * if_register_socket() fatally logs if it fails to
413 			 * create a socket, this is just a sanity check.
414 			 */
415 			log_fatal("Failed to create AF_INET socket %s:%d",
416 				  MDL);
417 		}
418 	}
419 
420 	info->rfdesc = global_v4_socket;
421 	global_v4_socket_references++;
422 #else
423 	/* If we're using the socket API for sending and receiving,
424 	   we don't need to register this interface twice. */
425 	info->rfdesc = if_register_socket(info, AF_INET, 0, NULL);
426 #endif /* IP_PKTINFO... */
427 	/* If this is a normal IPv4 address, get the hardware address. */
428 	if (strcmp(info->name, "fallback") != 0)
429 		get_hw_addr(info->name, &info->hw_address);
430 
431 	if (!quiet_interface_discovery)
432 		log_info ("Listening on Socket/%s%s%s",
433 		      info->name,
434 		      (info->shared_network ? "/" : ""),
435 		      (info->shared_network ?
436 		       info->shared_network->name : ""));
437 }
438 
if_deregister_receive(info)439 void if_deregister_receive (info)
440 	struct interface_info *info;
441 {
442 #if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO)
443 	/* Dereference the global v4 socket. */
444 	if ((info->rfdesc == global_v4_socket) &&
445 	    (global_v4_socket_references > 0)) {
446 		global_v4_socket_references--;
447 		info->rfdesc = -1;
448 	} else {
449 		log_fatal("Impossible condition at %s:%d", MDL);
450 	}
451 
452 	if (global_v4_socket_references == 0) {
453 		close(global_v4_socket);
454 		global_v4_socket = -1;
455 	}
456 #else
457 	close(info->rfdesc);
458 	info->rfdesc = -1;
459 #endif /* IP_PKTINFO... */
460 	if (!quiet_interface_discovery)
461 		log_info ("Disabling input on Socket/%s%s%s",
462 		      info -> name,
463 		      (info -> shared_network ? "/" : ""),
464 		      (info -> shared_network ?
465 		       info -> shared_network -> name : ""));
466 }
467 #endif /* USE_SOCKET_RECEIVE */
468 
469 
470 #ifdef DHCPv6
471 /*
472  * This function joins the interface to DHCPv6 multicast groups so we will
473  * receive multicast messages.
474  */
475 static void
if_register_multicast(struct interface_info * info)476 if_register_multicast(struct interface_info *info) {
477 	int sock = info->rfdesc;
478 	struct ipv6_mreq mreq;
479 
480 	if (inet_pton(AF_INET6, All_DHCP_Relay_Agents_and_Servers,
481 		      &mreq.ipv6mr_multiaddr) <= 0) {
482 		log_fatal("inet_pton: unable to convert '%s'",
483 			  All_DHCP_Relay_Agents_and_Servers);
484 	}
485 	mreq.ipv6mr_interface = if_nametoindex(info->name);
486 	if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP,
487 		       &mreq, sizeof(mreq)) < 0) {
488 		log_fatal("setsockopt: IPV6_JOIN_GROUP for %s: %m",
489 			  info->name);
490 	}
491 
492 	/*
493 	 * The relay agent code sets the streams so you know which way
494 	 * is up and down.  But a relay agent shouldn't join to the
495 	 * Server address, or else you get fun loops.  So up or down
496 	 * doesn't matter, we're just using that config to sense this is
497 	 * a relay agent.
498 	 */
499 	if ((info->flags & INTERFACE_STREAMS) == 0) {
500 		if (inet_pton(AF_INET6, All_DHCP_Servers,
501 			      &mreq.ipv6mr_multiaddr) <= 0) {
502 			log_fatal("inet_pton: unable to convert '%s'",
503 				  All_DHCP_Servers);
504 		}
505 		mreq.ipv6mr_interface = if_nametoindex(info->name);
506 		if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP,
507 			       &mreq, sizeof(mreq)) < 0) {
508 			log_fatal("setsockopt: IPV6_JOIN_GROUP for %s: %m",
509 				  info->name);
510 		}
511 	}
512 }
513 
514 void
if_register6(struct interface_info * info,int do_multicast)515 if_register6(struct interface_info *info, int do_multicast) {
516 	/* Bounce do_multicast to a stack variable because we may change it. */
517 	int req_multi = do_multicast;
518 
519 	if (no_global_v6_socket) {
520 		log_fatal("Impossible condition at %s:%d", MDL);
521 	}
522 
523 #if defined(RELAY_PORT)
524 	if (!relay_port ||
525 	    ((info->flags & INTERFACE_STREAMS) == INTERFACE_DOWNSTREAM)) {
526 #endif
527 	if (global_v6_socket_references == 0) {
528 		global_v6_socket = if_register_socket(info, AF_INET6,
529 						      &req_multi, NULL);
530 		if (global_v6_socket < 0) {
531 			/*
532 			 * if_register_socket() fatally logs if it fails to
533 			 * create a socket, this is just a sanity check.
534 			 */
535 			log_fatal("Impossible condition at %s:%d", MDL);
536 		} else if (bind_local_address6) {
537 			char addr6_str[INET6_ADDRSTRLEN];
538 
539 			if (inet_ntop(AF_INET6,
540 				      &local_address6,
541 				      addr6_str,
542 				      sizeof(addr6_str)) == NULL) {
543 				log_fatal("inet_ntop: unable to convert "
544 					  "local-address6");
545 			}
546 			log_info("Bound to [%s]:%d",
547 				 addr6_str,
548 				 (int) ntohs(*libdhcp_callbacks.local_port));
549 		} else {
550 			log_info("Bound to *:%d", (int) ntohs(*libdhcp_callbacks.local_port));
551 		}
552 	}
553 
554 	info->rfdesc = global_v6_socket;
555 	info->wfdesc = global_v6_socket;
556 	global_v6_socket_references++;
557 
558 #if defined(RELAY_PORT)
559 	} else {
560 	/*
561 	 * If relay port is defined, we need to register one
562 	 * IPv6 UPD socket to handle upstream server or relay agent
563 	 * with a non-547 UDP local port.
564 	 */
565 	if ((relay_port_v6_socket_references == 0) &&
566 	    ((info->flags & INTERFACE_STREAMS) == INTERFACE_UPSTREAM)) {
567 		relay_port_v6_socket = if_register_socket(info, AF_INET6,
568 							  &req_multi, NULL);
569 		if (relay_port_v6_socket < 0) {
570 			log_fatal("Impossible condition at %s:%d", MDL);
571 		} else {
572 			log_info("Bound to relay port *:%d",
573 				 (int) ntohs(relay_port));
574 		}
575 	}
576 	info->rfdesc = relay_port_v6_socket;
577 	info->wfdesc = relay_port_v6_socket;
578 	relay_port_v6_socket_references++;
579 	}
580 #endif
581 
582 	if (req_multi)
583 		if_register_multicast(info);
584 
585 	get_hw_addr(info->name, &info->hw_address);
586 
587 	if (!quiet_interface_discovery) {
588 		if (info->shared_network != NULL) {
589 			log_info("Listening on Socket/%d/%s/%s",
590 				 global_v6_socket, info->name,
591 				 info->shared_network->name);
592 			log_info("Sending on   Socket/%d/%s/%s",
593 				 global_v6_socket, info->name,
594 				 info->shared_network->name);
595 		} else {
596 			log_info("Listening on Socket/%s", info->name);
597 			log_info("Sending on   Socket/%s", info->name);
598 		}
599 	}
600 }
601 
602 /*
603  * Register an IPv6 socket bound to the link-local address of
604  * the argument interface (used by clients on a multiple interface box,
605  * vs. a server or a relay using the global IPv6 socket and running
606  * *only* in a single instance).
607  */
608 void
if_register_linklocal6(struct interface_info * info)609 if_register_linklocal6(struct interface_info *info) {
610 	int sock;
611 	int count;
612 	struct in6_addr *addr6 = NULL;
613 	int req_multi = 0;
614 
615 	if (global_v6_socket >= 0) {
616 		log_fatal("Impossible condition at %s:%d", MDL);
617 	}
618 
619 	no_global_v6_socket = 1;
620 
621 	/* get the (?) link-local address */
622 	for (count = 0; count < info->v6address_count; count++) {
623 		addr6 = &info->v6addresses[count];
624 		if (IN6_IS_ADDR_LINKLOCAL(addr6))
625 			break;
626 	}
627 
628 	if (!addr6) {
629 		log_fatal("no link-local IPv6 address for %s", info->name);
630 	}
631 
632 	sock = if_register_socket(info, AF_INET6, &req_multi, addr6);
633 
634 	if (sock < 0) {
635 		log_fatal("if_register_socket for %s fails", info->name);
636 	}
637 
638 	info->rfdesc = sock;
639 	info->wfdesc = sock;
640 
641 	get_hw_addr(info->name, &info->hw_address);
642 
643 	if (!quiet_interface_discovery) {
644 		if (info->shared_network != NULL) {
645 			log_info("Listening on Socket/%d/%s/%s",
646 				 global_v6_socket, info->name,
647 				 info->shared_network->name);
648 			log_info("Sending on   Socket/%d/%s/%s",
649 				 global_v6_socket, info->name,
650 				 info->shared_network->name);
651 		} else {
652 			log_info("Listening on Socket/%s", info->name);
653 			log_info("Sending on   Socket/%s", info->name);
654 		}
655 	}
656 }
657 
658 void
if_deregister6(struct interface_info * info)659 if_deregister6(struct interface_info *info) {
660 	/* client case */
661 	if (no_global_v6_socket) {
662 		close(info->rfdesc);
663 		info->rfdesc = -1;
664 		info->wfdesc = -1;
665 	} else if ((info->rfdesc == global_v6_socket) &&
666 		   (info->wfdesc == global_v6_socket) &&
667 		   (global_v6_socket_references > 0)) {
668 		/* Dereference the global v6 socket. */
669 		global_v6_socket_references--;
670 		info->rfdesc = -1;
671 		info->wfdesc = -1;
672 #if defined(RELAY_PORT)
673 	} else if (relay_port &&
674 		   (info->rfdesc == relay_port_v6_socket) &&
675 		   (info->wfdesc == relay_port_v6_socket) &&
676 		   (relay_port_v6_socket_references > 0)) {
677 		/* Dereference the relay port v6 socket. */
678 		relay_port_v6_socket_references--;
679 		info->rfdesc = -1;
680 		info->wfdesc = -1;
681 #endif
682 	} else {
683 		log_fatal("Impossible condition at %s:%d", MDL);
684 	}
685 
686 	if (!quiet_interface_discovery) {
687 		if (info->shared_network != NULL) {
688 			log_info("Disabling input on  Socket/%s/%s", info->name,
689 		       		 info->shared_network->name);
690 			log_info("Disabling output on Socket/%s/%s", info->name,
691 		       		 info->shared_network->name);
692 		} else {
693 			log_info("Disabling input on  Socket/%s", info->name);
694 			log_info("Disabling output on Socket/%s", info->name);
695 		}
696 	}
697 
698 	if (!no_global_v6_socket) {
699 		if (global_v6_socket_references == 0) {
700 			close(global_v6_socket);
701 			global_v6_socket = -1;
702 
703 			log_info("Unbound from *:%d",
704 				 (int) ntohs(*libdhcp_callbacks.local_port));
705 		}
706 #if defined(RELAY_PORT)
707 		if (relay_port && (relay_port_v6_socket_references == 0)) {
708 			close(relay_port_v6_socket);
709 			relay_port_v6_socket = -1;
710 
711 			log_info("Unbound from relay port *:%d",
712 				 (int) ntohs(relay_port));
713 		}
714 #endif
715 	}
716 }
717 #endif /* DHCPv6 */
718 
719 #if defined (USE_SOCKET_SEND) || defined (USE_SOCKET_FALLBACK)
send_packet(interface,packet,raw,len,from,to,hto)720 ssize_t send_packet (interface, packet, raw, len, from, to, hto)
721 	struct interface_info *interface;
722 	struct packet *packet;
723 	struct dhcp_packet *raw;
724 	size_t len;
725 	struct in_addr from;
726 	struct sockaddr_in *to;
727 	struct hardware *hto;
728 {
729 	int result;
730 #ifdef IGNORE_HOSTUNREACH
731 	int retry = 0;
732 	do {
733 #endif
734 #if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO)
735 		struct in_pktinfo pktinfo;
736 
737 		if (interface->ifp != NULL) {
738 			memset(&pktinfo, 0, sizeof (pktinfo));
739 			pktinfo.ipi_ifindex = interface->ifp->ifr_index;
740 			if (setsockopt(interface->wfdesc, IPPROTO_IP,
741 				       IP_PKTINFO, (char *)&pktinfo,
742 				       sizeof(pktinfo)) < 0)
743 				log_fatal("setsockopt: IP_PKTINFO for %s: %m",
744 					  (char*)(interface->ifp));
745 		}
746 #endif
747 		result = sendto (interface -> wfdesc, (char *)raw, len, 0,
748 				 (struct sockaddr *)to, sizeof *to);
749 #ifdef IGNORE_HOSTUNREACH
750 	} while (to -> sin_addr.s_addr == htonl (INADDR_BROADCAST) &&
751 		 result < 0 &&
752 		 (errno == EHOSTUNREACH ||
753 		  errno == ECONNREFUSED) &&
754 		 retry++ < 10);
755 #endif
756 	if (result < 0) {
757 		log_error ("send_packet: %m");
758 		if (errno == ENETUNREACH)
759 			log_error ("send_packet: please consult README file%s",
760 				   " regarding broadcast address.");
761 	}
762 	return result;
763 }
764 
765 #endif /* USE_SOCKET_SEND || USE_SOCKET_FALLBACK */
766 
767 #ifdef DHCPv6
768 /*
769  * Solaris 9 is missing the CMSG_LEN and CMSG_SPACE macros, so we will
770  * synthesize them (based on the BIND 9 technique).
771  */
772 
773 #ifndef CMSG_LEN
CMSG_LEN(size_t len)774 static size_t CMSG_LEN(size_t len) {
775 	size_t hdrlen;
776 	/*
777 	 * Cast NULL so that any pointer arithmetic performed by CMSG_DATA
778 	 * is correct.
779 	 */
780 	hdrlen = (size_t)CMSG_DATA(((struct cmsghdr *)NULL));
781 	return hdrlen + len;
782 }
783 #endif /* !CMSG_LEN */
784 
785 #ifndef CMSG_SPACE
CMSG_SPACE(size_t len)786 static size_t CMSG_SPACE(size_t len) {
787 	struct msghdr msg;
788 	struct cmsghdr *cmsgp;
789 
790 	/*
791 	 * XXX: The buffer length is an ad-hoc value, but should be enough
792 	 * in a practical sense.
793 	 */
794 	union {
795 		struct cmsghdr cmsg_sizer;
796 		u_int8_t pktinfo_sizer[sizeof(struct cmsghdr) + 1024];
797 	} dummybuf;
798 
799 	memset(&msg, 0, sizeof(msg));
800 	msg.msg_control = &dummybuf;
801 	msg.msg_controllen = sizeof(dummybuf);
802 
803 	cmsgp = (struct cmsghdr *)&dummybuf;
804 	cmsgp->cmsg_len = CMSG_LEN(len);
805 
806 	cmsgp = CMSG_NXTHDR(&msg, cmsgp);
807 	if (cmsgp != NULL) {
808 		return (char *)cmsgp - (char *)msg.msg_control;
809 	} else {
810 		return 0;
811 	}
812 }
813 #endif /* !CMSG_SPACE */
814 
815 #endif /* DHCPv6 */
816 
817 #if defined(DHCPv6) || \
818 	(defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && \
819 	 defined(USE_V4_PKTINFO))
820 /*
821  * For both send_packet6() and receive_packet6() we need to allocate
822  * space for the cmsg header information.  We do this once and reuse
823  * the buffer.  We also need the control buf for send_packet() and
824  * receive_packet() when we use a single socket and IP_PKTINFO to
825  * send the packet out the correct interface.
826  */
827 static void   *control_buf = NULL;
828 static size_t  control_buf_len = 0;
829 
830 static void
allocate_cmsg_cbuf(void)831 allocate_cmsg_cbuf(void) {
832 	control_buf_len = CMSG_SPACE(sizeof(struct in6_pktinfo));
833 	control_buf = dmalloc(control_buf_len, MDL);
834 	return;
835 }
836 #endif /* DHCPv6, IP_PKTINFO ... */
837 
838 #ifdef DHCPv6
839 /*
840  * For both send_packet6() and receive_packet6() we need to use the
841  * sendmsg()/recvmsg() functions rather than the simpler send()/recv()
842  * functions.
843  *
844  * In the case of send_packet6(), we need to do this in order to insure
845  * that the reply packet leaves on the same interface that it arrived
846  * on.
847  *
848  * In the case of receive_packet6(), we need to do this in order to
849  * get the IP address the packet was sent to. This is used to identify
850  * whether a packet is multicast or unicast.
851  *
852  * Helpful man pages: recvmsg, readv (talks about the iovec stuff), cmsg.
853  *
854  * Also see the sections in RFC 3542 about IPV6_PKTINFO.
855  */
856 
857 /* Send an IPv6 packet */
send_packet6(struct interface_info * interface,const unsigned char * raw,size_t len,struct sockaddr_in6 * to)858 ssize_t send_packet6(struct interface_info *interface,
859 		     const unsigned char *raw, size_t len,
860 		     struct sockaddr_in6 *to) {
861 	struct msghdr m;
862 	struct iovec v;
863 	struct sockaddr_in6 dst;
864 	int result;
865 	struct in6_pktinfo *pktinfo;
866 	struct cmsghdr *cmsg;
867 	unsigned int ifindex;
868 
869 	/*
870 	 * If necessary allocate space for the control message header.
871 	 * The space is common between send and receive.
872 	 */
873 
874 	if (control_buf == NULL) {
875 		allocate_cmsg_cbuf();
876 		if (control_buf == NULL) {
877 			log_error("send_packet6: unable to allocate cmsg header");
878 			return(ENOMEM);
879 		}
880 	}
881 	memset(control_buf, 0, control_buf_len);
882 
883 	/*
884 	 * Initialize our message header structure.
885 	 */
886 	memset(&m, 0, sizeof(m));
887 
888 	/*
889 	 * Set the target address we're sending to.
890 	 * Enforce the scope ID for bogus BSDs.
891 	 */
892 	memcpy(&dst, to, sizeof(dst));
893 	m.msg_name = &dst;
894 	m.msg_namelen = sizeof(dst);
895 	ifindex = if_nametoindex(interface->name);
896 
897 // Per OpenBSD patch-common_socket_c,v 1.7 2018/03/06 08:37:39 sthen Exp
898 // always set the scope id.  We'll leave the test for no global socket
899 // in place for all others.
900 #ifndef __OpenBSD__
901 	if (no_global_v6_socket)
902 #endif
903 		dst.sin6_scope_id = ifindex;
904 
905 	/*
906 	 * Set the data buffer we're sending. (Using this wacky
907 	 * "scatter-gather" stuff... we only have a single chunk
908 	 * of data to send, so we declare a single vector entry.)
909 	 */
910 	v.iov_base = (char *)raw;
911 	v.iov_len = len;
912 	m.msg_iov = &v;
913 	m.msg_iovlen = 1;
914 
915 	/*
916 	 * Setting the interface is a bit more involved.
917 	 *
918 	 * We have to create a "control message", and set that to
919 	 * define the IPv6 packet information. We could set the
920 	 * source address if we wanted, but we can safely let the
921 	 * kernel decide what that should be.
922 	 */
923 	m.msg_control = control_buf;
924 	m.msg_controllen = control_buf_len;
925 	cmsg = CMSG_FIRSTHDR(&m);
926 	INSIST(cmsg != NULL);
927 	cmsg->cmsg_level = IPPROTO_IPV6;
928 	cmsg->cmsg_type = IPV6_PKTINFO;
929 	cmsg->cmsg_len = CMSG_LEN(sizeof(*pktinfo));
930 	pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg);
931 	memset(pktinfo, 0, sizeof(*pktinfo));
932 	pktinfo->ipi6_addr = local_address6;
933 	pktinfo->ipi6_ifindex = ifindex;
934 
935 	result = sendmsg(interface->wfdesc, &m, 0);
936 	if (result < 0) {
937 		log_error("send_packet6: %m");
938 	}
939 	return result;
940 }
941 #endif /* DHCPv6 */
942 
943 #ifdef USE_SOCKET_RECEIVE
receive_packet(interface,buf,len,from,hfrom)944 ssize_t receive_packet (interface, buf, len, from, hfrom)
945 	struct interface_info *interface;
946 	unsigned char *buf;
947 	size_t len;
948 	struct sockaddr_in *from;
949 	struct hardware *hfrom;
950 {
951 #if !(defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO))
952 	SOCKLEN_T flen = sizeof *from;
953 #endif
954 	int result;
955 
956 	/*
957 	 * The normal Berkeley socket interface doesn't give us any way
958 	 * to know what hardware interface we received the message on,
959 	 * but we should at least make sure the structure is emptied.
960 	 */
961 	memset(hfrom, 0, sizeof(*hfrom));
962 
963 #ifdef IGNORE_HOSTUNREACH
964 	int retry = 0;
965 	do {
966 #endif
967 
968 #if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO)
969 	struct msghdr m;
970 	struct iovec v;
971 	struct cmsghdr *cmsg;
972 	struct in_pktinfo *pktinfo;
973 	unsigned int ifindex;
974 
975 	/*
976 	 * If necessary allocate space for the control message header.
977 	 * The space is common between send and receive.
978 	 */
979 	if (control_buf == NULL) {
980 		allocate_cmsg_cbuf();
981 		if (control_buf == NULL) {
982 			log_error("receive_packet: unable to allocate cmsg "
983 				  "header");
984 			return(ENOMEM);
985 		}
986 	}
987 	memset(control_buf, 0, control_buf_len);
988 
989 	/*
990 	 * Initialize our message header structure.
991 	 */
992 	memset(&m, 0, sizeof(m));
993 
994 	/*
995 	 * Point so we can get the from address.
996 	 */
997 	m.msg_name = from;
998 	m.msg_namelen = sizeof(*from);
999 
1000 	/*
1001 	 * Set the data buffer we're receiving. (Using this wacky
1002 	 * "scatter-gather" stuff... but we that doesn't really make
1003 	 * sense for us, so we use a single vector entry.)
1004 	 */
1005 	v.iov_base = buf;
1006 	v.iov_len = len;
1007 	m.msg_iov = &v;
1008 	m.msg_iovlen = 1;
1009 
1010 	/*
1011 	 * Getting the interface is a bit more involved.
1012 	 *
1013 	 * We set up some space for a "control message". We have
1014 	 * previously asked the kernel to give us packet
1015 	 * information (when we initialized the interface), so we
1016 	 * should get the interface index from that.
1017 	 */
1018 	m.msg_control = control_buf;
1019 	m.msg_controllen = control_buf_len;
1020 
1021 	result = recvmsg(interface->rfdesc, &m, 0);
1022 
1023 	if (result >= 0) {
1024 		/*
1025 		 * If we did read successfully, then we need to loop
1026 		 * through the control messages we received and
1027 		 * find the one with our inteface index.
1028 		 */
1029 		cmsg = CMSG_FIRSTHDR(&m);
1030 		while (cmsg != NULL) {
1031 			if ((cmsg->cmsg_level == IPPROTO_IP) &&
1032 			    (cmsg->cmsg_type == IP_PKTINFO)) {
1033 				pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg);
1034 				ifindex = pktinfo->ipi_ifindex;
1035 				/*
1036 				 * We pass the ifindex back to the caller
1037 				 * using the unused hfrom parameter avoiding
1038 				 * interface changes between sockets and
1039 				 * the discover code.
1040 				 */
1041 				memcpy(hfrom->hbuf, &ifindex, sizeof(ifindex));
1042 				return (result);
1043 			}
1044 			cmsg = CMSG_NXTHDR(&m, cmsg);
1045 		}
1046 
1047 		/*
1048 		 * We didn't find the necessary control message
1049 		 * flag it as an error
1050 		 */
1051 		result = -1;
1052 		errno = EIO;
1053 	}
1054 #else
1055 		result = recvfrom(interface -> rfdesc, (char *)buf, len, 0,
1056 				  (struct sockaddr *)from, &flen);
1057 #endif /* IP_PKTINFO ... */
1058 #ifdef IGNORE_HOSTUNREACH
1059 	} while (result < 0 &&
1060 		 (errno == EHOSTUNREACH ||
1061 		  errno == ECONNREFUSED) &&
1062 		 retry++ < 10);
1063 #endif
1064 	return (result);
1065 }
1066 
1067 #endif /* USE_SOCKET_RECEIVE */
1068 
1069 #ifdef DHCPv6
1070 ssize_t
receive_packet6(struct interface_info * interface,unsigned char * buf,size_t len,struct sockaddr_in6 * from,struct in6_addr * to_addr,unsigned int * if_idx)1071 receive_packet6(struct interface_info *interface,
1072 		unsigned char *buf, size_t len,
1073 		struct sockaddr_in6 *from, struct in6_addr *to_addr,
1074 		unsigned int *if_idx)
1075 {
1076 	struct msghdr m;
1077 	struct iovec v;
1078 	int result;
1079 	struct cmsghdr *cmsg;
1080 	struct in6_pktinfo *pktinfo;
1081 
1082 	/*
1083 	 * If necessary allocate space for the control message header.
1084 	 * The space is common between send and receive.
1085 	 */
1086 	if (control_buf == NULL) {
1087 		allocate_cmsg_cbuf();
1088 		if (control_buf == NULL) {
1089 			log_error("receive_packet6: unable to allocate cmsg "
1090 				  "header");
1091 			return(ENOMEM);
1092 		}
1093 	}
1094 	memset(control_buf, 0, control_buf_len);
1095 
1096 	/*
1097 	 * Initialize our message header structure.
1098 	 */
1099 	memset(&m, 0, sizeof(m));
1100 
1101 	/*
1102 	 * Point so we can get the from address.
1103 	 */
1104 	m.msg_name = from;
1105 	m.msg_namelen = sizeof(*from);
1106 
1107 	/*
1108 	 * Set the data buffer we're receiving. (Using this wacky
1109 	 * "scatter-gather" stuff... but we that doesn't really make
1110 	 * sense for us, so we use a single vector entry.)
1111 	 */
1112 	v.iov_base = buf;
1113 	v.iov_len = len;
1114 	m.msg_iov = &v;
1115 	m.msg_iovlen = 1;
1116 
1117 	/*
1118 	 * Getting the interface is a bit more involved.
1119 	 *
1120 	 * We set up some space for a "control message". We have
1121 	 * previously asked the kernel to give us packet
1122 	 * information (when we initialized the interface), so we
1123 	 * should get the destination address from that.
1124 	 */
1125 	m.msg_control = control_buf;
1126 	m.msg_controllen = control_buf_len;
1127 
1128 	result = recvmsg(interface->rfdesc, &m, 0);
1129 
1130 	if (result >= 0) {
1131 		/*
1132 		 * If we did read successfully, then we need to loop
1133 		 * through the control messages we received and
1134 		 * find the one with our destination address.
1135 		 */
1136 		cmsg = CMSG_FIRSTHDR(&m);
1137 		while (cmsg != NULL) {
1138 			if ((cmsg->cmsg_level == IPPROTO_IPV6) &&
1139 			    (cmsg->cmsg_type == IPV6_PKTINFO)) {
1140 				pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg);
1141 				*to_addr = pktinfo->ipi6_addr;
1142 				*if_idx = pktinfo->ipi6_ifindex;
1143 
1144 				return (result);
1145 			}
1146 			cmsg = CMSG_NXTHDR(&m, cmsg);
1147 		}
1148 
1149 		/*
1150 		 * We didn't find the necessary control message
1151 		 * flag is as an error
1152 		 */
1153 		result = -1;
1154 		errno = EIO;
1155 	}
1156 
1157 	return (result);
1158 }
1159 #endif /* DHCPv6 */
1160 
1161 #if defined (USE_SOCKET_FALLBACK)
1162 /* This just reads in a packet and silently discards it. */
1163 
fallback_discard(object)1164 isc_result_t fallback_discard (object)
1165 	omapi_object_t *object;
1166 {
1167 	char buf [1540];
1168 	struct sockaddr_in from;
1169 	SOCKLEN_T flen = sizeof from;
1170 	int status;
1171 	struct interface_info *interface;
1172 
1173 	if (object -> type != dhcp_type_interface)
1174 		return DHCP_R_INVALIDARG;
1175 	interface = (struct interface_info *)object;
1176 
1177 	status = recvfrom (interface -> wfdesc, buf, sizeof buf, 0,
1178 			   (struct sockaddr *)&from, &flen);
1179 #if defined (DEBUG)
1180 	/* Only report fallback discard errors if we're debugging. */
1181 	if (status < 0) {
1182 		log_error ("fallback_discard: %m");
1183 		return ISC_R_UNEXPECTED;
1184 	}
1185 #else
1186         /* ignore the fact that status value is never used */
1187         IGNORE_UNUSED(status);
1188 #endif
1189 	return ISC_R_SUCCESS;
1190 }
1191 #endif /* USE_SOCKET_FALLBACK */
1192 
1193 #if defined (USE_SOCKET_SEND)
can_unicast_without_arp(ip)1194 int can_unicast_without_arp (ip)
1195 	struct interface_info *ip;
1196 {
1197 	return 0;
1198 }
1199 
can_receive_unicast_unconfigured(ip)1200 int can_receive_unicast_unconfigured (ip)
1201 	struct interface_info *ip;
1202 {
1203 #if defined (SOCKET_CAN_RECEIVE_UNICAST_UNCONFIGURED)
1204 	return 1;
1205 #else
1206 	return 0;
1207 #endif
1208 }
1209 
supports_multiple_interfaces(ip)1210 int supports_multiple_interfaces (ip)
1211 	struct interface_info *ip;
1212 {
1213 #if defined(SO_BINDTODEVICE) || \
1214 	(defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && \
1215 	 defined(USE_V4_PKTINFO))
1216 	return(1);
1217 #else
1218 	return(0);
1219 #endif
1220 }
1221 
1222 /* If we have SO_BINDTODEVICE, set up a fallback interface; otherwise,
1223    do not. */
1224 
maybe_setup_fallback()1225 void maybe_setup_fallback ()
1226 {
1227 #if defined (USE_SOCKET_FALLBACK)
1228 	isc_result_t status;
1229 	struct interface_info *fbi = (struct interface_info *)0;
1230 	if (setup_fallback (&fbi, MDL)) {
1231 		fbi -> wfdesc = if_register_socket (fbi, AF_INET, 0, NULL);
1232 		fbi -> rfdesc = fbi -> wfdesc;
1233 		log_info ("Sending on   Socket/%s%s%s",
1234 		      fbi -> name,
1235 		      (fbi -> shared_network ? "/" : ""),
1236 		      (fbi -> shared_network ?
1237 		       fbi -> shared_network -> name : ""));
1238 
1239 		status = omapi_register_io_object ((omapi_object_t *)fbi,
1240 						   if_readsocket, 0,
1241 						   fallback_discard, 0, 0);
1242 		if (status != ISC_R_SUCCESS)
1243 			log_fatal ("Can't register I/O handle for %s: %s",
1244 				   fbi -> name, isc_result_totext (status));
1245 		interface_dereference (&fbi, MDL);
1246 	}
1247 #endif
1248 }
1249 
1250 
1251 #if defined(sun) && defined(USE_V4_PKTINFO)
1252 /* This code assumes the existence of SIOCGLIFHWADDR */
1253 void
get_hw_addr(const char * name,struct hardware * hw)1254 get_hw_addr(const char *name, struct hardware *hw) {
1255 	struct sockaddr_dl *dladdrp;
1256 	int sock, i;
1257 	struct lifreq lifr;
1258 
1259 	memset(&lifr, 0, sizeof (lifr));
1260 	(void) strlcpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
1261 	/*
1262 	 * Check if the interface is a virtual or IPMP interface - in those
1263 	 * cases it has no hw address, so generate a random one.
1264 	 */
1265 	if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ||
1266 	    ioctl(sock, SIOCGLIFFLAGS, &lifr) < 0) {
1267 		if (sock != -1)
1268 			(void) close(sock);
1269 
1270 #ifdef DHCPv6
1271 		/*
1272 		 * If approrpriate try this with an IPv6 socket
1273 		 */
1274 		if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) >= 0 &&
1275 		    ioctl(sock, SIOCGLIFFLAGS, &lifr) >= 0) {
1276 			goto flag_check;
1277 		}
1278 		if (sock != -1)
1279 			(void) close(sock);
1280 #endif
1281 		log_fatal("Couldn't get interface flags for %s: %m", name);
1282 
1283 	}
1284 
1285  flag_check:
1286 	if (lifr.lifr_flags & (IFF_VIRTUAL|IFF_IPMP)) {
1287 		hw->hlen = sizeof (hw->hbuf);
1288 		srandom((long)gethrtime());
1289 
1290 		hw->hbuf[0] = HTYPE_IPMP;
1291 		for (i = 1; i < hw->hlen; ++i) {
1292 			hw->hbuf[i] = random() % 256;
1293 		}
1294 
1295 		if (sock != -1)
1296 			(void) close(sock);
1297 		return;
1298 	}
1299 
1300 	if (ioctl(sock, SIOCGLIFHWADDR, &lifr) < 0)
1301 		log_fatal("Couldn't get interface hardware address for %s: %m",
1302 			  name);
1303 	dladdrp = (struct sockaddr_dl *)&lifr.lifr_addr;
1304 	hw->hlen = dladdrp->sdl_alen+1;
1305 	switch (dladdrp->sdl_type) {
1306 		case DL_CSMACD: /* IEEE 802.3 */
1307 		case DL_ETHER:
1308 			hw->hbuf[0] = HTYPE_ETHER;
1309 			break;
1310 		case DL_TPR:
1311 			hw->hbuf[0] = HTYPE_IEEE802;
1312 			break;
1313 		case DL_FDDI:
1314 			hw->hbuf[0] = HTYPE_FDDI;
1315 			break;
1316 		case DL_IB:
1317 			hw->hbuf[0] = HTYPE_INFINIBAND;
1318 			break;
1319 		default:
1320 			log_fatal("%s: unsupported DLPI MAC type %lu", name,
1321 				  (unsigned long)dladdrp->sdl_type);
1322 	}
1323 
1324 	memcpy(hw->hbuf+1, LLADDR(dladdrp), hw->hlen-1);
1325 
1326 	if (sock != -1)
1327 		(void) close(sock);
1328 }
1329 #endif /* defined(sun) */
1330 
1331 #endif /* USE_SOCKET_SEND */
1332