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