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