dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / lib / libdhcpagent / common / dhcpagent_ipc.c
blob6ffe7f915279b9eb20afe88f5062ea480450cda4
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 * Copyright (c) 2016 by Delphix. All rights reserved.
29 #include <string.h>
30 #include <unistd.h>
31 #include <stdlib.h>
32 #include <sys/uio.h>
33 #include <sys/socket.h>
34 #include <sys/types.h>
35 #include <fcntl.h>
36 #include <errno.h>
37 #include <limits.h>
38 #include <netinet/in.h>
39 #include <netinet/tcp.h>
40 #include <net/if.h>
41 #include <sys/sockio.h>
42 #include <sys/fcntl.h>
43 #include <sys/time.h>
44 #include <stdio.h> /* snprintf */
45 #include <arpa/inet.h> /* ntohl, ntohs, etc */
46 #include <poll.h>
48 #include "dhcpagent_ipc.h"
49 #include "dhcpagent_util.h"
52 * the protocol used here is a simple request/reply scheme: a client
53 * sends a dhcp_ipc_request_t message to the agent, and the agent
54 * sends a dhcp_ipc_reply_t back to the client. since the requests
55 * and replies can be variable-length, they are prefixed on "the wire"
56 * by a 32-bit number that tells the other end how many bytes to
57 * expect.
59 * the format of a request consists of a single dhcp_ipc_request_t;
60 * note that the length of this dhcp_ipc_request_t is variable (using
61 * the standard c array-of-size-1 trick). the type of the payload is
62 * given by `data_type', which is guaranteed to be `data_length' bytes
63 * long starting at `buffer'. note that `buffer' is guaranteed to be
64 * 32-bit aligned but it is poor taste to rely on this.
66 * the format of a reply is much the same: a single dhcp_ipc_reply_t;
67 * note again that the length of the dhcp_ipc_reply_t is variable.
68 * the type of the payload is given by `data_type', which is
69 * guaranteed to be `data_length' bytes long starting at `buffer'.
70 * once again, note that `buffer' is guaranteed to be 32-bit aligned
71 * but it is poor taste to rely on this.
73 * requests and replies can be paired up by comparing `ipc_id' fields.
76 #define BUFMAX 256
78 static int dhcp_ipc_timed_read(int, void *, unsigned int, int *);
79 static int getinfo_ifnames(const char *, dhcp_optnum_t *, DHCP_OPT **);
80 static char *get_ifnames(int, int);
82 /* must be kept in sync with enum in dhcpagent_ipc.h */
83 static const char *ipc_typestr[] = {
84 "drop", "extend", "ping", "release", "start", "status",
85 "inform", "get_tag"
89 * dhcp_ipc_alloc_request(): allocates a dhcp_ipc_request_t of the given type
90 * and interface, with a timeout of 0.
92 * input: dhcp_ipc_type_t: the type of ipc request to allocate
93 * const char *: the interface to associate the request with
94 * const void *: the payload to send with the message (NULL if none)
95 * uint32_t: the payload size (0 if none)
96 * dhcp_data_type_t: the description of the type of payload
97 * output: dhcp_ipc_request_t *: the request on success, NULL on failure
100 dhcp_ipc_request_t *
101 dhcp_ipc_alloc_request(dhcp_ipc_type_t type, const char *ifname,
102 const void *buffer, uint32_t buffer_size, dhcp_data_type_t data_type)
104 dhcp_ipc_request_t *request = calloc(1, DHCP_IPC_REQUEST_SIZE +
105 buffer_size);
107 if (request == NULL)
108 return (NULL);
110 request->message_type = type;
111 request->data_length = buffer_size;
112 request->data_type = data_type;
114 if (ifname != NULL)
115 (void) strlcpy(request->ifname, ifname, LIFNAMSIZ);
117 if (buffer != NULL)
118 (void) memcpy(request->buffer, buffer, buffer_size);
120 return (request);
124 * dhcp_ipc_alloc_reply(): allocates a dhcp_ipc_reply_t
126 * input: dhcp_ipc_request_t *: the request the reply is for
127 * int: the return code (0 for success, DHCP_IPC_E_* otherwise)
128 * const void *: the payload to send with the message (NULL if none)
129 * uint32_t: the payload size (0 if none)
130 * dhcp_data_type_t: the description of the type of payload
131 * output: dhcp_ipc_reply_t *: the reply on success, NULL on failure
134 dhcp_ipc_reply_t *
135 dhcp_ipc_alloc_reply(dhcp_ipc_request_t *request, int return_code,
136 const void *buffer, uint32_t buffer_size, dhcp_data_type_t data_type)
138 dhcp_ipc_reply_t *reply = calloc(1, DHCP_IPC_REPLY_SIZE + buffer_size);
140 if (reply == NULL)
141 return (NULL);
143 reply->message_type = request->message_type;
144 reply->ipc_id = request->ipc_id;
145 reply->return_code = return_code;
146 reply->data_length = buffer_size;
147 reply->data_type = data_type;
149 if (buffer != NULL)
150 (void) memcpy(reply->buffer, buffer, buffer_size);
152 return (reply);
156 * dhcp_ipc_get_data(): gets the data and data type from a dhcp_ipc_reply_t
158 * input: dhcp_ipc_reply_t *: the reply to get data from
159 * size_t *: the size of the resulting data
160 * dhcp_data_type_t *: the type of the message (returned)
161 * output: void *: a pointer to the data, if there is any.
164 void *
165 dhcp_ipc_get_data(dhcp_ipc_reply_t *reply, size_t *size, dhcp_data_type_t *type)
167 if (reply == NULL || reply->data_length == 0) {
168 *size = 0;
169 return (NULL);
172 if (type != NULL)
173 *type = reply->data_type;
175 *size = reply->data_length;
176 return (reply->buffer);
180 * dhcp_ipc_recv_msg(): gets a message using the agent's ipc protocol
182 * input: int: the file descriptor to get the message from
183 * void **: the address of a pointer to store the message
184 * (dynamically allocated)
185 * uint32_t: the minimum length of the packet
186 * int: the # of milliseconds to wait for the message (-1 is forever)
187 * output: int: DHCP_IPC_SUCCESS on success, DHCP_IPC_E_* otherwise
190 static int
191 dhcp_ipc_recv_msg(int fd, void **msg, uint32_t base_length, int msec)
193 int retval;
194 dhcp_ipc_reply_t *ipc_msg;
195 uint32_t length;
197 retval = dhcp_ipc_timed_read(fd, &length, sizeof (uint32_t), &msec);
198 if (retval != DHCP_IPC_SUCCESS)
199 return (retval);
201 if (length == 0)
202 return (DHCP_IPC_E_PROTO);
204 *msg = malloc(length);
205 if (*msg == NULL)
206 return (DHCP_IPC_E_MEMORY);
208 retval = dhcp_ipc_timed_read(fd, *msg, length, &msec);
209 if (retval != DHCP_IPC_SUCCESS) {
210 free(*msg);
211 return (retval);
214 if (length < base_length) {
215 free(*msg);
216 return (DHCP_IPC_E_PROTO);
220 * the data_length field is in the same place in either ipc message.
223 ipc_msg = (dhcp_ipc_reply_t *)(*msg);
224 if (ipc_msg->data_length + base_length != length) {
225 free(*msg);
226 return (DHCP_IPC_E_PROTO);
229 return (DHCP_IPC_SUCCESS);
233 * dhcp_ipc_recv_request(): gets a request using the agent's ipc protocol
235 * input: int: the file descriptor to get the message from
236 * dhcp_ipc_request_t **: address of a pointer to store the request
237 * (dynamically allocated)
238 * int: the # of milliseconds to wait for the message (-1 is forever)
239 * output: int: 0 on success, DHCP_IPC_E_* otherwise
243 dhcp_ipc_recv_request(int fd, dhcp_ipc_request_t **request, int msec)
245 int retval;
247 retval = dhcp_ipc_recv_msg(fd, (void **)request, DHCP_IPC_REQUEST_SIZE,
248 msec);
250 /* guarantee that ifname will be NUL-terminated */
251 if (retval == 0)
252 (*request)->ifname[LIFNAMSIZ - 1] = '\0';
254 return (retval);
258 * dhcp_ipc_recv_reply(): gets a reply using the agent's ipc protocol
260 * input: int: the file descriptor to get the message from
261 * dhcp_ipc_reply_t **: address of a pointer to store the reply
262 * (dynamically allocated)
263 * int32_t: timeout (in seconds), or DHCP_IPC_WAIT_FOREVER,
264 * or DHCP_IPC_WAIT_DEFAULT
265 * output: int: 0 on success, DHCP_IPC_E_* otherwise
268 static int
269 dhcp_ipc_recv_reply(int fd, dhcp_ipc_reply_t **reply, int32_t timeout)
272 * If the caller doesn't want to wait forever, and the amount of time
273 * it wants to wait is expressible as an integer number of milliseconds
274 * (as needed by the msg function), then we wait that amount of time
275 * plus an extra two seconds for the daemon to do its work. The extra
276 * two seconds is arbitrary; it should allow plenty of time for the
277 * daemon to respond within the existing timeout, as specified in the
278 * original request, so the only time we give up is when the daemon is
279 * stopped or otherwise malfunctioning.
281 * Note that the wait limit (milliseconds in an 'int') is over 24 days,
282 * so it's unlikely that any request will actually be that long, and
283 * it's unlikely that anyone will care if we wait forever on a request
284 * for a 30 day timer. The point is to protect against daemon
285 * malfunction in the usual cases, not to provide an absolute command
286 * timer.
288 if (timeout == DHCP_IPC_WAIT_DEFAULT)
289 timeout = DHCP_IPC_DEFAULT_WAIT;
290 if (timeout != DHCP_IPC_WAIT_FOREVER && timeout < INT_MAX / 1000 - 2)
291 timeout = (timeout + 2) * 1000;
292 else
293 timeout = -1;
294 return (dhcp_ipc_recv_msg(fd, (void **)reply, DHCP_IPC_REPLY_SIZE,
295 timeout));
299 * dhcp_ipc_send_msg(): transmits a message using the agent's ipc protocol
301 * input: int: the file descriptor to transmit on
302 * void *: the message to send
303 * uint32_t: the message length
304 * output: int: 0 on success, DHCP_IPC_E_* otherwise
307 static int
308 dhcp_ipc_send_msg(int fd, void *msg, uint32_t message_length)
310 struct iovec iovec[2];
312 iovec[0].iov_base = (caddr_t)&message_length;
313 iovec[0].iov_len = sizeof (uint32_t);
314 iovec[1].iov_base = msg;
315 iovec[1].iov_len = message_length;
317 if (writev(fd, iovec, sizeof (iovec) / sizeof (*iovec)) == -1)
318 return (DHCP_IPC_E_WRITEV);
320 return (0);
324 * dhcp_ipc_send_reply(): transmits a reply using the agent's ipc protocol
326 * input: int: the file descriptor to transmit on
327 * dhcp_ipc_reply_t *: the reply to send
328 * output: int: 0 on success, DHCP_IPC_E_* otherwise
332 dhcp_ipc_send_reply(int fd, dhcp_ipc_reply_t *reply)
334 return (dhcp_ipc_send_msg(fd, reply, DHCP_IPC_REPLY_SIZE +
335 reply->data_length));
339 * dhcp_ipc_send_request(): transmits a request using the agent's ipc protocol
341 * input: int: the file descriptor to transmit on
342 * dhcp_ipc_request_t *: the request to send
343 * output: int: 0 on success, DHCP_IPC_E_* otherwise
346 static int
347 dhcp_ipc_send_request(int fd, dhcp_ipc_request_t *request)
350 * for now, ipc_ids aren't really used, but they're intended
351 * to make it easy to send several requests and then collect
352 * all of the replies (and pair them with the requests).
355 request->ipc_id = gethrtime();
357 return (dhcp_ipc_send_msg(fd, request, DHCP_IPC_REQUEST_SIZE +
358 request->data_length));
362 * dhcp_ipc_make_request(): sends the provided request to the agent and reaps
363 * the reply
365 * input: dhcp_ipc_request_t *: the request to make
366 * dhcp_ipc_reply_t **: the reply (dynamically allocated)
367 * int32_t: timeout (in seconds), or DHCP_IPC_WAIT_FOREVER,
368 * or DHCP_IPC_WAIT_DEFAULT
369 * output: int: 0 on success, DHCP_IPC_E_* otherwise
373 dhcp_ipc_make_request(dhcp_ipc_request_t *request, dhcp_ipc_reply_t **reply,
374 int32_t timeout)
376 int fd, on, retval;
377 struct sockaddr_in sinv;
379 fd = socket(AF_INET, SOCK_STREAM, 0);
380 if (fd == -1)
381 return (DHCP_IPC_E_SOCKET);
384 * Bind a privileged port if we have sufficient privilege to do so.
385 * Continue as non-privileged otherwise.
387 on = 1;
388 (void) setsockopt(fd, IPPROTO_TCP, TCP_ANONPRIVBIND, &on, sizeof (on));
390 (void) memset(&sinv, 0, sizeof (sinv));
391 sinv.sin_family = AF_INET;
392 if (bind(fd, (struct sockaddr *)&sinv, sizeof (sinv)) == -1) {
393 (void) dhcp_ipc_close(fd);
394 return (DHCP_IPC_E_BIND);
397 sinv.sin_port = htons(IPPORT_DHCPAGENT);
398 sinv.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
399 retval = connect(fd, (struct sockaddr *)&sinv, sizeof (sinv));
400 if (retval == -1) {
401 (void) dhcp_ipc_close(fd);
402 return (DHCP_IPC_E_CONNECT);
405 request->timeout = timeout;
407 retval = dhcp_ipc_send_request(fd, request);
408 if (retval == 0)
409 retval = dhcp_ipc_recv_reply(fd, reply, timeout);
411 (void) dhcp_ipc_close(fd);
413 return (retval);
417 * dhcp_ipc_init(): initializes the ipc channel for use by the agent
419 * input: int *: the file descriptor to accept on (returned)
420 * output: int: 0 on success, DHCP_IPC_E_* otherwise
424 dhcp_ipc_init(int *listen_fd)
426 struct sockaddr_in sin;
427 int on = 1;
429 (void) memset(&sin, 0, sizeof (struct sockaddr_in));
431 sin.sin_family = AF_INET;
432 sin.sin_port = htons(IPPORT_DHCPAGENT);
433 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
435 *listen_fd = socket(AF_INET, SOCK_STREAM, 0);
436 if (*listen_fd == -1)
437 return (DHCP_IPC_E_SOCKET);
440 * we use SO_REUSEADDR here since in the case where there
441 * really is another daemon running that is using the agent's
442 * port, bind(3N) will fail. so we can't lose.
445 (void) setsockopt(*listen_fd, SOL_SOCKET, SO_REUSEADDR, &on,
446 sizeof (on));
448 if (bind(*listen_fd, (struct sockaddr *)&sin, sizeof (sin)) == -1) {
449 (void) close(*listen_fd);
450 return (DHCP_IPC_E_BIND);
453 if (listen(*listen_fd, DHCP_IPC_LISTEN_BACKLOG) == -1) {
454 (void) close(*listen_fd);
455 return (DHCP_IPC_E_LISTEN);
458 return (0);
462 * dhcp_ipc_accept(): accepts an incoming connection for the agent
464 * input: int: the file descriptor to accept on
465 * int *: the accepted file descriptor (returned)
466 * int *: nonzero if the client is privileged (returned)
467 * output: int: 0 on success, DHCP_IPC_E_* otherwise
468 * note: sets the socket into nonblocking mode
472 dhcp_ipc_accept(int listen_fd, int *fd, int *is_priv)
474 struct sockaddr_in sin_peer;
475 socklen_t sin_len = sizeof (sin_peer);
476 int sockflags;
479 * if we were extremely concerned with portability, we would
480 * set the socket into nonblocking mode before doing the
481 * accept(3N), since on BSD-based networking stacks, there is
482 * a potential race that can occur if the socket which
483 * connected to us performs a TCP RST before we accept, since
484 * BSD handles this case entirely in the kernel and as a
485 * result even though select said we will not block, we can
486 * end up blocking since there is no longer a connection to
487 * accept. on SVR4-based systems, this should be okay,
488 * and we will get EPROTO back, even though POSIX.1g says
489 * we should get ECONNABORTED.
492 *fd = accept(listen_fd, (struct sockaddr *)&sin_peer, &sin_len);
493 if (*fd == -1)
494 return (DHCP_IPC_E_ACCEPT);
496 /* get credentials */
497 *is_priv = ntohs(sin_peer.sin_port) < IPPORT_RESERVED;
500 * kick the socket into non-blocking mode so that later
501 * operations on the socket don't block and hold up the whole
502 * application. with the event demuxing approach, this may
503 * seem unnecessary, but in order to get partial reads/writes
504 * and to handle our internal protocol for passing data
505 * between the agent and its consumers, this is needed.
508 if ((sockflags = fcntl(*fd, F_GETFL, 0)) == -1) {
509 (void) close(*fd);
510 return (DHCP_IPC_E_FCNTL);
513 if (fcntl(*fd, F_SETFL, sockflags | O_NONBLOCK) == -1) {
514 (void) close(*fd);
515 return (DHCP_IPC_E_FCNTL);
518 return (0);
522 * dhcp_ipc_close(): closes an ipc descriptor
524 * input: int: the file descriptor to close
525 * output: int: 0 on success, DHCP_IPC_E_* otherwise
529 dhcp_ipc_close(int fd)
531 return ((close(fd) == -1) ? DHCP_IPC_E_CLOSE : 0);
535 * dhcp_ipc_strerror(): maps an ipc error code into a human-readable string
537 * input: int: the ipc error code to map
538 * output: const char *: the corresponding human-readable string
541 const char *
542 dhcp_ipc_strerror(int error)
544 /* note: this must be kept in sync with DHCP_IPC_E_* definitions */
545 const char *syscalls[] = {
546 "<unknown>", "socket", "fcntl", "read", "accept", "close",
547 "bind", "listen", "malloc", "connect", "writev", "poll"
550 const char *error_string;
551 static char buffer[BUFMAX];
553 switch (error) {
556 * none of these errors actually go over the wire.
557 * hence, we assume that errno is still fresh.
560 case DHCP_IPC_E_SOCKET: /* FALLTHRU */
561 case DHCP_IPC_E_FCNTL: /* FALLTHRU */
562 case DHCP_IPC_E_READ: /* FALLTHRU */
563 case DHCP_IPC_E_ACCEPT: /* FALLTHRU */
564 case DHCP_IPC_E_CLOSE: /* FALLTHRU */
565 case DHCP_IPC_E_BIND: /* FALLTHRU */
566 case DHCP_IPC_E_LISTEN: /* FALLTHRU */
567 case DHCP_IPC_E_CONNECT: /* FALLTHRU */
568 case DHCP_IPC_E_WRITEV: /* FALLTHRU */
569 case DHCP_IPC_E_POLL:
571 error_string = strerror(errno);
572 if (error_string == NULL)
573 error_string = "unknown error";
575 (void) snprintf(buffer, sizeof (buffer), "%s: %s",
576 syscalls[error], error_string);
578 error_string = buffer;
579 break;
581 case DHCP_IPC_E_MEMORY:
582 error_string = "out of memory";
583 break;
585 case DHCP_IPC_E_TIMEOUT:
586 error_string = "wait timed out, operation still pending...";
587 break;
589 case DHCP_IPC_E_INVIF:
590 error_string = "interface does not exist or cannot be managed "
591 "using DHCP";
592 break;
594 case DHCP_IPC_E_INT:
595 error_string = "internal error (might work later)";
596 break;
598 case DHCP_IPC_E_PERM:
599 error_string = "permission denied";
600 break;
602 case DHCP_IPC_E_OUTSTATE:
603 error_string = "interface not in appropriate state for command";
604 break;
606 case DHCP_IPC_E_PEND:
607 error_string = "interface currently has a pending command "
608 "(try later)";
609 break;
611 case DHCP_IPC_E_BOOTP:
612 error_string = "interface is administered with BOOTP, not DHCP";
613 break;
615 case DHCP_IPC_E_CMD_UNKNOWN:
616 error_string = "unknown command";
617 break;
619 case DHCP_IPC_E_UNKIF:
620 error_string = "interface is not under DHCP control";
621 break;
623 case DHCP_IPC_E_PROTO:
624 error_string = "ipc protocol violation";
625 break;
627 case DHCP_IPC_E_FAILEDIF:
628 error_string = "interface is in a FAILED state and must be "
629 "manually restarted";
630 break;
632 case DHCP_IPC_E_NOPRIMARY:
633 error_string = "primary interface requested but no primary "
634 "interface is set";
635 break;
637 case DHCP_IPC_E_NOIPIF:
638 error_string = "interface currently has no IP address";
639 break;
641 case DHCP_IPC_E_DOWNIF:
642 error_string = "interface is currently down";
643 break;
645 case DHCP_IPC_E_NOVALUE:
646 error_string = "no value was found for this option";
647 break;
649 case DHCP_IPC_E_RUNNING:
650 error_string = "DHCP is already running";
651 break;
653 case DHCP_IPC_E_SRVFAILED:
654 error_string = "DHCP server refused request";
655 break;
657 case DHCP_IPC_E_EOF:
658 error_string = "ipc connection closed";
659 break;
661 default:
662 error_string = "unknown error";
663 break;
667 * TODO: internationalize this error string
670 return (error_string);
674 * dhcp_string_to_request(): maps a string into a request code
676 * input: const char *: the string to map
677 * output: dhcp_ipc_type_t: the request code, or -1 if unknown
680 dhcp_ipc_type_t
681 dhcp_string_to_request(const char *request)
683 unsigned int i;
685 for (i = 0; i < DHCP_NIPC; i++)
686 if (strcmp(ipc_typestr[i], request) == 0)
687 return ((dhcp_ipc_type_t)i);
689 return ((dhcp_ipc_type_t)-1);
693 * dhcp_ipc_type_to_string(): maps an ipc command code into a human-readable
694 * string
696 * input: int: the ipc command code to map
697 * output: const char *: the corresponding human-readable string
700 const char *
701 dhcp_ipc_type_to_string(dhcp_ipc_type_t type)
703 if (type < 0 || type >= DHCP_NIPC)
704 return ("unknown");
705 else
706 return (ipc_typestr[(int)type]);
710 * getinfo_ifnames(): checks the value of a specified option on a list of
711 * interface names.
712 * input: const char *: a list of interface names to query (in order) for
713 * the option; "" queries the primary interface
714 * dhcp_optnum_t *: a description of the desired option
715 * DHCP_OPT **: filled in with the (dynamically allocated) value of
716 * the option upon success.
717 * output: int: DHCP_IPC_E_* on error, 0 on success or if no value was
718 * found but no error occurred either (*result will be NULL)
721 static int
722 getinfo_ifnames(const char *ifn, dhcp_optnum_t *optnum, DHCP_OPT **result)
724 dhcp_ipc_request_t *request;
725 dhcp_ipc_reply_t *reply;
726 char *ifnames, *ifnames_head;
727 DHCP_OPT *opt;
728 size_t opt_size;
729 int retval = 0;
731 *result = NULL;
732 ifnames_head = ifnames = strdup(ifn);
733 if (ifnames == NULL)
734 return (DHCP_IPC_E_MEMORY);
736 request = dhcp_ipc_alloc_request(DHCP_GET_TAG, "", optnum,
737 sizeof (dhcp_optnum_t), DHCP_TYPE_OPTNUM);
739 if (request == NULL) {
740 free(ifnames_head);
741 return (DHCP_IPC_E_MEMORY);
744 ifnames = strtok(ifnames, " ");
745 if (ifnames == NULL)
746 ifnames = "";
748 for (; ifnames != NULL; ifnames = strtok(NULL, " ")) {
750 (void) strlcpy(request->ifname, ifnames, LIFNAMSIZ);
751 retval = dhcp_ipc_make_request(request, &reply, 0);
752 if (retval != 0)
753 break;
755 if (reply->return_code == 0) {
756 opt = dhcp_ipc_get_data(reply, &opt_size, NULL);
757 if (opt_size > 2 && (opt->len == opt_size - 2)) {
758 *result = malloc(opt_size);
759 if (*result == NULL)
760 retval = DHCP_IPC_E_MEMORY;
761 else
762 (void) memcpy(*result, opt, opt_size);
764 free(reply);
765 break;
769 free(reply);
770 if (ifnames[0] == '\0')
771 break;
774 free(request);
775 free(ifnames_head);
777 return (retval);
781 * get_ifnames(): returns a space-separated list of interface names that
782 * match the specified flags
784 * input: int: flags which must be on in each interface returned
785 * int: flags which must be off in each interface returned
786 * output: char *: a dynamically-allocated list of interface names, or
787 * NULL upon failure.
790 static char *
791 get_ifnames(int flags_on, int flags_off)
793 struct ifconf ifc;
794 int n_ifs, i, sock_fd;
795 char *ifnames;
798 sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
799 if (sock_fd == -1)
800 return (NULL);
802 if ((ioctl(sock_fd, SIOCGIFNUM, &n_ifs) == -1) || (n_ifs <= 0)) {
803 (void) close(sock_fd);
804 return (NULL);
807 ifnames = calloc(1, n_ifs * (LIFNAMSIZ + 1));
808 ifc.ifc_len = n_ifs * sizeof (struct ifreq);
809 ifc.ifc_req = calloc(n_ifs, sizeof (struct ifreq));
810 if (ifc.ifc_req != NULL && ifnames != NULL) {
812 if (ioctl(sock_fd, SIOCGIFCONF, &ifc) == -1) {
813 (void) close(sock_fd);
814 free(ifnames);
815 free(ifc.ifc_req);
816 return (NULL);
819 for (i = 0; i < n_ifs; i++) {
821 if (ioctl(sock_fd, SIOCGIFFLAGS, &ifc.ifc_req[i]) == 0)
822 if ((ifc.ifc_req[i].ifr_flags &
823 (flags_on | flags_off)) != flags_on)
824 continue;
826 (void) strcat(ifnames, ifc.ifc_req[i].ifr_name);
827 (void) strcat(ifnames, " ");
830 if (strlen(ifnames) > 1)
831 ifnames[strlen(ifnames) - 1] = '\0';
834 (void) close(sock_fd);
835 free(ifc.ifc_req);
836 return (ifnames);
840 * dhcp_ipc_getinfo(): attempts to retrieve a value for the specified DHCP
841 * option; tries primary interface, then all DHCP-owned
842 * interfaces, then INFORMs on the remaining interfaces
843 * (these interfaces are dropped prior to returning).
844 * input: dhcp_optnum_t *: a description of the desired option
845 * DHCP_OPT **: filled in with the (dynamically allocated) value of
846 * the option upon success.
847 * int32_t: timeout (in seconds), or DHCP_IPC_WAIT_FOREVER,
848 * or DHCP_IPC_WAIT_DEFAULT.
849 * output: int: DHCP_IPC_E_* on error, 0 upon success.
853 dhcp_ipc_getinfo(dhcp_optnum_t *optnum, DHCP_OPT **result, int32_t timeout)
855 dhcp_ipc_request_t *request;
856 dhcp_ipc_reply_t *reply;
857 char *ifnames, *ifnames_copy, *ifnames_head;
858 int retval;
859 time_t start_time = time(NULL);
861 if (timeout == DHCP_IPC_WAIT_DEFAULT)
862 timeout = DHCP_IPC_DEFAULT_WAIT;
865 * wait at most 5 seconds for the agent to start.
868 if (dhcp_start_agent((timeout > 5 || timeout < 0) ? 5 : timeout) == -1)
869 return (DHCP_IPC_E_INT);
872 * check the primary interface for the option value first.
875 retval = getinfo_ifnames("", optnum, result);
876 if ((retval != 0) || (retval == 0 && *result != NULL))
877 return (retval);
880 * no luck. get a list of the interfaces under DHCP control
881 * and perform a GET_TAG on each one.
884 ifnames = get_ifnames(IFF_DHCPRUNNING, 0);
885 if (ifnames != NULL && strlen(ifnames) != 0) {
886 retval = getinfo_ifnames(ifnames, optnum, result);
887 if ((retval != 0) || (retval == 0 && *result != NULL)) {
888 free(ifnames);
889 return (retval);
892 free(ifnames);
895 * still no luck. retrieve a list of all interfaces on the
896 * system that could use DHCP but aren't. send INFORMs out on
897 * each one. after that, sit in a loop for the next `timeout'
898 * seconds, trying every second to see if a response for the
899 * option we want has come in on one of the interfaces.
902 ifnames = get_ifnames(IFF_UP|IFF_RUNNING, IFF_LOOPBACK|IFF_DHCPRUNNING);
903 if (ifnames == NULL || strlen(ifnames) == 0) {
904 free(ifnames);
905 return (DHCP_IPC_E_NOVALUE);
908 ifnames_head = ifnames_copy = strdup(ifnames);
909 if (ifnames_copy == NULL) {
910 free(ifnames);
911 return (DHCP_IPC_E_MEMORY);
914 request = dhcp_ipc_alloc_request(DHCP_INFORM, "", NULL, 0,
915 DHCP_TYPE_NONE);
916 if (request == NULL) {
917 free(ifnames);
918 free(ifnames_head);
919 return (DHCP_IPC_E_MEMORY);
922 ifnames_copy = strtok(ifnames_copy, " ");
923 for (; ifnames_copy != NULL; ifnames_copy = strtok(NULL, " ")) {
924 (void) strlcpy(request->ifname, ifnames_copy, LIFNAMSIZ);
925 if (dhcp_ipc_make_request(request, &reply, 0) == 0)
926 free(reply);
929 for (;;) {
930 if ((timeout != DHCP_IPC_WAIT_FOREVER) &&
931 (time(NULL) - start_time > timeout)) {
932 retval = DHCP_IPC_E_TIMEOUT;
933 break;
936 retval = getinfo_ifnames(ifnames, optnum, result);
937 if (retval != 0 || (retval == 0 && *result != NULL))
938 break;
940 (void) sleep(1);
944 * drop any interfaces that weren't under DHCP control before
945 * we got here; this keeps this function more of a black box
946 * and the behavior more consistent from call to call.
949 request->message_type = DHCP_DROP;
951 ifnames_copy = strcpy(ifnames_head, ifnames);
952 ifnames_copy = strtok(ifnames_copy, " ");
953 for (; ifnames_copy != NULL; ifnames_copy = strtok(NULL, " ")) {
954 (void) strlcpy(request->ifname, ifnames_copy, LIFNAMSIZ);
955 if (dhcp_ipc_make_request(request, &reply, 0) == 0)
956 free(reply);
959 free(request);
960 free(ifnames_head);
961 free(ifnames);
962 return (retval);
966 * dhcp_ipc_timed_read(): reads from a descriptor using a maximum timeout
968 * input: int: the file descriptor to read from
969 * void *: the buffer to read into
970 * unsigned int: the total length of data to read
971 * int *: the number of milliseconds to wait; the number of
972 * milliseconds left are returned (-1 is "forever")
973 * output: int: DHCP_IPC_SUCCESS on success, DHCP_IPC_E_* otherwise
976 static int
977 dhcp_ipc_timed_read(int fd, void *buffer, unsigned int length, int *msec)
979 unsigned int n_total = 0;
980 ssize_t n_read;
981 struct pollfd pollfd;
982 hrtime_t start, end;
983 int retv;
985 pollfd.fd = fd;
986 pollfd.events = POLLIN;
988 while (n_total < length) {
990 start = gethrtime();
992 retv = poll(&pollfd, 1, *msec);
993 if (retv == 0) {
994 /* This can happen only if *msec is not -1 */
995 *msec = 0;
996 return (DHCP_IPC_E_TIMEOUT);
999 if (*msec != -1) {
1000 end = gethrtime();
1001 *msec -= NSEC2MSEC(end - start);
1002 if (*msec < 0)
1003 *msec = 0;
1006 if (retv == -1) {
1007 if (errno != EINTR)
1008 return (DHCP_IPC_E_POLL);
1009 else if (*msec == 0)
1010 return (DHCP_IPC_E_TIMEOUT);
1011 continue;
1014 if (!(pollfd.revents & POLLIN)) {
1015 errno = EINVAL;
1016 return (DHCP_IPC_E_POLL);
1019 n_read = read(fd, (caddr_t)buffer + n_total, length - n_total);
1021 if (n_read == -1) {
1022 if (errno != EINTR)
1023 return (DHCP_IPC_E_READ);
1024 else if (*msec == 0)
1025 return (DHCP_IPC_E_TIMEOUT);
1026 continue;
1029 if (n_read == 0) {
1030 return (n_total == 0 ? DHCP_IPC_E_EOF :
1031 DHCP_IPC_E_PROTO);
1034 n_total += n_read;
1036 if (*msec == 0 && n_total < length)
1037 return (DHCP_IPC_E_TIMEOUT);
1040 return (DHCP_IPC_SUCCESS);