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