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