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
send_v6_request(dhcp_smach_t * dsmp)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
server_unicast_option(dhcp_smach_t * dsmp,PKT_LIST * plp)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
dhcp_requesting(iu_tq_t * tqp,void * arg)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
compute_points_v6(const PKT_LIST * pkt,const dhcp_smach_t * dsmp)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
compute_points_v4(const PKT_LIST * pkt)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 *
select_best(dhcp_smach_t * dsmp)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
accept_v4_acknak(dhcp_smach_t * dsmp,PKT_LIST * plp)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
accept_v6_message(dhcp_smach_t * dsmp,PKT_LIST * plp,const char * pname,uchar_t recv_type)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
dhcp_acknak_global(iu_eh_t * ehp,int fd,short events,iu_event_id_t id,void * arg)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
request_failed(dhcp_smach_t * dsmp)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
dhcp_packet_lif(iu_eh_t * ehp,int fd,short events,iu_event_id_t id,void * arg)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
dhcp_restart(dhcp_smach_t * dsmp)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
stop_requesting(dhcp_smach_t * dsmp,unsigned int n_requests)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