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