10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*2157Sdh155122 * Common Development and Distribution License (the "License"). 6*2157Sdh155122 * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 22*2157Sdh155122 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate * 250Sstevel@tonic-gate * REQUESTING state of the client state machine. 260Sstevel@tonic-gate */ 270Sstevel@tonic-gate 280Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 290Sstevel@tonic-gate 300Sstevel@tonic-gate #include <sys/types.h> 310Sstevel@tonic-gate #include <sys/stropts.h> /* FLUSHR/FLUSHW */ 320Sstevel@tonic-gate #include <netinet/in.h> 330Sstevel@tonic-gate #include <netinet/dhcp.h> 340Sstevel@tonic-gate #include <netinet/udp.h> 350Sstevel@tonic-gate #include <netinet/ip_var.h> 360Sstevel@tonic-gate #include <netinet/udp_var.h> 370Sstevel@tonic-gate #include <dhcp_hostconf.h> 380Sstevel@tonic-gate #include <arpa/inet.h> 390Sstevel@tonic-gate #include <string.h> 400Sstevel@tonic-gate #include <stdlib.h> 410Sstevel@tonic-gate #include <unistd.h> 420Sstevel@tonic-gate #include <dhcpmsg.h> 430Sstevel@tonic-gate 440Sstevel@tonic-gate #include "states.h" 450Sstevel@tonic-gate #include "util.h" 460Sstevel@tonic-gate #include "packet.h" 470Sstevel@tonic-gate #include "interface.h" 480Sstevel@tonic-gate #include "agent.h" 490Sstevel@tonic-gate #include "defaults.h" 500Sstevel@tonic-gate 510Sstevel@tonic-gate static PKT_LIST *select_best(PKT_LIST **); 520Sstevel@tonic-gate static void restart_dhcp(struct ifslist *); 530Sstevel@tonic-gate static stop_func_t stop_requesting; 540Sstevel@tonic-gate 550Sstevel@tonic-gate /* 560Sstevel@tonic-gate * dhcp_requesting(): checks if OFFER packets to come in from DHCP servers. 570Sstevel@tonic-gate * if so, chooses the best one, sends a REQUEST to the 580Sstevel@tonic-gate * server and registers an event handler to receive 590Sstevel@tonic-gate * the ACK/NAK 600Sstevel@tonic-gate * 610Sstevel@tonic-gate * input: iu_tq_t *: unused 620Sstevel@tonic-gate * void *: the interface receiving OFFER packets 630Sstevel@tonic-gate * output: void 640Sstevel@tonic-gate */ 650Sstevel@tonic-gate 660Sstevel@tonic-gate /* ARGSUSED */ 670Sstevel@tonic-gate void 680Sstevel@tonic-gate dhcp_requesting(iu_tq_t *tqp, void *arg) 690Sstevel@tonic-gate { 700Sstevel@tonic-gate struct ifslist *ifsp = (struct ifslist *)arg; 710Sstevel@tonic-gate dhcp_pkt_t *dpkt; 720Sstevel@tonic-gate PKT_LIST *offer; 730Sstevel@tonic-gate lease_t lease; 740Sstevel@tonic-gate 75*2157Sdh155122 ifsp->if_offer_timer = -1; 76*2157Sdh155122 770Sstevel@tonic-gate if (check_ifs(ifsp) == 0) { 780Sstevel@tonic-gate (void) release_ifs(ifsp); 790Sstevel@tonic-gate return; 800Sstevel@tonic-gate } 810Sstevel@tonic-gate 820Sstevel@tonic-gate /* 830Sstevel@tonic-gate * select the best OFFER; all others pitched. 840Sstevel@tonic-gate */ 850Sstevel@tonic-gate 860Sstevel@tonic-gate offer = select_best(&ifsp->if_recv_pkt_list); 870Sstevel@tonic-gate if (offer == NULL) { 880Sstevel@tonic-gate 890Sstevel@tonic-gate dhcpmsg(MSG_VERBOSE, "no OFFERs on %s, waiting...", 900Sstevel@tonic-gate ifsp->if_name); 910Sstevel@tonic-gate 920Sstevel@tonic-gate /* 930Sstevel@tonic-gate * no acceptable OFFERs have come in. reschedule 940Sstevel@tonic-gate * ourselves for callback. 950Sstevel@tonic-gate */ 960Sstevel@tonic-gate 97*2157Sdh155122 if ((ifsp->if_offer_timer = iu_schedule_timer(tq, 98*2157Sdh155122 ifsp->if_offer_wait, dhcp_requesting, ifsp)) == -1) { 990Sstevel@tonic-gate 1000Sstevel@tonic-gate /* 1010Sstevel@tonic-gate * ugh. the best we can do at this point is 1020Sstevel@tonic-gate * revert back to INIT and wait for a user to 1030Sstevel@tonic-gate * restart us. 1040Sstevel@tonic-gate */ 1050Sstevel@tonic-gate 1060Sstevel@tonic-gate ifsp->if_state = INIT; 1070Sstevel@tonic-gate ifsp->if_dflags |= DHCP_IF_FAILED; 1080Sstevel@tonic-gate 1090Sstevel@tonic-gate stop_pkt_retransmission(ifsp); 1100Sstevel@tonic-gate ipc_action_finish(ifsp, DHCP_IPC_E_MEMORY); 1110Sstevel@tonic-gate async_finish(ifsp); 1120Sstevel@tonic-gate 1130Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "dhcp_requesting: cannot " 1140Sstevel@tonic-gate "reschedule callback, reverting to INIT state on " 1150Sstevel@tonic-gate "%s", ifsp->if_name); 1160Sstevel@tonic-gate } else 1170Sstevel@tonic-gate hold_ifs(ifsp); 1180Sstevel@tonic-gate 1190Sstevel@tonic-gate return; 1200Sstevel@tonic-gate } 1210Sstevel@tonic-gate 1220Sstevel@tonic-gate stop_pkt_retransmission(ifsp); 1230Sstevel@tonic-gate 1240Sstevel@tonic-gate /* 1250Sstevel@tonic-gate * stop collecting packets. check to see whether we got an 1260Sstevel@tonic-gate * OFFER or a BOOTP packet. if we got a BOOTP packet, go to 1270Sstevel@tonic-gate * the BOUND state now. 1280Sstevel@tonic-gate */ 1290Sstevel@tonic-gate 1300Sstevel@tonic-gate if (iu_unregister_event(eh, ifsp->if_offer_id, NULL) != 0) { 1310Sstevel@tonic-gate (void) release_ifs(ifsp); 1320Sstevel@tonic-gate ifsp->if_offer_id = -1; 1330Sstevel@tonic-gate } 1340Sstevel@tonic-gate 1350Sstevel@tonic-gate if (offer->opts[CD_DHCP_TYPE] == NULL) { 1360Sstevel@tonic-gate 1370Sstevel@tonic-gate ifsp->if_state = REQUESTING; 1380Sstevel@tonic-gate 1390Sstevel@tonic-gate if (dhcp_bound(ifsp, offer) == 0) { 1400Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "dhcp_requesting: dhcp_bound " 1410Sstevel@tonic-gate "failed for %s", ifsp->if_name); 1420Sstevel@tonic-gate restart_dhcp(ifsp); 1430Sstevel@tonic-gate return; 1440Sstevel@tonic-gate } 1450Sstevel@tonic-gate 1460Sstevel@tonic-gate return; 1470Sstevel@tonic-gate } 1480Sstevel@tonic-gate 1490Sstevel@tonic-gate /* 1500Sstevel@tonic-gate * if we got a message from the server, display it. 1510Sstevel@tonic-gate */ 1520Sstevel@tonic-gate 1530Sstevel@tonic-gate if (offer->opts[CD_MESSAGE] != NULL) 1540Sstevel@tonic-gate print_server_msg(ifsp, offer->opts[CD_MESSAGE]); 1550Sstevel@tonic-gate 1560Sstevel@tonic-gate /* 1570Sstevel@tonic-gate * assemble a DHCPREQUEST, with the ciaddr field set to 0, 1580Sstevel@tonic-gate * since we got here from the INIT state. 1590Sstevel@tonic-gate */ 1600Sstevel@tonic-gate 1610Sstevel@tonic-gate dpkt = init_pkt(ifsp, REQUEST); 1620Sstevel@tonic-gate 1630Sstevel@tonic-gate /* 1640Sstevel@tonic-gate * grab the lease out of the OFFER; we know it's valid since 1650Sstevel@tonic-gate * select_best() already checked. The max dhcp message size 1660Sstevel@tonic-gate * option is set to the interface max, minus the size of the udp and 1670Sstevel@tonic-gate * ip headers. 1680Sstevel@tonic-gate */ 1690Sstevel@tonic-gate 1700Sstevel@tonic-gate (void) memcpy(&lease, offer->opts[CD_LEASE_TIME]->value, 1710Sstevel@tonic-gate sizeof (lease_t)); 1720Sstevel@tonic-gate 1730Sstevel@tonic-gate add_pkt_opt32(dpkt, CD_LEASE_TIME, lease); 1740Sstevel@tonic-gate add_pkt_opt16(dpkt, CD_MAX_DHCP_SIZE, htons(ifsp->if_max - 1750Sstevel@tonic-gate sizeof (struct udpiphdr))); 1760Sstevel@tonic-gate add_pkt_opt32(dpkt, CD_REQUESTED_IP_ADDR, offer->pkt->yiaddr.s_addr); 1770Sstevel@tonic-gate add_pkt_opt(dpkt, CD_SERVER_ID, offer->opts[CD_SERVER_ID]->value, 1780Sstevel@tonic-gate offer->opts[CD_SERVER_ID]->len); 1790Sstevel@tonic-gate 1800Sstevel@tonic-gate add_pkt_opt(dpkt, CD_CLASS_ID, class_id, class_id_len); 1810Sstevel@tonic-gate add_pkt_opt(dpkt, CD_REQUEST_LIST, ifsp->if_prl, ifsp->if_prllen); 1820Sstevel@tonic-gate 1830Sstevel@tonic-gate /* 1840Sstevel@tonic-gate * if_reqhost was set for this interface in dhcp_selecting() 1850Sstevel@tonic-gate * if the DF_REQUEST_HOSTNAME option set and a host name was 1860Sstevel@tonic-gate * found 1870Sstevel@tonic-gate */ 1880Sstevel@tonic-gate if (ifsp->if_reqhost != NULL) { 1890Sstevel@tonic-gate add_pkt_opt(dpkt, CD_HOSTNAME, ifsp->if_reqhost, 1900Sstevel@tonic-gate strlen(ifsp->if_reqhost)); 1910Sstevel@tonic-gate } 1920Sstevel@tonic-gate add_pkt_opt(dpkt, CD_END, NULL, 0); 1930Sstevel@tonic-gate 1940Sstevel@tonic-gate /* all done with the offer */ 1950Sstevel@tonic-gate free_pkt_list(&offer); 1960Sstevel@tonic-gate 1970Sstevel@tonic-gate /* 1980Sstevel@tonic-gate * send out the REQUEST, trying retransmissions. either a NAK 1990Sstevel@tonic-gate * or too many REQUEST attempts will revert us to SELECTING. 2000Sstevel@tonic-gate */ 2010Sstevel@tonic-gate 2020Sstevel@tonic-gate ifsp->if_state = REQUESTING; 2030Sstevel@tonic-gate (void) send_pkt(ifsp, dpkt, htonl(INADDR_BROADCAST), stop_requesting); 2040Sstevel@tonic-gate 2050Sstevel@tonic-gate /* 2060Sstevel@tonic-gate * wait for an ACK or NAK to come back from the server. if 2070Sstevel@tonic-gate * we can't register this event handler, then we won't be able 2080Sstevel@tonic-gate * to see the server's responses. the best we can really do 2090Sstevel@tonic-gate * in that case is drop back to INIT and hope someone notices. 2100Sstevel@tonic-gate */ 2110Sstevel@tonic-gate 2120Sstevel@tonic-gate if (register_acknak(ifsp) == 0) { 2130Sstevel@tonic-gate 2140Sstevel@tonic-gate ifsp->if_state = INIT; 2150Sstevel@tonic-gate ifsp->if_dflags |= DHCP_IF_FAILED; 2160Sstevel@tonic-gate 2170Sstevel@tonic-gate ipc_action_finish(ifsp, DHCP_IPC_E_MEMORY); 2180Sstevel@tonic-gate async_finish(ifsp); 2190Sstevel@tonic-gate 2200Sstevel@tonic-gate dhcpmsg(MSG_ERROR, "dhcp_requesting: cannot register to " 2210Sstevel@tonic-gate "collect ACK/NAK packets, reverting to INIT on %s", 2220Sstevel@tonic-gate ifsp->if_name); 2230Sstevel@tonic-gate } 2240Sstevel@tonic-gate } 2250Sstevel@tonic-gate 2260Sstevel@tonic-gate /* 2270Sstevel@tonic-gate * select_best(): selects the best OFFER packet from a list of OFFER packets 2280Sstevel@tonic-gate * 2290Sstevel@tonic-gate * input: PKT_LIST **: a list of packets to select the best from 2300Sstevel@tonic-gate * output: PKT_LIST *: the best packet, or NULL if none are acceptable 2310Sstevel@tonic-gate */ 2320Sstevel@tonic-gate 2330Sstevel@tonic-gate static PKT_LIST * 2340Sstevel@tonic-gate select_best(PKT_LIST **pkts) 2350Sstevel@tonic-gate { 2360Sstevel@tonic-gate PKT_LIST *current, *best = NULL; 2370Sstevel@tonic-gate uint32_t points, best_points = 0; 2380Sstevel@tonic-gate 2390Sstevel@tonic-gate /* 2400Sstevel@tonic-gate * pick out the best offer. point system. 2410Sstevel@tonic-gate * what's important? 2420Sstevel@tonic-gate * 2430Sstevel@tonic-gate * 0) DHCP 2440Sstevel@tonic-gate * 1) no option overload 2450Sstevel@tonic-gate * 2) encapsulated vendor option 2460Sstevel@tonic-gate * 3) non-null sname and siaddr fields 2470Sstevel@tonic-gate * 4) non-null file field 2480Sstevel@tonic-gate * 5) hostname 2490Sstevel@tonic-gate * 6) subnetmask 2500Sstevel@tonic-gate * 7) router 2510Sstevel@tonic-gate */ 2520Sstevel@tonic-gate 2530Sstevel@tonic-gate for (current = *pkts; current != NULL; current = current->next) { 2540Sstevel@tonic-gate 2550Sstevel@tonic-gate points = 0; 2560Sstevel@tonic-gate 2570Sstevel@tonic-gate if (current->opts[CD_DHCP_TYPE] == NULL) { 2580Sstevel@tonic-gate dhcpmsg(MSG_VERBOSE, "valid BOOTP reply"); 2590Sstevel@tonic-gate goto valid_offer; 2600Sstevel@tonic-gate } 2610Sstevel@tonic-gate 2620Sstevel@tonic-gate if (current->opts[CD_LEASE_TIME] == NULL) { 2630Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "select_best: OFFER without " 2640Sstevel@tonic-gate "lease time"); 2650Sstevel@tonic-gate continue; 2660Sstevel@tonic-gate } 2670Sstevel@tonic-gate 2680Sstevel@tonic-gate if (current->opts[CD_LEASE_TIME]->len != sizeof (lease_t)) { 2690Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "select_best: OFFER with garbled " 2700Sstevel@tonic-gate "lease time"); 2710Sstevel@tonic-gate continue; 2720Sstevel@tonic-gate } 2730Sstevel@tonic-gate 2740Sstevel@tonic-gate if (current->opts[CD_SERVER_ID] == NULL) { 2750Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "select_best: OFFER without " 2760Sstevel@tonic-gate "server id"); 2770Sstevel@tonic-gate continue; 2780Sstevel@tonic-gate } 2790Sstevel@tonic-gate 2800Sstevel@tonic-gate if (current->opts[CD_SERVER_ID]->len != sizeof (ipaddr_t)) { 2810Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "select_best: OFFER with garbled " 2820Sstevel@tonic-gate "server id"); 2830Sstevel@tonic-gate continue; 2840Sstevel@tonic-gate } 2850Sstevel@tonic-gate 2860Sstevel@tonic-gate /* valid DHCP OFFER. see if we got our parameters. */ 2870Sstevel@tonic-gate dhcpmsg(MSG_VERBOSE, "valid OFFER packet"); 2880Sstevel@tonic-gate points += 30; 2890Sstevel@tonic-gate 2900Sstevel@tonic-gate valid_offer: 2910Sstevel@tonic-gate if (current->rfc1048) 2920Sstevel@tonic-gate points += 5; 2930Sstevel@tonic-gate 2940Sstevel@tonic-gate /* 2950Sstevel@tonic-gate * also could be faked, though more difficult because 2960Sstevel@tonic-gate * the encapsulation is hard to encode on a BOOTP 2970Sstevel@tonic-gate * server; plus there's not as much real estate in the 2980Sstevel@tonic-gate * packet for options, so it's likely this option 2990Sstevel@tonic-gate * would get dropped. 3000Sstevel@tonic-gate */ 3010Sstevel@tonic-gate 3020Sstevel@tonic-gate if (current->opts[CD_VENDOR_SPEC] != NULL) 3030Sstevel@tonic-gate points += 80; 3040Sstevel@tonic-gate 3050Sstevel@tonic-gate if (current->opts[CD_SUBNETMASK] != NULL) 3060Sstevel@tonic-gate points++; 3070Sstevel@tonic-gate 3080Sstevel@tonic-gate if (current->opts[CD_ROUTER] != NULL) 3090Sstevel@tonic-gate points++; 3100Sstevel@tonic-gate 3110Sstevel@tonic-gate if (current->opts[CD_HOSTNAME] != NULL) 3120Sstevel@tonic-gate points += 5; 3130Sstevel@tonic-gate 3140Sstevel@tonic-gate dhcpmsg(MSG_DEBUG, "select_best: OFFER had %d points", points); 3150Sstevel@tonic-gate 3160Sstevel@tonic-gate if (points >= best_points) { 3170Sstevel@tonic-gate best_points = points; 3180Sstevel@tonic-gate best = current; 3190Sstevel@tonic-gate } 3200Sstevel@tonic-gate } 3210Sstevel@tonic-gate 3220Sstevel@tonic-gate if (best != NULL) { 3230Sstevel@tonic-gate dhcpmsg(MSG_DEBUG, "select_best: most points: %d", best_points); 3240Sstevel@tonic-gate remove_from_pkt_list(pkts, best); 3250Sstevel@tonic-gate } else 3260Sstevel@tonic-gate dhcpmsg(MSG_DEBUG, "select_best: no valid OFFER/BOOTP reply"); 3270Sstevel@tonic-gate 3280Sstevel@tonic-gate free_pkt_list(pkts); 3290Sstevel@tonic-gate return (best); 3300Sstevel@tonic-gate } 3310Sstevel@tonic-gate 3320Sstevel@tonic-gate /* 3330Sstevel@tonic-gate * dhcp_acknak(): processes reception of an ACK or NAK packet on an interface 3340Sstevel@tonic-gate * 3350Sstevel@tonic-gate * input: iu_eh_t *: unused 3360Sstevel@tonic-gate * int: the file descriptor the ACK/NAK arrived on 3370Sstevel@tonic-gate * short: unused 3380Sstevel@tonic-gate * iu_event_id_t: the id of this event callback with the handler 3390Sstevel@tonic-gate * void *: the interface that received the ACK or NAK 3400Sstevel@tonic-gate * output: void 3410Sstevel@tonic-gate */ 3420Sstevel@tonic-gate 3430Sstevel@tonic-gate /* ARGSUSED */ 3440Sstevel@tonic-gate void 3450Sstevel@tonic-gate dhcp_acknak(iu_eh_t *ehp, int fd, short events, iu_event_id_t id, void *arg) 3460Sstevel@tonic-gate { 3470Sstevel@tonic-gate struct ifslist *ifsp = (struct ifslist *)arg; 3480Sstevel@tonic-gate PKT_LIST *plp; 3490Sstevel@tonic-gate 3500Sstevel@tonic-gate if (check_ifs(ifsp) == 0) { 3510Sstevel@tonic-gate /* unregister_acknak() does our release_ifs() */ 3520Sstevel@tonic-gate (void) unregister_acknak(ifsp); 3530Sstevel@tonic-gate (void) ioctl(fd, I_FLUSH, FLUSHR|FLUSHW); 3540Sstevel@tonic-gate return; 3550Sstevel@tonic-gate } 3560Sstevel@tonic-gate 3570Sstevel@tonic-gate /* 3580Sstevel@tonic-gate * note that check_ifs() did our release_ifs() but we're not 3590Sstevel@tonic-gate * sure we're done yet; call hold_ifs() to reacquire our hold; 3600Sstevel@tonic-gate * if we're done, unregister_acknak() will release_ifs() below. 3610Sstevel@tonic-gate */ 3620Sstevel@tonic-gate 3630Sstevel@tonic-gate hold_ifs(ifsp); 3640Sstevel@tonic-gate 3650Sstevel@tonic-gate if (recv_pkt(ifsp, fd, DHCP_PACK|DHCP_PNAK, B_FALSE) == 0) 3660Sstevel@tonic-gate return; 3670Sstevel@tonic-gate 3680Sstevel@tonic-gate /* 3690Sstevel@tonic-gate * we've got a packet; make sure it's acceptable before 3700Sstevel@tonic-gate * cancelling the REQUEST retransmissions. 3710Sstevel@tonic-gate */ 3720Sstevel@tonic-gate 3730Sstevel@tonic-gate plp = ifsp->if_recv_pkt_list; 3740Sstevel@tonic-gate remove_from_pkt_list(&ifsp->if_recv_pkt_list, plp); 3750Sstevel@tonic-gate 3760Sstevel@tonic-gate if (*plp->opts[CD_DHCP_TYPE]->value == ACK) { 3770Sstevel@tonic-gate if (plp->opts[CD_LEASE_TIME] == NULL || 3780Sstevel@tonic-gate plp->opts[CD_LEASE_TIME]->len != sizeof (lease_t)) { 3790Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "dhcp_acknak: ACK packet on %s " 3800Sstevel@tonic-gate "missing mandatory lease option, ignored", 3810Sstevel@tonic-gate ifsp->if_name); 3820Sstevel@tonic-gate ifsp->if_bad_offers++; 3830Sstevel@tonic-gate free_pkt_list(&plp); 3840Sstevel@tonic-gate return; 3850Sstevel@tonic-gate } 3860Sstevel@tonic-gate if ((ifsp->if_state == RENEWING || 3870Sstevel@tonic-gate ifsp->if_state == REBINDING) && 3880Sstevel@tonic-gate ifsp->if_addr.s_addr != plp->pkt->yiaddr.s_addr) { 3890Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "dhcp_acknak: renewal ACK packet " 3900Sstevel@tonic-gate "has a different IP address (%s), ignored", 3910Sstevel@tonic-gate inet_ntoa(plp->pkt->yiaddr)); 3920Sstevel@tonic-gate ifsp->if_bad_offers++; 3930Sstevel@tonic-gate free_pkt_list(&plp); 3940Sstevel@tonic-gate return; 3950Sstevel@tonic-gate } 3960Sstevel@tonic-gate } 3970Sstevel@tonic-gate 3980Sstevel@tonic-gate /* 3990Sstevel@tonic-gate * looks good; cancel the retransmission timer and unregister 4000Sstevel@tonic-gate * the acknak handler. ACK to BOUND, NAK back to SELECTING. 4010Sstevel@tonic-gate */ 4020Sstevel@tonic-gate 4030Sstevel@tonic-gate stop_pkt_retransmission(ifsp); 4040Sstevel@tonic-gate (void) unregister_acknak(ifsp); 4050Sstevel@tonic-gate 4060Sstevel@tonic-gate if (*(plp->opts[CD_DHCP_TYPE]->value) == NAK) { 4070Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "dhcp_acknak: NAK on interface %s", 4080Sstevel@tonic-gate ifsp->if_name); 4090Sstevel@tonic-gate ifsp->if_bad_offers++; 4100Sstevel@tonic-gate free_pkt_list(&plp); 4110Sstevel@tonic-gate restart_dhcp(ifsp); 4120Sstevel@tonic-gate 4130Sstevel@tonic-gate /* 4140Sstevel@tonic-gate * remove any bogus cached configuration we might have 4150Sstevel@tonic-gate * around (right now would only happen if we got here 4160Sstevel@tonic-gate * from INIT_REBOOT). 4170Sstevel@tonic-gate */ 4180Sstevel@tonic-gate 4190Sstevel@tonic-gate (void) remove_hostconf(ifsp->if_name); 4200Sstevel@tonic-gate return; 4210Sstevel@tonic-gate } 4220Sstevel@tonic-gate 4230Sstevel@tonic-gate if (plp->opts[CD_SERVER_ID] == NULL || 4240Sstevel@tonic-gate plp->opts[CD_SERVER_ID]->len != sizeof (ipaddr_t)) { 4250Sstevel@tonic-gate dhcpmsg(MSG_ERROR, "dhcp_acknak: ACK with no valid server id, " 4260Sstevel@tonic-gate "restarting DHCP on %s", ifsp->if_name); 4270Sstevel@tonic-gate ifsp->if_bad_offers++; 4280Sstevel@tonic-gate free_pkt_list(&plp); 4290Sstevel@tonic-gate restart_dhcp(ifsp); 4300Sstevel@tonic-gate return; 4310Sstevel@tonic-gate } 4320Sstevel@tonic-gate 4330Sstevel@tonic-gate if (plp->opts[CD_MESSAGE] != NULL) 4340Sstevel@tonic-gate print_server_msg(ifsp, plp->opts[CD_MESSAGE]); 4350Sstevel@tonic-gate 4360Sstevel@tonic-gate if (dhcp_bound(ifsp, plp) == 0) { 4370Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "dhcp_acknak: dhcp_bound failed " 4380Sstevel@tonic-gate "for %s", ifsp->if_name); 4390Sstevel@tonic-gate restart_dhcp(ifsp); 4400Sstevel@tonic-gate return; 4410Sstevel@tonic-gate } 4420Sstevel@tonic-gate 4430Sstevel@tonic-gate dhcpmsg(MSG_VERBOSE, "ACK on interface %s", ifsp->if_name); 4440Sstevel@tonic-gate } 4450Sstevel@tonic-gate 4460Sstevel@tonic-gate /* 4470Sstevel@tonic-gate * restart_dhcp(): restarts DHCP (from INIT) on a given interface 4480Sstevel@tonic-gate * 4490Sstevel@tonic-gate * input: struct ifslist *: the interface to restart DHCP on 4500Sstevel@tonic-gate * output: void 4510Sstevel@tonic-gate */ 4520Sstevel@tonic-gate 4530Sstevel@tonic-gate static void 4540Sstevel@tonic-gate restart_dhcp(struct ifslist *ifsp) 4550Sstevel@tonic-gate { 4560Sstevel@tonic-gate if (iu_schedule_timer(tq, DHCP_RESTART_WAIT, dhcp_start, ifsp) == -1) { 4570Sstevel@tonic-gate 4580Sstevel@tonic-gate ifsp->if_state = INIT; 4590Sstevel@tonic-gate ifsp->if_dflags |= DHCP_IF_FAILED; 4600Sstevel@tonic-gate 4610Sstevel@tonic-gate ipc_action_finish(ifsp, DHCP_IPC_E_MEMORY); 4620Sstevel@tonic-gate async_finish(ifsp); 4630Sstevel@tonic-gate 4640Sstevel@tonic-gate dhcpmsg(MSG_ERROR, "restart_dhcp: cannot schedule dhcp_start, " 4650Sstevel@tonic-gate "reverting to INIT state on %s", ifsp->if_name); 4660Sstevel@tonic-gate } else 4670Sstevel@tonic-gate hold_ifs(ifsp); 4680Sstevel@tonic-gate } 4690Sstevel@tonic-gate 4700Sstevel@tonic-gate /* 4710Sstevel@tonic-gate * stop_requesting(): decides when to stop retransmitting REQUESTs 4720Sstevel@tonic-gate * 4730Sstevel@tonic-gate * input: struct ifslist *: the interface REQUESTs are being sent on 4740Sstevel@tonic-gate * unsigned int: the number of REQUESTs sent so far 4750Sstevel@tonic-gate * output: boolean_t: B_TRUE if retransmissions should stop 4760Sstevel@tonic-gate */ 4770Sstevel@tonic-gate 4780Sstevel@tonic-gate static boolean_t 4790Sstevel@tonic-gate stop_requesting(struct ifslist *ifsp, unsigned int n_requests) 4800Sstevel@tonic-gate { 4810Sstevel@tonic-gate if (n_requests >= DHCP_MAX_REQUESTS) { 4820Sstevel@tonic-gate 4830Sstevel@tonic-gate (void) unregister_acknak(ifsp); 4840Sstevel@tonic-gate 4850Sstevel@tonic-gate dhcpmsg(MSG_INFO, "no ACK/NAK to REQUESTING REQUEST, " 4860Sstevel@tonic-gate "restarting DHCP on %s", ifsp->if_name); 4870Sstevel@tonic-gate 4880Sstevel@tonic-gate dhcp_selecting(ifsp); 4890Sstevel@tonic-gate return (B_TRUE); 4900Sstevel@tonic-gate } 4910Sstevel@tonic-gate 4920Sstevel@tonic-gate return (B_FALSE); 4930Sstevel@tonic-gate } 494