17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5c2934490Sdh155122 * Common Development and Distribution License (the "License"). 6c2934490Sdh155122 * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate /* 22*e11c3f44Smeem * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 237c478bd9Sstevel@tonic-gate * Use is subject to license terms. 247c478bd9Sstevel@tonic-gate * 257c478bd9Sstevel@tonic-gate * REQUESTING state of the client state machine. 267c478bd9Sstevel@tonic-gate */ 277c478bd9Sstevel@tonic-gate 28d04ccbb3Scarlsonj #include <stdlib.h> 29d04ccbb3Scarlsonj #include <string.h> 30d04ccbb3Scarlsonj #include <search.h> 317c478bd9Sstevel@tonic-gate #include <sys/types.h> 327c478bd9Sstevel@tonic-gate #include <netinet/in.h> 337c478bd9Sstevel@tonic-gate #include <netinet/dhcp.h> 347c478bd9Sstevel@tonic-gate #include <netinet/udp.h> 357c478bd9Sstevel@tonic-gate #include <netinet/ip_var.h> 367c478bd9Sstevel@tonic-gate #include <netinet/udp_var.h> 377c478bd9Sstevel@tonic-gate #include <arpa/inet.h> 38d04ccbb3Scarlsonj #include <dhcp_hostconf.h> 39d04ccbb3Scarlsonj #include <dhcpagent_util.h> 407c478bd9Sstevel@tonic-gate #include <dhcpmsg.h> 417c478bd9Sstevel@tonic-gate 427c478bd9Sstevel@tonic-gate #include "states.h" 437c478bd9Sstevel@tonic-gate #include "util.h" 447c478bd9Sstevel@tonic-gate #include "packet.h" 457c478bd9Sstevel@tonic-gate #include "interface.h" 467c478bd9Sstevel@tonic-gate #include "agent.h" 477c478bd9Sstevel@tonic-gate 48d04ccbb3Scarlsonj static PKT_LIST *select_best(dhcp_smach_t *); 49d04ccbb3Scarlsonj static void request_failed(dhcp_smach_t *); 507c478bd9Sstevel@tonic-gate static stop_func_t stop_requesting; 517c478bd9Sstevel@tonic-gate 527c478bd9Sstevel@tonic-gate /* 53d04ccbb3Scarlsonj * send_v6_request(): sends a DHCPv6 Request message and switches to REQUESTING 54d04ccbb3Scarlsonj * state. This is a separate function because a NoBinding 55d04ccbb3Scarlsonj * response can also cause us to do this. 56d04ccbb3Scarlsonj * 57d04ccbb3Scarlsonj * input: dhcp_smach_t *: the state machine 58d04ccbb3Scarlsonj * output: none 59d04ccbb3Scarlsonj */ 60d04ccbb3Scarlsonj 61d04ccbb3Scarlsonj void 62d04ccbb3Scarlsonj send_v6_request(dhcp_smach_t *dsmp) 63d04ccbb3Scarlsonj { 64d04ccbb3Scarlsonj dhcp_pkt_t *dpkt; 65d04ccbb3Scarlsonj dhcpv6_ia_na_t d6in; 66d04ccbb3Scarlsonj 67d04ccbb3Scarlsonj dpkt = init_pkt(dsmp, DHCPV6_MSG_REQUEST); 68d04ccbb3Scarlsonj (void) add_pkt_opt(dpkt, DHCPV6_OPT_SERVERID, dsmp->dsm_serverid, 69d04ccbb3Scarlsonj dsmp->dsm_serveridlen); 70d04ccbb3Scarlsonj 71d04ccbb3Scarlsonj /* Add an IA_NA option for our controlling LIF */ 72d04ccbb3Scarlsonj d6in.d6in_iaid = htonl(dsmp->dsm_lif->lif_iaid); 73d04ccbb3Scarlsonj d6in.d6in_t1 = htonl(0); 74d04ccbb3Scarlsonj d6in.d6in_t2 = htonl(0); 75d04ccbb3Scarlsonj (void) add_pkt_opt(dpkt, DHCPV6_OPT_IA_NA, 76d04ccbb3Scarlsonj (dhcpv6_option_t *)&d6in + 1, 77d04ccbb3Scarlsonj sizeof (d6in) - sizeof (dhcpv6_option_t)); 78d04ccbb3Scarlsonj 79d04ccbb3Scarlsonj /* Add required Option Request option */ 80d04ccbb3Scarlsonj (void) add_pkt_prl(dpkt, dsmp); 81d04ccbb3Scarlsonj 82d04ccbb3Scarlsonj (void) send_pkt_v6(dsmp, dpkt, dsmp->dsm_server, stop_requesting, 83d04ccbb3Scarlsonj DHCPV6_REQ_TIMEOUT, DHCPV6_REQ_MAX_RT); 84d04ccbb3Scarlsonj 85d04ccbb3Scarlsonj /* For DHCPv6, state switch cannot fail */ 86d04ccbb3Scarlsonj (void) set_smach_state(dsmp, REQUESTING); 87d04ccbb3Scarlsonj } 88d04ccbb3Scarlsonj 89d04ccbb3Scarlsonj /* 90d04ccbb3Scarlsonj * server_unicast_option(): determines the server address to use based on the 91d04ccbb3Scarlsonj * DHCPv6 Server Unicast option present in the given 92d04ccbb3Scarlsonj * packet. 93d04ccbb3Scarlsonj * 94d04ccbb3Scarlsonj * input: dhcp_smach_t *: the state machine 95d04ccbb3Scarlsonj * PKT_LIST *: received packet (Advertisement or Reply) 96d04ccbb3Scarlsonj * output: none 97d04ccbb3Scarlsonj */ 98d04ccbb3Scarlsonj 99d04ccbb3Scarlsonj void 100d04ccbb3Scarlsonj server_unicast_option(dhcp_smach_t *dsmp, PKT_LIST *plp) 101d04ccbb3Scarlsonj { 102d04ccbb3Scarlsonj const dhcpv6_option_t *d6o; 103d04ccbb3Scarlsonj uint_t olen; 104d04ccbb3Scarlsonj 105d04ccbb3Scarlsonj d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_UNICAST, &olen); 106d04ccbb3Scarlsonj olen -= sizeof (*d6o); 107d04ccbb3Scarlsonj /* LINTED: no consequent */ 108d04ccbb3Scarlsonj if (d6o == NULL) { 109d04ccbb3Scarlsonj /* No Server Unicast option specified */ 110d04ccbb3Scarlsonj } else if (olen != sizeof (dsmp->dsm_server)) { 111d04ccbb3Scarlsonj dhcpmsg(MSG_WARNING, "server_unicast_option: %s has Server " 112d04ccbb3Scarlsonj "Unicast option with bad length", 113d04ccbb3Scarlsonj pkt_type_to_string(pkt_recv_type(plp), B_TRUE)); 114d04ccbb3Scarlsonj } else { 115d04ccbb3Scarlsonj in6_addr_t addr; 116d04ccbb3Scarlsonj 117d04ccbb3Scarlsonj (void) memcpy(&addr, d6o + 1, olen); 118d04ccbb3Scarlsonj if (IN6_IS_ADDR_UNSPECIFIED(&addr)) { 119d04ccbb3Scarlsonj dhcpmsg(MSG_WARNING, "server_unicast_option: unicast " 120d04ccbb3Scarlsonj "to unspecified address ignored"); 121d04ccbb3Scarlsonj } else if (IN6_IS_ADDR_MULTICAST(&addr)) { 122d04ccbb3Scarlsonj dhcpmsg(MSG_WARNING, "server_unicast_option: unicast " 123d04ccbb3Scarlsonj "to multicast address ignored"); 124d04ccbb3Scarlsonj } else if (IN6_IS_ADDR_V4COMPAT(&addr) || 125d04ccbb3Scarlsonj IN6_IS_ADDR_V4MAPPED(&addr)) { 126d04ccbb3Scarlsonj dhcpmsg(MSG_WARNING, "server_unicast_option: unicast " 127d04ccbb3Scarlsonj "to invalid address ignored"); 128d04ccbb3Scarlsonj } else { 129d04ccbb3Scarlsonj dsmp->dsm_server = addr; 130d04ccbb3Scarlsonj } 131d04ccbb3Scarlsonj } 132d04ccbb3Scarlsonj } 133d04ccbb3Scarlsonj 134d04ccbb3Scarlsonj /* 1357c478bd9Sstevel@tonic-gate * dhcp_requesting(): checks if OFFER packets to come in from DHCP servers. 1367c478bd9Sstevel@tonic-gate * if so, chooses the best one, sends a REQUEST to the 1377c478bd9Sstevel@tonic-gate * server and registers an event handler to receive 138d04ccbb3Scarlsonj * the ACK/NAK. This may be called by the offer timer or 139d04ccbb3Scarlsonj * by any function that wants to check for offers after 140d04ccbb3Scarlsonj * canceling that timer. 1417c478bd9Sstevel@tonic-gate * 142d04ccbb3Scarlsonj * input: iu_tq_t *: timer queue; non-NULL if this is a timer callback 143d04ccbb3Scarlsonj * void *: the state machine receiving OFFER packets 1447c478bd9Sstevel@tonic-gate * output: void 1457c478bd9Sstevel@tonic-gate */ 1467c478bd9Sstevel@tonic-gate 1477c478bd9Sstevel@tonic-gate void 1487c478bd9Sstevel@tonic-gate dhcp_requesting(iu_tq_t *tqp, void *arg) 1497c478bd9Sstevel@tonic-gate { 150d04ccbb3Scarlsonj dhcp_smach_t *dsmp = arg; 1517c478bd9Sstevel@tonic-gate dhcp_pkt_t *dpkt; 1527c478bd9Sstevel@tonic-gate PKT_LIST *offer; 1537c478bd9Sstevel@tonic-gate lease_t lease; 154d04ccbb3Scarlsonj boolean_t isv6 = dsmp->dsm_isv6; 1557c478bd9Sstevel@tonic-gate 156d04ccbb3Scarlsonj /* 157d04ccbb3Scarlsonj * We assume here that if tqp is set, then this means we're being 158d04ccbb3Scarlsonj * called back by the offer wait timer. If so, then drop our hold 159d04ccbb3Scarlsonj * on the state machine. Otherwise, cancel the timer if it's running. 160d04ccbb3Scarlsonj */ 161d04ccbb3Scarlsonj if (tqp != NULL) { 162d04ccbb3Scarlsonj dhcpmsg(MSG_VERBOSE, 163d04ccbb3Scarlsonj "dhcp_requesting: offer wait timer on v%d %s", 164d04ccbb3Scarlsonj isv6 ? 6 : 4, dsmp->dsm_name); 165d04ccbb3Scarlsonj dsmp->dsm_offer_timer = -1; 166d04ccbb3Scarlsonj if (!verify_smach(dsmp)) 1677c478bd9Sstevel@tonic-gate return; 168d04ccbb3Scarlsonj } else { 169d04ccbb3Scarlsonj cancel_offer_timer(dsmp); 1707c478bd9Sstevel@tonic-gate } 1717c478bd9Sstevel@tonic-gate 1727c478bd9Sstevel@tonic-gate /* 1737c478bd9Sstevel@tonic-gate * select the best OFFER; all others pitched. 1747c478bd9Sstevel@tonic-gate */ 1757c478bd9Sstevel@tonic-gate 176d04ccbb3Scarlsonj offer = select_best(dsmp); 1777c478bd9Sstevel@tonic-gate if (offer == NULL) { 1787c478bd9Sstevel@tonic-gate 179d04ccbb3Scarlsonj dhcpmsg(MSG_VERBOSE, 180d04ccbb3Scarlsonj "no OFFERs/Advertisements on %s, waiting...", 181d04ccbb3Scarlsonj dsmp->dsm_name); 1827c478bd9Sstevel@tonic-gate 1837c478bd9Sstevel@tonic-gate /* 1847c478bd9Sstevel@tonic-gate * no acceptable OFFERs have come in. reschedule 185d04ccbb3Scarlsonj * ourself for callback. 1867c478bd9Sstevel@tonic-gate */ 1877c478bd9Sstevel@tonic-gate 188d04ccbb3Scarlsonj if ((dsmp->dsm_offer_timer = iu_schedule_timer(tq, 189d04ccbb3Scarlsonj dsmp->dsm_offer_wait, dhcp_requesting, dsmp)) == -1) { 1907c478bd9Sstevel@tonic-gate 1917c478bd9Sstevel@tonic-gate /* 1927c478bd9Sstevel@tonic-gate * ugh. the best we can do at this point is 1937c478bd9Sstevel@tonic-gate * revert back to INIT and wait for a user to 1947c478bd9Sstevel@tonic-gate * restart us. 1957c478bd9Sstevel@tonic-gate */ 1967c478bd9Sstevel@tonic-gate 1977c478bd9Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "dhcp_requesting: cannot " 1987c478bd9Sstevel@tonic-gate "reschedule callback, reverting to INIT state on " 199d04ccbb3Scarlsonj "%s", dsmp->dsm_name); 200d04ccbb3Scarlsonj 201d04ccbb3Scarlsonj stop_pkt_retransmission(dsmp); 202d04ccbb3Scarlsonj (void) set_smach_state(dsmp, INIT); 203d04ccbb3Scarlsonj dsmp->dsm_dflags |= DHCP_IF_FAILED; 204d04ccbb3Scarlsonj ipc_action_finish(dsmp, DHCP_IPC_E_MEMORY); 205d04ccbb3Scarlsonj } else { 206d04ccbb3Scarlsonj hold_smach(dsmp); 207d04ccbb3Scarlsonj } 2087c478bd9Sstevel@tonic-gate 2097c478bd9Sstevel@tonic-gate return; 2107c478bd9Sstevel@tonic-gate } 2117c478bd9Sstevel@tonic-gate 212d04ccbb3Scarlsonj /* 213d04ccbb3Scarlsonj * With IPv4, the DHCPREQUEST packet we're about to transmit implicitly 214d04ccbb3Scarlsonj * declines all other offers we've received. We can no longer use any 215d04ccbb3Scarlsonj * cached offers, so we must discard them now. With DHCPv6, though, 216d04ccbb3Scarlsonj * we're permitted to hang onto the advertisements (offers) and try 217d04ccbb3Scarlsonj * them if the preferred one doesn't pan out. 218d04ccbb3Scarlsonj */ 219d04ccbb3Scarlsonj if (!isv6) 220d04ccbb3Scarlsonj free_pkt_list(&dsmp->dsm_recv_pkt_list); 221d04ccbb3Scarlsonj 222d04ccbb3Scarlsonj /* stop collecting packets. */ 223d04ccbb3Scarlsonj 224d04ccbb3Scarlsonj stop_pkt_retransmission(dsmp); 2257c478bd9Sstevel@tonic-gate 2267c478bd9Sstevel@tonic-gate /* 227d04ccbb3Scarlsonj * For IPv4, check to see whether we got an OFFER or a BOOTP packet. 228d04ccbb3Scarlsonj * If we got a BOOTP packet, go to the BOUND state now. 2297c478bd9Sstevel@tonic-gate */ 230d04ccbb3Scarlsonj if (!isv6 && offer->opts[CD_DHCP_TYPE] == NULL) { 231d04ccbb3Scarlsonj free_pkt_list(&dsmp->dsm_recv_pkt_list); 2327c478bd9Sstevel@tonic-gate 233d04ccbb3Scarlsonj if (!set_smach_state(dsmp, REQUESTING)) { 234d04ccbb3Scarlsonj dhcp_restart(dsmp); 235d04ccbb3Scarlsonj return; 2367c478bd9Sstevel@tonic-gate } 2377c478bd9Sstevel@tonic-gate 238d04ccbb3Scarlsonj if (!dhcp_bound(dsmp, offer)) { 2397c478bd9Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "dhcp_requesting: dhcp_bound " 240d04ccbb3Scarlsonj "failed for %s", dsmp->dsm_name); 241d04ccbb3Scarlsonj dhcp_restart(dsmp); 2427c478bd9Sstevel@tonic-gate return; 2437c478bd9Sstevel@tonic-gate } 2447c478bd9Sstevel@tonic-gate 2457c478bd9Sstevel@tonic-gate return; 2467c478bd9Sstevel@tonic-gate } 2477c478bd9Sstevel@tonic-gate 248d04ccbb3Scarlsonj if (isv6) { 249d04ccbb3Scarlsonj const char *estr, *msg; 250d04ccbb3Scarlsonj const dhcpv6_option_t *d6o; 251d04ccbb3Scarlsonj uint_t olen, msglen; 252d04ccbb3Scarlsonj 253d04ccbb3Scarlsonj /* If there's a Status Code option, print the message */ 254d04ccbb3Scarlsonj d6o = dhcpv6_pkt_option(offer, NULL, DHCPV6_OPT_STATUS_CODE, 255d04ccbb3Scarlsonj &olen); 256d04ccbb3Scarlsonj (void) dhcpv6_status_code(d6o, olen, &estr, &msg, &msglen); 257d04ccbb3Scarlsonj print_server_msg(dsmp, msg, msglen); 258d04ccbb3Scarlsonj 259d04ccbb3Scarlsonj /* Copy in the Server ID (guaranteed to be present now) */ 260d04ccbb3Scarlsonj if (!save_server_id(dsmp, offer)) 261d04ccbb3Scarlsonj goto failure; 262d04ccbb3Scarlsonj 2637c478bd9Sstevel@tonic-gate /* 264d04ccbb3Scarlsonj * Determine how to send this message. If the Advertisement 265d04ccbb3Scarlsonj * (offer) has the unicast option, then use the address 266d04ccbb3Scarlsonj * specified in the option. Otherwise, send via multicast. 2677c478bd9Sstevel@tonic-gate */ 268d04ccbb3Scarlsonj server_unicast_option(dsmp, offer); 2697c478bd9Sstevel@tonic-gate 270d04ccbb3Scarlsonj send_v6_request(dsmp); 271d04ccbb3Scarlsonj } else { 272d04ccbb3Scarlsonj /* if we got a message from the server, display it. */ 273d04ccbb3Scarlsonj if (offer->opts[CD_MESSAGE] != NULL) { 274d04ccbb3Scarlsonj print_server_msg(dsmp, 275d04ccbb3Scarlsonj (char *)offer->opts[CD_MESSAGE]->value, 276d04ccbb3Scarlsonj offer->opts[CD_MESSAGE]->len); 277d04ccbb3Scarlsonj } 2787c478bd9Sstevel@tonic-gate 2797c478bd9Sstevel@tonic-gate /* 2807c478bd9Sstevel@tonic-gate * assemble a DHCPREQUEST, with the ciaddr field set to 0, 2817c478bd9Sstevel@tonic-gate * since we got here from the INIT state. 2827c478bd9Sstevel@tonic-gate */ 2837c478bd9Sstevel@tonic-gate 284d04ccbb3Scarlsonj dpkt = init_pkt(dsmp, REQUEST); 2857c478bd9Sstevel@tonic-gate 2867c478bd9Sstevel@tonic-gate /* 287d04ccbb3Scarlsonj * Grab the lease out of the OFFER; we know it's valid because 2887c478bd9Sstevel@tonic-gate * select_best() already checked. The max dhcp message size 289d04ccbb3Scarlsonj * option is set to the interface max, minus the size of the 290d04ccbb3Scarlsonj * udp and ip headers. 2917c478bd9Sstevel@tonic-gate */ 2927c478bd9Sstevel@tonic-gate 2937c478bd9Sstevel@tonic-gate (void) memcpy(&lease, offer->opts[CD_LEASE_TIME]->value, 2947c478bd9Sstevel@tonic-gate sizeof (lease_t)); 2957c478bd9Sstevel@tonic-gate 296d04ccbb3Scarlsonj (void) add_pkt_opt32(dpkt, CD_LEASE_TIME, lease); 297d04ccbb3Scarlsonj (void) add_pkt_opt16(dpkt, CD_MAX_DHCP_SIZE, 298d04ccbb3Scarlsonj htons(dsmp->dsm_lif->lif_max - sizeof (struct udpiphdr))); 299d04ccbb3Scarlsonj (void) add_pkt_opt32(dpkt, CD_REQUESTED_IP_ADDR, 300d04ccbb3Scarlsonj offer->pkt->yiaddr.s_addr); 301d04ccbb3Scarlsonj (void) add_pkt_opt(dpkt, CD_SERVER_ID, 302d04ccbb3Scarlsonj offer->opts[CD_SERVER_ID]->value, 3037c478bd9Sstevel@tonic-gate offer->opts[CD_SERVER_ID]->len); 3047c478bd9Sstevel@tonic-gate 305f4b3ec61Sdh155122 if (class_id_len != 0) { 306f4b3ec61Sdh155122 (void) add_pkt_opt(dpkt, CD_CLASS_ID, class_id, 307f4b3ec61Sdh155122 class_id_len); 308f4b3ec61Sdh155122 } 309d04ccbb3Scarlsonj (void) add_pkt_prl(dpkt, dsmp); 3107c478bd9Sstevel@tonic-gate 3117c478bd9Sstevel@tonic-gate /* 312d04ccbb3Scarlsonj * dsm_reqhost was set for this state machine in 313d04ccbb3Scarlsonj * dhcp_selecting() if the DF_REQUEST_HOSTNAME option set and a 314d04ccbb3Scarlsonj * host name was found 3157c478bd9Sstevel@tonic-gate */ 316d04ccbb3Scarlsonj if (dsmp->dsm_reqhost != NULL) { 317d04ccbb3Scarlsonj (void) add_pkt_opt(dpkt, CD_HOSTNAME, dsmp->dsm_reqhost, 318d04ccbb3Scarlsonj strlen(dsmp->dsm_reqhost)); 3197c478bd9Sstevel@tonic-gate } 320d04ccbb3Scarlsonj (void) add_pkt_opt(dpkt, CD_END, NULL, 0); 3217c478bd9Sstevel@tonic-gate 3227c478bd9Sstevel@tonic-gate /* 3237c478bd9Sstevel@tonic-gate * send out the REQUEST, trying retransmissions. either a NAK 3247c478bd9Sstevel@tonic-gate * or too many REQUEST attempts will revert us to SELECTING. 3257c478bd9Sstevel@tonic-gate */ 3267c478bd9Sstevel@tonic-gate 327d04ccbb3Scarlsonj if (!set_smach_state(dsmp, REQUESTING)) { 328d04ccbb3Scarlsonj dhcpmsg(MSG_ERROR, "dhcp_requesting: cannot switch to " 329d04ccbb3Scarlsonj "REQUESTING state; reverting to INIT on %s", 330d04ccbb3Scarlsonj dsmp->dsm_name); 331d04ccbb3Scarlsonj goto failure; 332d04ccbb3Scarlsonj } 333d04ccbb3Scarlsonj 334d04ccbb3Scarlsonj (void) send_pkt(dsmp, dpkt, htonl(INADDR_BROADCAST), 335d04ccbb3Scarlsonj stop_requesting); 336d04ccbb3Scarlsonj } 337d04ccbb3Scarlsonj 338d04ccbb3Scarlsonj /* all done with the offer */ 339d04ccbb3Scarlsonj free_pkt_entry(offer); 340d04ccbb3Scarlsonj 341d04ccbb3Scarlsonj return; 342d04ccbb3Scarlsonj 343d04ccbb3Scarlsonj failure: 344d04ccbb3Scarlsonj dsmp->dsm_dflags |= DHCP_IF_FAILED; 345d04ccbb3Scarlsonj (void) set_smach_state(dsmp, INIT); 346d04ccbb3Scarlsonj ipc_action_finish(dsmp, DHCP_IPC_E_MEMORY); 347d04ccbb3Scarlsonj free_pkt_list(&dsmp->dsm_recv_pkt_list); 348d04ccbb3Scarlsonj } 3497c478bd9Sstevel@tonic-gate 3507c478bd9Sstevel@tonic-gate /* 351d04ccbb3Scarlsonj * compute_points_v6(): compute the number of "points" for a given v6 352d04ccbb3Scarlsonj * advertisement. 353d04ccbb3Scarlsonj * 354d04ccbb3Scarlsonj * input: const PKT_LIST *: packet to inspect 355d04ccbb3Scarlsonj * const dhcp_smach_t *: state machine that received the packet 356d04ccbb3Scarlsonj * output: int: -1 to discard, -2 to accept immediately, >=0 for preference. 3577c478bd9Sstevel@tonic-gate */ 3587c478bd9Sstevel@tonic-gate 359d04ccbb3Scarlsonj static int 360d04ccbb3Scarlsonj compute_points_v6(const PKT_LIST *pkt, const dhcp_smach_t *dsmp) 361d04ccbb3Scarlsonj { 362d04ccbb3Scarlsonj char abuf[INET6_ADDRSTRLEN]; 363d04ccbb3Scarlsonj int points = 0; 364d04ccbb3Scarlsonj const dhcpv6_option_t *d6o, *d6so; 365d04ccbb3Scarlsonj uint_t olen, solen; 366d04ccbb3Scarlsonj int i; 367d04ccbb3Scarlsonj const char *estr, *msg; 368d04ccbb3Scarlsonj uint_t msglen; 3697c478bd9Sstevel@tonic-gate 370d04ccbb3Scarlsonj /* 371d04ccbb3Scarlsonj * Look through the packet contents. Valid packets must have our 372d04ccbb3Scarlsonj * client ID and a server ID, which has already been checked by 373e704a8f2Smeem * dhcp_packet_lif. Bonus points for each option. 374d04ccbb3Scarlsonj */ 3757c478bd9Sstevel@tonic-gate 376d04ccbb3Scarlsonj /* One point for having a valid message. */ 377d04ccbb3Scarlsonj points++; 3787c478bd9Sstevel@tonic-gate 379d04ccbb3Scarlsonj /* 380d04ccbb3Scarlsonj * Per RFC 3315, if the Advertise message says, "yes, we have no 381d04ccbb3Scarlsonj * bananas today," then ignore the entire message. (Why it's just 382d04ccbb3Scarlsonj * _this_ error and no other is a bit of a mystery, but a standard is a 383d04ccbb3Scarlsonj * standard.) 384d04ccbb3Scarlsonj */ 385d04ccbb3Scarlsonj d6o = dhcpv6_pkt_option(pkt, NULL, DHCPV6_OPT_STATUS_CODE, &olen); 386d04ccbb3Scarlsonj if (dhcpv6_status_code(d6o, olen, &estr, &msg, &msglen) == 387d04ccbb3Scarlsonj DHCPV6_STAT_NOADDRS) { 388d04ccbb3Scarlsonj dhcpmsg(MSG_INFO, 389d04ccbb3Scarlsonj "discard advertisement from %s on %s: no address status", 390d04ccbb3Scarlsonj inet_ntop(AF_INET6, 391d04ccbb3Scarlsonj &((struct sockaddr_in6 *)&pkt->pktfrom)->sin6_addr, 392d04ccbb3Scarlsonj abuf, sizeof (abuf)), dsmp->dsm_name); 393d04ccbb3Scarlsonj return (-1); 3947c478bd9Sstevel@tonic-gate } 395d04ccbb3Scarlsonj 396d04ccbb3Scarlsonj /* Two points for each batch of offered IP addresses */ 397d04ccbb3Scarlsonj d6o = NULL; 398d04ccbb3Scarlsonj while ((d6o = dhcpv6_pkt_option(pkt, d6o, DHCPV6_OPT_IA_NA, 399d04ccbb3Scarlsonj &olen)) != NULL) { 400d04ccbb3Scarlsonj 401d04ccbb3Scarlsonj /* 402d04ccbb3Scarlsonj * Note that it's possible to have "no bananas" on an 403d04ccbb3Scarlsonj * individual IA. We must look for that here. 404d04ccbb3Scarlsonj * 405d04ccbb3Scarlsonj * RFC 3315 section 17.1.3 does not refer to the status code 406d04ccbb3Scarlsonj * embedded in the IA itself. However, the TAHI test suite 407d04ccbb3Scarlsonj * checks for this specific case. Because it's extremely 408d04ccbb3Scarlsonj * unlikely that any usable server is going to report that it 409d04ccbb3Scarlsonj * has no addresses on a network using DHCP for address 410d04ccbb3Scarlsonj * assignment, we allow such messages to be dropped. 411d04ccbb3Scarlsonj */ 412d04ccbb3Scarlsonj d6so = dhcpv6_find_option( 413d04ccbb3Scarlsonj (const char *)d6o + sizeof (dhcpv6_ia_na_t), 414d04ccbb3Scarlsonj olen - sizeof (dhcpv6_ia_na_t), NULL, 415d04ccbb3Scarlsonj DHCPV6_OPT_STATUS_CODE, &solen); 416d04ccbb3Scarlsonj if (dhcpv6_status_code(d6so, solen, &estr, &msg, &msglen) == 417d04ccbb3Scarlsonj DHCPV6_STAT_NOADDRS) 418d04ccbb3Scarlsonj return (-1); 419d04ccbb3Scarlsonj points += 2; 4207c478bd9Sstevel@tonic-gate } 4217c478bd9Sstevel@tonic-gate 4227c478bd9Sstevel@tonic-gate /* 423d04ccbb3Scarlsonj * Note that we drive on in the case where there are no addresses. The 424d04ccbb3Scarlsonj * hope here is that we'll at least get some useful configuration 425d04ccbb3Scarlsonj * information. 426d04ccbb3Scarlsonj */ 427d04ccbb3Scarlsonj 428d04ccbb3Scarlsonj /* One point for each requested option */ 429d04ccbb3Scarlsonj for (i = 0; i < dsmp->dsm_prllen; i++) { 430d04ccbb3Scarlsonj if (dhcpv6_pkt_option(pkt, NULL, dsmp->dsm_prl[i], NULL) != 431d04ccbb3Scarlsonj NULL) 432d04ccbb3Scarlsonj points++; 433d04ccbb3Scarlsonj } 434d04ccbb3Scarlsonj 435d04ccbb3Scarlsonj /* 436d04ccbb3Scarlsonj * Ten points for each point of "preference." Note: the value 255 is 437d04ccbb3Scarlsonj * special. It means "stop right now and select this server." 438d04ccbb3Scarlsonj */ 439d04ccbb3Scarlsonj d6o = dhcpv6_pkt_option(pkt, NULL, DHCPV6_OPT_PREFERENCE, &olen); 440d04ccbb3Scarlsonj if (d6o != NULL && olen == sizeof (*d6o) + 1) { 441d04ccbb3Scarlsonj int pref = *(const uchar_t *)(d6o + 1); 442d04ccbb3Scarlsonj 443d04ccbb3Scarlsonj if (pref == 255) 444d04ccbb3Scarlsonj return (-2); 445d04ccbb3Scarlsonj points += 10 * pref; 446d04ccbb3Scarlsonj } 447d04ccbb3Scarlsonj 448d04ccbb3Scarlsonj return (points); 449d04ccbb3Scarlsonj } 450d04ccbb3Scarlsonj 451d04ccbb3Scarlsonj /* 452d04ccbb3Scarlsonj * compute_points_v4(): compute the number of "points" for a given v4 offer. 4537c478bd9Sstevel@tonic-gate * 454d04ccbb3Scarlsonj * input: const PKT_LIST *: packet to inspect 455d04ccbb3Scarlsonj * const dhcp_smach_t *: state machine that received the packet 456d04ccbb3Scarlsonj * output: int: -1 to discard, >=0 for preference. 457d04ccbb3Scarlsonj */ 458d04ccbb3Scarlsonj 459d04ccbb3Scarlsonj static int 460d04ccbb3Scarlsonj compute_points_v4(const PKT_LIST *pkt) 461d04ccbb3Scarlsonj { 462d04ccbb3Scarlsonj int points = 0; 463d04ccbb3Scarlsonj 464d04ccbb3Scarlsonj if (pkt->opts[CD_DHCP_TYPE] == NULL) { 465d04ccbb3Scarlsonj dhcpmsg(MSG_VERBOSE, "compute_points_v4: valid BOOTP reply"); 466d04ccbb3Scarlsonj goto valid_offer; 467d04ccbb3Scarlsonj } 468d04ccbb3Scarlsonj 469d04ccbb3Scarlsonj if (pkt->opts[CD_LEASE_TIME] == NULL) { 470d04ccbb3Scarlsonj dhcpmsg(MSG_WARNING, "compute_points_v4: OFFER without lease " 471d04ccbb3Scarlsonj "time"); 472d04ccbb3Scarlsonj return (-1); 473d04ccbb3Scarlsonj } 474d04ccbb3Scarlsonj 475d04ccbb3Scarlsonj if (pkt->opts[CD_LEASE_TIME]->len != sizeof (lease_t)) { 476d04ccbb3Scarlsonj dhcpmsg(MSG_WARNING, "compute_points_v4: OFFER with garbled " 477d04ccbb3Scarlsonj "lease time"); 478d04ccbb3Scarlsonj return (-1); 479d04ccbb3Scarlsonj } 480d04ccbb3Scarlsonj 481d04ccbb3Scarlsonj if (pkt->opts[CD_SERVER_ID] == NULL) { 482d04ccbb3Scarlsonj dhcpmsg(MSG_WARNING, "compute_points_v4: OFFER without server " 483d04ccbb3Scarlsonj "id"); 484d04ccbb3Scarlsonj return (-1); 485d04ccbb3Scarlsonj } 486d04ccbb3Scarlsonj 487d04ccbb3Scarlsonj if (pkt->opts[CD_SERVER_ID]->len != sizeof (ipaddr_t)) { 488d04ccbb3Scarlsonj dhcpmsg(MSG_WARNING, "compute_points_v4: OFFER with garbled " 489d04ccbb3Scarlsonj "server id"); 490d04ccbb3Scarlsonj return (-1); 491d04ccbb3Scarlsonj } 492d04ccbb3Scarlsonj 493d04ccbb3Scarlsonj /* valid DHCP OFFER. see if we got our parameters. */ 494d04ccbb3Scarlsonj dhcpmsg(MSG_VERBOSE, "compute_points_v4: valid OFFER packet"); 495d04ccbb3Scarlsonj points += 30; 496d04ccbb3Scarlsonj 497d04ccbb3Scarlsonj valid_offer: 498d04ccbb3Scarlsonj if (pkt->rfc1048) 499d04ccbb3Scarlsonj points += 5; 500d04ccbb3Scarlsonj 501d04ccbb3Scarlsonj /* 502d04ccbb3Scarlsonj * also could be faked, though more difficult because the encapsulation 503d04ccbb3Scarlsonj * is hard to encode on a BOOTP server; plus there's not as much real 504d04ccbb3Scarlsonj * estate in the packet for options, so it's likely this option would 505d04ccbb3Scarlsonj * get dropped. 506d04ccbb3Scarlsonj */ 507d04ccbb3Scarlsonj 508d04ccbb3Scarlsonj if (pkt->opts[CD_VENDOR_SPEC] != NULL) 509d04ccbb3Scarlsonj points += 80; 510d04ccbb3Scarlsonj 511d04ccbb3Scarlsonj if (pkt->opts[CD_SUBNETMASK] != NULL) 512d04ccbb3Scarlsonj points++; 513d04ccbb3Scarlsonj 514d04ccbb3Scarlsonj if (pkt->opts[CD_ROUTER] != NULL) 515d04ccbb3Scarlsonj points++; 516d04ccbb3Scarlsonj 517d04ccbb3Scarlsonj if (pkt->opts[CD_HOSTNAME] != NULL) 518d04ccbb3Scarlsonj points += 5; 519d04ccbb3Scarlsonj 520d04ccbb3Scarlsonj return (points); 521d04ccbb3Scarlsonj } 522d04ccbb3Scarlsonj 523d04ccbb3Scarlsonj /* 524d04ccbb3Scarlsonj * select_best(): selects the best offer from a list of IPv4 OFFER packets or 525d04ccbb3Scarlsonj * DHCPv6 Advertise packets. 526d04ccbb3Scarlsonj * 527d04ccbb3Scarlsonj * input: dhcp_smach_t *: state machine with enqueued offers 5287c478bd9Sstevel@tonic-gate * output: PKT_LIST *: the best packet, or NULL if none are acceptable 5297c478bd9Sstevel@tonic-gate */ 5307c478bd9Sstevel@tonic-gate 5317c478bd9Sstevel@tonic-gate static PKT_LIST * 532d04ccbb3Scarlsonj select_best(dhcp_smach_t *dsmp) 5337c478bd9Sstevel@tonic-gate { 534d04ccbb3Scarlsonj PKT_LIST *current = dsmp->dsm_recv_pkt_list; 535d04ccbb3Scarlsonj PKT_LIST *next, *best = NULL; 536d04ccbb3Scarlsonj int points, best_points = -1; 5377c478bd9Sstevel@tonic-gate 5387c478bd9Sstevel@tonic-gate /* 5397c478bd9Sstevel@tonic-gate * pick out the best offer. point system. 540d04ccbb3Scarlsonj * what's important for IPv4? 5417c478bd9Sstevel@tonic-gate * 542d04ccbb3Scarlsonj * 0) DHCP (30 points) 5437c478bd9Sstevel@tonic-gate * 1) no option overload 544d04ccbb3Scarlsonj * 2) encapsulated vendor option (80 points) 5457c478bd9Sstevel@tonic-gate * 3) non-null sname and siaddr fields 5467c478bd9Sstevel@tonic-gate * 4) non-null file field 547d04ccbb3Scarlsonj * 5) hostname (5 points) 548d04ccbb3Scarlsonj * 6) subnetmask (1 point) 549d04ccbb3Scarlsonj * 7) router (1 point) 5507c478bd9Sstevel@tonic-gate */ 5517c478bd9Sstevel@tonic-gate 552d04ccbb3Scarlsonj for (; current != NULL; current = next) { 553d04ccbb3Scarlsonj next = current->next; 5547c478bd9Sstevel@tonic-gate 555d04ccbb3Scarlsonj points = current->isv6 ? 556d04ccbb3Scarlsonj compute_points_v6(current, dsmp) : 557d04ccbb3Scarlsonj compute_points_v4(current); 5587c478bd9Sstevel@tonic-gate 5597c478bd9Sstevel@tonic-gate /* 560d04ccbb3Scarlsonj * Just discard any unacceptable entries we encounter. 5617c478bd9Sstevel@tonic-gate */ 562d04ccbb3Scarlsonj if (points == -1) { 563d04ccbb3Scarlsonj remque(current); 564d04ccbb3Scarlsonj free_pkt_entry(current); 565d04ccbb3Scarlsonj continue; 566d04ccbb3Scarlsonj } 5677c478bd9Sstevel@tonic-gate 5687c478bd9Sstevel@tonic-gate dhcpmsg(MSG_DEBUG, "select_best: OFFER had %d points", points); 5697c478bd9Sstevel@tonic-gate 570d04ccbb3Scarlsonj /* Special case: stop now and select */ 571d04ccbb3Scarlsonj if (points == -2) { 572d04ccbb3Scarlsonj best = current; 573d04ccbb3Scarlsonj break; 574d04ccbb3Scarlsonj } 575d04ccbb3Scarlsonj 5767c478bd9Sstevel@tonic-gate if (points >= best_points) { 5777c478bd9Sstevel@tonic-gate best_points = points; 5787c478bd9Sstevel@tonic-gate best = current; 5797c478bd9Sstevel@tonic-gate } 5807c478bd9Sstevel@tonic-gate } 5817c478bd9Sstevel@tonic-gate 5827c478bd9Sstevel@tonic-gate if (best != NULL) { 5837c478bd9Sstevel@tonic-gate dhcpmsg(MSG_DEBUG, "select_best: most points: %d", best_points); 584d04ccbb3Scarlsonj remque(best); 585d04ccbb3Scarlsonj } else { 5867c478bd9Sstevel@tonic-gate dhcpmsg(MSG_DEBUG, "select_best: no valid OFFER/BOOTP reply"); 587d04ccbb3Scarlsonj } 5887c478bd9Sstevel@tonic-gate 5897c478bd9Sstevel@tonic-gate return (best); 5907c478bd9Sstevel@tonic-gate } 5917c478bd9Sstevel@tonic-gate 5927c478bd9Sstevel@tonic-gate /* 593d04ccbb3Scarlsonj * accept_v4_acknak(): determine what to do with a DHCPv4 ACK/NAK based on the 594d04ccbb3Scarlsonj * current state. If we're renewing or rebinding, the ACK 595d04ccbb3Scarlsonj * must be for the same address and must have a new lease 596d04ccbb3Scarlsonj * time. If it's a NAK, then our cache is garbage, and we 597d04ccbb3Scarlsonj * must restart. Finally, call dhcp_bound on accepted 598d04ccbb3Scarlsonj * ACKs. 5997c478bd9Sstevel@tonic-gate * 600d04ccbb3Scarlsonj * input: dhcp_smach_t *: the state machine to handle the ACK/NAK 601d04ccbb3Scarlsonj * PKT_LIST *: the ACK/NAK message 6027c478bd9Sstevel@tonic-gate * output: void 6037c478bd9Sstevel@tonic-gate */ 6047c478bd9Sstevel@tonic-gate 605d04ccbb3Scarlsonj static void 606d04ccbb3Scarlsonj accept_v4_acknak(dhcp_smach_t *dsmp, PKT_LIST *plp) 6077c478bd9Sstevel@tonic-gate { 608906cb642Scarlsonj /* Account for received and processed messages */ 609906cb642Scarlsonj dsmp->dsm_received++; 610906cb642Scarlsonj 6117c478bd9Sstevel@tonic-gate if (*plp->opts[CD_DHCP_TYPE]->value == ACK) { 612cfb9c9abScarlsonj if (dsmp->dsm_state != INFORM_SENT && 613cfb9c9abScarlsonj dsmp->dsm_state != INFORMATION && 614cfb9c9abScarlsonj (plp->opts[CD_LEASE_TIME] == NULL || 615cfb9c9abScarlsonj plp->opts[CD_LEASE_TIME]->len != sizeof (lease_t))) { 616d04ccbb3Scarlsonj dhcpmsg(MSG_WARNING, "accept_v4_acknak: ACK packet on " 617d04ccbb3Scarlsonj "%s missing mandatory lease option, ignored", 618d04ccbb3Scarlsonj dsmp->dsm_name); 619d04ccbb3Scarlsonj dsmp->dsm_bad_offers++; 620d04ccbb3Scarlsonj free_pkt_entry(plp); 6217c478bd9Sstevel@tonic-gate return; 6227c478bd9Sstevel@tonic-gate } 623d04ccbb3Scarlsonj if ((dsmp->dsm_state == RENEWING || 624d04ccbb3Scarlsonj dsmp->dsm_state == REBINDING) && 625d04ccbb3Scarlsonj dsmp->dsm_leases->dl_lifs->lif_addr != 626d04ccbb3Scarlsonj plp->pkt->yiaddr.s_addr) { 627d04ccbb3Scarlsonj dhcpmsg(MSG_WARNING, "accept_v4_acknak: renewal ACK " 628d04ccbb3Scarlsonj "packet has a different IP address (%s), ignored", 6297c478bd9Sstevel@tonic-gate inet_ntoa(plp->pkt->yiaddr)); 630d04ccbb3Scarlsonj dsmp->dsm_bad_offers++; 631d04ccbb3Scarlsonj free_pkt_entry(plp); 6327c478bd9Sstevel@tonic-gate return; 6337c478bd9Sstevel@tonic-gate } 6347c478bd9Sstevel@tonic-gate } 6357c478bd9Sstevel@tonic-gate 6367c478bd9Sstevel@tonic-gate /* 6377c478bd9Sstevel@tonic-gate * looks good; cancel the retransmission timer and unregister 6387c478bd9Sstevel@tonic-gate * the acknak handler. ACK to BOUND, NAK back to SELECTING. 6397c478bd9Sstevel@tonic-gate */ 6407c478bd9Sstevel@tonic-gate 641d04ccbb3Scarlsonj stop_pkt_retransmission(dsmp); 6427c478bd9Sstevel@tonic-gate 643d04ccbb3Scarlsonj if (*plp->opts[CD_DHCP_TYPE]->value == NAK) { 644d04ccbb3Scarlsonj dhcpmsg(MSG_WARNING, "accept_v4_acknak: NAK on interface %s", 645d04ccbb3Scarlsonj dsmp->dsm_name); 646d04ccbb3Scarlsonj dsmp->dsm_bad_offers++; 647d04ccbb3Scarlsonj free_pkt_entry(plp); 648d04ccbb3Scarlsonj dhcp_restart(dsmp); 6497c478bd9Sstevel@tonic-gate 6507c478bd9Sstevel@tonic-gate /* 6517c478bd9Sstevel@tonic-gate * remove any bogus cached configuration we might have 6527c478bd9Sstevel@tonic-gate * around (right now would only happen if we got here 6537c478bd9Sstevel@tonic-gate * from INIT_REBOOT). 6547c478bd9Sstevel@tonic-gate */ 6557c478bd9Sstevel@tonic-gate 656d04ccbb3Scarlsonj (void) remove_hostconf(dsmp->dsm_name, dsmp->dsm_isv6); 6577c478bd9Sstevel@tonic-gate return; 6587c478bd9Sstevel@tonic-gate } 6597c478bd9Sstevel@tonic-gate 6607c478bd9Sstevel@tonic-gate if (plp->opts[CD_SERVER_ID] == NULL || 6617c478bd9Sstevel@tonic-gate plp->opts[CD_SERVER_ID]->len != sizeof (ipaddr_t)) { 662d04ccbb3Scarlsonj dhcpmsg(MSG_ERROR, "accept_v4_acknak: ACK with no valid " 663906cb642Scarlsonj "server id on %s", dsmp->dsm_name); 664d04ccbb3Scarlsonj dsmp->dsm_bad_offers++; 665d04ccbb3Scarlsonj free_pkt_entry(plp); 666d04ccbb3Scarlsonj dhcp_restart(dsmp); 6677c478bd9Sstevel@tonic-gate return; 6687c478bd9Sstevel@tonic-gate } 6697c478bd9Sstevel@tonic-gate 670d04ccbb3Scarlsonj if (plp->opts[CD_MESSAGE] != NULL) { 671d04ccbb3Scarlsonj print_server_msg(dsmp, (char *)plp->opts[CD_MESSAGE]->value, 672d04ccbb3Scarlsonj plp->opts[CD_MESSAGE]->len); 6737c478bd9Sstevel@tonic-gate } 6747c478bd9Sstevel@tonic-gate 675d04ccbb3Scarlsonj dhcpmsg(MSG_VERBOSE, "accept_v4_acknak: ACK on %s", dsmp->dsm_name); 676d04ccbb3Scarlsonj if (!dhcp_bound(dsmp, plp)) { 677d04ccbb3Scarlsonj dhcpmsg(MSG_WARNING, "accept_v4_acknak: dhcp_bound failed " 678d04ccbb3Scarlsonj "for %s", dsmp->dsm_name); 679d04ccbb3Scarlsonj dhcp_restart(dsmp); 680d04ccbb3Scarlsonj } 6817c478bd9Sstevel@tonic-gate } 6827c478bd9Sstevel@tonic-gate 6837c478bd9Sstevel@tonic-gate /* 684d04ccbb3Scarlsonj * accept_v6_message(): determine what to do with a DHCPv6 message based on the 685d04ccbb3Scarlsonj * current state. 6867c478bd9Sstevel@tonic-gate * 687d04ccbb3Scarlsonj * input: dhcp_smach_t *: the state machine to handle the message 688d04ccbb3Scarlsonj * PKT_LIST *: the DHCPv6 message 689d04ccbb3Scarlsonj * const char *: type of message (for logging) 690d04ccbb3Scarlsonj * uchar_t: type of message (extracted from packet) 691d04ccbb3Scarlsonj * output: void 692d04ccbb3Scarlsonj */ 693d04ccbb3Scarlsonj 694d04ccbb3Scarlsonj static void 695d04ccbb3Scarlsonj accept_v6_message(dhcp_smach_t *dsmp, PKT_LIST *plp, const char *pname, 696d04ccbb3Scarlsonj uchar_t recv_type) 697d04ccbb3Scarlsonj { 698d04ccbb3Scarlsonj const dhcpv6_option_t *d6o; 699d04ccbb3Scarlsonj uint_t olen; 700d04ccbb3Scarlsonj const char *estr, *msg; 701d04ccbb3Scarlsonj uint_t msglen; 702d04ccbb3Scarlsonj int status; 703d04ccbb3Scarlsonj 704906cb642Scarlsonj /* Account for received and processed messages */ 705906cb642Scarlsonj dsmp->dsm_received++; 706906cb642Scarlsonj 707906cb642Scarlsonj /* We don't yet support Reconfigure at all. */ 708906cb642Scarlsonj if (recv_type == DHCPV6_MSG_RECONFIGURE) { 709906cb642Scarlsonj dhcpmsg(MSG_VERBOSE, "accept_v6_message: ignored Reconfigure " 710906cb642Scarlsonj "on %s", dsmp->dsm_name); 711906cb642Scarlsonj free_pkt_entry(plp); 712906cb642Scarlsonj return; 713906cb642Scarlsonj } 714906cb642Scarlsonj 715d04ccbb3Scarlsonj /* 716d04ccbb3Scarlsonj * All valid DHCPv6 messages must have our Client ID specified. 717d04ccbb3Scarlsonj */ 718d04ccbb3Scarlsonj d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_CLIENTID, &olen); 719d04ccbb3Scarlsonj olen -= sizeof (*d6o); 720d04ccbb3Scarlsonj if (d6o == NULL || olen != dsmp->dsm_cidlen || 721d04ccbb3Scarlsonj memcmp(d6o + 1, dsmp->dsm_cid, olen) != 0) { 722d04ccbb3Scarlsonj dhcpmsg(MSG_VERBOSE, 723d04ccbb3Scarlsonj "accept_v6_message: discarded %s on %s: %s Client ID", 724d04ccbb3Scarlsonj pname, dsmp->dsm_name, d6o == NULL ? "no" : "wrong"); 725d04ccbb3Scarlsonj free_pkt_entry(plp); 726d04ccbb3Scarlsonj return; 727d04ccbb3Scarlsonj } 728d04ccbb3Scarlsonj 729d04ccbb3Scarlsonj /* 730d04ccbb3Scarlsonj * All valid DHCPv6 messages must have a Server ID specified. 731d04ccbb3Scarlsonj * 732d04ccbb3Scarlsonj * If this is a Reply and it's not in response to Solicit, Confirm, 733d04ccbb3Scarlsonj * Rebind, or Information-Request, then it must also match the Server 734d04ccbb3Scarlsonj * ID we're expecting. 735d04ccbb3Scarlsonj * 736d04ccbb3Scarlsonj * For Reply in the Solicit, Confirm, Rebind, and Information-Request 737d04ccbb3Scarlsonj * cases, the Server ID needs to be saved. This is done inside of 738d04ccbb3Scarlsonj * dhcp_bound(). 739d04ccbb3Scarlsonj */ 740d04ccbb3Scarlsonj d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_SERVERID, &olen); 741d04ccbb3Scarlsonj if (d6o == NULL) { 742d04ccbb3Scarlsonj dhcpmsg(MSG_DEBUG, 743d04ccbb3Scarlsonj "accept_v6_message: discarded %s on %s: no Server ID", 744d04ccbb3Scarlsonj pname, dsmp->dsm_name); 745d04ccbb3Scarlsonj free_pkt_entry(plp); 746d04ccbb3Scarlsonj return; 747d04ccbb3Scarlsonj } 748d04ccbb3Scarlsonj if (recv_type == DHCPV6_MSG_REPLY && dsmp->dsm_state != SELECTING && 749d04ccbb3Scarlsonj dsmp->dsm_state != INIT_REBOOT && dsmp->dsm_state != REBINDING && 750d04ccbb3Scarlsonj dsmp->dsm_state != INFORM_SENT) { 751d04ccbb3Scarlsonj olen -= sizeof (*d6o); 752d04ccbb3Scarlsonj if (olen != dsmp->dsm_serveridlen || 753d04ccbb3Scarlsonj memcmp(d6o + 1, dsmp->dsm_serverid, olen) != 0) { 754d04ccbb3Scarlsonj dhcpmsg(MSG_DEBUG, "accept_v6_message: discarded %s on " 755d04ccbb3Scarlsonj "%s: wrong Server ID", pname, dsmp->dsm_name); 756d04ccbb3Scarlsonj free_pkt_entry(plp); 757d04ccbb3Scarlsonj return; 758d04ccbb3Scarlsonj } 759d04ccbb3Scarlsonj } 760d04ccbb3Scarlsonj 761d04ccbb3Scarlsonj /* 762d04ccbb3Scarlsonj * Break out of the switch if the input message needs to be discarded. 763d04ccbb3Scarlsonj * Return from the function if the message has been enqueued or 764d04ccbb3Scarlsonj * consumed. 765d04ccbb3Scarlsonj */ 766d04ccbb3Scarlsonj switch (dsmp->dsm_state) { 767d04ccbb3Scarlsonj case SELECTING: 768d04ccbb3Scarlsonj /* A Reply message signifies a Rapid-Commit. */ 769d04ccbb3Scarlsonj if (recv_type == DHCPV6_MSG_REPLY) { 770d04ccbb3Scarlsonj if (dhcpv6_pkt_option(plp, NULL, 771d04ccbb3Scarlsonj DHCPV6_OPT_RAPID_COMMIT, &olen) == NULL) { 772d04ccbb3Scarlsonj dhcpmsg(MSG_DEBUG, "accept_v6_message: Reply " 773d04ccbb3Scarlsonj "on %s lacks Rapid-Commit; ignoring", 774d04ccbb3Scarlsonj dsmp->dsm_name); 775d04ccbb3Scarlsonj break; 776d04ccbb3Scarlsonj } 777d04ccbb3Scarlsonj dhcpmsg(MSG_VERBOSE, 778d04ccbb3Scarlsonj "accept_v6_message: rapid-commit Reply on %s", 779d04ccbb3Scarlsonj dsmp->dsm_name); 780d04ccbb3Scarlsonj cancel_offer_timer(dsmp); 781d04ccbb3Scarlsonj goto rapid_commit; 782d04ccbb3Scarlsonj } 783d04ccbb3Scarlsonj 784d04ccbb3Scarlsonj /* Otherwise, we're looking for Advertisements. */ 785d04ccbb3Scarlsonj if (recv_type != DHCPV6_MSG_ADVERTISE) 786d04ccbb3Scarlsonj break; 787d04ccbb3Scarlsonj 788d04ccbb3Scarlsonj /* 789d04ccbb3Scarlsonj * Special case: if this advertisement has preference 255, then 790d04ccbb3Scarlsonj * we must stop right now and select this server. 791d04ccbb3Scarlsonj */ 792d04ccbb3Scarlsonj d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_PREFERENCE, 793d04ccbb3Scarlsonj &olen); 794d04ccbb3Scarlsonj if (d6o != NULL && olen == sizeof (*d6o) + 1 && 795d04ccbb3Scarlsonj *(const uchar_t *)(d6o + 1) == 255) { 796d04ccbb3Scarlsonj pkt_smach_enqueue(dsmp, plp); 797d04ccbb3Scarlsonj dhcpmsg(MSG_DEBUG, "accept_v6_message: preference 255;" 798d04ccbb3Scarlsonj " immediate Request on %s", dsmp->dsm_name); 799d04ccbb3Scarlsonj dhcp_requesting(NULL, dsmp); 800d04ccbb3Scarlsonj } else { 801d04ccbb3Scarlsonj pkt_smach_enqueue(dsmp, plp); 802d04ccbb3Scarlsonj } 803d04ccbb3Scarlsonj return; 804d04ccbb3Scarlsonj 805d04ccbb3Scarlsonj case PRE_BOUND: 806d04ccbb3Scarlsonj case BOUND: 807d04ccbb3Scarlsonj /* 808d04ccbb3Scarlsonj * Not looking for anything in these states. (If we 809d04ccbb3Scarlsonj * implemented reconfigure, that might go here.) 810d04ccbb3Scarlsonj */ 811d04ccbb3Scarlsonj break; 812d04ccbb3Scarlsonj 813d04ccbb3Scarlsonj case REQUESTING: 814d04ccbb3Scarlsonj case INIT_REBOOT: 815d04ccbb3Scarlsonj case RENEWING: 816d04ccbb3Scarlsonj case REBINDING: 817d04ccbb3Scarlsonj case INFORM_SENT: 818d04ccbb3Scarlsonj /* 819d04ccbb3Scarlsonj * We're looking for Reply messages. 820d04ccbb3Scarlsonj */ 821d04ccbb3Scarlsonj if (recv_type != DHCPV6_MSG_REPLY) 822d04ccbb3Scarlsonj break; 823d04ccbb3Scarlsonj dhcpmsg(MSG_VERBOSE, 824d04ccbb3Scarlsonj "accept_v6_message: received Reply message on %s", 825d04ccbb3Scarlsonj dsmp->dsm_name); 826d04ccbb3Scarlsonj rapid_commit: 827d04ccbb3Scarlsonj /* 828d04ccbb3Scarlsonj * Extract the status code option. If one is present and the 829d04ccbb3Scarlsonj * request failed, then try to go to another advertisement in 830d04ccbb3Scarlsonj * the list or restart the selection machinery. 831d04ccbb3Scarlsonj */ 832d04ccbb3Scarlsonj d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_STATUS_CODE, 833d04ccbb3Scarlsonj &olen); 834d04ccbb3Scarlsonj status = dhcpv6_status_code(d6o, olen, &estr, &msg, &msglen); 835d04ccbb3Scarlsonj /* 836d04ccbb3Scarlsonj * Check for the UseMulticast status code. If this is present, 837d04ccbb3Scarlsonj * and if we were actually using unicast, then drop back and 838d04ccbb3Scarlsonj * try again. If we weren't using unicast, then just pretend 839d04ccbb3Scarlsonj * we never saw this message -- the peer is confused. (TAHI 840d04ccbb3Scarlsonj * does this.) 841d04ccbb3Scarlsonj */ 842d04ccbb3Scarlsonj if (status == DHCPV6_STAT_USEMCAST) { 843d04ccbb3Scarlsonj if (IN6_IS_ADDR_MULTICAST( 844d04ccbb3Scarlsonj &dsmp->dsm_send_dest.v6.sin6_addr)) { 845d04ccbb3Scarlsonj break; 846d04ccbb3Scarlsonj } else { 847d04ccbb3Scarlsonj free_pkt_entry(plp); 848d04ccbb3Scarlsonj dsmp->dsm_send_dest.v6.sin6_addr = 849d04ccbb3Scarlsonj ipv6_all_dhcp_relay_and_servers; 850d04ccbb3Scarlsonj retransmit_now(dsmp); 851d04ccbb3Scarlsonj return; 852d04ccbb3Scarlsonj } 853d04ccbb3Scarlsonj } 854d04ccbb3Scarlsonj print_server_msg(dsmp, msg, msglen); 855d04ccbb3Scarlsonj /* 856d04ccbb3Scarlsonj * We treat NoBinding at the top level as "success." Granted, 857d04ccbb3Scarlsonj * this doesn't make much sense, but the TAHI test suite does 858d04ccbb3Scarlsonj * this. NoBinding really only makes sense in the context of a 859d04ccbb3Scarlsonj * specific IA, as it refers to the GUID:IAID binding, so 860d04ccbb3Scarlsonj * ignoring it at the top level is safe. 861d04ccbb3Scarlsonj */ 862d04ccbb3Scarlsonj if (status == DHCPV6_STAT_SUCCESS || 863d04ccbb3Scarlsonj status == DHCPV6_STAT_NOBINDING) { 864d04ccbb3Scarlsonj if (dhcp_bound(dsmp, plp)) { 865d04ccbb3Scarlsonj /* 866d04ccbb3Scarlsonj * dhcp_bound will stop retransmission on 867d04ccbb3Scarlsonj * success, if that's called for. 868d04ccbb3Scarlsonj */ 869d04ccbb3Scarlsonj server_unicast_option(dsmp, plp); 870d04ccbb3Scarlsonj } else { 871d04ccbb3Scarlsonj stop_pkt_retransmission(dsmp); 872d04ccbb3Scarlsonj dhcpmsg(MSG_WARNING, "accept_v6_message: " 873d04ccbb3Scarlsonj "dhcp_bound failed for %s", dsmp->dsm_name); 874d04ccbb3Scarlsonj (void) remove_hostconf(dsmp->dsm_name, 875d04ccbb3Scarlsonj dsmp->dsm_isv6); 876d04ccbb3Scarlsonj dhcp_restart(dsmp); 877d04ccbb3Scarlsonj } 878d04ccbb3Scarlsonj } else { 879d04ccbb3Scarlsonj dhcpmsg(MSG_WARNING, "accept_v6_message: Reply: %s", 880d04ccbb3Scarlsonj estr); 881d04ccbb3Scarlsonj stop_pkt_retransmission(dsmp); 882d04ccbb3Scarlsonj free_pkt_entry(plp); 883d04ccbb3Scarlsonj if (dsmp->dsm_state == INFORM_SENT) { 884d04ccbb3Scarlsonj (void) set_smach_state(dsmp, INIT); 885d04ccbb3Scarlsonj ipc_action_finish(dsmp, DHCP_IPC_E_SRVFAILED); 886d04ccbb3Scarlsonj } else { 887d04ccbb3Scarlsonj (void) remove_hostconf(dsmp->dsm_name, 888d04ccbb3Scarlsonj dsmp->dsm_isv6); 889d04ccbb3Scarlsonj request_failed(dsmp); 890d04ccbb3Scarlsonj } 891d04ccbb3Scarlsonj } 892d04ccbb3Scarlsonj return; 893d04ccbb3Scarlsonj 894d04ccbb3Scarlsonj case DECLINING: 895d04ccbb3Scarlsonj /* 896d04ccbb3Scarlsonj * We're looking for Reply messages. 897d04ccbb3Scarlsonj */ 898d04ccbb3Scarlsonj if (recv_type != DHCPV6_MSG_REPLY) 899d04ccbb3Scarlsonj break; 900d04ccbb3Scarlsonj stop_pkt_retransmission(dsmp); 901d04ccbb3Scarlsonj /* 902d04ccbb3Scarlsonj * Extract the status code option. Note that it's not a 903d04ccbb3Scarlsonj * failure if the server reports an error. 904d04ccbb3Scarlsonj */ 905d04ccbb3Scarlsonj d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_STATUS_CODE, 906d04ccbb3Scarlsonj &olen); 907d04ccbb3Scarlsonj if (dhcpv6_status_code(d6o, olen, &estr, &msg, 908d04ccbb3Scarlsonj &msglen) == DHCPV6_STAT_SUCCESS) { 909d04ccbb3Scarlsonj print_server_msg(dsmp, msg, msglen); 910d04ccbb3Scarlsonj } else { 911d04ccbb3Scarlsonj dhcpmsg(MSG_WARNING, "accept_v6_message: Reply: %s", 912d04ccbb3Scarlsonj estr); 913d04ccbb3Scarlsonj } 914d04ccbb3Scarlsonj free_pkt_entry(plp); 915d04ccbb3Scarlsonj if (dsmp->dsm_leases == NULL) { 916d04ccbb3Scarlsonj dhcpmsg(MSG_VERBOSE, "accept_v6_message: %s has no " 917906cb642Scarlsonj "leases left", dsmp->dsm_name); 918d04ccbb3Scarlsonj dhcp_restart(dsmp); 919d04ccbb3Scarlsonj } else if (dsmp->dsm_lif_wait == 0) { 920d04ccbb3Scarlsonj (void) set_smach_state(dsmp, BOUND); 921d04ccbb3Scarlsonj } else { 922d04ccbb3Scarlsonj (void) set_smach_state(dsmp, PRE_BOUND); 923d04ccbb3Scarlsonj } 924d04ccbb3Scarlsonj return; 925d04ccbb3Scarlsonj 926d04ccbb3Scarlsonj case RELEASING: 927d04ccbb3Scarlsonj /* 928d04ccbb3Scarlsonj * We're looking for Reply messages. 929d04ccbb3Scarlsonj */ 930d04ccbb3Scarlsonj if (recv_type != DHCPV6_MSG_REPLY) 931d04ccbb3Scarlsonj break; 932d04ccbb3Scarlsonj stop_pkt_retransmission(dsmp); 933d04ccbb3Scarlsonj /* 934d04ccbb3Scarlsonj * Extract the status code option. 935d04ccbb3Scarlsonj */ 936d04ccbb3Scarlsonj d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_STATUS_CODE, 937d04ccbb3Scarlsonj &olen); 938d04ccbb3Scarlsonj if (dhcpv6_status_code(d6o, olen, &estr, &msg, 939d04ccbb3Scarlsonj &msglen) == DHCPV6_STAT_SUCCESS) { 940d04ccbb3Scarlsonj print_server_msg(dsmp, msg, msglen); 941d04ccbb3Scarlsonj } else { 942d04ccbb3Scarlsonj dhcpmsg(MSG_WARNING, "accept_v6_message: Reply: %s", 943d04ccbb3Scarlsonj estr); 944d04ccbb3Scarlsonj } 945d04ccbb3Scarlsonj free_pkt_entry(plp); 946d04ccbb3Scarlsonj finished_smach(dsmp, DHCP_IPC_SUCCESS); 947d04ccbb3Scarlsonj return; 948d04ccbb3Scarlsonj } 949d04ccbb3Scarlsonj 950d04ccbb3Scarlsonj /* 951d04ccbb3Scarlsonj * Break from above switch means that the message must be discarded. 952d04ccbb3Scarlsonj */ 953d04ccbb3Scarlsonj dhcpmsg(MSG_VERBOSE, 954d04ccbb3Scarlsonj "accept_v6_message: discarded v6 %s on %s; state %s", 955d04ccbb3Scarlsonj pname, dsmp->dsm_name, dhcp_state_to_string(dsmp->dsm_state)); 956d04ccbb3Scarlsonj free_pkt_entry(plp); 957d04ccbb3Scarlsonj } 958d04ccbb3Scarlsonj 959d04ccbb3Scarlsonj /* 960e704a8f2Smeem * dhcp_acknak_global(): Processes reception of an ACK or NAK packet on the 961d04ccbb3Scarlsonj * global socket -- broadcast packets for IPv4, all 962d04ccbb3Scarlsonj * packets for DHCPv6. 963d04ccbb3Scarlsonj * 964d04ccbb3Scarlsonj * input: iu_eh_t *: unused 965d04ccbb3Scarlsonj * int: the global file descriptor the ACK/NAK arrived on 966d04ccbb3Scarlsonj * short: unused 967d04ccbb3Scarlsonj * iu_event_id_t: unused 968d04ccbb3Scarlsonj * void *: unused 969d04ccbb3Scarlsonj * output: void 970d04ccbb3Scarlsonj */ 971d04ccbb3Scarlsonj 972d04ccbb3Scarlsonj /* ARGSUSED */ 973d04ccbb3Scarlsonj void 974e704a8f2Smeem dhcp_acknak_global(iu_eh_t *ehp, int fd, short events, iu_event_id_t id, 975d04ccbb3Scarlsonj void *arg) 976d04ccbb3Scarlsonj { 977d04ccbb3Scarlsonj PKT_LIST *plp; 978d04ccbb3Scarlsonj dhcp_pif_t *pif; 979d04ccbb3Scarlsonj uchar_t recv_type; 980d04ccbb3Scarlsonj const char *pname; 981d04ccbb3Scarlsonj uint_t xid; 982d04ccbb3Scarlsonj dhcp_smach_t *dsmp; 983d04ccbb3Scarlsonj boolean_t isv6 = (fd == v6_sock_fd); 984e704a8f2Smeem struct sockaddr_in sin; 985e704a8f2Smeem const char *reason; 986e704a8f2Smeem size_t sinlen = sizeof (sin); 987e704a8f2Smeem int sock; 988d04ccbb3Scarlsonj 989e704a8f2Smeem plp = recv_pkt(fd, get_max_mtu(isv6), isv6); 990948f2876Sss150715 if (plp == NULL) 991d04ccbb3Scarlsonj return; 992d04ccbb3Scarlsonj 993d04ccbb3Scarlsonj recv_type = pkt_recv_type(plp); 994d04ccbb3Scarlsonj pname = pkt_type_to_string(recv_type, isv6); 995d04ccbb3Scarlsonj 996d04ccbb3Scarlsonj /* 997f7d61273Smeem * Find the corresponding state machine and pif. 998d04ccbb3Scarlsonj * 999d04ccbb3Scarlsonj * Note that DHCPv6 Reconfigure would be special: it's not the reply to 1000d04ccbb3Scarlsonj * any transaction, and thus we would need to search on transaction ID 1001d04ccbb3Scarlsonj * zero (all state machines) to find the match. However, Reconfigure 1002d04ccbb3Scarlsonj * is not yet supported. 1003d04ccbb3Scarlsonj */ 1004d04ccbb3Scarlsonj xid = pkt_get_xid(plp->pkt, isv6); 1005e704a8f2Smeem 1006d04ccbb3Scarlsonj for (dsmp = lookup_smach_by_xid(xid, NULL, isv6); dsmp != NULL; 1007d04ccbb3Scarlsonj dsmp = lookup_smach_by_xid(xid, dsmp, isv6)) { 1008f7d61273Smeem pif = dsmp->dsm_lif->lif_pif; 1009*e11c3f44Smeem if (pif->pif_index == plp->ifindex || 1010*e11c3f44Smeem pif->pif_under_ipmp && pif->pif_grindex == plp->ifindex) 1011d04ccbb3Scarlsonj break; 1012d04ccbb3Scarlsonj } 1013e704a8f2Smeem 1014e704a8f2Smeem if (dsmp == NULL) { 1015f7d61273Smeem dhcpmsg(MSG_VERBOSE, "dhcp_acknak_global: ignored v%d %s packet" 1016f7d61273Smeem " on ifindex %d: unknown state machine", isv6 ? 6 : 4, 1017f7d61273Smeem pname, plp->ifindex); 1018f7d61273Smeem free_pkt_entry(plp); 1019f7d61273Smeem return; 1020f7d61273Smeem } 1021f7d61273Smeem 1022f7d61273Smeem if (!isv6 && !pkt_v4_match(recv_type, DHCP_PACK|DHCP_PNAK)) { 1023f7d61273Smeem reason = "not ACK or NAK"; 1024e704a8f2Smeem goto drop; 1025e704a8f2Smeem } 1026e704a8f2Smeem 1027e704a8f2Smeem /* 1028e704a8f2Smeem * For IPv4, most packets will be handled by dhcp_packet_lif(). The 1029e704a8f2Smeem * only exceptions are broadcast packets sent when lif_sock_ip_fd has 1030e704a8f2Smeem * bound to something other than INADDR_ANY. 1031e704a8f2Smeem */ 1032e704a8f2Smeem if (!isv6) { 1033e704a8f2Smeem sock = dsmp->dsm_lif->lif_sock_ip_fd; 1034e704a8f2Smeem 1035e704a8f2Smeem if (getsockname(sock, (struct sockaddr *)&sin, &sinlen) != -1 && 1036e704a8f2Smeem sin.sin_addr.s_addr == INADDR_ANY) { 1037e704a8f2Smeem reason = "handled by lif_sock_ip_fd"; 1038e704a8f2Smeem goto drop; 1039e704a8f2Smeem } 1040d04ccbb3Scarlsonj } 1041d04ccbb3Scarlsonj 1042d04ccbb3Scarlsonj /* 1043d04ccbb3Scarlsonj * We've got a packet; make sure it's acceptable and cancel the REQUEST 1044d04ccbb3Scarlsonj * retransmissions. 1045d04ccbb3Scarlsonj */ 1046d04ccbb3Scarlsonj if (isv6) 1047d04ccbb3Scarlsonj accept_v6_message(dsmp, plp, pname, recv_type); 1048d04ccbb3Scarlsonj else 1049d04ccbb3Scarlsonj accept_v4_acknak(dsmp, plp); 1050e704a8f2Smeem return; 1051e704a8f2Smeem drop: 1052e704a8f2Smeem dhcpmsg(MSG_VERBOSE, "dhcp_acknak_global: ignored v%d %s packet for %s " 1053e704a8f2Smeem "received on global socket: %s", isv6 ? 6 : 4, pname, pif->pif_name, 1054e704a8f2Smeem reason); 1055e704a8f2Smeem free_pkt_entry(plp); 1056d04ccbb3Scarlsonj } 1057d04ccbb3Scarlsonj 1058d04ccbb3Scarlsonj /* 1059d04ccbb3Scarlsonj * request_failed(): Attempt to request an address has failed. Take an 1060d04ccbb3Scarlsonj * appropriate action. 1061d04ccbb3Scarlsonj * 1062d04ccbb3Scarlsonj * input: dhcp_smach_t *: state machine that has failed 1063d04ccbb3Scarlsonj * output: void 1064d04ccbb3Scarlsonj */ 1065d04ccbb3Scarlsonj 1066d04ccbb3Scarlsonj static void 1067d04ccbb3Scarlsonj request_failed(dhcp_smach_t *dsmp) 1068d04ccbb3Scarlsonj { 1069d04ccbb3Scarlsonj PKT_LIST *offer; 1070d04ccbb3Scarlsonj 1071d04ccbb3Scarlsonj dsmp->dsm_server = ipv6_all_dhcp_relay_and_servers; 1072d04ccbb3Scarlsonj if ((offer = select_best(dsmp)) != NULL) { 1073d04ccbb3Scarlsonj insque(offer, &dsmp->dsm_recv_pkt_list); 1074d04ccbb3Scarlsonj dhcp_requesting(NULL, dsmp); 1075d04ccbb3Scarlsonj } else { 1076d04ccbb3Scarlsonj dhcpmsg(MSG_INFO, "no offers left on %s; restarting", 1077d04ccbb3Scarlsonj dsmp->dsm_name); 1078d04ccbb3Scarlsonj dhcp_selecting(dsmp); 1079d04ccbb3Scarlsonj } 1080d04ccbb3Scarlsonj } 1081d04ccbb3Scarlsonj 1082d04ccbb3Scarlsonj /* 1083e704a8f2Smeem * dhcp_packet_lif(): Processes reception of an ACK, NAK, or OFFER packet on 1084e704a8f2Smeem * a given logical interface for IPv4 (only). 1085d04ccbb3Scarlsonj * 1086d04ccbb3Scarlsonj * input: iu_eh_t *: unused 1087e704a8f2Smeem * int: the file descriptor the packet arrived on 1088d04ccbb3Scarlsonj * short: unused 1089d04ccbb3Scarlsonj * iu_event_id_t: the id of this event callback with the handler 1090d04ccbb3Scarlsonj * void *: pointer to logical interface receiving message 1091d04ccbb3Scarlsonj * output: void 1092d04ccbb3Scarlsonj */ 1093d04ccbb3Scarlsonj 1094d04ccbb3Scarlsonj /* ARGSUSED */ 1095d04ccbb3Scarlsonj void 1096e704a8f2Smeem dhcp_packet_lif(iu_eh_t *ehp, int fd, short events, iu_event_id_t id, 1097d04ccbb3Scarlsonj void *arg) 1098d04ccbb3Scarlsonj { 1099d04ccbb3Scarlsonj dhcp_lif_t *lif = arg; 1100d04ccbb3Scarlsonj PKT_LIST *plp; 1101d04ccbb3Scarlsonj uchar_t recv_type; 1102d04ccbb3Scarlsonj const char *pname; 1103d04ccbb3Scarlsonj uint_t xid; 1104d04ccbb3Scarlsonj dhcp_smach_t *dsmp; 1105d04ccbb3Scarlsonj 1106e704a8f2Smeem if ((plp = recv_pkt(fd, lif->lif_max, B_FALSE)) == NULL) 1107d04ccbb3Scarlsonj return; 1108d04ccbb3Scarlsonj 1109d04ccbb3Scarlsonj recv_type = pkt_recv_type(plp); 1110d04ccbb3Scarlsonj pname = pkt_type_to_string(recv_type, B_FALSE); 1111d04ccbb3Scarlsonj 1112e704a8f2Smeem if (!pkt_v4_match(recv_type, 1113e704a8f2Smeem DHCP_PACK | DHCP_PNAK | DHCP_PUNTYPED | DHCP_POFFER)) { 1114e704a8f2Smeem dhcpmsg(MSG_VERBOSE, "dhcp_packet_lif: ignored v4 %s packet " 1115d04ccbb3Scarlsonj "received via LIF %s", pname, lif->lif_name); 1116d04ccbb3Scarlsonj free_pkt_entry(plp); 1117d04ccbb3Scarlsonj return; 1118d04ccbb3Scarlsonj } 1119d04ccbb3Scarlsonj 1120d04ccbb3Scarlsonj /* 1121e704a8f2Smeem * Find the corresponding state machine. 1122d04ccbb3Scarlsonj */ 1123d04ccbb3Scarlsonj xid = pkt_get_xid(plp->pkt, B_FALSE); 1124d04ccbb3Scarlsonj for (dsmp = lookup_smach_by_xid(xid, NULL, B_FALSE); dsmp != NULL; 1125d04ccbb3Scarlsonj dsmp = lookup_smach_by_xid(xid, dsmp, B_FALSE)) { 1126d04ccbb3Scarlsonj if (dsmp->dsm_lif == lif) 1127d04ccbb3Scarlsonj break; 1128d04ccbb3Scarlsonj } 1129d04ccbb3Scarlsonj 1130e704a8f2Smeem if (dsmp == NULL) 1131e704a8f2Smeem goto drop; 1132e704a8f2Smeem 1133e704a8f2Smeem if (pkt_v4_match(recv_type, DHCP_PACK|DHCP_PNAK)) { 1134d04ccbb3Scarlsonj /* 1135e704a8f2Smeem * We've got an ACK/NAK; make sure it's acceptable and cancel 1136e704a8f2Smeem * the REQUEST retransmissions. 1137d04ccbb3Scarlsonj */ 1138d04ccbb3Scarlsonj accept_v4_acknak(dsmp, plp); 1139e704a8f2Smeem } else { 1140e704a8f2Smeem if (is_bound_state(dsmp->dsm_state)) 1141e704a8f2Smeem goto drop; 1142e704a8f2Smeem /* 1143e704a8f2Smeem * Must be an OFFER or a BOOTP message: enqueue it for later 1144e704a8f2Smeem * processing by select_best(). 1145e704a8f2Smeem */ 1146e704a8f2Smeem pkt_smach_enqueue(dsmp, plp); 1147e704a8f2Smeem } 1148e704a8f2Smeem return; 1149e704a8f2Smeem drop: 1150e704a8f2Smeem dhcpmsg(MSG_VERBOSE, "dhcp_packet_lif: ignored %s packet xid " 1151e704a8f2Smeem "%x received via LIF %s; %s", pname, xid, lif->lif_name, 1152e704a8f2Smeem dsmp == NULL ? "unknown state machine" : "bound"); 1153e704a8f2Smeem free_pkt_entry(plp); 1154d04ccbb3Scarlsonj } 1155d04ccbb3Scarlsonj 1156d04ccbb3Scarlsonj /* 1157906cb642Scarlsonj * dhcp_restart(): restarts DHCP (from INIT) on a given state machine, but only 1158906cb642Scarlsonj * if we're leasing addresses. Doesn't restart for information- 1159906cb642Scarlsonj * only interfaces. 1160d04ccbb3Scarlsonj * 1161d04ccbb3Scarlsonj * input: dhcp_smach_t *: the state machine to restart DHCP on 11627c478bd9Sstevel@tonic-gate * output: void 11637c478bd9Sstevel@tonic-gate */ 11647c478bd9Sstevel@tonic-gate 116569bb4bb4Scarlsonj void 1166d04ccbb3Scarlsonj dhcp_restart(dhcp_smach_t *dsmp) 11677c478bd9Sstevel@tonic-gate { 1168906cb642Scarlsonj if (dsmp->dsm_state == INFORM_SENT || dsmp->dsm_state == INFORMATION) 1169906cb642Scarlsonj return; 1170906cb642Scarlsonj 1171d04ccbb3Scarlsonj /* 1172d04ccbb3Scarlsonj * As we're returning to INIT state, we need to discard any leases we 1173d04ccbb3Scarlsonj * may have, and (for v4) canonize the LIF. There's a bit of tension 1174d04ccbb3Scarlsonj * between keeping around a possibly still working address, and obeying 1175d04ccbb3Scarlsonj * the RFCs. A more elaborate design would be to mark the addresses as 1176d04ccbb3Scarlsonj * DEPRECATED, and then start a removal timer. Such a design would 1177d04ccbb3Scarlsonj * probably compromise testing. 1178d04ccbb3Scarlsonj */ 1179d04ccbb3Scarlsonj deprecate_leases(dsmp); 11807c478bd9Sstevel@tonic-gate 1181cfb9c9abScarlsonj if (!set_start_timer(dsmp)) { 118269bb4bb4Scarlsonj dhcpmsg(MSG_ERROR, "dhcp_restart: cannot schedule dhcp_start, " 1183d04ccbb3Scarlsonj "reverting to INIT state on %s", dsmp->dsm_name); 1184d04ccbb3Scarlsonj 1185d04ccbb3Scarlsonj (void) set_smach_state(dsmp, INIT); 1186d04ccbb3Scarlsonj dsmp->dsm_dflags |= DHCP_IF_FAILED; 1187d04ccbb3Scarlsonj ipc_action_finish(dsmp, DHCP_IPC_E_MEMORY); 1188906cb642Scarlsonj } else { 1189906cb642Scarlsonj dhcpmsg(MSG_DEBUG, "dhcp_restart: restarting DHCP on %s", 1190906cb642Scarlsonj dsmp->dsm_name); 1191d04ccbb3Scarlsonj } 11927c478bd9Sstevel@tonic-gate } 11937c478bd9Sstevel@tonic-gate 11947c478bd9Sstevel@tonic-gate /* 11957c478bd9Sstevel@tonic-gate * stop_requesting(): decides when to stop retransmitting REQUESTs 11967c478bd9Sstevel@tonic-gate * 1197d04ccbb3Scarlsonj * input: dhcp_smach_t *: the state machine REQUESTs are being sent from 11987c478bd9Sstevel@tonic-gate * unsigned int: the number of REQUESTs sent so far 11997c478bd9Sstevel@tonic-gate * output: boolean_t: B_TRUE if retransmissions should stop 12007c478bd9Sstevel@tonic-gate */ 12017c478bd9Sstevel@tonic-gate 12027c478bd9Sstevel@tonic-gate static boolean_t 1203d04ccbb3Scarlsonj stop_requesting(dhcp_smach_t *dsmp, unsigned int n_requests) 12047c478bd9Sstevel@tonic-gate { 1205d04ccbb3Scarlsonj uint_t maxreq; 12067c478bd9Sstevel@tonic-gate 1207d04ccbb3Scarlsonj maxreq = dsmp->dsm_isv6 ? DHCPV6_REQ_MAX_RC : DHCP_MAX_REQUESTS; 1208d04ccbb3Scarlsonj if (n_requests >= maxreq) { 12097c478bd9Sstevel@tonic-gate 1210d04ccbb3Scarlsonj dhcpmsg(MSG_INFO, "no ACK/NAK/Reply to REQUEST on %s", 1211d04ccbb3Scarlsonj dsmp->dsm_name); 12127c478bd9Sstevel@tonic-gate 1213d04ccbb3Scarlsonj request_failed(dsmp); 12147c478bd9Sstevel@tonic-gate return (B_TRUE); 1215d04ccbb3Scarlsonj } else { 12167c478bd9Sstevel@tonic-gate return (B_FALSE); 12177c478bd9Sstevel@tonic-gate } 1218d04ccbb3Scarlsonj } 1219