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 #include <stdio.h> /* snprintf */ 43 44 #include "defaults.h" 45 #include "arp_check.h" 46 #include "states.h" 47 #include "packet.h" 48 #include "util.h" 49 #include "agent.h" 50 #include "interface.h" 51 #include "script_handler.h" 52 53 #define IS_DHCP(plp) ((plp)->opts[CD_DHCP_TYPE] != NULL) 54 55 static int configure_if(struct ifslist *); 56 static int configure_timers(struct ifslist *); 57 58 /* 59 * bound_event_cb(): callback for script_start on the event EVENT_BOUND 60 * 61 * input: struct ifslist *: the interface configured 62 * const char *: unused 63 * output: int: always 1 64 */ 65 66 /* ARGSUSED */ 67 static int 68 bound_event_cb(struct ifslist *ifsp, const char *msg) 69 { 70 ipc_action_finish(ifsp, DHCP_IPC_SUCCESS); 71 async_finish(ifsp); 72 return (1); 73 } 74 75 /* 76 * dhcp_bound(): configures an interface and ifs using information contained 77 * in the ACK packet and sets up lease timers. before starting, 78 * the requested address is arped to make sure it's not in use. 79 * 80 * input: struct ifslist *: the interface to move to bound 81 * PKT_LIST *: the ACK packet, or NULL if it should use ifsp->if_ack 82 * output: int: 0 on failure, 1 on success 83 */ 84 85 int 86 dhcp_bound(struct ifslist *ifsp, PKT_LIST *ack) 87 { 88 lease_t cur_lease, new_lease; 89 int msg_level; 90 const char *noext = "lease renewed but time not extended"; 91 uint_t minleft; 92 93 if (ack != NULL) { 94 /* If ack we're replacing is not the original, then free it */ 95 if (ifsp->if_ack != ifsp->if_orig_ack) 96 free_pkt_list(&ifsp->if_ack); 97 ifsp->if_ack = ack; 98 /* Save the first ack as the original */ 99 if (ifsp->if_orig_ack == NULL) 100 ifsp->if_orig_ack = ack; 101 } 102 103 switch (ifsp->if_state) { 104 105 case ADOPTING: 106 107 /* 108 * if we're adopting an interface, the lease timers 109 * only provide an upper bound since we don't know 110 * from what time they are relative to. assume we 111 * have a lease time of at most DHCP_ADOPT_LEASE_MAX. 112 */ 113 114 if (!IS_DHCP(ifsp->if_ack)) 115 return (0); 116 117 (void) memcpy(&new_lease, 118 ifsp->if_ack->opts[CD_LEASE_TIME]->value, sizeof (lease_t)); 119 120 new_lease = htonl(MIN(ntohl(new_lease), DHCP_ADOPT_LEASE_MAX)); 121 122 (void) memcpy(ifsp->if_ack->opts[CD_LEASE_TIME]->value, 123 &new_lease, sizeof (lease_t)); 124 125 /* 126 * we have no idea when the REQUEST that generated 127 * this ACK was sent, but for diagnostic purposes 128 * we'll assume its close to the current time. 129 */ 130 131 ifsp->if_newstart_monosec = monosec(); 132 133 /* FALLTHRU into REQUESTING/INIT_REBOOT */ 134 135 case REQUESTING: 136 case INIT_REBOOT: 137 138 if (configure_if(ifsp) == 0) 139 return (0); 140 141 if (configure_timers(ifsp) == 0) 142 return (0); 143 144 /* 145 * if the state is ADOPTING, event loop has not been started 146 * at this time, so don't run the script. 147 */ 148 149 if (ifsp->if_state != ADOPTING) { 150 (void) script_start(ifsp, EVENT_BOUND, bound_event_cb, 151 NULL, NULL); 152 } 153 154 break; 155 156 case RENEWING: 157 case REBINDING: 158 case BOUND: 159 160 cur_lease = ifsp->if_lease; 161 if (configure_timers(ifsp) == 0) 162 return (0); 163 164 /* 165 * if the current lease is mysteriously close to the new 166 * lease, warn the user. unless there's less than a minute 167 * left, round to the closest minute. 168 */ 169 170 if (abs((ifsp->if_newstart_monosec + ifsp->if_lease) - 171 (ifsp->if_curstart_monosec + cur_lease)) < DHCP_LEASE_EPS) { 172 173 if (ifsp->if_lease < DHCP_LEASE_ERROR_THRESH) 174 msg_level = MSG_ERROR; 175 else 176 msg_level = MSG_VERBOSE; 177 178 minleft = (ifsp->if_lease + 30) / 60; 179 180 if (ifsp->if_lease < 60) { 181 dhcpmsg(msg_level, "%s; expires in %d seconds", 182 noext, ifsp->if_lease); 183 } else if (minleft == 1) { 184 dhcpmsg(msg_level, "%s; expires in 1 minute", 185 noext); 186 } else { 187 dhcpmsg(msg_level, "%s; expires in %d minutes", 188 noext, minleft); 189 } 190 } 191 192 (void) script_start(ifsp, EVENT_EXTEND, bound_event_cb, 193 NULL, NULL); 194 195 break; 196 197 case INFORM_SENT: 198 199 (void) bound_event_cb(ifsp, NULL); 200 ifsp->if_state = INFORMATION; 201 break; 202 203 default: 204 /* something is really bizarre... */ 205 dhcpmsg(MSG_DEBUG, "dhcp_bound: called in unexpected state"); 206 return (0); 207 } 208 209 if (ifsp->if_state != INFORMATION) { 210 ifsp->if_state = BOUND; 211 ifsp->if_curstart_monosec = ifsp->if_newstart_monosec; 212 } 213 214 /* 215 * remove any stale hostconf file that might be lying around for 216 * this interface. (in general, it's harmless, since we'll write a 217 * fresh one when we exit anyway, but just to reduce confusion..) 218 */ 219 220 (void) remove_hostconf(ifsp->if_name); 221 return (1); 222 } 223 224 /* 225 * configure_timers(): configures the lease timers on an interface 226 * 227 * input: struct ifslist *: the interface to configure (with a valid if_ack) 228 * output: int: 1 on success, 0 on failure 229 */ 230 231 int 232 configure_timers(struct ifslist *ifsp) 233 { 234 lease_t lease, t1, t2; 235 236 if (ifsp->if_ack->opts[CD_DHCP_TYPE] != NULL && 237 (ifsp->if_ack->opts[CD_LEASE_TIME] == NULL || 238 ifsp->if_ack->opts[CD_LEASE_TIME]->len != sizeof (lease_t))) { 239 send_decline(ifsp, "Missing or corrupted lease time", 240 &ifsp->if_ack->pkt->yiaddr); 241 dhcpmsg(MSG_WARNING, "configure_timers: missing or corrupted " 242 "lease time in ACK on %s", ifsp->if_name); 243 return (0); 244 } 245 246 cancel_ifs_timers(ifsp); 247 248 /* 249 * type has already been verified as ACK. if type is not set, 250 * then we got a BOOTP packet. we now fetch the t1, t2, and 251 * lease options out of the packet into variables. they are 252 * returned as relative host-byte-ordered times. 253 */ 254 255 get_pkt_times(ifsp->if_ack, &lease, &t1, &t2); 256 257 ifsp->if_t1 = t1; 258 ifsp->if_t2 = t2; 259 ifsp->if_lease = lease; 260 261 if (ifsp->if_lease == DHCP_PERM) { 262 dhcpmsg(MSG_INFO, "%s acquired permanent lease", ifsp->if_name); 263 return (1); 264 } 265 266 dhcpmsg(MSG_INFO, "%s acquired lease, expires %s", ifsp->if_name, 267 monosec_to_string(ifsp->if_newstart_monosec + ifsp->if_lease)); 268 269 dhcpmsg(MSG_INFO, "%s begins renewal at %s", ifsp->if_name, 270 monosec_to_string(ifsp->if_newstart_monosec + ifsp->if_t1)); 271 272 dhcpmsg(MSG_INFO, "%s begins rebinding at %s", ifsp->if_name, 273 monosec_to_string(ifsp->if_newstart_monosec + ifsp->if_t2)); 274 275 /* 276 * according to RFC2131, there is no minimum lease time, but don't 277 * set up renew/rebind timers if lease is shorter than DHCP_REBIND_MIN. 278 */ 279 280 if (schedule_ifs_timer(ifsp, DHCP_LEASE_TIMER, lease, dhcp_expire) == 0) 281 goto failure; 282 283 if (lease < DHCP_REBIND_MIN) { 284 dhcpmsg(MSG_WARNING, "dhcp_bound: lease on %s is for " 285 "less than %d seconds!", ifsp->if_name, DHCP_REBIND_MIN); 286 return (1); 287 } 288 289 if (schedule_ifs_timer(ifsp, DHCP_T1_TIMER, t1, dhcp_renew) == 0) 290 goto failure; 291 292 if (schedule_ifs_timer(ifsp, DHCP_T2_TIMER, t2, dhcp_rebind) == 0) 293 goto failure; 294 295 return (1); 296 297 failure: 298 cancel_ifs_timers(ifsp); 299 dhcpmsg(MSG_WARNING, "dhcp_bound: cannot schedule lease timers"); 300 return (0); 301 } 302 303 /* 304 * configure_if(): configures an interface with DHCP parameters from an ACK 305 * 306 * input: struct ifslist *: the interface to configure (with a valid if_ack) 307 * output: int: 1 on success, 0 on failure 308 */ 309 310 static int 311 configure_if(struct ifslist *ifsp) 312 { 313 struct ifreq ifr; 314 struct sockaddr_in *sin; 315 PKT_LIST *ack = ifsp->if_ack; 316 DHCP_OPT *router_list; 317 uchar_t *target_hwaddr; 318 int i; 319 char in_use[256] = "IP address already in use by"; 320 321 /* 322 * if we're using DHCP, then we'll have a valid CD_SERVER_ID 323 * (we checked in dhcp_acknak()); set it now so that 324 * ifsp->if_server is valid in case we need to send_decline(). 325 * note that we use comparisons against opts[CD_DHCP_TYPE] 326 * since we haven't set DHCP_IF_BOOTP yet (we don't do that 327 * until we're sure we want the offered address.) 328 */ 329 330 if (ifsp->if_ack->opts[CD_DHCP_TYPE] != NULL) 331 (void) memcpy(&ifsp->if_server.s_addr, 332 ack->opts[CD_SERVER_ID]->value, sizeof (ipaddr_t)); 333 334 /* no big deal if this fails; we'll just have less diagnostics */ 335 target_hwaddr = malloc(ifsp->if_hwlen); 336 337 if (arp_check(ifsp, 0, ack->pkt->yiaddr.s_addr, target_hwaddr, 338 ifsp->if_hwlen, df_get_int(ifsp->if_name, DF_ARP_WAIT)) == 1) { 339 340 for (i = 0; i < ifsp->if_hwlen; i++) 341 (void) snprintf(in_use, sizeof (in_use), "%s %02x", 342 in_use, target_hwaddr[i]); 343 344 dhcpmsg(MSG_ERROR, in_use); 345 346 if (ifsp->if_ack->opts[CD_DHCP_TYPE] != NULL) 347 send_decline(ifsp, in_use, &ack->pkt->yiaddr); 348 349 ifsp->if_bad_offers++; 350 free(target_hwaddr); 351 return (0); 352 } 353 free(target_hwaddr); 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 502 /* 503 * add each provided router; we'll clean them up when the 504 * interface goes away or when our lease expires. 505 */ 506 507 router_list = ack->opts[CD_ROUTER]; 508 if (router_list && (router_list->len % sizeof (ipaddr_t)) == 0) { 509 510 ifsp->if_nrouters = router_list->len / sizeof (ipaddr_t); 511 ifsp->if_routers = malloc(router_list->len); 512 if (ifsp->if_routers == NULL) { 513 dhcpmsg(MSG_ERR, "configure_if: cannot allocate " 514 "default router list, ignoring default routers"); 515 ifsp->if_nrouters = 0; 516 } 517 518 for (i = 0; i < ifsp->if_nrouters; i++) { 519 520 (void) memcpy(&ifsp->if_routers[i].s_addr, 521 router_list->value + (i * sizeof (ipaddr_t)), 522 sizeof (ipaddr_t)); 523 524 if (add_default_route(ifsp->if_name, 525 &ifsp->if_routers[i]) == 0) { 526 dhcpmsg(MSG_ERR, "configure_if: cannot add " 527 "default router %s on %s", inet_ntoa( 528 ifsp->if_routers[i]), ifsp->if_name); 529 ifsp->if_routers[i].s_addr = htonl(INADDR_ANY); 530 continue; 531 } 532 533 dhcpmsg(MSG_INFO, "added default router %s on %s", 534 inet_ntoa(ifsp->if_routers[i]), ifsp->if_name); 535 } 536 } 537 538 ifsp->if_sock_ip_fd = socket(AF_INET, SOCK_DGRAM, 0); 539 if (ifsp->if_sock_ip_fd == -1) { 540 dhcpmsg(MSG_ERR, "configure_if: cannot create socket on %s", 541 ifsp->if_name); 542 return (0); 543 } 544 545 if (bind_sock(ifsp->if_sock_ip_fd, IPPORT_BOOTPC, 546 ntohl(ifsp->if_addr.s_addr)) == 0) { 547 dhcpmsg(MSG_ERR, "configure_if: cannot bind socket on %s", 548 ifsp->if_name); 549 return (0); 550 } 551 552 /* 553 * we wait until here to bind if_sock_fd because it turns out 554 * the kernel has difficulties doing binds before interfaces 555 * are up (although it may work sometimes, it doesn't work all 556 * the time.) that's okay, because we don't use if_sock_fd 557 * for receiving data until we're BOUND anyway. 558 */ 559 560 if (bind_sock(ifsp->if_sock_fd, IPPORT_BOOTPC, INADDR_BROADCAST) == 0) { 561 dhcpmsg(MSG_ERR, "configure_if: cannot bind broadcast socket " 562 "on %s", ifsp->if_name); 563 return (0); 564 } 565 566 /* 567 * we'll be using if_sock_fd for the remainder of the lease; 568 * blackhole if_dlpi_fd. 569 */ 570 571 set_packet_filter(ifsp->if_dlpi_fd, blackhole_filter, 0, "blackhole"); 572 573 if (ack->opts[CD_DHCP_TYPE] == NULL) 574 ifsp->if_dflags |= DHCP_IF_BOOTP; 575 576 dhcpmsg(MSG_DEBUG, "configure_if: bound ifsp->if_sock_ip_fd"); 577 return (1); 578 } 579