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