Print this page
4823 don't open-code NSEC2MSEC and MSEC2NSEC
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/lib/libdhcpagent/common/dhcpagent_ipc.c
+++ new/usr/src/lib/libdhcpagent/common/dhcpagent_ipc.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21 /*
22 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 23 * Use is subject to license terms.
24 24 */
25 25
26 26 #include <string.h>
27 27 #include <unistd.h>
28 28 #include <stdlib.h>
29 29 #include <sys/uio.h>
30 30 #include <sys/socket.h>
31 31 #include <sys/types.h>
32 32 #include <fcntl.h>
33 33 #include <errno.h>
34 34 #include <limits.h>
35 35 #include <netinet/in.h>
36 36 #include <netinet/tcp.h>
37 37 #include <net/if.h>
38 38 #include <sys/sockio.h>
39 39 #include <sys/fcntl.h>
40 40 #include <sys/time.h>
41 41 #include <stdio.h> /* snprintf */
42 42 #include <arpa/inet.h> /* ntohl, ntohs, etc */
43 43
44 44 #include "dhcpagent_ipc.h"
45 45 #include "dhcpagent_util.h"
46 46
47 47 /*
48 48 * the protocol used here is a simple request/reply scheme: a client
49 49 * sends a dhcp_ipc_request_t message to the agent, and the agent
50 50 * sends a dhcp_ipc_reply_t back to the client. since the requests
51 51 * and replies can be variable-length, they are prefixed on "the wire"
52 52 * by a 32-bit number that tells the other end how many bytes to
53 53 * expect.
54 54 *
55 55 * the format of a request consists of a single dhcp_ipc_request_t;
56 56 * note that the length of this dhcp_ipc_request_t is variable (using
57 57 * the standard c array-of-size-1 trick). the type of the payload is
58 58 * given by `data_type', which is guaranteed to be `data_length' bytes
59 59 * long starting at `buffer'. note that `buffer' is guaranteed to be
60 60 * 32-bit aligned but it is poor taste to rely on this.
61 61 *
62 62 * the format of a reply is much the same: a single dhcp_ipc_reply_t;
63 63 * note again that the length of the dhcp_ipc_reply_t is variable.
64 64 * the type of the payload is given by `data_type', which is
65 65 * guaranteed to be `data_length' bytes long starting at `buffer'.
66 66 * once again, note that `buffer' is guaranteed to be 32-bit aligned
67 67 * but it is poor taste to rely on this.
68 68 *
69 69 * requests and replies can be paired up by comparing `ipc_id' fields.
70 70 */
71 71
72 72 #define BUFMAX 256
73 73
74 74 static int dhcp_ipc_timed_read(int, void *, unsigned int, int *);
75 75 static int getinfo_ifnames(const char *, dhcp_optnum_t *, DHCP_OPT **);
76 76 static char *get_ifnames(int, int);
77 77
78 78 /* must be kept in sync with enum in dhcpagent_ipc.h */
79 79 static const char *ipc_typestr[] = {
80 80 "drop", "extend", "ping", "release", "start", "status",
81 81 "inform", "get_tag"
82 82 };
83 83
84 84 /*
85 85 * dhcp_ipc_alloc_request(): allocates a dhcp_ipc_request_t of the given type
86 86 * and interface, with a timeout of 0.
87 87 *
88 88 * input: dhcp_ipc_type_t: the type of ipc request to allocate
89 89 * const char *: the interface to associate the request with
90 90 * const void *: the payload to send with the message (NULL if none)
91 91 * uint32_t: the payload size (0 if none)
92 92 * dhcp_data_type_t: the description of the type of payload
93 93 * output: dhcp_ipc_request_t *: the request on success, NULL on failure
94 94 */
95 95
96 96 dhcp_ipc_request_t *
97 97 dhcp_ipc_alloc_request(dhcp_ipc_type_t type, const char *ifname,
98 98 const void *buffer, uint32_t buffer_size, dhcp_data_type_t data_type)
99 99 {
100 100 dhcp_ipc_request_t *request = calloc(1, DHCP_IPC_REQUEST_SIZE +
101 101 buffer_size);
102 102
103 103 if (request == NULL)
104 104 return (NULL);
105 105
106 106 request->message_type = type;
107 107 request->data_length = buffer_size;
108 108 request->data_type = data_type;
109 109
110 110 if (ifname != NULL)
111 111 (void) strlcpy(request->ifname, ifname, LIFNAMSIZ);
112 112
113 113 if (buffer != NULL)
114 114 (void) memcpy(request->buffer, buffer, buffer_size);
115 115
116 116 return (request);
117 117 }
118 118
119 119 /*
120 120 * dhcp_ipc_alloc_reply(): allocates a dhcp_ipc_reply_t
121 121 *
122 122 * input: dhcp_ipc_request_t *: the request the reply is for
123 123 * int: the return code (0 for success, DHCP_IPC_E_* otherwise)
124 124 * const void *: the payload to send with the message (NULL if none)
125 125 * uint32_t: the payload size (0 if none)
126 126 * dhcp_data_type_t: the description of the type of payload
127 127 * output: dhcp_ipc_reply_t *: the reply on success, NULL on failure
128 128 */
129 129
130 130 dhcp_ipc_reply_t *
131 131 dhcp_ipc_alloc_reply(dhcp_ipc_request_t *request, int return_code,
132 132 const void *buffer, uint32_t buffer_size, dhcp_data_type_t data_type)
133 133 {
134 134 dhcp_ipc_reply_t *reply = calloc(1, DHCP_IPC_REPLY_SIZE + buffer_size);
135 135
136 136 if (reply == NULL)
137 137 return (NULL);
138 138
139 139 reply->message_type = request->message_type;
140 140 reply->ipc_id = request->ipc_id;
141 141 reply->return_code = return_code;
142 142 reply->data_length = buffer_size;
143 143 reply->data_type = data_type;
144 144
145 145 if (buffer != NULL)
146 146 (void) memcpy(reply->buffer, buffer, buffer_size);
147 147
148 148 return (reply);
149 149 }
150 150
151 151 /*
152 152 * dhcp_ipc_get_data(): gets the data and data type from a dhcp_ipc_reply_t
153 153 *
154 154 * input: dhcp_ipc_reply_t *: the reply to get data from
155 155 * size_t *: the size of the resulting data
156 156 * dhcp_data_type_t *: the type of the message (returned)
157 157 * output: void *: a pointer to the data, if there is any.
158 158 */
159 159
160 160 void *
161 161 dhcp_ipc_get_data(dhcp_ipc_reply_t *reply, size_t *size, dhcp_data_type_t *type)
162 162 {
163 163 if (reply == NULL || reply->data_length == 0) {
164 164 *size = 0;
165 165 return (NULL);
166 166 }
167 167
168 168 if (type != NULL)
169 169 *type = reply->data_type;
170 170
171 171 *size = reply->data_length;
172 172 return (reply->buffer);
173 173 }
174 174
175 175 /*
176 176 * dhcp_ipc_recv_msg(): gets a message using the agent's ipc protocol
177 177 *
178 178 * input: int: the file descriptor to get the message from
179 179 * void **: the address of a pointer to store the message
180 180 * (dynamically allocated)
181 181 * uint32_t: the minimum length of the packet
182 182 * int: the # of milliseconds to wait for the message (-1 is forever)
183 183 * output: int: DHCP_IPC_SUCCESS on success, DHCP_IPC_E_* otherwise
184 184 */
185 185
186 186 static int
187 187 dhcp_ipc_recv_msg(int fd, void **msg, uint32_t base_length, int msec)
188 188 {
189 189 int retval;
190 190 dhcp_ipc_reply_t *ipc_msg;
191 191 uint32_t length;
192 192
193 193 retval = dhcp_ipc_timed_read(fd, &length, sizeof (uint32_t), &msec);
194 194 if (retval != DHCP_IPC_SUCCESS)
195 195 return (retval);
196 196
197 197 if (length == 0)
198 198 return (DHCP_IPC_E_PROTO);
199 199
200 200 *msg = malloc(length);
201 201 if (*msg == NULL)
202 202 return (DHCP_IPC_E_MEMORY);
203 203
204 204 retval = dhcp_ipc_timed_read(fd, *msg, length, &msec);
205 205 if (retval != DHCP_IPC_SUCCESS) {
206 206 free(*msg);
207 207 return (retval);
208 208 }
209 209
210 210 if (length < base_length) {
211 211 free(*msg);
212 212 return (DHCP_IPC_E_PROTO);
213 213 }
214 214
215 215 /*
216 216 * the data_length field is in the same place in either ipc message.
217 217 */
218 218
219 219 ipc_msg = (dhcp_ipc_reply_t *)(*msg);
220 220 if (ipc_msg->data_length + base_length != length) {
221 221 free(*msg);
222 222 return (DHCP_IPC_E_PROTO);
223 223 }
224 224
225 225 return (DHCP_IPC_SUCCESS);
226 226 }
227 227
228 228 /*
229 229 * dhcp_ipc_recv_request(): gets a request using the agent's ipc protocol
230 230 *
231 231 * input: int: the file descriptor to get the message from
232 232 * dhcp_ipc_request_t **: address of a pointer to store the request
233 233 * (dynamically allocated)
234 234 * int: the # of milliseconds to wait for the message (-1 is forever)
235 235 * output: int: 0 on success, DHCP_IPC_E_* otherwise
236 236 */
237 237
238 238 int
239 239 dhcp_ipc_recv_request(int fd, dhcp_ipc_request_t **request, int msec)
240 240 {
241 241 int retval;
242 242
243 243 retval = dhcp_ipc_recv_msg(fd, (void **)request, DHCP_IPC_REQUEST_SIZE,
244 244 msec);
245 245
246 246 /* guarantee that ifname will be NUL-terminated */
247 247 if (retval == 0)
248 248 (*request)->ifname[LIFNAMSIZ - 1] = '\0';
249 249
250 250 return (retval);
251 251 }
252 252
253 253 /*
254 254 * dhcp_ipc_recv_reply(): gets a reply using the agent's ipc protocol
255 255 *
256 256 * input: int: the file descriptor to get the message from
257 257 * dhcp_ipc_reply_t **: address of a pointer to store the reply
258 258 * (dynamically allocated)
259 259 * int32_t: timeout (in seconds), or DHCP_IPC_WAIT_FOREVER,
260 260 * or DHCP_IPC_WAIT_DEFAULT
261 261 * output: int: 0 on success, DHCP_IPC_E_* otherwise
262 262 */
263 263
264 264 static int
265 265 dhcp_ipc_recv_reply(int fd, dhcp_ipc_reply_t **reply, int32_t timeout)
266 266 {
267 267 /*
268 268 * If the caller doesn't want to wait forever, and the amount of time
269 269 * he wants to wait is expressible as an integer number of milliseconds
270 270 * (as needed by the msg function), then we wait that amount of time
271 271 * plus an extra two seconds for the daemon to do its work. The extra
272 272 * two seconds is arbitrary; it should allow plenty of time for the
273 273 * daemon to respond within the existing timeout, as specified in the
274 274 * original request, so the only time we give up is when the daemon is
275 275 * stopped or otherwise malfunctioning.
276 276 *
277 277 * Note that the wait limit (milliseconds in an 'int') is over 24 days,
278 278 * so it's unlikely that any request will actually be that long, and
279 279 * it's unlikely that anyone will care if we wait forever on a request
280 280 * for a 30 day timer. The point is to protect against daemon
281 281 * malfunction in the usual cases, not to provide an absolute command
282 282 * timer.
283 283 */
284 284 if (timeout == DHCP_IPC_WAIT_DEFAULT)
285 285 timeout = DHCP_IPC_DEFAULT_WAIT;
286 286 if (timeout != DHCP_IPC_WAIT_FOREVER && timeout < INT_MAX / 1000 - 2)
287 287 timeout = (timeout + 2) * 1000;
288 288 else
289 289 timeout = -1;
290 290 return (dhcp_ipc_recv_msg(fd, (void **)reply, DHCP_IPC_REPLY_SIZE,
291 291 timeout));
292 292 }
293 293
294 294 /*
295 295 * dhcp_ipc_send_msg(): transmits a message using the agent's ipc protocol
296 296 *
297 297 * input: int: the file descriptor to transmit on
298 298 * void *: the message to send
299 299 * uint32_t: the message length
300 300 * output: int: 0 on success, DHCP_IPC_E_* otherwise
301 301 */
302 302
303 303 static int
304 304 dhcp_ipc_send_msg(int fd, void *msg, uint32_t message_length)
305 305 {
306 306 struct iovec iovec[2];
307 307
308 308 iovec[0].iov_base = (caddr_t)&message_length;
309 309 iovec[0].iov_len = sizeof (uint32_t);
310 310 iovec[1].iov_base = msg;
311 311 iovec[1].iov_len = message_length;
312 312
313 313 if (writev(fd, iovec, sizeof (iovec) / sizeof (*iovec)) == -1)
314 314 return (DHCP_IPC_E_WRITEV);
315 315
316 316 return (0);
317 317 }
318 318
319 319 /*
320 320 * dhcp_ipc_send_reply(): transmits a reply using the agent's ipc protocol
321 321 *
322 322 * input: int: the file descriptor to transmit on
323 323 * dhcp_ipc_reply_t *: the reply to send
324 324 * output: int: 0 on success, DHCP_IPC_E_* otherwise
325 325 */
326 326
327 327 int
328 328 dhcp_ipc_send_reply(int fd, dhcp_ipc_reply_t *reply)
329 329 {
330 330 return (dhcp_ipc_send_msg(fd, reply, DHCP_IPC_REPLY_SIZE +
331 331 reply->data_length));
332 332 }
333 333
334 334 /*
335 335 * dhcp_ipc_send_request(): transmits a request using the agent's ipc protocol
336 336 *
337 337 * input: int: the file descriptor to transmit on
338 338 * dhcp_ipc_request_t *: the request to send
339 339 * output: int: 0 on success, DHCP_IPC_E_* otherwise
340 340 */
341 341
342 342 static int
343 343 dhcp_ipc_send_request(int fd, dhcp_ipc_request_t *request)
344 344 {
345 345 /*
346 346 * for now, ipc_ids aren't really used, but they're intended
347 347 * to make it easy to send several requests and then collect
348 348 * all of the replies (and pair them with the requests).
349 349 */
350 350
351 351 request->ipc_id = gethrtime();
352 352
353 353 return (dhcp_ipc_send_msg(fd, request, DHCP_IPC_REQUEST_SIZE +
354 354 request->data_length));
355 355 }
356 356
357 357 /*
358 358 * dhcp_ipc_make_request(): sends the provided request to the agent and reaps
359 359 * the reply
360 360 *
361 361 * input: dhcp_ipc_request_t *: the request to make
362 362 * dhcp_ipc_reply_t **: the reply (dynamically allocated)
363 363 * int32_t: timeout (in seconds), or DHCP_IPC_WAIT_FOREVER,
364 364 * or DHCP_IPC_WAIT_DEFAULT
365 365 * output: int: 0 on success, DHCP_IPC_E_* otherwise
366 366 */
367 367
368 368 int
369 369 dhcp_ipc_make_request(dhcp_ipc_request_t *request, dhcp_ipc_reply_t **reply,
370 370 int32_t timeout)
371 371 {
372 372 int fd, on, retval;
373 373 struct sockaddr_in sinv;
374 374
375 375 fd = socket(AF_INET, SOCK_STREAM, 0);
376 376 if (fd == -1)
377 377 return (DHCP_IPC_E_SOCKET);
378 378
379 379 /*
380 380 * Bind a privileged port if we have sufficient privilege to do so.
381 381 * Continue as non-privileged otherwise.
382 382 */
383 383 on = 1;
384 384 (void) setsockopt(fd, IPPROTO_TCP, TCP_ANONPRIVBIND, &on, sizeof (on));
385 385
386 386 (void) memset(&sinv, 0, sizeof (sinv));
387 387 sinv.sin_family = AF_INET;
388 388 if (bind(fd, (struct sockaddr *)&sinv, sizeof (sinv)) == -1) {
389 389 (void) dhcp_ipc_close(fd);
390 390 return (DHCP_IPC_E_BIND);
391 391 }
392 392
393 393 sinv.sin_port = htons(IPPORT_DHCPAGENT);
394 394 sinv.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
395 395 retval = connect(fd, (struct sockaddr *)&sinv, sizeof (sinv));
396 396 if (retval == -1) {
397 397 (void) dhcp_ipc_close(fd);
398 398 return (DHCP_IPC_E_CONNECT);
399 399 }
400 400
401 401 request->timeout = timeout;
402 402
403 403 retval = dhcp_ipc_send_request(fd, request);
404 404 if (retval == 0)
405 405 retval = dhcp_ipc_recv_reply(fd, reply, timeout);
406 406
407 407 (void) dhcp_ipc_close(fd);
408 408
409 409 return (retval);
410 410 }
411 411
412 412 /*
413 413 * dhcp_ipc_init(): initializes the ipc channel for use by the agent
414 414 *
415 415 * input: int *: the file descriptor to accept on (returned)
416 416 * output: int: 0 on success, DHCP_IPC_E_* otherwise
417 417 */
418 418
419 419 int
420 420 dhcp_ipc_init(int *listen_fd)
421 421 {
422 422 struct sockaddr_in sin;
423 423 int on = 1;
424 424
425 425 (void) memset(&sin, 0, sizeof (struct sockaddr_in));
426 426
427 427 sin.sin_family = AF_INET;
428 428 sin.sin_port = htons(IPPORT_DHCPAGENT);
429 429 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
430 430
431 431 *listen_fd = socket(AF_INET, SOCK_STREAM, 0);
432 432 if (*listen_fd == -1)
433 433 return (DHCP_IPC_E_SOCKET);
434 434
435 435 /*
436 436 * we use SO_REUSEADDR here since in the case where there
437 437 * really is another daemon running that is using the agent's
438 438 * port, bind(3N) will fail. so we can't lose.
439 439 */
440 440
441 441 (void) setsockopt(*listen_fd, SOL_SOCKET, SO_REUSEADDR, &on,
442 442 sizeof (on));
443 443
444 444 if (bind(*listen_fd, (struct sockaddr *)&sin, sizeof (sin)) == -1) {
445 445 (void) close(*listen_fd);
446 446 return (DHCP_IPC_E_BIND);
447 447 }
448 448
449 449 if (listen(*listen_fd, DHCP_IPC_LISTEN_BACKLOG) == -1) {
450 450 (void) close(*listen_fd);
451 451 return (DHCP_IPC_E_LISTEN);
452 452 }
453 453
454 454 return (0);
455 455 }
456 456
457 457 /*
458 458 * dhcp_ipc_accept(): accepts an incoming connection for the agent
459 459 *
460 460 * input: int: the file descriptor to accept on
461 461 * int *: the accepted file descriptor (returned)
462 462 * int *: nonzero if the client is privileged (returned)
463 463 * output: int: 0 on success, DHCP_IPC_E_* otherwise
464 464 * note: sets the socket into nonblocking mode
465 465 */
466 466
467 467 int
468 468 dhcp_ipc_accept(int listen_fd, int *fd, int *is_priv)
469 469 {
470 470 struct sockaddr_in sin_peer;
471 471 int sin_len = sizeof (sin_peer);
472 472 int sockflags;
473 473
474 474 /*
475 475 * if we were extremely concerned with portability, we would
476 476 * set the socket into nonblocking mode before doing the
477 477 * accept(3N), since on BSD-based networking stacks, there is
478 478 * a potential race that can occur if the socket which
479 479 * connected to us performs a TCP RST before we accept, since
480 480 * BSD handles this case entirely in the kernel and as a
481 481 * result even though select said we will not block, we can
482 482 * end up blocking since there is no longer a connection to
483 483 * accept. on SVR4-based systems, this should be okay,
484 484 * and we will get EPROTO back, even though POSIX.1g says
485 485 * we should get ECONNABORTED.
486 486 */
487 487
488 488 *fd = accept(listen_fd, (struct sockaddr *)&sin_peer, &sin_len);
489 489 if (*fd == -1)
490 490 return (DHCP_IPC_E_ACCEPT);
491 491
492 492 /* get credentials */
493 493 *is_priv = ntohs(sin_peer.sin_port) < IPPORT_RESERVED;
494 494
495 495 /*
496 496 * kick the socket into non-blocking mode so that later
497 497 * operations on the socket don't block and hold up the whole
498 498 * application. with the event demuxing approach, this may
499 499 * seem unnecessary, but in order to get partial reads/writes
500 500 * and to handle our internal protocol for passing data
501 501 * between the agent and its consumers, this is needed.
502 502 */
503 503
504 504 if ((sockflags = fcntl(*fd, F_GETFL, 0)) == -1) {
505 505 (void) close(*fd);
506 506 return (DHCP_IPC_E_FCNTL);
507 507 }
508 508
509 509 if (fcntl(*fd, F_SETFL, sockflags | O_NONBLOCK) == -1) {
510 510 (void) close(*fd);
511 511 return (DHCP_IPC_E_FCNTL);
512 512 }
513 513
514 514 return (0);
515 515 }
516 516
517 517 /*
518 518 * dhcp_ipc_close(): closes an ipc descriptor
519 519 *
520 520 * input: int: the file descriptor to close
521 521 * output: int: 0 on success, DHCP_IPC_E_* otherwise
522 522 */
523 523
524 524 int
525 525 dhcp_ipc_close(int fd)
526 526 {
527 527 return ((close(fd) == -1) ? DHCP_IPC_E_CLOSE : 0);
528 528 }
529 529
530 530 /*
531 531 * dhcp_ipc_strerror(): maps an ipc error code into a human-readable string
532 532 *
533 533 * input: int: the ipc error code to map
534 534 * output: const char *: the corresponding human-readable string
535 535 */
536 536
537 537 const char *
538 538 dhcp_ipc_strerror(int error)
539 539 {
540 540 /* note: this must be kept in sync with DHCP_IPC_E_* definitions */
541 541 const char *syscalls[] = {
542 542 "<unknown>", "socket", "fcntl", "read", "accept", "close",
543 543 "bind", "listen", "malloc", "connect", "writev", "poll"
544 544 };
545 545
546 546 const char *error_string;
547 547 static char buffer[BUFMAX];
548 548
549 549 switch (error) {
550 550
551 551 /*
552 552 * none of these errors actually go over the wire.
553 553 * hence, we assume that errno is still fresh.
554 554 */
555 555
556 556 case DHCP_IPC_E_SOCKET: /* FALLTHRU */
557 557 case DHCP_IPC_E_FCNTL: /* FALLTHRU */
558 558 case DHCP_IPC_E_READ: /* FALLTHRU */
559 559 case DHCP_IPC_E_ACCEPT: /* FALLTHRU */
560 560 case DHCP_IPC_E_CLOSE: /* FALLTHRU */
561 561 case DHCP_IPC_E_BIND: /* FALLTHRU */
562 562 case DHCP_IPC_E_LISTEN: /* FALLTHRU */
563 563 case DHCP_IPC_E_CONNECT: /* FALLTHRU */
564 564 case DHCP_IPC_E_WRITEV: /* FALLTHRU */
565 565 case DHCP_IPC_E_POLL:
566 566
567 567 error_string = strerror(errno);
568 568 if (error_string == NULL)
569 569 error_string = "unknown error";
570 570
571 571 (void) snprintf(buffer, sizeof (buffer), "%s: %s",
572 572 syscalls[error], error_string);
573 573
574 574 error_string = buffer;
575 575 break;
576 576
577 577 case DHCP_IPC_E_MEMORY:
578 578 error_string = "out of memory";
579 579 break;
580 580
581 581 case DHCP_IPC_E_TIMEOUT:
582 582 error_string = "wait timed out, operation still pending...";
583 583 break;
584 584
585 585 case DHCP_IPC_E_INVIF:
586 586 error_string = "interface does not exist or cannot be managed "
587 587 "using DHCP";
588 588 break;
589 589
590 590 case DHCP_IPC_E_INT:
591 591 error_string = "internal error (might work later)";
592 592 break;
593 593
594 594 case DHCP_IPC_E_PERM:
595 595 error_string = "permission denied";
596 596 break;
597 597
598 598 case DHCP_IPC_E_OUTSTATE:
599 599 error_string = "interface not in appropriate state for command";
600 600 break;
601 601
602 602 case DHCP_IPC_E_PEND:
603 603 error_string = "interface currently has a pending command "
604 604 "(try later)";
605 605 break;
606 606
607 607 case DHCP_IPC_E_BOOTP:
608 608 error_string = "interface is administered with BOOTP, not DHCP";
609 609 break;
610 610
611 611 case DHCP_IPC_E_CMD_UNKNOWN:
612 612 error_string = "unknown command";
613 613 break;
614 614
615 615 case DHCP_IPC_E_UNKIF:
616 616 error_string = "interface is not under DHCP control";
617 617 break;
618 618
619 619 case DHCP_IPC_E_PROTO:
620 620 error_string = "ipc protocol violation";
621 621 break;
622 622
623 623 case DHCP_IPC_E_FAILEDIF:
624 624 error_string = "interface is in a FAILED state and must be "
625 625 "manually restarted";
626 626 break;
627 627
628 628 case DHCP_IPC_E_NOPRIMARY:
629 629 error_string = "primary interface requested but no primary "
630 630 "interface is set";
631 631 break;
632 632
633 633 case DHCP_IPC_E_NOIPIF:
634 634 error_string = "interface currently has no IP address";
635 635 break;
636 636
637 637 case DHCP_IPC_E_DOWNIF:
638 638 error_string = "interface is currently down";
639 639 break;
640 640
641 641 case DHCP_IPC_E_NOVALUE:
642 642 error_string = "no value was found for this option";
643 643 break;
644 644
645 645 case DHCP_IPC_E_RUNNING:
646 646 error_string = "DHCP is already running";
647 647 break;
648 648
649 649 case DHCP_IPC_E_SRVFAILED:
650 650 error_string = "DHCP server refused request";
651 651 break;
652 652
653 653 case DHCP_IPC_E_EOF:
654 654 error_string = "ipc connection closed";
655 655 break;
656 656
657 657 default:
658 658 error_string = "unknown error";
659 659 break;
660 660 }
661 661
662 662 /*
663 663 * TODO: internationalize this error string
664 664 */
665 665
666 666 return (error_string);
667 667 }
668 668
669 669 /*
670 670 * dhcp_string_to_request(): maps a string into a request code
671 671 *
672 672 * input: const char *: the string to map
673 673 * output: dhcp_ipc_type_t: the request code, or -1 if unknown
674 674 */
675 675
676 676 dhcp_ipc_type_t
677 677 dhcp_string_to_request(const char *request)
678 678 {
679 679 unsigned int i;
680 680
681 681 for (i = 0; i < DHCP_NIPC; i++)
682 682 if (strcmp(ipc_typestr[i], request) == 0)
683 683 return ((dhcp_ipc_type_t)i);
684 684
685 685 return ((dhcp_ipc_type_t)-1);
686 686 }
687 687
688 688 /*
689 689 * dhcp_ipc_type_to_string(): maps an ipc command code into a human-readable
690 690 * string
691 691 *
692 692 * input: int: the ipc command code to map
693 693 * output: const char *: the corresponding human-readable string
694 694 */
695 695
696 696 const char *
697 697 dhcp_ipc_type_to_string(dhcp_ipc_type_t type)
698 698 {
699 699 if (type < 0 || type >= DHCP_NIPC)
700 700 return ("unknown");
701 701 else
702 702 return (ipc_typestr[(int)type]);
703 703 }
704 704
705 705 /*
706 706 * getinfo_ifnames(): checks the value of a specified option on a list of
707 707 * interface names.
708 708 * input: const char *: a list of interface names to query (in order) for
709 709 * the option; "" queries the primary interface
710 710 * dhcp_optnum_t *: a description of the desired option
711 711 * DHCP_OPT **: filled in with the (dynamically allocated) value of
712 712 * the option upon success.
713 713 * output: int: DHCP_IPC_E_* on error, 0 on success or if no value was
714 714 * found but no error occurred either (*result will be NULL)
715 715 */
716 716
717 717 static int
718 718 getinfo_ifnames(const char *ifn, dhcp_optnum_t *optnum, DHCP_OPT **result)
719 719 {
720 720 dhcp_ipc_request_t *request;
721 721 dhcp_ipc_reply_t *reply;
722 722 char *ifnames, *ifnames_head;
723 723 DHCP_OPT *opt;
724 724 size_t opt_size;
725 725 int retval = 0;
726 726
727 727 *result = NULL;
728 728 ifnames_head = ifnames = strdup(ifn);
729 729 if (ifnames == NULL)
730 730 return (DHCP_IPC_E_MEMORY);
731 731
732 732 request = dhcp_ipc_alloc_request(DHCP_GET_TAG, "", optnum,
733 733 sizeof (dhcp_optnum_t), DHCP_TYPE_OPTNUM);
734 734
735 735 if (request == NULL) {
736 736 free(ifnames_head);
737 737 return (DHCP_IPC_E_MEMORY);
738 738 }
739 739
740 740 ifnames = strtok(ifnames, " ");
741 741 if (ifnames == NULL)
742 742 ifnames = "";
743 743
744 744 for (; ifnames != NULL; ifnames = strtok(NULL, " ")) {
745 745
746 746 (void) strlcpy(request->ifname, ifnames, LIFNAMSIZ);
747 747 retval = dhcp_ipc_make_request(request, &reply, 0);
748 748 if (retval != 0)
749 749 break;
750 750
751 751 if (reply->return_code == 0) {
752 752 opt = dhcp_ipc_get_data(reply, &opt_size, NULL);
753 753 if (opt_size > 2 && (opt->len == opt_size - 2)) {
754 754 *result = malloc(opt_size);
755 755 if (*result == NULL)
756 756 retval = DHCP_IPC_E_MEMORY;
757 757 else
758 758 (void) memcpy(*result, opt, opt_size);
759 759
760 760 free(reply);
761 761 break;
762 762 }
763 763 }
764 764
765 765 free(reply);
766 766 if (ifnames[0] == '\0')
767 767 break;
768 768 }
769 769
770 770 free(request);
771 771 free(ifnames_head);
772 772
773 773 return (retval);
774 774 }
775 775
776 776 /*
777 777 * get_ifnames(): returns a space-separated list of interface names that
778 778 * match the specified flags
779 779 *
780 780 * input: int: flags which must be on in each interface returned
781 781 * int: flags which must be off in each interface returned
782 782 * output: char *: a dynamically-allocated list of interface names, or
783 783 * NULL upon failure.
784 784 */
785 785
786 786 static char *
787 787 get_ifnames(int flags_on, int flags_off)
788 788 {
789 789 struct ifconf ifc;
790 790 int n_ifs, i, sock_fd;
791 791 char *ifnames;
792 792
793 793
794 794 sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
795 795 if (sock_fd == -1)
796 796 return (NULL);
797 797
798 798 if ((ioctl(sock_fd, SIOCGIFNUM, &n_ifs) == -1) || (n_ifs <= 0)) {
799 799 (void) close(sock_fd);
800 800 return (NULL);
801 801 }
802 802
803 803 ifnames = calloc(1, n_ifs * (LIFNAMSIZ + 1));
804 804 ifc.ifc_len = n_ifs * sizeof (struct ifreq);
805 805 ifc.ifc_req = calloc(n_ifs, sizeof (struct ifreq));
806 806 if (ifc.ifc_req != NULL && ifnames != NULL) {
807 807
808 808 if (ioctl(sock_fd, SIOCGIFCONF, &ifc) == -1) {
809 809 (void) close(sock_fd);
810 810 free(ifnames);
811 811 free(ifc.ifc_req);
812 812 return (NULL);
813 813 }
814 814
815 815 for (i = 0; i < n_ifs; i++) {
816 816
817 817 if (ioctl(sock_fd, SIOCGIFFLAGS, &ifc.ifc_req[i]) == 0)
818 818 if ((ifc.ifc_req[i].ifr_flags &
819 819 (flags_on | flags_off)) != flags_on)
820 820 continue;
821 821
822 822 (void) strcat(ifnames, ifc.ifc_req[i].ifr_name);
823 823 (void) strcat(ifnames, " ");
824 824 }
825 825
826 826 if (strlen(ifnames) > 1)
827 827 ifnames[strlen(ifnames) - 1] = '\0';
828 828 }
829 829
830 830 (void) close(sock_fd);
831 831 free(ifc.ifc_req);
832 832 return (ifnames);
833 833 }
834 834
835 835 /*
836 836 * dhcp_ipc_getinfo(): attempts to retrieve a value for the specified DHCP
837 837 * option; tries primary interface, then all DHCP-owned
838 838 * interfaces, then INFORMs on the remaining interfaces
839 839 * (these interfaces are dropped prior to returning).
840 840 * input: dhcp_optnum_t *: a description of the desired option
841 841 * DHCP_OPT **: filled in with the (dynamically allocated) value of
842 842 * the option upon success.
843 843 * int32_t: timeout (in seconds), or DHCP_IPC_WAIT_FOREVER,
844 844 * or DHCP_IPC_WAIT_DEFAULT.
845 845 * output: int: DHCP_IPC_E_* on error, 0 upon success.
846 846 */
847 847
848 848 int
849 849 dhcp_ipc_getinfo(dhcp_optnum_t *optnum, DHCP_OPT **result, int32_t timeout)
850 850 {
851 851 dhcp_ipc_request_t *request;
852 852 dhcp_ipc_reply_t *reply;
853 853 char *ifnames, *ifnames_copy, *ifnames_head;
854 854 int retval;
855 855 time_t start_time = time(NULL);
856 856
857 857 if (timeout == DHCP_IPC_WAIT_DEFAULT)
858 858 timeout = DHCP_IPC_DEFAULT_WAIT;
859 859
860 860 /*
861 861 * wait at most 5 seconds for the agent to start.
862 862 */
863 863
864 864 if (dhcp_start_agent((timeout > 5 || timeout < 0) ? 5 : timeout) == -1)
865 865 return (DHCP_IPC_E_INT);
866 866
867 867 /*
868 868 * check the primary interface for the option value first.
869 869 */
870 870
871 871 retval = getinfo_ifnames("", optnum, result);
872 872 if ((retval != 0) || (retval == 0 && *result != NULL))
873 873 return (retval);
874 874
875 875 /*
876 876 * no luck. get a list of the interfaces under DHCP control
877 877 * and perform a GET_TAG on each one.
878 878 */
879 879
880 880 ifnames = get_ifnames(IFF_DHCPRUNNING, 0);
881 881 if (ifnames != NULL && strlen(ifnames) != 0) {
882 882 retval = getinfo_ifnames(ifnames, optnum, result);
883 883 if ((retval != 0) || (retval == 0 && *result != NULL)) {
884 884 free(ifnames);
885 885 return (retval);
886 886 }
887 887 }
888 888 free(ifnames);
889 889
890 890 /*
891 891 * still no luck. retrieve a list of all interfaces on the
892 892 * system that could use DHCP but aren't. send INFORMs out on
893 893 * each one. after that, sit in a loop for the next `timeout'
894 894 * seconds, trying every second to see if a response for the
895 895 * option we want has come in on one of the interfaces.
896 896 */
897 897
898 898 ifnames = get_ifnames(IFF_UP|IFF_RUNNING, IFF_LOOPBACK|IFF_DHCPRUNNING);
899 899 if (ifnames == NULL || strlen(ifnames) == 0) {
900 900 free(ifnames);
901 901 return (DHCP_IPC_E_NOVALUE);
902 902 }
903 903
904 904 ifnames_head = ifnames_copy = strdup(ifnames);
905 905 if (ifnames_copy == NULL) {
906 906 free(ifnames);
907 907 return (DHCP_IPC_E_MEMORY);
908 908 }
909 909
910 910 request = dhcp_ipc_alloc_request(DHCP_INFORM, "", NULL, 0,
911 911 DHCP_TYPE_NONE);
912 912 if (request == NULL) {
913 913 free(ifnames);
914 914 free(ifnames_head);
915 915 return (DHCP_IPC_E_MEMORY);
916 916 }
917 917
918 918 ifnames_copy = strtok(ifnames_copy, " ");
919 919 for (; ifnames_copy != NULL; ifnames_copy = strtok(NULL, " ")) {
920 920 (void) strlcpy(request->ifname, ifnames_copy, LIFNAMSIZ);
921 921 if (dhcp_ipc_make_request(request, &reply, 0) == 0)
922 922 free(reply);
923 923 }
924 924
925 925 for (;;) {
926 926 if ((timeout != DHCP_IPC_WAIT_FOREVER) &&
927 927 (time(NULL) - start_time > timeout)) {
928 928 retval = DHCP_IPC_E_TIMEOUT;
929 929 break;
930 930 }
931 931
932 932 retval = getinfo_ifnames(ifnames, optnum, result);
933 933 if (retval != 0 || (retval == 0 && *result != NULL))
934 934 break;
935 935
936 936 (void) sleep(1);
937 937 }
938 938
939 939 /*
940 940 * drop any interfaces that weren't under DHCP control before
941 941 * we got here; this keeps this function more of a black box
942 942 * and the behavior more consistent from call to call.
943 943 */
944 944
945 945 request->message_type = DHCP_DROP;
946 946
947 947 ifnames_copy = strcpy(ifnames_head, ifnames);
948 948 ifnames_copy = strtok(ifnames_copy, " ");
949 949 for (; ifnames_copy != NULL; ifnames_copy = strtok(NULL, " ")) {
950 950 (void) strlcpy(request->ifname, ifnames_copy, LIFNAMSIZ);
951 951 if (dhcp_ipc_make_request(request, &reply, 0) == 0)
952 952 free(reply);
953 953 }
954 954
955 955 free(request);
956 956 free(ifnames_head);
957 957 free(ifnames);
958 958 return (retval);
959 959 }
960 960
961 961 /*
962 962 * dhcp_ipc_timed_read(): reads from a descriptor using a maximum timeout
963 963 *
964 964 * input: int: the file descriptor to read from
965 965 * void *: the buffer to read into
966 966 * unsigned int: the total length of data to read
967 967 * int *: the number of milliseconds to wait; the number of
968 968 * milliseconds left are returned (-1 is "forever")
969 969 * output: int: DHCP_IPC_SUCCESS on success, DHCP_IPC_E_* otherwise
970 970 */
971 971
972 972 static int
973 973 dhcp_ipc_timed_read(int fd, void *buffer, unsigned int length, int *msec)
974 974 {
975 975 unsigned int n_total = 0;
976 976 ssize_t n_read;
977 977 struct pollfd pollfd;
978 978 hrtime_t start, end;
979 979 int retv;
980 980
981 981 pollfd.fd = fd;
982 982 pollfd.events = POLLIN;
983 983
984 984 while (n_total < length) {
985 985
986 986 start = gethrtime();
↓ open down ↓ |
986 lines elided |
↑ open up ↑ |
987 987
988 988 retv = poll(&pollfd, 1, *msec);
989 989 if (retv == 0) {
990 990 /* This can happen only if *msec is not -1 */
991 991 *msec = 0;
992 992 return (DHCP_IPC_E_TIMEOUT);
993 993 }
994 994
995 995 if (*msec != -1) {
996 996 end = gethrtime();
997 - *msec -= (end - start) / (NANOSEC / MILLISEC);
997 + *msec -= NSEC2MSEC(end - start);
998 998 if (*msec < 0)
999 999 *msec = 0;
1000 1000 }
1001 1001
1002 1002 if (retv == -1) {
1003 1003 if (errno != EINTR)
1004 1004 return (DHCP_IPC_E_POLL);
1005 1005 else if (*msec == 0)
1006 1006 return (DHCP_IPC_E_TIMEOUT);
1007 1007 continue;
1008 1008 }
1009 1009
1010 1010 if (!(pollfd.revents & POLLIN)) {
1011 1011 errno = EINVAL;
1012 1012 return (DHCP_IPC_E_POLL);
1013 1013 }
1014 1014
1015 1015 n_read = read(fd, (caddr_t)buffer + n_total, length - n_total);
1016 1016
1017 1017 if (n_read == -1) {
1018 1018 if (errno != EINTR)
1019 1019 return (DHCP_IPC_E_READ);
1020 1020 else if (*msec == 0)
1021 1021 return (DHCP_IPC_E_TIMEOUT);
1022 1022 continue;
1023 1023 }
1024 1024
1025 1025 if (n_read == 0) {
1026 1026 return (n_total == 0 ? DHCP_IPC_E_EOF :
1027 1027 DHCP_IPC_E_PROTO);
1028 1028 }
1029 1029
1030 1030 n_total += n_read;
1031 1031
1032 1032 if (*msec == 0 && n_total < length)
1033 1033 return (DHCP_IPC_E_TIMEOUT);
1034 1034 }
1035 1035
1036 1036 return (DHCP_IPC_SUCCESS);
1037 1037 }
↓ open down ↓ |
30 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX