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