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