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 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * 25 * BOUND state of the DHCP client state machine. 26 */ 27 28 #pragma ident "%Z%%M% %I% %E% SMI" 29 30 #include <sys/socket.h> 31 #include <sys/types.h> 32 #include <string.h> 33 #include <netinet/in.h> 34 #include <sys/sockio.h> 35 #include <unistd.h> 36 #include <time.h> 37 #include <arpa/inet.h> 38 #include <stdlib.h> 39 #include <sys/sysmacros.h> 40 #include <dhcp_hostconf.h> 41 #include <dhcpmsg.h> 42 43 #include "states.h" 44 #include "packet.h" 45 #include "util.h" 46 #include "agent.h" 47 #include "interface.h" 48 #include "script_handler.h" 49 50 #define IS_DHCP(plp) ((plp)->opts[CD_DHCP_TYPE] != NULL) 51 52 static int configure_if(struct ifslist *); 53 static int configure_bound(struct ifslist *); 54 static int configure_timers(struct ifslist *); 55 56 /* 57 * bound_event_cb(): callback for script_start on the event EVENT_BOUND 58 * 59 * input: struct ifslist *: the interface configured 60 * const char *: unused 61 * output: int: always 1 62 */ 63 64 /* ARGSUSED */ 65 static int 66 bound_event_cb(struct ifslist *ifsp, const char *msg) 67 { 68 ipc_action_finish(ifsp, DHCP_IPC_SUCCESS); 69 async_finish(ifsp); 70 return (1); 71 } 72 73 /* 74 * dhcp_bound(): configures an interface and ifs using information contained 75 * in the ACK packet and sets up lease timers. before starting, 76 * the requested address is arped to make sure it's not in use. 77 * 78 * input: struct ifslist *: the interface to move to bound 79 * PKT_LIST *: the ACK packet, or NULL if it should use ifsp->if_ack 80 * output: int: 0 on failure, 1 on success 81 */ 82 83 int 84 dhcp_bound(struct ifslist *ifsp, PKT_LIST *ack) 85 { 86 lease_t cur_lease, new_lease; 87 int msg_level; 88 const char *noext = "lease renewed but time not extended"; 89 uint_t minleft; 90 91 if (ack != NULL) { 92 /* If ack we're replacing is not the original, then free it */ 93 if (ifsp->if_ack != ifsp->if_orig_ack) 94 free_pkt_list(&ifsp->if_ack); 95 ifsp->if_ack = ack; 96 /* Save the first ack as the original */ 97 if (ifsp->if_orig_ack == NULL) 98 ifsp->if_orig_ack = ack; 99 } 100 101 switch (ifsp->if_state) { 102 103 case ADOPTING: 104 105 /* 106 * if we're adopting an interface, the lease timers 107 * only provide an upper bound since we don't know 108 * from what time they are relative to. assume we 109 * have a lease time of at most DHCP_ADOPT_LEASE_MAX. 110 */ 111 112 if (!IS_DHCP(ifsp->if_ack)) 113 return (0); 114 115 (void) memcpy(&new_lease, 116 ifsp->if_ack->opts[CD_LEASE_TIME]->value, sizeof (lease_t)); 117 118 new_lease = htonl(MIN(ntohl(new_lease), DHCP_ADOPT_LEASE_MAX)); 119 120 (void) memcpy(ifsp->if_ack->opts[CD_LEASE_TIME]->value, 121 &new_lease, sizeof (lease_t)); 122 123 /* 124 * we have no idea when the REQUEST that generated 125 * this ACK was sent, but for diagnostic purposes 126 * we'll assume its close to the current time. 127 */ 128 ifsp->if_newstart_monosec = monosec(); 129 130 if (configure_if(ifsp) == 0) 131 return (0); 132 133 if (configure_timers(ifsp) == 0) 134 return (0); 135 136 ifsp->if_curstart_monosec = ifsp->if_newstart_monosec; 137 break; 138 139 case REQUESTING: 140 case INIT_REBOOT: 141 142 if (configure_if(ifsp) == 0) 143 return (0); 144 145 if (configure_timers(ifsp) == 0) 146 return (0); 147 148 /* 149 * We will continue configuring this interface via 150 * dhcp_bound_complete, once kernel DAD completes. 151 */ 152 ifsp->if_state = PRE_BOUND; 153 break; 154 155 case PRE_BOUND: 156 /* This is just a duplicate ack; silently ignore it */ 157 return (1); 158 159 case RENEWING: 160 case REBINDING: 161 case BOUND: 162 cur_lease = ifsp->if_lease; 163 if (configure_timers(ifsp) == 0) 164 return (0); 165 166 /* 167 * if the current lease is mysteriously close to the new 168 * lease, warn the user. unless there's less than a minute 169 * left, round to the closest minute. 170 */ 171 172 if (abs((ifsp->if_newstart_monosec + ifsp->if_lease) - 173 (ifsp->if_curstart_monosec + cur_lease)) < DHCP_LEASE_EPS) { 174 175 if (ifsp->if_lease < DHCP_LEASE_ERROR_THRESH) 176 msg_level = MSG_ERROR; 177 else 178 msg_level = MSG_VERBOSE; 179 180 minleft = (ifsp->if_lease + 30) / 60; 181 182 if (ifsp->if_lease < 60) { 183 dhcpmsg(msg_level, "%s; expires in %d seconds", 184 noext, ifsp->if_lease); 185 } else if (minleft == 1) { 186 dhcpmsg(msg_level, "%s; expires in 1 minute", 187 noext); 188 } else { 189 dhcpmsg(msg_level, "%s; expires in %d minutes", 190 noext, minleft); 191 } 192 } 193 194 (void) script_start(ifsp, EVENT_EXTEND, bound_event_cb, 195 NULL, NULL); 196 197 ifsp->if_state = BOUND; 198 ifsp->if_curstart_monosec = ifsp->if_newstart_monosec; 199 break; 200 201 case INFORM_SENT: 202 203 (void) bound_event_cb(ifsp, NULL); 204 ifsp->if_state = INFORMATION; 205 break; 206 207 default: 208 /* something is really bizarre... */ 209 dhcpmsg(MSG_DEBUG, "dhcp_bound: called in unexpected state"); 210 return (0); 211 } 212 213 /* 214 * remove any stale hostconf file that might be lying around for 215 * this interface. (in general, it's harmless, since we'll write a 216 * fresh one when we exit anyway, but just to reduce confusion..) 217 */ 218 219 (void) remove_hostconf(ifsp->if_name); 220 return (1); 221 } 222 223 /* 224 * dhcp_bound_complete(): complete interface configuration after DAD 225 * 226 * input: struct ifslist *: the interface to configure 227 * output: none 228 */ 229 230 void 231 dhcp_bound_complete(struct ifslist *ifsp) 232 { 233 if (configure_bound(ifsp) == 0) 234 return; 235 236 /* 237 * if the state is ADOPTING, event loop has not been started 238 * at this time; so don't run the EVENT_BOUND script. 239 */ 240 if (ifsp->if_state != ADOPTING) { 241 (void) script_start(ifsp, EVENT_BOUND, bound_event_cb, NULL, 242 NULL); 243 } 244 245 ifsp->if_curstart_monosec = ifsp->if_newstart_monosec; 246 ifsp->if_state = BOUND; 247 } 248 249 /* 250 * configure_timers(): configures the lease timers on an interface 251 * 252 * input: struct ifslist *: the interface to configure (with a valid if_ack) 253 * output: int: 1 on success, 0 on failure 254 */ 255 256 static int 257 configure_timers(struct ifslist *ifsp) 258 { 259 lease_t lease, t1, t2; 260 261 if (ifsp->if_ack->opts[CD_DHCP_TYPE] != NULL && 262 (ifsp->if_ack->opts[CD_LEASE_TIME] == NULL || 263 ifsp->if_ack->opts[CD_LEASE_TIME]->len != sizeof (lease_t))) { 264 send_decline(ifsp, "Missing or corrupted lease time", 265 &ifsp->if_ack->pkt->yiaddr); 266 dhcpmsg(MSG_WARNING, "configure_timers: missing or corrupted " 267 "lease time in ACK on %s", ifsp->if_name); 268 return (0); 269 } 270 271 cancel_ifs_timers(ifsp); 272 273 /* 274 * type has already been verified as ACK. if type is not set, 275 * then we got a BOOTP packet. we now fetch the t1, t2, and 276 * lease options out of the packet into variables. they are 277 * returned as relative host-byte-ordered times. 278 */ 279 280 get_pkt_times(ifsp->if_ack, &lease, &t1, &t2); 281 282 ifsp->if_t1 = t1; 283 ifsp->if_t2 = t2; 284 ifsp->if_lease = lease; 285 286 if (ifsp->if_lease == DHCP_PERM) { 287 dhcpmsg(MSG_INFO, "%s acquired permanent lease", ifsp->if_name); 288 return (1); 289 } 290 291 dhcpmsg(MSG_INFO, "%s acquired lease, expires %s", ifsp->if_name, 292 monosec_to_string(ifsp->if_newstart_monosec + ifsp->if_lease)); 293 294 dhcpmsg(MSG_INFO, "%s begins renewal at %s", ifsp->if_name, 295 monosec_to_string(ifsp->if_newstart_monosec + ifsp->if_t1)); 296 297 dhcpmsg(MSG_INFO, "%s begins rebinding at %s", ifsp->if_name, 298 monosec_to_string(ifsp->if_newstart_monosec + ifsp->if_t2)); 299 300 /* 301 * according to RFC2131, there is no minimum lease time, but don't 302 * set up renew/rebind timers if lease is shorter than DHCP_REBIND_MIN. 303 */ 304 305 if (schedule_ifs_timer(ifsp, DHCP_LEASE_TIMER, lease, dhcp_expire) == 0) 306 goto failure; 307 308 if (lease < DHCP_REBIND_MIN) { 309 dhcpmsg(MSG_WARNING, "dhcp_bound: lease on %s is for " 310 "less than %d seconds!", ifsp->if_name, DHCP_REBIND_MIN); 311 return (1); 312 } 313 314 if (schedule_ifs_timer(ifsp, DHCP_T1_TIMER, t1, dhcp_renew) == 0) 315 goto failure; 316 317 if (schedule_ifs_timer(ifsp, DHCP_T2_TIMER, t2, dhcp_rebind) == 0) 318 goto failure; 319 320 return (1); 321 322 failure: 323 cancel_ifs_timers(ifsp); 324 dhcpmsg(MSG_WARNING, "dhcp_bound: cannot schedule lease timers"); 325 return (0); 326 } 327 328 /* 329 * configure_if(): configures an interface with DHCP parameters from an ACK 330 * 331 * input: struct ifslist *: the interface to configure (with a valid if_ack) 332 * output: int: 1 on success, 0 on failure 333 */ 334 335 static int 336 configure_if(struct ifslist *ifsp) 337 { 338 struct ifreq ifr; 339 struct sockaddr_in *sin; 340 PKT_LIST *ack = ifsp->if_ack; 341 342 /* 343 * if we're using DHCP, then we'll have a valid CD_SERVER_ID 344 * (we checked in dhcp_acknak()); set it now so that 345 * ifsp->if_server is valid in case we need to send_decline(). 346 * note that we use comparisons against opts[CD_DHCP_TYPE] 347 * since we haven't set DHCP_IF_BOOTP yet (we don't do that 348 * until we're sure we want the offered address.) 349 */ 350 351 if (ifsp->if_ack->opts[CD_DHCP_TYPE] != NULL) 352 (void) memcpy(&ifsp->if_server.s_addr, 353 ack->opts[CD_SERVER_ID]->value, sizeof (ipaddr_t)); 354 355 ifsp->if_addr.s_addr = ack->pkt->yiaddr.s_addr; 356 if (ifsp->if_addr.s_addr == htonl(INADDR_ANY)) { 357 dhcpmsg(MSG_ERROR, "configure_if: got invalid IP address"); 358 return (0); 359 } 360 361 (void) memset(&ifr, 0, sizeof (struct ifreq)); 362 (void) strlcpy(ifr.ifr_name, ifsp->if_name, IFNAMSIZ); 363 364 /* 365 * bring the interface online. note that there is no optimal 366 * order here: it is considered bad taste (and in > solaris 7, 367 * likely illegal) to bring an interface up before it has an 368 * ip address. however, due to an apparent bug in sun fddi 369 * 5.0, fddi will not obtain a network routing entry unless 370 * the interface is brought up before it has an ip address. 371 * we take the lesser of the two evils; if fddi customers have 372 * problems, they can get a newer fddi distribution which 373 * fixes the problem. 374 */ 375 376 /* LINTED [ifr_addr is a sockaddr which will be aligned] */ 377 sin = (struct sockaddr_in *)&ifr.ifr_addr; 378 sin->sin_family = AF_INET; 379 380 if (ack->opts[CD_SUBNETMASK] != NULL && 381 ack->opts[CD_SUBNETMASK]->len == sizeof (ipaddr_t)) { 382 383 (void) memcpy(&ifsp->if_netmask.s_addr, 384 ack->opts[CD_SUBNETMASK]->value, sizeof (ipaddr_t)); 385 386 } else { 387 388 if (ack->opts[CD_SUBNETMASK] != NULL && 389 ack->opts[CD_SUBNETMASK]->len != sizeof (ipaddr_t)) 390 dhcpmsg(MSG_WARNING, "configure_if: specified subnet " 391 "mask length is %d instead of %d, ignoring", 392 ack->opts[CD_SUBNETMASK]->len, sizeof (ipaddr_t)); 393 394 /* 395 * no legitimate IP subnet mask specified.. use best 396 * guess. recall that if_addr is in network order, so 397 * imagine it's 0x11223344: then when it is read into 398 * a register on x86, it becomes 0x44332211, so we 399 * must ntohl() it to convert it to 0x11223344 in 400 * order to use the macros in <netinet/in.h>. 401 */ 402 403 if (IN_CLASSA(ntohl(ifsp->if_addr.s_addr))) 404 ifsp->if_netmask.s_addr = htonl(IN_CLASSA_NET); 405 else if (IN_CLASSB(ntohl(ifsp->if_addr.s_addr))) 406 ifsp->if_netmask.s_addr = htonl(IN_CLASSB_NET); 407 else if (IN_CLASSC(ntohl(ifsp->if_addr.s_addr))) 408 ifsp->if_netmask.s_addr = htonl(IN_CLASSC_NET); 409 else /* must be class d */ 410 ifsp->if_netmask.s_addr = htonl(IN_CLASSD_NET); 411 412 dhcpmsg(MSG_WARNING, "configure_if: no IP netmask specified " 413 "for %s, making best guess", ifsp->if_name); 414 } 415 416 dhcpmsg(MSG_INFO, "setting IP netmask to %s on %s", 417 inet_ntoa(ifsp->if_netmask), ifsp->if_name); 418 419 sin->sin_addr = ifsp->if_netmask; 420 if (ioctl(ifsp->if_sock_fd, SIOCSIFNETMASK, &ifr) == -1) { 421 dhcpmsg(MSG_ERR, "cannot set IP netmask on %s", ifsp->if_name); 422 return (0); 423 } 424 425 dhcpmsg(MSG_INFO, "setting IP address to %s on %s", 426 inet_ntoa(ifsp->if_addr), ifsp->if_name); 427 428 sin->sin_addr = ifsp->if_addr; 429 if (ioctl(ifsp->if_sock_fd, SIOCSIFADDR, &ifr) == -1) { 430 dhcpmsg(MSG_ERR, "configure_if: cannot set IP address on %s", 431 ifsp->if_name); 432 return (0); 433 } 434 435 if (ack->opts[CD_BROADCASTADDR] != NULL && 436 ack->opts[CD_BROADCASTADDR]->len == sizeof (ipaddr_t)) { 437 438 (void) memcpy(&ifsp->if_broadcast.s_addr, 439 ack->opts[CD_BROADCASTADDR]->value, sizeof (ipaddr_t)); 440 441 } else { 442 443 if (ack->opts[CD_BROADCASTADDR] != NULL && 444 ack->opts[CD_BROADCASTADDR]->len != sizeof (ipaddr_t)) 445 dhcpmsg(MSG_WARNING, "configure_if: specified " 446 "broadcast address length is %d instead of %d, " 447 "ignoring", ack->opts[CD_BROADCASTADDR]->len, 448 sizeof (ipaddr_t)); 449 450 /* 451 * no legitimate IP broadcast specified. compute it 452 * from the IP address and netmask. 453 */ 454 455 ifsp->if_broadcast.s_addr = ifsp->if_addr.s_addr & 456 ifsp->if_netmask.s_addr | ~ifsp->if_netmask.s_addr; 457 458 dhcpmsg(MSG_WARNING, "configure_if: no IP broadcast specified " 459 "for %s, making best guess", ifsp->if_name); 460 } 461 462 if (ioctl(ifsp->if_sock_fd, SIOCGIFFLAGS, &ifr) == -1) { 463 dhcpmsg(MSG_ERR, "configure_if: cannot get interface flags for " 464 "%s", ifsp->if_name); 465 return (0); 466 } 467 468 ifr.ifr_flags |= IFF_UP; 469 470 if (ioctl(ifsp->if_sock_fd, SIOCSIFFLAGS, &ifr) == -1) { 471 dhcpmsg(MSG_ERR, "configure_if: cannot set interface flags for " 472 "%s", ifsp->if_name); 473 return (0); 474 } 475 476 /* 477 * the kernel will set the broadcast address for us as part of 478 * bringing the interface up. since experience has shown that dhcp 479 * servers sometimes provide a bogus broadcast address, we let the 480 * kernel set it so that it's guaranteed to be correct. 481 * 482 * also, note any inconsistencies and save the broadcast address the 483 * kernel set so that we can watch for changes to it. 484 */ 485 486 if (ioctl(ifsp->if_sock_fd, SIOCGIFBRDADDR, &ifr) == -1) { 487 dhcpmsg(MSG_ERR, "configure_if: cannot get broadcast address " 488 "for %s", ifsp->if_name); 489 return (0); 490 } 491 492 if (ifsp->if_broadcast.s_addr != sin->sin_addr.s_addr) { 493 dhcpmsg(MSG_WARNING, "incorrect broadcast address %s specified " 494 "for %s; ignoring", inet_ntoa(ifsp->if_broadcast), 495 ifsp->if_name); 496 } 497 498 ifsp->if_broadcast = sin->sin_addr; 499 dhcpmsg(MSG_INFO, "using broadcast address %s on %s", 500 inet_ntoa(ifsp->if_broadcast), ifsp->if_name); 501 return (1); 502 } 503 504 /* 505 * configure_bound(): configures routing with DHCP parameters from an ACK, 506 * and sets up the if_sock_ip_fd socket used for lease 507 * renewal. 508 * 509 * input: struct ifslist *: the interface to configure (with a valid if_ack) 510 * output: int: 1 on success, 0 on failure 511 */ 512 513 static int 514 configure_bound(struct ifslist *ifsp) 515 { 516 PKT_LIST *ack = ifsp->if_ack; 517 DHCP_OPT *router_list; 518 int i; 519 520 /* 521 * add each provided router; we'll clean them up when the 522 * interface goes away or when our lease expires. 523 */ 524 525 router_list = ack->opts[CD_ROUTER]; 526 if (router_list && (router_list->len % sizeof (ipaddr_t)) == 0) { 527 528 ifsp->if_nrouters = router_list->len / sizeof (ipaddr_t); 529 ifsp->if_routers = malloc(router_list->len); 530 if (ifsp->if_routers == NULL) { 531 dhcpmsg(MSG_ERR, "configure_bound: cannot allocate " 532 "default router list, ignoring default routers"); 533 ifsp->if_nrouters = 0; 534 } 535 536 for (i = 0; i < ifsp->if_nrouters; i++) { 537 538 (void) memcpy(&ifsp->if_routers[i].s_addr, 539 router_list->value + (i * sizeof (ipaddr_t)), 540 sizeof (ipaddr_t)); 541 542 if (add_default_route(ifsp->if_name, 543 &ifsp->if_routers[i]) == 0) { 544 dhcpmsg(MSG_ERR, "configure_bound: cannot add " 545 "default router %s on %s", inet_ntoa( 546 ifsp->if_routers[i]), ifsp->if_name); 547 ifsp->if_routers[i].s_addr = htonl(INADDR_ANY); 548 continue; 549 } 550 551 dhcpmsg(MSG_INFO, "added default router %s on %s", 552 inet_ntoa(ifsp->if_routers[i]), ifsp->if_name); 553 } 554 } 555 556 ifsp->if_sock_ip_fd = socket(AF_INET, SOCK_DGRAM, 0); 557 if (ifsp->if_sock_ip_fd == -1) { 558 dhcpmsg(MSG_ERR, "configure_bound: cannot create socket on %s", 559 ifsp->if_name); 560 return (0); 561 } 562 563 if (bind_sock(ifsp->if_sock_ip_fd, IPPORT_BOOTPC, 564 ntohl(ifsp->if_addr.s_addr)) == 0) { 565 dhcpmsg(MSG_ERR, "configure_bound: cannot bind socket on %s", 566 ifsp->if_name); 567 return (0); 568 } 569 570 /* 571 * we wait until here to bind if_sock_fd because it turns out 572 * the kernel has difficulties doing binds before interfaces 573 * are up (although it may work sometimes, it doesn't work all 574 * the time.) that's okay, because we don't use if_sock_fd 575 * for receiving data until we're BOUND anyway. 576 */ 577 578 if (bind_sock(ifsp->if_sock_fd, IPPORT_BOOTPC, INADDR_BROADCAST) == 0) { 579 dhcpmsg(MSG_ERR, "configure_bound: cannot bind broadcast " 580 "socket on %s", ifsp->if_name); 581 return (0); 582 } 583 584 /* 585 * we'll be using if_sock_fd for the remainder of the lease; 586 * blackhole if_dlpi_fd. 587 */ 588 589 set_packet_filter(ifsp->if_dlpi_fd, blackhole_filter, 0, "blackhole"); 590 591 if (ack->opts[CD_DHCP_TYPE] == NULL) 592 ifsp->if_dflags |= DHCP_IF_BOOTP; 593 594 dhcpmsg(MSG_DEBUG, "configure_bound: bound ifsp->if_sock_ip_fd"); 595 return (1); 596 } 597