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