1 /*
2 * CDDL HEADER START
3 *
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.
7 *
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.
12 *
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]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25 /*
26 * Copyright (c) 2016 by Delphix. All rights reserved.
27 */
28
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
47 #include "dhcpagent_ipc.h"
48 #include "dhcpagent_util.h"
49
50 /*
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
56 * expect.
57 *
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.
64 *
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.
71 *
72 * requests and replies can be paired up by comparing `ipc_id' fields.
73 */
74
75 #define BUFMAX 256
76
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);
80
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",
84 "inform", "get_tag"
85 };
86
87 /*
88 * dhcp_ipc_alloc_request(): allocates a dhcp_ipc_request_t of the given type
89 * and interface, with a timeout of 0.
90 *
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
97 */
98
99 dhcp_ipc_request_t *
dhcp_ipc_alloc_request(dhcp_ipc_type_t type,const char * ifname,const void * buffer,uint32_t buffer_size,dhcp_data_type_t data_type)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)
102 {
103 dhcp_ipc_request_t *request = calloc(1, DHCP_IPC_REQUEST_SIZE +
104 buffer_size);
105
106 if (request == NULL)
107 return (NULL);
108
109 request->message_type = type;
110 request->data_length = buffer_size;
111 request->data_type = data_type;
112
113 if (ifname != NULL)
114 (void) strlcpy(request->ifname, ifname, LIFNAMSIZ);
115
116 if (buffer != NULL)
117 (void) memcpy(request->buffer, buffer, buffer_size);
118
119 return (request);
120 }
121
122 /*
123 * dhcp_ipc_alloc_reply(): allocates a dhcp_ipc_reply_t
124 *
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
131 */
132
133 dhcp_ipc_reply_t *
dhcp_ipc_alloc_reply(dhcp_ipc_request_t * request,int return_code,const void * buffer,uint32_t buffer_size,dhcp_data_type_t data_type)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)
136 {
137 dhcp_ipc_reply_t *reply = calloc(1, DHCP_IPC_REPLY_SIZE + buffer_size);
138
139 if (reply == NULL)
140 return (NULL);
141
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;
147
148 if (buffer != NULL)
149 (void) memcpy(reply->buffer, buffer, buffer_size);
150
151 return (reply);
152 }
153
154 /*
155 * dhcp_ipc_get_data(): gets the data and data type from a dhcp_ipc_reply_t
156 *
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.
161 */
162
163 void *
dhcp_ipc_get_data(dhcp_ipc_reply_t * reply,size_t * size,dhcp_data_type_t * type)164 dhcp_ipc_get_data(dhcp_ipc_reply_t *reply, size_t *size, dhcp_data_type_t *type)
165 {
166 if (reply == NULL || reply->data_length == 0) {
167 *size = 0;
168 return (NULL);
169 }
170
171 if (type != NULL)
172 *type = reply->data_type;
173
174 *size = reply->data_length;
175 return (reply->buffer);
176 }
177
178 /*
179 * dhcp_ipc_recv_msg(): gets a message using the agent's ipc protocol
180 *
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
187 */
188
189 static int
dhcp_ipc_recv_msg(int fd,void ** msg,uint32_t base_length,int msec)190 dhcp_ipc_recv_msg(int fd, void **msg, uint32_t base_length, int msec)
191 {
192 int retval;
193 dhcp_ipc_reply_t *ipc_msg;
194 uint32_t length;
195
196 retval = dhcp_ipc_timed_read(fd, &length, sizeof (uint32_t), &msec);
197 if (retval != DHCP_IPC_SUCCESS)
198 return (retval);
199
200 if (length == 0)
201 return (DHCP_IPC_E_PROTO);
202
203 *msg = malloc(length);
204 if (*msg == NULL)
205 return (DHCP_IPC_E_MEMORY);
206
207 retval = dhcp_ipc_timed_read(fd, *msg, length, &msec);
208 if (retval != DHCP_IPC_SUCCESS) {
209 free(*msg);
210 return (retval);
211 }
212
213 if (length < base_length) {
214 free(*msg);
215 return (DHCP_IPC_E_PROTO);
216 }
217
218 /*
219 * the data_length field is in the same place in either ipc message.
220 */
221
222 ipc_msg = (dhcp_ipc_reply_t *)(*msg);
223 if (ipc_msg->data_length + base_length != length) {
224 free(*msg);
225 return (DHCP_IPC_E_PROTO);
226 }
227
228 return (DHCP_IPC_SUCCESS);
229 }
230
231 /*
232 * dhcp_ipc_recv_request(): gets a request using the agent's ipc protocol
233 *
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
239 */
240
241 int
dhcp_ipc_recv_request(int fd,dhcp_ipc_request_t ** request,int msec)242 dhcp_ipc_recv_request(int fd, dhcp_ipc_request_t **request, int msec)
243 {
244 int retval;
245
246 retval = dhcp_ipc_recv_msg(fd, (void **)request, DHCP_IPC_REQUEST_SIZE,
247 msec);
248
249 /* guarantee that ifname will be NUL-terminated */
250 if (retval == 0)
251 (*request)->ifname[LIFNAMSIZ - 1] = '\0';
252
253 return (retval);
254 }
255
256 /*
257 * dhcp_ipc_recv_reply(): gets a reply using the agent's ipc protocol
258 *
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
265 */
266
267 static int
dhcp_ipc_recv_reply(int fd,dhcp_ipc_reply_t ** reply,int32_t timeout)268 dhcp_ipc_recv_reply(int fd, dhcp_ipc_reply_t **reply, int32_t timeout)
269 {
270 /*
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.
279 *
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
285 * timer.
286 */
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;
291 else
292 timeout = -1;
293 return (dhcp_ipc_recv_msg(fd, (void **)reply, DHCP_IPC_REPLY_SIZE,
294 timeout));
295 }
296
297 /*
298 * dhcp_ipc_send_msg(): transmits a message using the agent's ipc protocol
299 *
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
304 */
305
306 static int
dhcp_ipc_send_msg(int fd,void * msg,uint32_t message_length)307 dhcp_ipc_send_msg(int fd, void *msg, uint32_t message_length)
308 {
309 struct iovec iovec[2];
310
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;
315
316 if (writev(fd, iovec, sizeof (iovec) / sizeof (*iovec)) == -1)
317 return (DHCP_IPC_E_WRITEV);
318
319 return (0);
320 }
321
322 /*
323 * dhcp_ipc_send_reply(): transmits a reply using the agent's ipc protocol
324 *
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
328 */
329
330 int
dhcp_ipc_send_reply(int fd,dhcp_ipc_reply_t * reply)331 dhcp_ipc_send_reply(int fd, dhcp_ipc_reply_t *reply)
332 {
333 return (dhcp_ipc_send_msg(fd, reply, DHCP_IPC_REPLY_SIZE +
334 reply->data_length));
335 }
336
337 /*
338 * dhcp_ipc_send_request(): transmits a request using the agent's ipc protocol
339 *
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
343 */
344
345 static int
dhcp_ipc_send_request(int fd,dhcp_ipc_request_t * request)346 dhcp_ipc_send_request(int fd, dhcp_ipc_request_t *request)
347 {
348 /*
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).
352 */
353
354 request->ipc_id = gethrtime();
355
356 return (dhcp_ipc_send_msg(fd, request, DHCP_IPC_REQUEST_SIZE +
357 request->data_length));
358 }
359
360 /*
361 * dhcp_ipc_make_request(): sends the provided request to the agent and reaps
362 * the reply
363 *
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
369 */
370
371 int
dhcp_ipc_make_request(dhcp_ipc_request_t * request,dhcp_ipc_reply_t ** reply,int32_t timeout)372 dhcp_ipc_make_request(dhcp_ipc_request_t *request, dhcp_ipc_reply_t **reply,
373 int32_t timeout)
374 {
375 int fd, on, retval;
376 struct sockaddr_in sinv;
377
378 fd = socket(AF_INET, SOCK_STREAM, 0);
379 if (fd == -1)
380 return (DHCP_IPC_E_SOCKET);
381
382 /*
383 * Bind a privileged port if we have sufficient privilege to do so.
384 * Continue as non-privileged otherwise.
385 */
386 on = 1;
387 (void) setsockopt(fd, IPPROTO_TCP, TCP_ANONPRIVBIND, &on, sizeof (on));
388
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);
394 }
395
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));
399 if (retval == -1) {
400 (void) dhcp_ipc_close(fd);
401 return (DHCP_IPC_E_CONNECT);
402 }
403
404 request->timeout = timeout;
405
406 retval = dhcp_ipc_send_request(fd, request);
407 if (retval == 0)
408 retval = dhcp_ipc_recv_reply(fd, reply, timeout);
409
410 (void) dhcp_ipc_close(fd);
411
412 return (retval);
413 }
414
415 /*
416 * dhcp_ipc_init(): initializes the ipc channel for use by the agent
417 *
418 * input: int *: the file descriptor to accept on (returned)
419 * output: int: 0 on success, DHCP_IPC_E_* otherwise
420 */
421
422 int
dhcp_ipc_init(int * listen_fd)423 dhcp_ipc_init(int *listen_fd)
424 {
425 struct sockaddr_in sin;
426 int on = 1;
427
428 (void) memset(&sin, 0, sizeof (struct sockaddr_in));
429
430 sin.sin_family = AF_INET;
431 sin.sin_port = htons(IPPORT_DHCPAGENT);
432 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
433
434 *listen_fd = socket(AF_INET, SOCK_STREAM, 0);
435 if (*listen_fd == -1)
436 return (DHCP_IPC_E_SOCKET);
437
438 /*
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(3SOCKET) will fail. so we can't lose.
442 */
443
444 (void) setsockopt(*listen_fd, SOL_SOCKET, SO_REUSEADDR, &on,
445 sizeof (on));
446
447 if (bind(*listen_fd, (struct sockaddr *)&sin, sizeof (sin)) == -1) {
448 (void) close(*listen_fd);
449 return (DHCP_IPC_E_BIND);
450 }
451
452 if (listen(*listen_fd, DHCP_IPC_LISTEN_BACKLOG) == -1) {
453 (void) close(*listen_fd);
454 return (DHCP_IPC_E_LISTEN);
455 }
456
457 return (0);
458 }
459
460 /*
461 * dhcp_ipc_accept(): accepts an incoming connection for the agent
462 *
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
468 */
469
470 int
dhcp_ipc_accept(int listen_fd,int * fd,int * is_priv)471 dhcp_ipc_accept(int listen_fd, int *fd, int *is_priv)
472 {
473 struct sockaddr_in sin_peer;
474 int sin_len = sizeof (sin_peer);
475 int sockflags;
476
477 /*
478 * if we were extremely concerned with portability, we would
479 * set the socket into nonblocking mode before doing the
480 * accept(3SOCKET), 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.
489 */
490
491 *fd = accept(listen_fd, (struct sockaddr *)&sin_peer, &sin_len);
492 if (*fd == -1)
493 return (DHCP_IPC_E_ACCEPT);
494
495 /* get credentials */
496 *is_priv = ntohs(sin_peer.sin_port) < IPPORT_RESERVED;
497
498 /*
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.
505 */
506
507 if ((sockflags = fcntl(*fd, F_GETFL, 0)) == -1) {
508 (void) close(*fd);
509 return (DHCP_IPC_E_FCNTL);
510 }
511
512 if (fcntl(*fd, F_SETFL, sockflags | O_NONBLOCK) == -1) {
513 (void) close(*fd);
514 return (DHCP_IPC_E_FCNTL);
515 }
516
517 return (0);
518 }
519
520 /*
521 * dhcp_ipc_close(): closes an ipc descriptor
522 *
523 * input: int: the file descriptor to close
524 * output: int: 0 on success, DHCP_IPC_E_* otherwise
525 */
526
527 int
dhcp_ipc_close(int fd)528 dhcp_ipc_close(int fd)
529 {
530 return ((close(fd) == -1) ? DHCP_IPC_E_CLOSE : 0);
531 }
532
533 /*
534 * dhcp_ipc_strerror(): maps an ipc error code into a human-readable string
535 *
536 * input: int: the ipc error code to map
537 * output: const char *: the corresponding human-readable string
538 */
539
540 const char *
dhcp_ipc_strerror(int error)541 dhcp_ipc_strerror(int error)
542 {
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"
547 };
548
549 const char *error_string;
550 static char buffer[BUFMAX];
551
552 switch (error) {
553
554 /*
555 * none of these errors actually go over the wire.
556 * hence, we assume that errno is still fresh.
557 */
558
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:
569
570 error_string = strerror(errno);
571 if (error_string == NULL)
572 error_string = "unknown error";
573
574 (void) snprintf(buffer, sizeof (buffer), "%s: %s",
575 syscalls[error], error_string);
576
577 error_string = buffer;
578 break;
579
580 case DHCP_IPC_E_MEMORY:
581 error_string = "out of memory";
582 break;
583
584 case DHCP_IPC_E_TIMEOUT:
585 error_string = "wait timed out, operation still pending...";
586 break;
587
588 case DHCP_IPC_E_INVIF:
589 error_string = "interface does not exist or cannot be managed "
590 "using DHCP";
591 break;
592
593 case DHCP_IPC_E_INT:
594 error_string = "internal error (might work later)";
595 break;
596
597 case DHCP_IPC_E_PERM:
598 error_string = "permission denied";
599 break;
600
601 case DHCP_IPC_E_OUTSTATE:
602 error_string = "interface not in appropriate state for command";
603 break;
604
605 case DHCP_IPC_E_PEND:
606 error_string = "interface currently has a pending command "
607 "(try later)";
608 break;
609
610 case DHCP_IPC_E_BOOTP:
611 error_string = "interface is administered with BOOTP, not DHCP";
612 break;
613
614 case DHCP_IPC_E_CMD_UNKNOWN:
615 error_string = "unknown command";
616 break;
617
618 case DHCP_IPC_E_UNKIF:
619 error_string = "interface is not under DHCP control";
620 break;
621
622 case DHCP_IPC_E_PROTO:
623 error_string = "ipc protocol violation";
624 break;
625
626 case DHCP_IPC_E_FAILEDIF:
627 error_string = "interface is in a FAILED state and must be "
628 "manually restarted";
629 break;
630
631 case DHCP_IPC_E_NOPRIMARY:
632 error_string = "primary interface requested but no primary "
633 "interface is set";
634 break;
635
636 case DHCP_IPC_E_NOIPIF:
637 error_string = "interface currently has no IP address";
638 break;
639
640 case DHCP_IPC_E_DOWNIF:
641 error_string = "interface is currently down";
642 break;
643
644 case DHCP_IPC_E_NOVALUE:
645 error_string = "no value was found for this option";
646 break;
647
648 case DHCP_IPC_E_RUNNING:
649 error_string = "DHCP is already running";
650 break;
651
652 case DHCP_IPC_E_SRVFAILED:
653 error_string = "DHCP server refused request";
654 break;
655
656 case DHCP_IPC_E_EOF:
657 error_string = "ipc connection closed";
658 break;
659
660 default:
661 error_string = "unknown error";
662 break;
663 }
664
665 /*
666 * TODO: internationalize this error string
667 */
668
669 return (error_string);
670 }
671
672 /*
673 * dhcp_string_to_request(): maps a string into a request code
674 *
675 * input: const char *: the string to map
676 * output: dhcp_ipc_type_t: the request code, or -1 if unknown
677 */
678
679 dhcp_ipc_type_t
dhcp_string_to_request(const char * request)680 dhcp_string_to_request(const char *request)
681 {
682 unsigned int i;
683
684 for (i = 0; i < DHCP_NIPC; i++)
685 if (strcmp(ipc_typestr[i], request) == 0)
686 return ((dhcp_ipc_type_t)i);
687
688 return ((dhcp_ipc_type_t)-1);
689 }
690
691 /*
692 * dhcp_ipc_type_to_string(): maps an ipc command code into a human-readable
693 * string
694 *
695 * input: int: the ipc command code to map
696 * output: const char *: the corresponding human-readable string
697 */
698
699 const char *
dhcp_ipc_type_to_string(dhcp_ipc_type_t type)700 dhcp_ipc_type_to_string(dhcp_ipc_type_t type)
701 {
702 if (type < 0 || type >= DHCP_NIPC)
703 return ("unknown");
704 else
705 return (ipc_typestr[(int)type]);
706 }
707
708 /*
709 * getinfo_ifnames(): checks the value of a specified option on a list of
710 * interface names.
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)
718 */
719
720 static int
getinfo_ifnames(const char * ifn,dhcp_optnum_t * optnum,DHCP_OPT ** result)721 getinfo_ifnames(const char *ifn, dhcp_optnum_t *optnum, DHCP_OPT **result)
722 {
723 dhcp_ipc_request_t *request;
724 dhcp_ipc_reply_t *reply;
725 char *ifnames, *ifnames_head;
726 DHCP_OPT *opt;
727 size_t opt_size;
728 int retval = 0;
729
730 *result = NULL;
731 ifnames_head = ifnames = strdup(ifn);
732 if (ifnames == NULL)
733 return (DHCP_IPC_E_MEMORY);
734
735 request = dhcp_ipc_alloc_request(DHCP_GET_TAG, "", optnum,
736 sizeof (dhcp_optnum_t), DHCP_TYPE_OPTNUM);
737
738 if (request == NULL) {
739 free(ifnames_head);
740 return (DHCP_IPC_E_MEMORY);
741 }
742
743 ifnames = strtok(ifnames, " ");
744 if (ifnames == NULL)
745 ifnames = "";
746
747 for (; ifnames != NULL; ifnames = strtok(NULL, " ")) {
748
749 (void) strlcpy(request->ifname, ifnames, LIFNAMSIZ);
750 retval = dhcp_ipc_make_request(request, &reply, 0);
751 if (retval != 0)
752 break;
753
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);
758 if (*result == NULL)
759 retval = DHCP_IPC_E_MEMORY;
760 else
761 (void) memcpy(*result, opt, opt_size);
762
763 free(reply);
764 break;
765 }
766 }
767
768 free(reply);
769 if (ifnames[0] == '\0')
770 break;
771 }
772
773 free(request);
774 free(ifnames_head);
775
776 return (retval);
777 }
778
779 /*
780 * get_ifnames(): returns a space-separated list of interface names that
781 * match the specified flags
782 *
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
786 * NULL upon failure.
787 */
788
789 static char *
get_ifnames(int flags_on,int flags_off)790 get_ifnames(int flags_on, int flags_off)
791 {
792 struct ifconf ifc;
793 int n_ifs, i, sock_fd;
794 char *ifnames;
795
796
797 sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
798 if (sock_fd == -1)
799 return (NULL);
800
801 if ((ioctl(sock_fd, SIOCGIFNUM, &n_ifs) == -1) || (n_ifs <= 0)) {
802 (void) close(sock_fd);
803 return (NULL);
804 }
805
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) {
810
811 if (ioctl(sock_fd, SIOCGIFCONF, &ifc) == -1) {
812 (void) close(sock_fd);
813 free(ifnames);
814 free(ifc.ifc_req);
815 return (NULL);
816 }
817
818 for (i = 0; i < n_ifs; i++) {
819
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)
823 continue;
824
825 (void) strcat(ifnames, ifc.ifc_req[i].ifr_name);
826 (void) strcat(ifnames, " ");
827 }
828
829 if (strlen(ifnames) > 1)
830 ifnames[strlen(ifnames) - 1] = '\0';
831 }
832
833 (void) close(sock_fd);
834 free(ifc.ifc_req);
835 return (ifnames);
836 }
837
838 /*
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.
849 */
850
851 int
dhcp_ipc_getinfo(dhcp_optnum_t * optnum,DHCP_OPT ** result,int32_t timeout)852 dhcp_ipc_getinfo(dhcp_optnum_t *optnum, DHCP_OPT **result, int32_t timeout)
853 {
854 dhcp_ipc_request_t *request;
855 dhcp_ipc_reply_t *reply;
856 char *ifnames, *ifnames_copy, *ifnames_head;
857 int retval;
858 time_t start_time = time(NULL);
859
860 if (timeout == DHCP_IPC_WAIT_DEFAULT)
861 timeout = DHCP_IPC_DEFAULT_WAIT;
862
863 /*
864 * wait at most 5 seconds for the agent to start.
865 */
866
867 if (dhcp_start_agent((timeout > 5 || timeout < 0) ? 5 : timeout) == -1)
868 return (DHCP_IPC_E_INT);
869
870 /*
871 * check the primary interface for the option value first.
872 */
873
874 retval = getinfo_ifnames("", optnum, result);
875 if ((retval != 0) || (retval == 0 && *result != NULL))
876 return (retval);
877
878 /*
879 * no luck. get a list of the interfaces under DHCP control
880 * and perform a GET_TAG on each one.
881 */
882
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)) {
887 free(ifnames);
888 return (retval);
889 }
890 }
891 free(ifnames);
892
893 /*
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.
899 */
900
901 ifnames = get_ifnames(IFF_UP|IFF_RUNNING, IFF_LOOPBACK|IFF_DHCPRUNNING);
902 if (ifnames == NULL || strlen(ifnames) == 0) {
903 free(ifnames);
904 return (DHCP_IPC_E_NOVALUE);
905 }
906
907 ifnames_head = ifnames_copy = strdup(ifnames);
908 if (ifnames_copy == NULL) {
909 free(ifnames);
910 return (DHCP_IPC_E_MEMORY);
911 }
912
913 request = dhcp_ipc_alloc_request(DHCP_INFORM, "", NULL, 0,
914 DHCP_TYPE_NONE);
915 if (request == NULL) {
916 free(ifnames);
917 free(ifnames_head);
918 return (DHCP_IPC_E_MEMORY);
919 }
920
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)
925 free(reply);
926 }
927
928 for (;;) {
929 if ((timeout != DHCP_IPC_WAIT_FOREVER) &&
930 (time(NULL) - start_time > timeout)) {
931 retval = DHCP_IPC_E_TIMEOUT;
932 break;
933 }
934
935 retval = getinfo_ifnames(ifnames, optnum, result);
936 if (retval != 0 || (retval == 0 && *result != NULL))
937 break;
938
939 (void) sleep(1);
940 }
941
942 /*
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.
946 */
947
948 request->message_type = DHCP_DROP;
949
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)
955 free(reply);
956 }
957
958 free(request);
959 free(ifnames_head);
960 free(ifnames);
961 return (retval);
962 }
963
964 /*
965 * dhcp_ipc_timed_read(): reads from a descriptor using a maximum timeout
966 *
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
973 */
974
975 static int
dhcp_ipc_timed_read(int fd,void * buffer,unsigned int length,int * msec)976 dhcp_ipc_timed_read(int fd, void *buffer, unsigned int length, int *msec)
977 {
978 unsigned int n_total = 0;
979 ssize_t n_read;
980 struct pollfd pollfd;
981 hrtime_t start, end;
982 int retv;
983
984 pollfd.fd = fd;
985 pollfd.events = POLLIN;
986
987 while (n_total < length) {
988
989 start = gethrtime();
990
991 retv = poll(&pollfd, 1, *msec);
992 if (retv == 0) {
993 /* This can happen only if *msec is not -1 */
994 *msec = 0;
995 return (DHCP_IPC_E_TIMEOUT);
996 }
997
998 if (*msec != -1) {
999 end = gethrtime();
1000 *msec -= NSEC2MSEC(end - start);
1001 if (*msec < 0)
1002 *msec = 0;
1003 }
1004
1005 if (retv == -1) {
1006 if (errno != EINTR)
1007 return (DHCP_IPC_E_POLL);
1008 else if (*msec == 0)
1009 return (DHCP_IPC_E_TIMEOUT);
1010 continue;
1011 }
1012
1013 if (!(pollfd.revents & POLLIN)) {
1014 errno = EINVAL;
1015 return (DHCP_IPC_E_POLL);
1016 }
1017
1018 n_read = read(fd, (caddr_t)buffer + n_total, length - n_total);
1019
1020 if (n_read == -1) {
1021 if (errno != EINTR)
1022 return (DHCP_IPC_E_READ);
1023 else if (*msec == 0)
1024 return (DHCP_IPC_E_TIMEOUT);
1025 continue;
1026 }
1027
1028 if (n_read == 0) {
1029 return (n_total == 0 ? DHCP_IPC_E_EOF :
1030 DHCP_IPC_E_PROTO);
1031 }
1032
1033 n_total += n_read;
1034
1035 if (*msec == 0 && n_total < length)
1036 return (DHCP_IPC_E_TIMEOUT);
1037 }
1038
1039 return (DHCP_IPC_SUCCESS);
1040 }
1041