1*0Sstevel@tonic-gate /* 2*0Sstevel@tonic-gate * CDDL HEADER START 3*0Sstevel@tonic-gate * 4*0Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*0Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*0Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*0Sstevel@tonic-gate * with the License. 8*0Sstevel@tonic-gate * 9*0Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*0Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*0Sstevel@tonic-gate * See the License for the specific language governing permissions 12*0Sstevel@tonic-gate * and limitations under the License. 13*0Sstevel@tonic-gate * 14*0Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*0Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*0Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*0Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*0Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*0Sstevel@tonic-gate * 20*0Sstevel@tonic-gate * CDDL HEADER END 21*0Sstevel@tonic-gate */ 22*0Sstevel@tonic-gate /* 23*0Sstevel@tonic-gate * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24*0Sstevel@tonic-gate * Use is subject to license terms. 25*0Sstevel@tonic-gate * 26*0Sstevel@tonic-gate * REQUESTING state of the client state machine. 27*0Sstevel@tonic-gate */ 28*0Sstevel@tonic-gate 29*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 30*0Sstevel@tonic-gate 31*0Sstevel@tonic-gate #include <sys/types.h> 32*0Sstevel@tonic-gate #include <sys/stropts.h> /* FLUSHR/FLUSHW */ 33*0Sstevel@tonic-gate #include <netinet/in.h> 34*0Sstevel@tonic-gate #include <netinet/dhcp.h> 35*0Sstevel@tonic-gate #include <netinet/udp.h> 36*0Sstevel@tonic-gate #include <netinet/ip_var.h> 37*0Sstevel@tonic-gate #include <netinet/udp_var.h> 38*0Sstevel@tonic-gate #include <dhcp_hostconf.h> 39*0Sstevel@tonic-gate #include <arpa/inet.h> 40*0Sstevel@tonic-gate #include <string.h> 41*0Sstevel@tonic-gate #include <stdlib.h> 42*0Sstevel@tonic-gate #include <unistd.h> 43*0Sstevel@tonic-gate #include <dhcpmsg.h> 44*0Sstevel@tonic-gate 45*0Sstevel@tonic-gate #include "states.h" 46*0Sstevel@tonic-gate #include "util.h" 47*0Sstevel@tonic-gate #include "packet.h" 48*0Sstevel@tonic-gate #include "interface.h" 49*0Sstevel@tonic-gate #include "agent.h" 50*0Sstevel@tonic-gate #include "defaults.h" 51*0Sstevel@tonic-gate 52*0Sstevel@tonic-gate static PKT_LIST *select_best(PKT_LIST **); 53*0Sstevel@tonic-gate static void restart_dhcp(struct ifslist *); 54*0Sstevel@tonic-gate static stop_func_t stop_requesting; 55*0Sstevel@tonic-gate 56*0Sstevel@tonic-gate /* 57*0Sstevel@tonic-gate * dhcp_requesting(): checks if OFFER packets to come in from DHCP servers. 58*0Sstevel@tonic-gate * if so, chooses the best one, sends a REQUEST to the 59*0Sstevel@tonic-gate * server and registers an event handler to receive 60*0Sstevel@tonic-gate * the ACK/NAK 61*0Sstevel@tonic-gate * 62*0Sstevel@tonic-gate * input: iu_tq_t *: unused 63*0Sstevel@tonic-gate * void *: the interface receiving OFFER packets 64*0Sstevel@tonic-gate * output: void 65*0Sstevel@tonic-gate */ 66*0Sstevel@tonic-gate 67*0Sstevel@tonic-gate /* ARGSUSED */ 68*0Sstevel@tonic-gate void 69*0Sstevel@tonic-gate dhcp_requesting(iu_tq_t *tqp, void *arg) 70*0Sstevel@tonic-gate { 71*0Sstevel@tonic-gate struct ifslist *ifsp = (struct ifslist *)arg; 72*0Sstevel@tonic-gate dhcp_pkt_t *dpkt; 73*0Sstevel@tonic-gate PKT_LIST *offer; 74*0Sstevel@tonic-gate lease_t lease; 75*0Sstevel@tonic-gate 76*0Sstevel@tonic-gate if (check_ifs(ifsp) == 0) { 77*0Sstevel@tonic-gate (void) release_ifs(ifsp); 78*0Sstevel@tonic-gate return; 79*0Sstevel@tonic-gate } 80*0Sstevel@tonic-gate 81*0Sstevel@tonic-gate /* 82*0Sstevel@tonic-gate * select the best OFFER; all others pitched. 83*0Sstevel@tonic-gate */ 84*0Sstevel@tonic-gate 85*0Sstevel@tonic-gate offer = select_best(&ifsp->if_recv_pkt_list); 86*0Sstevel@tonic-gate if (offer == NULL) { 87*0Sstevel@tonic-gate 88*0Sstevel@tonic-gate dhcpmsg(MSG_VERBOSE, "no OFFERs on %s, waiting...", 89*0Sstevel@tonic-gate ifsp->if_name); 90*0Sstevel@tonic-gate 91*0Sstevel@tonic-gate /* 92*0Sstevel@tonic-gate * no acceptable OFFERs have come in. reschedule 93*0Sstevel@tonic-gate * ourselves for callback. 94*0Sstevel@tonic-gate */ 95*0Sstevel@tonic-gate 96*0Sstevel@tonic-gate if (iu_schedule_timer(tq, ifsp->if_offer_wait, 97*0Sstevel@tonic-gate dhcp_requesting, ifsp) == -1) { 98*0Sstevel@tonic-gate 99*0Sstevel@tonic-gate /* 100*0Sstevel@tonic-gate * ugh. the best we can do at this point is 101*0Sstevel@tonic-gate * revert back to INIT and wait for a user to 102*0Sstevel@tonic-gate * restart us. 103*0Sstevel@tonic-gate */ 104*0Sstevel@tonic-gate 105*0Sstevel@tonic-gate ifsp->if_state = INIT; 106*0Sstevel@tonic-gate ifsp->if_dflags |= DHCP_IF_FAILED; 107*0Sstevel@tonic-gate 108*0Sstevel@tonic-gate stop_pkt_retransmission(ifsp); 109*0Sstevel@tonic-gate ipc_action_finish(ifsp, DHCP_IPC_E_MEMORY); 110*0Sstevel@tonic-gate async_finish(ifsp); 111*0Sstevel@tonic-gate 112*0Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "dhcp_requesting: cannot " 113*0Sstevel@tonic-gate "reschedule callback, reverting to INIT state on " 114*0Sstevel@tonic-gate "%s", ifsp->if_name); 115*0Sstevel@tonic-gate } else 116*0Sstevel@tonic-gate hold_ifs(ifsp); 117*0Sstevel@tonic-gate 118*0Sstevel@tonic-gate return; 119*0Sstevel@tonic-gate } 120*0Sstevel@tonic-gate 121*0Sstevel@tonic-gate stop_pkt_retransmission(ifsp); 122*0Sstevel@tonic-gate 123*0Sstevel@tonic-gate /* 124*0Sstevel@tonic-gate * stop collecting packets. check to see whether we got an 125*0Sstevel@tonic-gate * OFFER or a BOOTP packet. if we got a BOOTP packet, go to 126*0Sstevel@tonic-gate * the BOUND state now. 127*0Sstevel@tonic-gate */ 128*0Sstevel@tonic-gate 129*0Sstevel@tonic-gate if (iu_unregister_event(eh, ifsp->if_offer_id, NULL) != 0) { 130*0Sstevel@tonic-gate (void) release_ifs(ifsp); 131*0Sstevel@tonic-gate ifsp->if_offer_id = -1; 132*0Sstevel@tonic-gate } 133*0Sstevel@tonic-gate 134*0Sstevel@tonic-gate if (offer->opts[CD_DHCP_TYPE] == NULL) { 135*0Sstevel@tonic-gate 136*0Sstevel@tonic-gate ifsp->if_state = REQUESTING; 137*0Sstevel@tonic-gate 138*0Sstevel@tonic-gate if (dhcp_bound(ifsp, offer) == 0) { 139*0Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "dhcp_requesting: dhcp_bound " 140*0Sstevel@tonic-gate "failed for %s", ifsp->if_name); 141*0Sstevel@tonic-gate restart_dhcp(ifsp); 142*0Sstevel@tonic-gate return; 143*0Sstevel@tonic-gate } 144*0Sstevel@tonic-gate 145*0Sstevel@tonic-gate return; 146*0Sstevel@tonic-gate } 147*0Sstevel@tonic-gate 148*0Sstevel@tonic-gate /* 149*0Sstevel@tonic-gate * if we got a message from the server, display it. 150*0Sstevel@tonic-gate */ 151*0Sstevel@tonic-gate 152*0Sstevel@tonic-gate if (offer->opts[CD_MESSAGE] != NULL) 153*0Sstevel@tonic-gate print_server_msg(ifsp, offer->opts[CD_MESSAGE]); 154*0Sstevel@tonic-gate 155*0Sstevel@tonic-gate /* 156*0Sstevel@tonic-gate * assemble a DHCPREQUEST, with the ciaddr field set to 0, 157*0Sstevel@tonic-gate * since we got here from the INIT state. 158*0Sstevel@tonic-gate */ 159*0Sstevel@tonic-gate 160*0Sstevel@tonic-gate dpkt = init_pkt(ifsp, REQUEST); 161*0Sstevel@tonic-gate 162*0Sstevel@tonic-gate /* 163*0Sstevel@tonic-gate * grab the lease out of the OFFER; we know it's valid since 164*0Sstevel@tonic-gate * select_best() already checked. The max dhcp message size 165*0Sstevel@tonic-gate * option is set to the interface max, minus the size of the udp and 166*0Sstevel@tonic-gate * ip headers. 167*0Sstevel@tonic-gate */ 168*0Sstevel@tonic-gate 169*0Sstevel@tonic-gate (void) memcpy(&lease, offer->opts[CD_LEASE_TIME]->value, 170*0Sstevel@tonic-gate sizeof (lease_t)); 171*0Sstevel@tonic-gate 172*0Sstevel@tonic-gate add_pkt_opt32(dpkt, CD_LEASE_TIME, lease); 173*0Sstevel@tonic-gate add_pkt_opt16(dpkt, CD_MAX_DHCP_SIZE, htons(ifsp->if_max - 174*0Sstevel@tonic-gate sizeof (struct udpiphdr))); 175*0Sstevel@tonic-gate add_pkt_opt32(dpkt, CD_REQUESTED_IP_ADDR, offer->pkt->yiaddr.s_addr); 176*0Sstevel@tonic-gate add_pkt_opt(dpkt, CD_SERVER_ID, offer->opts[CD_SERVER_ID]->value, 177*0Sstevel@tonic-gate offer->opts[CD_SERVER_ID]->len); 178*0Sstevel@tonic-gate 179*0Sstevel@tonic-gate add_pkt_opt(dpkt, CD_CLASS_ID, class_id, class_id_len); 180*0Sstevel@tonic-gate add_pkt_opt(dpkt, CD_REQUEST_LIST, ifsp->if_prl, ifsp->if_prllen); 181*0Sstevel@tonic-gate 182*0Sstevel@tonic-gate /* 183*0Sstevel@tonic-gate * if_reqhost was set for this interface in dhcp_selecting() 184*0Sstevel@tonic-gate * if the DF_REQUEST_HOSTNAME option set and a host name was 185*0Sstevel@tonic-gate * found 186*0Sstevel@tonic-gate */ 187*0Sstevel@tonic-gate if (ifsp->if_reqhost != NULL) { 188*0Sstevel@tonic-gate add_pkt_opt(dpkt, CD_HOSTNAME, ifsp->if_reqhost, 189*0Sstevel@tonic-gate strlen(ifsp->if_reqhost)); 190*0Sstevel@tonic-gate } 191*0Sstevel@tonic-gate add_pkt_opt(dpkt, CD_END, NULL, 0); 192*0Sstevel@tonic-gate 193*0Sstevel@tonic-gate /* all done with the offer */ 194*0Sstevel@tonic-gate free_pkt_list(&offer); 195*0Sstevel@tonic-gate 196*0Sstevel@tonic-gate /* 197*0Sstevel@tonic-gate * send out the REQUEST, trying retransmissions. either a NAK 198*0Sstevel@tonic-gate * or too many REQUEST attempts will revert us to SELECTING. 199*0Sstevel@tonic-gate */ 200*0Sstevel@tonic-gate 201*0Sstevel@tonic-gate ifsp->if_state = REQUESTING; 202*0Sstevel@tonic-gate (void) send_pkt(ifsp, dpkt, htonl(INADDR_BROADCAST), stop_requesting); 203*0Sstevel@tonic-gate 204*0Sstevel@tonic-gate /* 205*0Sstevel@tonic-gate * wait for an ACK or NAK to come back from the server. if 206*0Sstevel@tonic-gate * we can't register this event handler, then we won't be able 207*0Sstevel@tonic-gate * to see the server's responses. the best we can really do 208*0Sstevel@tonic-gate * in that case is drop back to INIT and hope someone notices. 209*0Sstevel@tonic-gate */ 210*0Sstevel@tonic-gate 211*0Sstevel@tonic-gate if (register_acknak(ifsp) == 0) { 212*0Sstevel@tonic-gate 213*0Sstevel@tonic-gate ifsp->if_state = INIT; 214*0Sstevel@tonic-gate ifsp->if_dflags |= DHCP_IF_FAILED; 215*0Sstevel@tonic-gate 216*0Sstevel@tonic-gate ipc_action_finish(ifsp, DHCP_IPC_E_MEMORY); 217*0Sstevel@tonic-gate async_finish(ifsp); 218*0Sstevel@tonic-gate 219*0Sstevel@tonic-gate dhcpmsg(MSG_ERROR, "dhcp_requesting: cannot register to " 220*0Sstevel@tonic-gate "collect ACK/NAK packets, reverting to INIT on %s", 221*0Sstevel@tonic-gate ifsp->if_name); 222*0Sstevel@tonic-gate } 223*0Sstevel@tonic-gate } 224*0Sstevel@tonic-gate 225*0Sstevel@tonic-gate /* 226*0Sstevel@tonic-gate * select_best(): selects the best OFFER packet from a list of OFFER packets 227*0Sstevel@tonic-gate * 228*0Sstevel@tonic-gate * input: PKT_LIST **: a list of packets to select the best from 229*0Sstevel@tonic-gate * output: PKT_LIST *: the best packet, or NULL if none are acceptable 230*0Sstevel@tonic-gate */ 231*0Sstevel@tonic-gate 232*0Sstevel@tonic-gate static PKT_LIST * 233*0Sstevel@tonic-gate select_best(PKT_LIST **pkts) 234*0Sstevel@tonic-gate { 235*0Sstevel@tonic-gate PKT_LIST *current, *best = NULL; 236*0Sstevel@tonic-gate uint32_t points, best_points = 0; 237*0Sstevel@tonic-gate 238*0Sstevel@tonic-gate /* 239*0Sstevel@tonic-gate * pick out the best offer. point system. 240*0Sstevel@tonic-gate * what's important? 241*0Sstevel@tonic-gate * 242*0Sstevel@tonic-gate * 0) DHCP 243*0Sstevel@tonic-gate * 1) no option overload 244*0Sstevel@tonic-gate * 2) encapsulated vendor option 245*0Sstevel@tonic-gate * 3) non-null sname and siaddr fields 246*0Sstevel@tonic-gate * 4) non-null file field 247*0Sstevel@tonic-gate * 5) hostname 248*0Sstevel@tonic-gate * 6) subnetmask 249*0Sstevel@tonic-gate * 7) router 250*0Sstevel@tonic-gate */ 251*0Sstevel@tonic-gate 252*0Sstevel@tonic-gate for (current = *pkts; current != NULL; current = current->next) { 253*0Sstevel@tonic-gate 254*0Sstevel@tonic-gate points = 0; 255*0Sstevel@tonic-gate 256*0Sstevel@tonic-gate if (current->opts[CD_DHCP_TYPE] == NULL) { 257*0Sstevel@tonic-gate dhcpmsg(MSG_VERBOSE, "valid BOOTP reply"); 258*0Sstevel@tonic-gate goto valid_offer; 259*0Sstevel@tonic-gate } 260*0Sstevel@tonic-gate 261*0Sstevel@tonic-gate if (current->opts[CD_LEASE_TIME] == NULL) { 262*0Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "select_best: OFFER without " 263*0Sstevel@tonic-gate "lease time"); 264*0Sstevel@tonic-gate continue; 265*0Sstevel@tonic-gate } 266*0Sstevel@tonic-gate 267*0Sstevel@tonic-gate if (current->opts[CD_LEASE_TIME]->len != sizeof (lease_t)) { 268*0Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "select_best: OFFER with garbled " 269*0Sstevel@tonic-gate "lease time"); 270*0Sstevel@tonic-gate continue; 271*0Sstevel@tonic-gate } 272*0Sstevel@tonic-gate 273*0Sstevel@tonic-gate if (current->opts[CD_SERVER_ID] == NULL) { 274*0Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "select_best: OFFER without " 275*0Sstevel@tonic-gate "server id"); 276*0Sstevel@tonic-gate continue; 277*0Sstevel@tonic-gate } 278*0Sstevel@tonic-gate 279*0Sstevel@tonic-gate if (current->opts[CD_SERVER_ID]->len != sizeof (ipaddr_t)) { 280*0Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "select_best: OFFER with garbled " 281*0Sstevel@tonic-gate "server id"); 282*0Sstevel@tonic-gate continue; 283*0Sstevel@tonic-gate } 284*0Sstevel@tonic-gate 285*0Sstevel@tonic-gate /* valid DHCP OFFER. see if we got our parameters. */ 286*0Sstevel@tonic-gate dhcpmsg(MSG_VERBOSE, "valid OFFER packet"); 287*0Sstevel@tonic-gate points += 30; 288*0Sstevel@tonic-gate 289*0Sstevel@tonic-gate valid_offer: 290*0Sstevel@tonic-gate if (current->rfc1048) 291*0Sstevel@tonic-gate points += 5; 292*0Sstevel@tonic-gate 293*0Sstevel@tonic-gate /* 294*0Sstevel@tonic-gate * also could be faked, though more difficult because 295*0Sstevel@tonic-gate * the encapsulation is hard to encode on a BOOTP 296*0Sstevel@tonic-gate * server; plus there's not as much real estate in the 297*0Sstevel@tonic-gate * packet for options, so it's likely this option 298*0Sstevel@tonic-gate * would get dropped. 299*0Sstevel@tonic-gate */ 300*0Sstevel@tonic-gate 301*0Sstevel@tonic-gate if (current->opts[CD_VENDOR_SPEC] != NULL) 302*0Sstevel@tonic-gate points += 80; 303*0Sstevel@tonic-gate 304*0Sstevel@tonic-gate if (current->opts[CD_SUBNETMASK] != NULL) 305*0Sstevel@tonic-gate points++; 306*0Sstevel@tonic-gate 307*0Sstevel@tonic-gate if (current->opts[CD_ROUTER] != NULL) 308*0Sstevel@tonic-gate points++; 309*0Sstevel@tonic-gate 310*0Sstevel@tonic-gate if (current->opts[CD_HOSTNAME] != NULL) 311*0Sstevel@tonic-gate points += 5; 312*0Sstevel@tonic-gate 313*0Sstevel@tonic-gate dhcpmsg(MSG_DEBUG, "select_best: OFFER had %d points", points); 314*0Sstevel@tonic-gate 315*0Sstevel@tonic-gate if (points >= best_points) { 316*0Sstevel@tonic-gate best_points = points; 317*0Sstevel@tonic-gate best = current; 318*0Sstevel@tonic-gate } 319*0Sstevel@tonic-gate } 320*0Sstevel@tonic-gate 321*0Sstevel@tonic-gate if (best != NULL) { 322*0Sstevel@tonic-gate dhcpmsg(MSG_DEBUG, "select_best: most points: %d", best_points); 323*0Sstevel@tonic-gate remove_from_pkt_list(pkts, best); 324*0Sstevel@tonic-gate } else 325*0Sstevel@tonic-gate dhcpmsg(MSG_DEBUG, "select_best: no valid OFFER/BOOTP reply"); 326*0Sstevel@tonic-gate 327*0Sstevel@tonic-gate free_pkt_list(pkts); 328*0Sstevel@tonic-gate return (best); 329*0Sstevel@tonic-gate } 330*0Sstevel@tonic-gate 331*0Sstevel@tonic-gate /* 332*0Sstevel@tonic-gate * dhcp_acknak(): processes reception of an ACK or NAK packet on an interface 333*0Sstevel@tonic-gate * 334*0Sstevel@tonic-gate * input: iu_eh_t *: unused 335*0Sstevel@tonic-gate * int: the file descriptor the ACK/NAK arrived on 336*0Sstevel@tonic-gate * short: unused 337*0Sstevel@tonic-gate * iu_event_id_t: the id of this event callback with the handler 338*0Sstevel@tonic-gate * void *: the interface that received the ACK or NAK 339*0Sstevel@tonic-gate * output: void 340*0Sstevel@tonic-gate */ 341*0Sstevel@tonic-gate 342*0Sstevel@tonic-gate /* ARGSUSED */ 343*0Sstevel@tonic-gate void 344*0Sstevel@tonic-gate dhcp_acknak(iu_eh_t *ehp, int fd, short events, iu_event_id_t id, void *arg) 345*0Sstevel@tonic-gate { 346*0Sstevel@tonic-gate struct ifslist *ifsp = (struct ifslist *)arg; 347*0Sstevel@tonic-gate PKT_LIST *plp; 348*0Sstevel@tonic-gate 349*0Sstevel@tonic-gate if (check_ifs(ifsp) == 0) { 350*0Sstevel@tonic-gate /* unregister_acknak() does our release_ifs() */ 351*0Sstevel@tonic-gate (void) unregister_acknak(ifsp); 352*0Sstevel@tonic-gate (void) ioctl(fd, I_FLUSH, FLUSHR|FLUSHW); 353*0Sstevel@tonic-gate return; 354*0Sstevel@tonic-gate } 355*0Sstevel@tonic-gate 356*0Sstevel@tonic-gate /* 357*0Sstevel@tonic-gate * note that check_ifs() did our release_ifs() but we're not 358*0Sstevel@tonic-gate * sure we're done yet; call hold_ifs() to reacquire our hold; 359*0Sstevel@tonic-gate * if we're done, unregister_acknak() will release_ifs() below. 360*0Sstevel@tonic-gate */ 361*0Sstevel@tonic-gate 362*0Sstevel@tonic-gate hold_ifs(ifsp); 363*0Sstevel@tonic-gate 364*0Sstevel@tonic-gate if (recv_pkt(ifsp, fd, DHCP_PACK|DHCP_PNAK, B_FALSE) == 0) 365*0Sstevel@tonic-gate return; 366*0Sstevel@tonic-gate 367*0Sstevel@tonic-gate /* 368*0Sstevel@tonic-gate * we've got a packet; make sure it's acceptable before 369*0Sstevel@tonic-gate * cancelling the REQUEST retransmissions. 370*0Sstevel@tonic-gate */ 371*0Sstevel@tonic-gate 372*0Sstevel@tonic-gate plp = ifsp->if_recv_pkt_list; 373*0Sstevel@tonic-gate remove_from_pkt_list(&ifsp->if_recv_pkt_list, plp); 374*0Sstevel@tonic-gate 375*0Sstevel@tonic-gate if (*plp->opts[CD_DHCP_TYPE]->value == ACK) { 376*0Sstevel@tonic-gate if (plp->opts[CD_LEASE_TIME] == NULL || 377*0Sstevel@tonic-gate plp->opts[CD_LEASE_TIME]->len != sizeof (lease_t)) { 378*0Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "dhcp_acknak: ACK packet on %s " 379*0Sstevel@tonic-gate "missing mandatory lease option, ignored", 380*0Sstevel@tonic-gate ifsp->if_name); 381*0Sstevel@tonic-gate ifsp->if_bad_offers++; 382*0Sstevel@tonic-gate free_pkt_list(&plp); 383*0Sstevel@tonic-gate return; 384*0Sstevel@tonic-gate } 385*0Sstevel@tonic-gate if ((ifsp->if_state == RENEWING || 386*0Sstevel@tonic-gate ifsp->if_state == REBINDING) && 387*0Sstevel@tonic-gate ifsp->if_addr.s_addr != plp->pkt->yiaddr.s_addr) { 388*0Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "dhcp_acknak: renewal ACK packet " 389*0Sstevel@tonic-gate "has a different IP address (%s), ignored", 390*0Sstevel@tonic-gate inet_ntoa(plp->pkt->yiaddr)); 391*0Sstevel@tonic-gate ifsp->if_bad_offers++; 392*0Sstevel@tonic-gate free_pkt_list(&plp); 393*0Sstevel@tonic-gate return; 394*0Sstevel@tonic-gate } 395*0Sstevel@tonic-gate } 396*0Sstevel@tonic-gate 397*0Sstevel@tonic-gate /* 398*0Sstevel@tonic-gate * looks good; cancel the retransmission timer and unregister 399*0Sstevel@tonic-gate * the acknak handler. ACK to BOUND, NAK back to SELECTING. 400*0Sstevel@tonic-gate */ 401*0Sstevel@tonic-gate 402*0Sstevel@tonic-gate stop_pkt_retransmission(ifsp); 403*0Sstevel@tonic-gate (void) unregister_acknak(ifsp); 404*0Sstevel@tonic-gate 405*0Sstevel@tonic-gate if (*(plp->opts[CD_DHCP_TYPE]->value) == NAK) { 406*0Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "dhcp_acknak: NAK on interface %s", 407*0Sstevel@tonic-gate ifsp->if_name); 408*0Sstevel@tonic-gate ifsp->if_bad_offers++; 409*0Sstevel@tonic-gate free_pkt_list(&plp); 410*0Sstevel@tonic-gate restart_dhcp(ifsp); 411*0Sstevel@tonic-gate 412*0Sstevel@tonic-gate /* 413*0Sstevel@tonic-gate * remove any bogus cached configuration we might have 414*0Sstevel@tonic-gate * around (right now would only happen if we got here 415*0Sstevel@tonic-gate * from INIT_REBOOT). 416*0Sstevel@tonic-gate */ 417*0Sstevel@tonic-gate 418*0Sstevel@tonic-gate (void) remove_hostconf(ifsp->if_name); 419*0Sstevel@tonic-gate return; 420*0Sstevel@tonic-gate } 421*0Sstevel@tonic-gate 422*0Sstevel@tonic-gate if (plp->opts[CD_SERVER_ID] == NULL || 423*0Sstevel@tonic-gate plp->opts[CD_SERVER_ID]->len != sizeof (ipaddr_t)) { 424*0Sstevel@tonic-gate dhcpmsg(MSG_ERROR, "dhcp_acknak: ACK with no valid server id, " 425*0Sstevel@tonic-gate "restarting DHCP on %s", ifsp->if_name); 426*0Sstevel@tonic-gate ifsp->if_bad_offers++; 427*0Sstevel@tonic-gate free_pkt_list(&plp); 428*0Sstevel@tonic-gate restart_dhcp(ifsp); 429*0Sstevel@tonic-gate return; 430*0Sstevel@tonic-gate } 431*0Sstevel@tonic-gate 432*0Sstevel@tonic-gate if (plp->opts[CD_MESSAGE] != NULL) 433*0Sstevel@tonic-gate print_server_msg(ifsp, plp->opts[CD_MESSAGE]); 434*0Sstevel@tonic-gate 435*0Sstevel@tonic-gate if (dhcp_bound(ifsp, plp) == 0) { 436*0Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "dhcp_acknak: dhcp_bound failed " 437*0Sstevel@tonic-gate "for %s", ifsp->if_name); 438*0Sstevel@tonic-gate restart_dhcp(ifsp); 439*0Sstevel@tonic-gate return; 440*0Sstevel@tonic-gate } 441*0Sstevel@tonic-gate 442*0Sstevel@tonic-gate dhcpmsg(MSG_VERBOSE, "ACK on interface %s", ifsp->if_name); 443*0Sstevel@tonic-gate } 444*0Sstevel@tonic-gate 445*0Sstevel@tonic-gate /* 446*0Sstevel@tonic-gate * restart_dhcp(): restarts DHCP (from INIT) on a given interface 447*0Sstevel@tonic-gate * 448*0Sstevel@tonic-gate * input: struct ifslist *: the interface to restart DHCP on 449*0Sstevel@tonic-gate * output: void 450*0Sstevel@tonic-gate */ 451*0Sstevel@tonic-gate 452*0Sstevel@tonic-gate static void 453*0Sstevel@tonic-gate restart_dhcp(struct ifslist *ifsp) 454*0Sstevel@tonic-gate { 455*0Sstevel@tonic-gate if (iu_schedule_timer(tq, DHCP_RESTART_WAIT, dhcp_start, ifsp) == -1) { 456*0Sstevel@tonic-gate 457*0Sstevel@tonic-gate ifsp->if_state = INIT; 458*0Sstevel@tonic-gate ifsp->if_dflags |= DHCP_IF_FAILED; 459*0Sstevel@tonic-gate 460*0Sstevel@tonic-gate ipc_action_finish(ifsp, DHCP_IPC_E_MEMORY); 461*0Sstevel@tonic-gate async_finish(ifsp); 462*0Sstevel@tonic-gate 463*0Sstevel@tonic-gate dhcpmsg(MSG_ERROR, "restart_dhcp: cannot schedule dhcp_start, " 464*0Sstevel@tonic-gate "reverting to INIT state on %s", ifsp->if_name); 465*0Sstevel@tonic-gate } else 466*0Sstevel@tonic-gate hold_ifs(ifsp); 467*0Sstevel@tonic-gate } 468*0Sstevel@tonic-gate 469*0Sstevel@tonic-gate /* 470*0Sstevel@tonic-gate * stop_requesting(): decides when to stop retransmitting REQUESTs 471*0Sstevel@tonic-gate * 472*0Sstevel@tonic-gate * input: struct ifslist *: the interface REQUESTs are being sent on 473*0Sstevel@tonic-gate * unsigned int: the number of REQUESTs sent so far 474*0Sstevel@tonic-gate * output: boolean_t: B_TRUE if retransmissions should stop 475*0Sstevel@tonic-gate */ 476*0Sstevel@tonic-gate 477*0Sstevel@tonic-gate static boolean_t 478*0Sstevel@tonic-gate stop_requesting(struct ifslist *ifsp, unsigned int n_requests) 479*0Sstevel@tonic-gate { 480*0Sstevel@tonic-gate if (n_requests >= DHCP_MAX_REQUESTS) { 481*0Sstevel@tonic-gate 482*0Sstevel@tonic-gate (void) unregister_acknak(ifsp); 483*0Sstevel@tonic-gate 484*0Sstevel@tonic-gate dhcpmsg(MSG_INFO, "no ACK/NAK to REQUESTING REQUEST, " 485*0Sstevel@tonic-gate "restarting DHCP on %s", ifsp->if_name); 486*0Sstevel@tonic-gate 487*0Sstevel@tonic-gate dhcp_selecting(ifsp); 488*0Sstevel@tonic-gate return (B_TRUE); 489*0Sstevel@tonic-gate } 490*0Sstevel@tonic-gate 491*0Sstevel@tonic-gate return (B_FALSE); 492*0Sstevel@tonic-gate } 493