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]
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.
33 #include <sys/socket.h>
34 #include <sys/types.h>
38 #include <netinet/in.h>
39 #include <netinet/tcp.h>
41 #include <sys/sockio.h>
42 #include <sys/fcntl.h>
44 #include <stdio.h> /* snprintf */
45 #include <arpa/inet.h> /* ntohl, ntohs, etc */
47 #include "dhcpagent_ipc.h"
48 #include "dhcpagent_util.h"
51 * the protocol used here is a simple request/reply scheme: a client
52 * sends a dhcp_ipc_request_t message to the agent, and the agent
53 * sends a dhcp_ipc_reply_t back to the client. since the requests
54 * and replies can be variable-length, they are prefixed on "the wire"
55 * by a 32-bit number that tells the other end how many bytes to
58 * the format of a request consists of a single dhcp_ipc_request_t;
59 * note that the length of this dhcp_ipc_request_t is variable (using
60 * the standard c array-of-size-1 trick). the type of the payload is
61 * given by `data_type', which is guaranteed to be `data_length' bytes
62 * long starting at `buffer'. note that `buffer' is guaranteed to be
63 * 32-bit aligned but it is poor taste to rely on this.
65 * the format of a reply is much the same: a single dhcp_ipc_reply_t;
66 * note again that the length of the dhcp_ipc_reply_t is variable.
67 * the type of the payload is given by `data_type', which is
68 * guaranteed to be `data_length' bytes long starting at `buffer'.
69 * once again, note that `buffer' is guaranteed to be 32-bit aligned
70 * but it is poor taste to rely on this.
72 * requests and replies can be paired up by comparing `ipc_id' fields.
77 static int dhcp_ipc_timed_read(int, void *, unsigned int, int *);
78 static int getinfo_ifnames(const char *, dhcp_optnum_t
*, DHCP_OPT
**);
79 static char *get_ifnames(int, int);
81 /* must be kept in sync with enum in dhcpagent_ipc.h */
82 static const char *ipc_typestr
[] = {
83 "drop", "extend", "ping", "release", "start", "status",
88 * dhcp_ipc_alloc_request(): allocates a dhcp_ipc_request_t of the given type
89 * and interface, with a timeout of 0.
91 * input: dhcp_ipc_type_t: the type of ipc request to allocate
92 * const char *: the interface to associate the request with
93 * const void *: the payload to send with the message (NULL if none)
94 * uint32_t: the payload size (0 if none)
95 * dhcp_data_type_t: the description of the type of payload
96 * output: dhcp_ipc_request_t *: the request on success, NULL on failure
100 dhcp_ipc_alloc_request(dhcp_ipc_type_t type
, const char *ifname
,
101 const void *buffer
, uint32_t buffer_size
, dhcp_data_type_t data_type
)
103 dhcp_ipc_request_t
*request
= calloc(1, DHCP_IPC_REQUEST_SIZE
+
109 request
->message_type
= type
;
110 request
->data_length
= buffer_size
;
111 request
->data_type
= data_type
;
114 (void) strlcpy(request
->ifname
, ifname
, LIFNAMSIZ
);
117 (void) memcpy(request
->buffer
, buffer
, buffer_size
);
123 * dhcp_ipc_alloc_reply(): allocates a dhcp_ipc_reply_t
125 * input: dhcp_ipc_request_t *: the request the reply is for
126 * int: the return code (0 for success, DHCP_IPC_E_* otherwise)
127 * const void *: the payload to send with the message (NULL if none)
128 * uint32_t: the payload size (0 if none)
129 * dhcp_data_type_t: the description of the type of payload
130 * output: dhcp_ipc_reply_t *: the reply on success, NULL on failure
134 dhcp_ipc_alloc_reply(dhcp_ipc_request_t
*request
, int return_code
,
135 const void *buffer
, uint32_t buffer_size
, dhcp_data_type_t data_type
)
137 dhcp_ipc_reply_t
*reply
= calloc(1, DHCP_IPC_REPLY_SIZE
+ buffer_size
);
142 reply
->message_type
= request
->message_type
;
143 reply
->ipc_id
= request
->ipc_id
;
144 reply
->return_code
= return_code
;
145 reply
->data_length
= buffer_size
;
146 reply
->data_type
= data_type
;
149 (void) memcpy(reply
->buffer
, buffer
, buffer_size
);
155 * dhcp_ipc_get_data(): gets the data and data type from a dhcp_ipc_reply_t
157 * input: dhcp_ipc_reply_t *: the reply to get data from
158 * size_t *: the size of the resulting data
159 * dhcp_data_type_t *: the type of the message (returned)
160 * output: void *: a pointer to the data, if there is any.
164 dhcp_ipc_get_data(dhcp_ipc_reply_t
*reply
, size_t *size
, dhcp_data_type_t
*type
)
166 if (reply
== NULL
|| reply
->data_length
== 0) {
172 *type
= reply
->data_type
;
174 *size
= reply
->data_length
;
175 return (reply
->buffer
);
179 * dhcp_ipc_recv_msg(): gets a message using the agent's ipc protocol
181 * input: int: the file descriptor to get the message from
182 * void **: the address of a pointer to store the message
183 * (dynamically allocated)
184 * uint32_t: the minimum length of the packet
185 * int: the # of milliseconds to wait for the message (-1 is forever)
186 * output: int: DHCP_IPC_SUCCESS on success, DHCP_IPC_E_* otherwise
190 dhcp_ipc_recv_msg(int fd
, void **msg
, uint32_t base_length
, int msec
)
193 dhcp_ipc_reply_t
*ipc_msg
;
196 retval
= dhcp_ipc_timed_read(fd
, &length
, sizeof (uint32_t), &msec
);
197 if (retval
!= DHCP_IPC_SUCCESS
)
201 return (DHCP_IPC_E_PROTO
);
203 *msg
= malloc(length
);
205 return (DHCP_IPC_E_MEMORY
);
207 retval
= dhcp_ipc_timed_read(fd
, *msg
, length
, &msec
);
208 if (retval
!= DHCP_IPC_SUCCESS
) {
213 if (length
< base_length
) {
215 return (DHCP_IPC_E_PROTO
);
219 * the data_length field is in the same place in either ipc message.
222 ipc_msg
= (dhcp_ipc_reply_t
*)(*msg
);
223 if (ipc_msg
->data_length
+ base_length
!= length
) {
225 return (DHCP_IPC_E_PROTO
);
228 return (DHCP_IPC_SUCCESS
);
232 * dhcp_ipc_recv_request(): gets a request using the agent's ipc protocol
234 * input: int: the file descriptor to get the message from
235 * dhcp_ipc_request_t **: address of a pointer to store the request
236 * (dynamically allocated)
237 * int: the # of milliseconds to wait for the message (-1 is forever)
238 * output: int: 0 on success, DHCP_IPC_E_* otherwise
242 dhcp_ipc_recv_request(int fd
, dhcp_ipc_request_t
**request
, int msec
)
246 retval
= dhcp_ipc_recv_msg(fd
, (void **)request
, DHCP_IPC_REQUEST_SIZE
,
249 /* guarantee that ifname will be NUL-terminated */
251 (*request
)->ifname
[LIFNAMSIZ
- 1] = '\0';
257 * dhcp_ipc_recv_reply(): gets a reply using the agent's ipc protocol
259 * input: int: the file descriptor to get the message from
260 * dhcp_ipc_reply_t **: address of a pointer to store the reply
261 * (dynamically allocated)
262 * int32_t: timeout (in seconds), or DHCP_IPC_WAIT_FOREVER,
263 * or DHCP_IPC_WAIT_DEFAULT
264 * output: int: 0 on success, DHCP_IPC_E_* otherwise
268 dhcp_ipc_recv_reply(int fd
, dhcp_ipc_reply_t
**reply
, int32_t timeout
)
271 * If the caller doesn't want to wait forever, and the amount of time
272 * it wants to wait is expressible as an integer number of milliseconds
273 * (as needed by the msg function), then we wait that amount of time
274 * plus an extra two seconds for the daemon to do its work. The extra
275 * two seconds is arbitrary; it should allow plenty of time for the
276 * daemon to respond within the existing timeout, as specified in the
277 * original request, so the only time we give up is when the daemon is
278 * stopped or otherwise malfunctioning.
280 * Note that the wait limit (milliseconds in an 'int') is over 24 days,
281 * so it's unlikely that any request will actually be that long, and
282 * it's unlikely that anyone will care if we wait forever on a request
283 * for a 30 day timer. The point is to protect against daemon
284 * malfunction in the usual cases, not to provide an absolute command
287 if (timeout
== DHCP_IPC_WAIT_DEFAULT
)
288 timeout
= DHCP_IPC_DEFAULT_WAIT
;
289 if (timeout
!= DHCP_IPC_WAIT_FOREVER
&& timeout
< INT_MAX
/ 1000 - 2)
290 timeout
= (timeout
+ 2) * 1000;
293 return (dhcp_ipc_recv_msg(fd
, (void **)reply
, DHCP_IPC_REPLY_SIZE
,
298 * dhcp_ipc_send_msg(): transmits a message using the agent's ipc protocol
300 * input: int: the file descriptor to transmit on
301 * void *: the message to send
302 * uint32_t: the message length
303 * output: int: 0 on success, DHCP_IPC_E_* otherwise
307 dhcp_ipc_send_msg(int fd
, void *msg
, uint32_t message_length
)
309 struct iovec iovec
[2];
311 iovec
[0].iov_base
= (caddr_t
)&message_length
;
312 iovec
[0].iov_len
= sizeof (uint32_t);
313 iovec
[1].iov_base
= msg
;
314 iovec
[1].iov_len
= message_length
;
316 if (writev(fd
, iovec
, sizeof (iovec
) / sizeof (*iovec
)) == -1)
317 return (DHCP_IPC_E_WRITEV
);
323 * dhcp_ipc_send_reply(): transmits a reply using the agent's ipc protocol
325 * input: int: the file descriptor to transmit on
326 * dhcp_ipc_reply_t *: the reply to send
327 * output: int: 0 on success, DHCP_IPC_E_* otherwise
331 dhcp_ipc_send_reply(int fd
, dhcp_ipc_reply_t
*reply
)
333 return (dhcp_ipc_send_msg(fd
, reply
, DHCP_IPC_REPLY_SIZE
+
334 reply
->data_length
));
338 * dhcp_ipc_send_request(): transmits a request using the agent's ipc protocol
340 * input: int: the file descriptor to transmit on
341 * dhcp_ipc_request_t *: the request to send
342 * output: int: 0 on success, DHCP_IPC_E_* otherwise
346 dhcp_ipc_send_request(int fd
, dhcp_ipc_request_t
*request
)
349 * for now, ipc_ids aren't really used, but they're intended
350 * to make it easy to send several requests and then collect
351 * all of the replies (and pair them with the requests).
354 request
->ipc_id
= gethrtime();
356 return (dhcp_ipc_send_msg(fd
, request
, DHCP_IPC_REQUEST_SIZE
+
357 request
->data_length
));
361 * dhcp_ipc_make_request(): sends the provided request to the agent and reaps
364 * input: dhcp_ipc_request_t *: the request to make
365 * dhcp_ipc_reply_t **: the reply (dynamically allocated)
366 * int32_t: timeout (in seconds), or DHCP_IPC_WAIT_FOREVER,
367 * or DHCP_IPC_WAIT_DEFAULT
368 * output: int: 0 on success, DHCP_IPC_E_* otherwise
372 dhcp_ipc_make_request(dhcp_ipc_request_t
*request
, dhcp_ipc_reply_t
**reply
,
376 struct sockaddr_in sinv
;
378 fd
= socket(AF_INET
, SOCK_STREAM
, 0);
380 return (DHCP_IPC_E_SOCKET
);
383 * Bind a privileged port if we have sufficient privilege to do so.
384 * Continue as non-privileged otherwise.
387 (void) setsockopt(fd
, IPPROTO_TCP
, TCP_ANONPRIVBIND
, &on
, sizeof (on
));
389 (void) memset(&sinv
, 0, sizeof (sinv
));
390 sinv
.sin_family
= AF_INET
;
391 if (bind(fd
, (struct sockaddr
*)&sinv
, sizeof (sinv
)) == -1) {
392 (void) dhcp_ipc_close(fd
);
393 return (DHCP_IPC_E_BIND
);
396 sinv
.sin_port
= htons(IPPORT_DHCPAGENT
);
397 sinv
.sin_addr
.s_addr
= htonl(INADDR_LOOPBACK
);
398 retval
= connect(fd
, (struct sockaddr
*)&sinv
, sizeof (sinv
));
400 (void) dhcp_ipc_close(fd
);
401 return (DHCP_IPC_E_CONNECT
);
404 request
->timeout
= timeout
;
406 retval
= dhcp_ipc_send_request(fd
, request
);
408 retval
= dhcp_ipc_recv_reply(fd
, reply
, timeout
);
410 (void) dhcp_ipc_close(fd
);
416 * dhcp_ipc_init(): initializes the ipc channel for use by the agent
418 * input: int *: the file descriptor to accept on (returned)
419 * output: int: 0 on success, DHCP_IPC_E_* otherwise
423 dhcp_ipc_init(int *listen_fd
)
425 struct sockaddr_in sin
;
428 (void) memset(&sin
, 0, sizeof (struct sockaddr_in
));
430 sin
.sin_family
= AF_INET
;
431 sin
.sin_port
= htons(IPPORT_DHCPAGENT
);
432 sin
.sin_addr
.s_addr
= htonl(INADDR_LOOPBACK
);
434 *listen_fd
= socket(AF_INET
, SOCK_STREAM
, 0);
435 if (*listen_fd
== -1)
436 return (DHCP_IPC_E_SOCKET
);
439 * we use SO_REUSEADDR here since in the case where there
440 * really is another daemon running that is using the agent's
441 * port, bind(3N) will fail. so we can't lose.
444 (void) setsockopt(*listen_fd
, SOL_SOCKET
, SO_REUSEADDR
, &on
,
447 if (bind(*listen_fd
, (struct sockaddr
*)&sin
, sizeof (sin
)) == -1) {
448 (void) close(*listen_fd
);
449 return (DHCP_IPC_E_BIND
);
452 if (listen(*listen_fd
, DHCP_IPC_LISTEN_BACKLOG
) == -1) {
453 (void) close(*listen_fd
);
454 return (DHCP_IPC_E_LISTEN
);
461 * dhcp_ipc_accept(): accepts an incoming connection for the agent
463 * input: int: the file descriptor to accept on
464 * int *: the accepted file descriptor (returned)
465 * int *: nonzero if the client is privileged (returned)
466 * output: int: 0 on success, DHCP_IPC_E_* otherwise
467 * note: sets the socket into nonblocking mode
471 dhcp_ipc_accept(int listen_fd
, int *fd
, int *is_priv
)
473 struct sockaddr_in sin_peer
;
474 int sin_len
= sizeof (sin_peer
);
478 * if we were extremely concerned with portability, we would
479 * set the socket into nonblocking mode before doing the
480 * accept(3N), since on BSD-based networking stacks, there is
481 * a potential race that can occur if the socket which
482 * connected to us performs a TCP RST before we accept, since
483 * BSD handles this case entirely in the kernel and as a
484 * result even though select said we will not block, we can
485 * end up blocking since there is no longer a connection to
486 * accept. on SVR4-based systems, this should be okay,
487 * and we will get EPROTO back, even though POSIX.1g says
488 * we should get ECONNABORTED.
491 *fd
= accept(listen_fd
, (struct sockaddr
*)&sin_peer
, &sin_len
);
493 return (DHCP_IPC_E_ACCEPT
);
495 /* get credentials */
496 *is_priv
= ntohs(sin_peer
.sin_port
) < IPPORT_RESERVED
;
499 * kick the socket into non-blocking mode so that later
500 * operations on the socket don't block and hold up the whole
501 * application. with the event demuxing approach, this may
502 * seem unnecessary, but in order to get partial reads/writes
503 * and to handle our internal protocol for passing data
504 * between the agent and its consumers, this is needed.
507 if ((sockflags
= fcntl(*fd
, F_GETFL
, 0)) == -1) {
509 return (DHCP_IPC_E_FCNTL
);
512 if (fcntl(*fd
, F_SETFL
, sockflags
| O_NONBLOCK
) == -1) {
514 return (DHCP_IPC_E_FCNTL
);
521 * dhcp_ipc_close(): closes an ipc descriptor
523 * input: int: the file descriptor to close
524 * output: int: 0 on success, DHCP_IPC_E_* otherwise
528 dhcp_ipc_close(int fd
)
530 return ((close(fd
) == -1) ? DHCP_IPC_E_CLOSE
: 0);
534 * dhcp_ipc_strerror(): maps an ipc error code into a human-readable string
536 * input: int: the ipc error code to map
537 * output: const char *: the corresponding human-readable string
541 dhcp_ipc_strerror(int error
)
543 /* note: this must be kept in sync with DHCP_IPC_E_* definitions */
544 const char *syscalls
[] = {
545 "<unknown>", "socket", "fcntl", "read", "accept", "close",
546 "bind", "listen", "malloc", "connect", "writev", "poll"
549 const char *error_string
;
550 static char buffer
[BUFMAX
];
555 * none of these errors actually go over the wire.
556 * hence, we assume that errno is still fresh.
559 case DHCP_IPC_E_SOCKET
: /* FALLTHRU */
560 case DHCP_IPC_E_FCNTL
: /* FALLTHRU */
561 case DHCP_IPC_E_READ
: /* FALLTHRU */
562 case DHCP_IPC_E_ACCEPT
: /* FALLTHRU */
563 case DHCP_IPC_E_CLOSE
: /* FALLTHRU */
564 case DHCP_IPC_E_BIND
: /* FALLTHRU */
565 case DHCP_IPC_E_LISTEN
: /* FALLTHRU */
566 case DHCP_IPC_E_CONNECT
: /* FALLTHRU */
567 case DHCP_IPC_E_WRITEV
: /* FALLTHRU */
568 case DHCP_IPC_E_POLL
:
570 error_string
= strerror(errno
);
571 if (error_string
== NULL
)
572 error_string
= "unknown error";
574 (void) snprintf(buffer
, sizeof (buffer
), "%s: %s",
575 syscalls
[error
], error_string
);
577 error_string
= buffer
;
580 case DHCP_IPC_E_MEMORY
:
581 error_string
= "out of memory";
584 case DHCP_IPC_E_TIMEOUT
:
585 error_string
= "wait timed out, operation still pending...";
588 case DHCP_IPC_E_INVIF
:
589 error_string
= "interface does not exist or cannot be managed "
594 error_string
= "internal error (might work later)";
597 case DHCP_IPC_E_PERM
:
598 error_string
= "permission denied";
601 case DHCP_IPC_E_OUTSTATE
:
602 error_string
= "interface not in appropriate state for command";
605 case DHCP_IPC_E_PEND
:
606 error_string
= "interface currently has a pending command "
610 case DHCP_IPC_E_BOOTP
:
611 error_string
= "interface is administered with BOOTP, not DHCP";
614 case DHCP_IPC_E_CMD_UNKNOWN
:
615 error_string
= "unknown command";
618 case DHCP_IPC_E_UNKIF
:
619 error_string
= "interface is not under DHCP control";
622 case DHCP_IPC_E_PROTO
:
623 error_string
= "ipc protocol violation";
626 case DHCP_IPC_E_FAILEDIF
:
627 error_string
= "interface is in a FAILED state and must be "
628 "manually restarted";
631 case DHCP_IPC_E_NOPRIMARY
:
632 error_string
= "primary interface requested but no primary "
636 case DHCP_IPC_E_NOIPIF
:
637 error_string
= "interface currently has no IP address";
640 case DHCP_IPC_E_DOWNIF
:
641 error_string
= "interface is currently down";
644 case DHCP_IPC_E_NOVALUE
:
645 error_string
= "no value was found for this option";
648 case DHCP_IPC_E_RUNNING
:
649 error_string
= "DHCP is already running";
652 case DHCP_IPC_E_SRVFAILED
:
653 error_string
= "DHCP server refused request";
657 error_string
= "ipc connection closed";
661 error_string
= "unknown error";
666 * TODO: internationalize this error string
669 return (error_string
);
673 * dhcp_string_to_request(): maps a string into a request code
675 * input: const char *: the string to map
676 * output: dhcp_ipc_type_t: the request code, or -1 if unknown
680 dhcp_string_to_request(const char *request
)
684 for (i
= 0; i
< DHCP_NIPC
; i
++)
685 if (strcmp(ipc_typestr
[i
], request
) == 0)
686 return ((dhcp_ipc_type_t
)i
);
688 return ((dhcp_ipc_type_t
)-1);
692 * dhcp_ipc_type_to_string(): maps an ipc command code into a human-readable
695 * input: int: the ipc command code to map
696 * output: const char *: the corresponding human-readable string
700 dhcp_ipc_type_to_string(dhcp_ipc_type_t type
)
702 if (type
< 0 || type
>= DHCP_NIPC
)
705 return (ipc_typestr
[(int)type
]);
709 * getinfo_ifnames(): checks the value of a specified option on a list of
711 * input: const char *: a list of interface names to query (in order) for
712 * the option; "" queries the primary interface
713 * dhcp_optnum_t *: a description of the desired option
714 * DHCP_OPT **: filled in with the (dynamically allocated) value of
715 * the option upon success.
716 * output: int: DHCP_IPC_E_* on error, 0 on success or if no value was
717 * found but no error occurred either (*result will be NULL)
721 getinfo_ifnames(const char *ifn
, dhcp_optnum_t
*optnum
, DHCP_OPT
**result
)
723 dhcp_ipc_request_t
*request
;
724 dhcp_ipc_reply_t
*reply
;
725 char *ifnames
, *ifnames_head
;
731 ifnames_head
= ifnames
= strdup(ifn
);
733 return (DHCP_IPC_E_MEMORY
);
735 request
= dhcp_ipc_alloc_request(DHCP_GET_TAG
, "", optnum
,
736 sizeof (dhcp_optnum_t
), DHCP_TYPE_OPTNUM
);
738 if (request
== NULL
) {
740 return (DHCP_IPC_E_MEMORY
);
743 ifnames
= strtok(ifnames
, " ");
747 for (; ifnames
!= NULL
; ifnames
= strtok(NULL
, " ")) {
749 (void) strlcpy(request
->ifname
, ifnames
, LIFNAMSIZ
);
750 retval
= dhcp_ipc_make_request(request
, &reply
, 0);
754 if (reply
->return_code
== 0) {
755 opt
= dhcp_ipc_get_data(reply
, &opt_size
, NULL
);
756 if (opt_size
> 2 && (opt
->len
== opt_size
- 2)) {
757 *result
= malloc(opt_size
);
759 retval
= DHCP_IPC_E_MEMORY
;
761 (void) memcpy(*result
, opt
, opt_size
);
769 if (ifnames
[0] == '\0')
780 * get_ifnames(): returns a space-separated list of interface names that
781 * match the specified flags
783 * input: int: flags which must be on in each interface returned
784 * int: flags which must be off in each interface returned
785 * output: char *: a dynamically-allocated list of interface names, or
790 get_ifnames(int flags_on
, int flags_off
)
793 int n_ifs
, i
, sock_fd
;
797 sock_fd
= socket(AF_INET
, SOCK_DGRAM
, 0);
801 if ((ioctl(sock_fd
, SIOCGIFNUM
, &n_ifs
) == -1) || (n_ifs
<= 0)) {
802 (void) close(sock_fd
);
806 ifnames
= calloc(1, n_ifs
* (LIFNAMSIZ
+ 1));
807 ifc
.ifc_len
= n_ifs
* sizeof (struct ifreq
);
808 ifc
.ifc_req
= calloc(n_ifs
, sizeof (struct ifreq
));
809 if (ifc
.ifc_req
!= NULL
&& ifnames
!= NULL
) {
811 if (ioctl(sock_fd
, SIOCGIFCONF
, &ifc
) == -1) {
812 (void) close(sock_fd
);
818 for (i
= 0; i
< n_ifs
; i
++) {
820 if (ioctl(sock_fd
, SIOCGIFFLAGS
, &ifc
.ifc_req
[i
]) == 0)
821 if ((ifc
.ifc_req
[i
].ifr_flags
&
822 (flags_on
| flags_off
)) != flags_on
)
825 (void) strcat(ifnames
, ifc
.ifc_req
[i
].ifr_name
);
826 (void) strcat(ifnames
, " ");
829 if (strlen(ifnames
) > 1)
830 ifnames
[strlen(ifnames
) - 1] = '\0';
833 (void) close(sock_fd
);
839 * dhcp_ipc_getinfo(): attempts to retrieve a value for the specified DHCP
840 * option; tries primary interface, then all DHCP-owned
841 * interfaces, then INFORMs on the remaining interfaces
842 * (these interfaces are dropped prior to returning).
843 * input: dhcp_optnum_t *: a description of the desired option
844 * DHCP_OPT **: filled in with the (dynamically allocated) value of
845 * the option upon success.
846 * int32_t: timeout (in seconds), or DHCP_IPC_WAIT_FOREVER,
847 * or DHCP_IPC_WAIT_DEFAULT.
848 * output: int: DHCP_IPC_E_* on error, 0 upon success.
852 dhcp_ipc_getinfo(dhcp_optnum_t
*optnum
, DHCP_OPT
**result
, int32_t timeout
)
854 dhcp_ipc_request_t
*request
;
855 dhcp_ipc_reply_t
*reply
;
856 char *ifnames
, *ifnames_copy
, *ifnames_head
;
858 time_t start_time
= time(NULL
);
860 if (timeout
== DHCP_IPC_WAIT_DEFAULT
)
861 timeout
= DHCP_IPC_DEFAULT_WAIT
;
864 * wait at most 5 seconds for the agent to start.
867 if (dhcp_start_agent((timeout
> 5 || timeout
< 0) ? 5 : timeout
) == -1)
868 return (DHCP_IPC_E_INT
);
871 * check the primary interface for the option value first.
874 retval
= getinfo_ifnames("", optnum
, result
);
875 if ((retval
!= 0) || (retval
== 0 && *result
!= NULL
))
879 * no luck. get a list of the interfaces under DHCP control
880 * and perform a GET_TAG on each one.
883 ifnames
= get_ifnames(IFF_DHCPRUNNING
, 0);
884 if (ifnames
!= NULL
&& strlen(ifnames
) != 0) {
885 retval
= getinfo_ifnames(ifnames
, optnum
, result
);
886 if ((retval
!= 0) || (retval
== 0 && *result
!= NULL
)) {
894 * still no luck. retrieve a list of all interfaces on the
895 * system that could use DHCP but aren't. send INFORMs out on
896 * each one. after that, sit in a loop for the next `timeout'
897 * seconds, trying every second to see if a response for the
898 * option we want has come in on one of the interfaces.
901 ifnames
= get_ifnames(IFF_UP
|IFF_RUNNING
, IFF_LOOPBACK
|IFF_DHCPRUNNING
);
902 if (ifnames
== NULL
|| strlen(ifnames
) == 0) {
904 return (DHCP_IPC_E_NOVALUE
);
907 ifnames_head
= ifnames_copy
= strdup(ifnames
);
908 if (ifnames_copy
== NULL
) {
910 return (DHCP_IPC_E_MEMORY
);
913 request
= dhcp_ipc_alloc_request(DHCP_INFORM
, "", NULL
, 0,
915 if (request
== NULL
) {
918 return (DHCP_IPC_E_MEMORY
);
921 ifnames_copy
= strtok(ifnames_copy
, " ");
922 for (; ifnames_copy
!= NULL
; ifnames_copy
= strtok(NULL
, " ")) {
923 (void) strlcpy(request
->ifname
, ifnames_copy
, LIFNAMSIZ
);
924 if (dhcp_ipc_make_request(request
, &reply
, 0) == 0)
929 if ((timeout
!= DHCP_IPC_WAIT_FOREVER
) &&
930 (time(NULL
) - start_time
> timeout
)) {
931 retval
= DHCP_IPC_E_TIMEOUT
;
935 retval
= getinfo_ifnames(ifnames
, optnum
, result
);
936 if (retval
!= 0 || (retval
== 0 && *result
!= NULL
))
943 * drop any interfaces that weren't under DHCP control before
944 * we got here; this keeps this function more of a black box
945 * and the behavior more consistent from call to call.
948 request
->message_type
= DHCP_DROP
;
950 ifnames_copy
= strcpy(ifnames_head
, ifnames
);
951 ifnames_copy
= strtok(ifnames_copy
, " ");
952 for (; ifnames_copy
!= NULL
; ifnames_copy
= strtok(NULL
, " ")) {
953 (void) strlcpy(request
->ifname
, ifnames_copy
, LIFNAMSIZ
);
954 if (dhcp_ipc_make_request(request
, &reply
, 0) == 0)
965 * dhcp_ipc_timed_read(): reads from a descriptor using a maximum timeout
967 * input: int: the file descriptor to read from
968 * void *: the buffer to read into
969 * unsigned int: the total length of data to read
970 * int *: the number of milliseconds to wait; the number of
971 * milliseconds left are returned (-1 is "forever")
972 * output: int: DHCP_IPC_SUCCESS on success, DHCP_IPC_E_* otherwise
976 dhcp_ipc_timed_read(int fd
, void *buffer
, unsigned int length
, int *msec
)
978 unsigned int n_total
= 0;
980 struct pollfd pollfd
;
985 pollfd
.events
= POLLIN
;
987 while (n_total
< length
) {
991 retv
= poll(&pollfd
, 1, *msec
);
993 /* This can happen only if *msec is not -1 */
995 return (DHCP_IPC_E_TIMEOUT
);
1000 *msec
-= NSEC2MSEC(end
- start
);
1007 return (DHCP_IPC_E_POLL
);
1008 else if (*msec
== 0)
1009 return (DHCP_IPC_E_TIMEOUT
);
1013 if (!(pollfd
.revents
& POLLIN
)) {
1015 return (DHCP_IPC_E_POLL
);
1018 n_read
= read(fd
, (caddr_t
)buffer
+ n_total
, length
- n_total
);
1022 return (DHCP_IPC_E_READ
);
1023 else if (*msec
== 0)
1024 return (DHCP_IPC_E_TIMEOUT
);
1029 return (n_total
== 0 ? DHCP_IPC_E_EOF
:
1035 if (*msec
== 0 && n_total
< length
)
1036 return (DHCP_IPC_E_TIMEOUT
);
1039 return (DHCP_IPC_SUCCESS
);