1/*
2 * Copyright (c) 1999-2014 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23/*
24 * dhcp.c
25 * - DHCP configuration threads
26 * - contains dhcp_thread() and inform_thread()
27 */
28/*
29 * Modification History
30 *
31 * May 16, 2000		Dieter Siegmund (dieter@apple.com)
32 * - reworked to fit within the new event-driven framework
33 *
34 * October 4, 2000	Dieter Siegmund (dieter@apple.com)
35 * - added code to unpublish interface state if the link goes
36 *   down and stays down for more than 4 seconds
37 * - modified INFORM to process link change events as well
38 *
39 * February 1, 2002	Dieter Siegmund (dieter@apple.com)
40 * - changes for NetBoot
41 */
42
43#include <stdlib.h>
44#include <unistd.h>
45#include <string.h>
46#include <stdio.h>
47#include <sys/types.h>
48#include <sys/wait.h>
49#include <sys/errno.h>
50#include <sys/socket.h>
51#include <sys/ioctl.h>
52#include <sys/sockio.h>
53#include <ctype.h>
54#include <net/if.h>
55#include <net/ethernet.h>
56#include <netinet/in.h>
57#include <netinet/udp.h>
58#include <netinet/in_systm.h>
59#include <netinet/ip.h>
60#include <netinet/bootp.h>
61#include <arpa/inet.h>
62#include <syslog.h>
63#include <TargetConditionals.h>
64#include <IOKit/IOMessage.h>
65#include <IOKit/IOKitLib.h>
66#include <IOKit/pwr_mgt/IOPMLib.h>
67
68#include "rfc_options.h"
69#include "dhcp_options.h"
70#include "dhcp.h"
71#include "interfaces.h"
72#include "util.h"
73#include <net/if_types.h>
74#include "host_identifier.h"
75#include "dhcplib.h"
76
77#include "ipconfigd_threads.h"
78#include "DHCPLease.h"
79#include "symbol_scope.h"
80#include "cfutil.h"
81
82#define SUGGESTED_LEASE_LENGTH		(60 * 60 * 24 * 30 * 3) /* 3 months */
83
84typedef struct {
85    boolean_t			valid;
86    absolute_time_t		expiration;
87    dhcp_lease_time_t		length;
88    absolute_time_t		start;
89    absolute_time_t		t1;
90    absolute_time_t		t2;
91    boolean_t			needs_write;
92} lease_info_t;
93
94typedef struct {
95    boolean_t		allow_wake_with_short_lease;
96    arp_client_t *	arp;
97    bootp_client_t *	client;
98    void *		client_id;
99    int			client_id_len;
100    struct in_addr	conflicting_address;
101    boolean_t		disable_arp_collision_detection;
102    boolean_t		gathering;
103    boolean_t		got_nak;
104    lease_info_t	lease;
105    DHCPLeaseList	lease_list;
106    boolean_t		must_broadcast;
107    struct dhcp * 	request;
108    int			request_size;
109    absolute_time_t	renew_rebind_time;
110    boolean_t		resolve_router_timed_out;
111    struct saved_pkt	saved;
112    dhcp_cstate_t	state;
113    absolute_time_t	start_secs;
114    timer_callout_t *	timer;
115    int			try;
116    uint32_t		txbuf[DHCP_PACKET_MIN/sizeof(uint32_t)];
117    u_int32_t		xid;
118    boolean_t		user_warned;
119    int			wait_secs;
120    absolute_time_t	wake_time;
121} Service_dhcp_t;
122
123typedef struct {
124    arp_client_t *	arp;
125    bootp_client_t *	client;
126    boolean_t		gathering;
127    struct in_addr	our_mask;
128    struct dhcp * 	request;
129    int			request_size;
130    boolean_t		resolve_router_timed_out;
131    struct saved_pkt	saved;
132    absolute_time_t	start_secs;
133    timer_callout_t *	timer;
134    int			try;
135    uint32_t		txbuf[DHCP_PACKET_MIN/sizeof(uint32_t)];
136    u_int32_t		xid;
137    boolean_t		user_warned;
138    int			wait_secs;
139} Service_inform_t;
140
141static void
142dhcp_init(ServiceRef service_p, IFEventID_t event_id, void * event_data);
143
144static void
145dhcp_init_reboot(ServiceRef service_p, IFEventID_t event_id,
146		 void * event_data);
147
148static void
149dhcp_arp_router(ServiceRef service_p, IFEventID_t event_id,
150		void * event_data);
151
152static void
153dhcp_select(ServiceRef service_p, IFEventID_t event_id, void * event_data);
154
155static void
156dhcp_bound(ServiceRef service_p, IFEventID_t event_id, void * event_data);
157
158static void
159dhcp_renew_rebind(ServiceRef service_p, IFEventID_t event_id,
160		  void * event_data);
161
162static void
163dhcp_unbound(ServiceRef service_p, IFEventID_t event_id, void * event_data);
164
165static void
166dhcp_decline(ServiceRef service_p, IFEventID_t event_id, void * event_data);
167
168static void
169dhcp_release(ServiceRef service_p);
170
171static void
172dhcp_no_server(ServiceRef service_p, IFEventID_t event_id, void * event_data);
173
174static boolean_t
175dhcp_check_lease(ServiceRef service_p, absolute_time_t current_time);
176
177static void
178dhcp_check_router(ServiceRef service_p, IFEventID_t event_id, void * event_data);
179
180static boolean_t
181get_server_identifier(dhcpol_t * options, struct in_addr * server_ip)
182{
183    struct in_addr * 	ipaddr_p;
184
185    ipaddr_p = (struct in_addr *)
186	dhcpol_find_with_length(options, dhcptag_server_identifier_e,
187				sizeof(*ipaddr_p));
188    if (ipaddr_p != NULL) {
189	*server_ip = *ipaddr_p;
190    }
191    return (ipaddr_p != NULL);
192}
193
194#define DHCP_ADDRESS_RATING_POOR        0
195#define DHCP_ADDRESS_RATING_FAIR        100
196#define DHCP_ADDRESS_RATING_GOOD        1000
197
198static
199int
200get_rating_for_ip_address(struct in_addr * iaddr)
201{
202    if (ip_is_private(*iaddr)) {
203	return (DHCP_ADDRESS_RATING_FAIR);
204    }
205
206    if (ip_is_linklocal(*iaddr)) {
207	return (DHCP_ADDRESS_RATING_POOR);
208    }
209
210    return (DHCP_ADDRESS_RATING_GOOD);
211}
212
213static const uint8_t dhcp_static_default_params[] = {
214    dhcptag_subnet_mask_e,
215    dhcptag_router_e,
216    dhcptag_domain_name_server_e,
217    dhcptag_domain_name_e,
218    dhcptag_domain_search_e,
219    dhcptag_proxy_auto_discovery_url_e,
220#if ! TARGET_OS_EMBEDDED
221    dhcptag_ldap_url_e,
222    dhcptag_nb_over_tcpip_name_server_e,
223    dhcptag_nb_over_tcpip_node_type_e,
224#endif /* ! TARGET_OS_EMBEDDED */
225};
226#define	N_DHCP_STATIC_DEFAULT_PARAMS 	(sizeof(dhcp_static_default_params) / sizeof(dhcp_static_default_params[0]))
227
228static uint8_t * dhcp_default_params = (uint8_t *)dhcp_static_default_params;
229static int	n_dhcp_default_params = N_DHCP_STATIC_DEFAULT_PARAMS;
230
231static uint8_t * dhcp_params = (uint8_t *)dhcp_static_default_params;
232static int	n_dhcp_params = N_DHCP_STATIC_DEFAULT_PARAMS;
233
234void
235dhcp_set_default_parameters(uint8_t * params, int n_params)
236{
237    if (params && n_params) {
238	dhcp_default_params = params;
239	n_dhcp_default_params = n_params;
240    }
241    else {
242	dhcp_default_params = (uint8_t *)dhcp_static_default_params;
243	n_dhcp_default_params = N_DHCP_STATIC_DEFAULT_PARAMS;
244    }
245    dhcp_params = dhcp_default_params;
246    n_dhcp_params = n_dhcp_default_params;
247    return;
248}
249
250static uint8_t *
251S_merge_parameters(uint8_t * params, int n_params, int * n_ret)
252{
253    int		i;
254    uint8_t *	ret = dhcp_default_params;
255    uint8_t *	new = NULL;
256    int		new_end = 0;
257
258    *n_ret = n_dhcp_default_params;
259    if (params == NULL || n_params == 0) {
260	goto done;
261    }
262    /* allocate the worst case size ie. no duplicates */
263    new = (uint8_t *)malloc(n_dhcp_default_params + n_params);
264    if (new == NULL) {
265	goto done;
266    }
267    bcopy(dhcp_default_params, new, n_dhcp_default_params);
268    for (i = 0, new_end = n_dhcp_default_params; i < n_params; i++) {
269	boolean_t	already_there = FALSE;
270	int 		j;
271
272	for (j = 0; j < new_end; j++) {
273	    if (new[j] == params[i]) {
274		/* already in requested parameters list, ignore it */
275		already_there = TRUE;
276		break;
277	    }
278	}
279	if (already_there == FALSE) {
280	    new[new_end++] = params[i];
281	}
282    }
283    if (new_end > n_dhcp_default_params) {
284	ret = new;
285	*n_ret = new_end;
286    }
287    else {
288	free(new);
289	new = NULL;
290    }
291 done:
292    return (ret);
293}
294
295static void
296S_print_char_array(CFMutableStringRef str, uint8_t * params, int n_params)
297{
298    int i;
299
300    for (i = 0; i < n_params; i++) {
301	if (i == 0)
302	    STRING_APPEND(str, "%d", params[i]);
303	else
304	    STRING_APPEND(str, ", %d", params[i]);
305    }
306    return;
307}
308
309void
310dhcp_set_additional_parameters(uint8_t * params, int n_params)
311{
312    if (dhcp_params && dhcp_params != dhcp_default_params) {
313	free(dhcp_params);
314    }
315    dhcp_params = S_merge_parameters(params, n_params, &n_dhcp_params);
316    if (params) {
317	free(params);
318    }
319    if (G_IPConfiguration_verbose) {
320	CFMutableStringRef	str;
321
322	str = CFStringCreateMutable(NULL, 0);
323	S_print_char_array(str, dhcp_params, n_dhcp_params);
324	my_log(LOG_DEBUG, "DHCP requested parameters = { %@ }", str);
325	CFRelease(str);
326    }
327    return;
328}
329
330bool
331dhcp_parameter_is_ok(uint8_t param)
332{
333    int i;
334
335    switch (param) {
336    case dhcptag_lease_time_e:
337    case dhcptag_dhcp_message_type_e:
338    case dhcptag_server_identifier_e:
339    case dhcptag_message_e:
340    case dhcptag_renewal_t1_time_value_e:
341    case dhcptag_rebinding_t2_time_value_e:
342    case dhcptag_vendor_class_identifier_e:
343    case dhcptag_client_identifier_e:
344	return (TRUE);
345    default:
346	break;
347    }
348    for (i = 0; i < n_dhcp_params; i++) {
349	if (dhcp_params[i] == param) {
350	    return (TRUE);
351	}
352    }
353    return (FALSE);
354}
355
356
357static void
358add_computer_name(dhcpoa_t * options_p)
359{
360    /* add the computer name as the host_name option */
361    char *	name = computer_name();
362
363    if (name) {
364	if (dhcpoa_add(options_p, dhcptag_host_name_e, (int)strlen(name), name)
365	    != dhcpoa_success_e) {
366	    my_log(LOG_NOTICE, "make_dhcp_request: couldn't add host_name, %s",
367		   dhcpoa_err(options_p));
368	}
369    }
370    return;
371}
372
373static struct dhcp *
374make_dhcp_request(struct dhcp * request, int pkt_size,
375		  dhcp_msgtype_t msg,
376		  const uint8_t * hwaddr, uint8_t hwtype, uint8_t hwlen,
377		  const void * cid, int cid_len, boolean_t must_broadcast,
378		  dhcpoa_t * options_p)
379{
380    char * 	buf = NULL;
381    uint8_t 	cid_type = 0;
382
383    /* if no client id was specified, use the hardware address */
384    if (cid == NULL || cid_len == 0) {
385	cid = hwaddr;
386	cid_len = hwlen;
387	cid_type = hwtype;
388    }
389
390    bzero(request, pkt_size);
391    request->dp_htype = hwtype;
392    request->dp_op = BOOTREQUEST;
393
394    switch (hwtype) {
395    default:
396    case ARPHRD_ETHER:
397	request->dp_hlen = hwlen;
398	bcopy(hwaddr, request->dp_chaddr, hwlen);
399	break;
400    case ARPHRD_IEEE1394:
401	request->dp_hlen = 0; /* RFC 2855 */
402	if (cid == hwaddr) {
403	    /* if client id is the hardware address, set the right type */
404	    cid_type = ARPHRD_IEEE1394_EUI64;
405	}
406	break;
407    }
408    if (must_broadcast || G_must_broadcast) {
409	request->dp_flags = htons(DHCP_FLAGS_BROADCAST);
410    }
411    bcopy(G_rfc_magic, request->dp_options, sizeof(G_rfc_magic));
412    dhcpoa_init(options_p, request->dp_options + sizeof(G_rfc_magic),
413		pkt_size - sizeof(struct dhcp) - sizeof(G_rfc_magic));
414
415    /* make the request a dhcp message */
416    if (dhcpoa_add_dhcpmsg(options_p, msg) != dhcpoa_success_e) {
417	my_log(LOG_ERR,
418	       "make_dhcp_request: couldn't add dhcp message tag %d, %s", msg,
419	       dhcpoa_err(options_p));
420	goto err;
421    }
422
423    if (msg != dhcp_msgtype_decline_e && msg != dhcp_msgtype_release_e) {
424	u_int16_t	max_message_size = htons(1500); /* max receive size */
425
426	/* add the list of required parameters */
427	if (dhcpoa_add(options_p, dhcptag_parameter_request_list_e,
428			n_dhcp_params, dhcp_params)
429	    != dhcpoa_success_e) {
430	    my_log(LOG_ERR, "make_dhcp_request: "
431		   "couldn't add parameter request list, %s",
432		   dhcpoa_err(options_p));
433	    goto err;
434	}
435	/* add the max message size */
436	if (dhcpoa_add(options_p, dhcptag_max_dhcp_message_size_e,
437		       sizeof(max_message_size), &max_message_size)
438	    != dhcpoa_success_e) {
439	    my_log(LOG_ERR, "make_dhcp_request: "
440		    "couldn't add max message size, %s",
441		    dhcpoa_err(options_p));
442	    goto err;
443	}
444    }
445
446    /* add the client identifier to the request packet */
447    buf = malloc(cid_len + 1);
448    if (buf == NULL) {
449	my_log(LOG_ERR, "make_dhcp_request: malloc failed, %s (%d)",
450	       strerror(errno), errno);
451	goto err;
452    }
453    *buf = cid_type;
454    bcopy(cid, buf + 1, cid_len);
455    if (dhcpoa_add(options_p, dhcptag_client_identifier_e, cid_len + 1, buf)
456	!= dhcpoa_success_e) {
457	free(buf);
458	my_log(LOG_ERR, "make_dhcp_request: "
459	       "couldn't add client identifier, %s",
460	       dhcpoa_err(options_p));
461	goto err;
462    }
463    free(buf);
464    return (request);
465  err:
466    return (NULL);
467}
468
469/*
470 * Function: verify_packet
471 * Purpose:
472 */
473static boolean_t
474verify_packet(bootp_receive_data_t * pkt, u_int32_t xid, interface_t * if_p,
475	      dhcp_msgtype_t * msgtype_p, struct in_addr * server_ip,
476	      boolean_t * is_dhcp)
477{
478    if (dhcp_packet_match((struct bootp *)pkt->data, xid,
479			  (uint8_t) if_link_arptype(if_p),
480			  if_link_address(if_p),
481			  if_link_length(if_p))) {
482	/*
483	 * A BOOTP packet should be one that doesn't contain
484	 * a dhcp message.  Unfortunately, some stupid BOOTP servers
485	 * are unaware of DHCP and RFC-standard options, and simply
486         * echo back what we sent in the options area.  This is the
487	 * reason for checking for DISCOVER, REQUEST and INFORM: they are
488	 * invalid responses in the DHCP protocol, so we assume that
489	 * the server is blindly echoing what we send.
490	 */
491	if (is_dhcp_packet(&pkt->options, msgtype_p) == FALSE
492	    || *msgtype_p == dhcp_msgtype_discover_e
493	    || *msgtype_p == dhcp_msgtype_request_e
494	    || *msgtype_p == dhcp_msgtype_inform_e) {
495	    /* BOOTP packet */
496	    if (G_dhcp_accepts_bootp == FALSE) {
497		return (FALSE);
498	    }
499	    *msgtype_p = dhcp_msgtype_none_e;
500	    *is_dhcp = FALSE;
501	    *server_ip = pkt->data->dp_siaddr;
502	    return (TRUE);
503	}
504	*is_dhcp = TRUE;
505	server_ip->s_addr = 0;
506	(void)get_server_identifier(&pkt->options, server_ip);
507	/* matching DHCP packet */
508	return (TRUE);
509    }
510    return (FALSE);
511}
512
513/**
514 **
515 ** INFORM Functions
516 **
517 */
518static void
519inform_set_dhcp_info(Service_inform_t * inform, dhcp_info_t * dhcp_info_p)
520{
521    bzero(dhcp_info_p, sizeof(*dhcp_info_p));
522    if (inform->saved.pkt_size != 0) {
523	dhcp_info_p->pkt = (uint8_t *)inform->saved.pkt;
524	dhcp_info_p->pkt_size = inform->saved.pkt_size;
525	dhcp_info_p->options = &inform->saved.options;
526    }
527    return;
528}
529
530static void
531inform_publish_success(ServiceRef service_p)
532{
533    dhcp_info_t		dhcp_info;
534    Service_inform_t *	inform;
535
536    inform = (Service_inform_t *)ServiceGetPrivate(service_p);
537    inform_set_dhcp_info(inform, &dhcp_info);
538    ServicePublishSuccessIPv4(service_p, &dhcp_info);
539    return;
540}
541
542
543static void
544inform_cancel_pending_events(ServiceRef service_p)
545{
546    Service_inform_t *	inform;
547
548    inform = (Service_inform_t *)ServiceGetPrivate(service_p);
549    if (inform == NULL)
550	return;
551    if (inform->timer) {
552	timer_cancel(inform->timer);
553    }
554    if (inform->client) {
555	bootp_client_disable_receive(inform->client);
556    }
557    if (inform->arp) {
558	arp_client_cancel(inform->arp);
559    }
560    return;
561}
562
563static void
564inform_inactive(ServiceRef service_p)
565{
566    Service_inform_t *	inform;
567
568    inform = (Service_inform_t *)ServiceGetPrivate(service_p);
569    inform_cancel_pending_events(service_p);
570    service_remove_address(service_p);
571    dhcpol_free(&inform->saved.options);
572    service_publish_failure(service_p, ipconfig_status_media_inactive_e);
573    return;
574}
575
576static void
577inform_failed(ServiceRef service_p, ipconfig_status_t status)
578{
579    inform_cancel_pending_events(service_p);
580    service_publish_failure(service_p, status);
581    return;
582}
583
584static void
585inform_resolve_router_callback(ServiceRef service_p,
586			       router_arp_status_t status);
587
588static void
589inform_resolve_router_retry(void * arg0, void * arg1, void * arg2)
590{
591    Service_inform_t *	inform;
592    ServiceRef 		service_p = (ServiceRef)arg0;
593
594    inform  = (Service_inform_t *)ServiceGetPrivate(service_p);
595    service_resolve_router(service_p, inform->arp,
596			   inform_resolve_router_callback,
597			   inform->saved.our_ip);
598    return;
599}
600
601static void
602inform_resolve_router_callback(ServiceRef service_p,
603			       router_arp_status_t status)
604{
605    Service_inform_t *	inform;
606    struct timeval	tv;
607
608    inform = (Service_inform_t *)ServiceGetPrivate(service_p);
609    switch (status) {
610    case router_arp_status_no_response_e:
611	/* try again in 60 seconds */
612	tv.tv_sec = 60;
613	tv.tv_usec = 0;
614	timer_set_relative(inform->timer, tv,
615			   (timer_func_t *)inform_resolve_router_retry,
616			   service_p, NULL, NULL);
617	if (inform->resolve_router_timed_out) {
618	    break;
619	}
620	/* publish what we have so far */
621	inform->resolve_router_timed_out = TRUE;
622	inform_publish_success(service_p);
623	break;
624    case router_arp_status_success_e:
625	inform->resolve_router_timed_out = FALSE;
626	inform_publish_success(service_p);
627	break;
628    default:
629    case router_arp_status_failed_e:
630	break;
631    }
632}
633
634static void
635inform_success(ServiceRef service_p)
636{
637    Service_inform_t *	inform;
638    void *		option;
639
640    inform = (Service_inform_t *)ServiceGetPrivate(service_p);
641    option = dhcpol_find_with_length(&inform->saved.options,
642				     dhcptag_subnet_mask_e,
643				     sizeof(struct in_addr));
644    if (service_requested_ip_mask(service_p).s_addr == 0
645	&& option != NULL) {
646	inform->our_mask = *((struct in_addr *)option);
647
648	/* reset the interface address with the new mask */
649	(void)service_set_address(service_p,
650				  service_requested_ip_addr(service_p),
651				  inform->our_mask,
652				  G_ip_zeroes);
653    }
654    inform_cancel_pending_events(service_p);
655    inform->resolve_router_timed_out = FALSE;
656    if (service_update_router_address(service_p, &inform->saved.options,
657				      inform->saved.our_ip)
658	&& service_resolve_router(service_p, inform->arp,
659				  inform_resolve_router_callback,
660				  service_requested_ip_addr(service_p))) {
661	/* router resolution started */
662    }
663    else {
664	inform_publish_success(service_p);
665    }
666    return;
667}
668
669static void
670inform_request(ServiceRef service_p, IFEventID_t event_id, void * event_data)
671{
672    absolute_time_t	current_time = timer_current_secs();
673    interface_t *	if_p = service_interface(service_p);
674    Service_inform_t *	inform;
675    ipconfig_status_t	status = ipconfig_status_success_e;
676
677    inform = (Service_inform_t *)ServiceGetPrivate(service_p);
678    switch (event_id) {
679      case IFEventID_start_e: {
680	  dhcpoa_t		options;
681
682	  inform->start_secs = current_time;
683
684	  /* clean-up anything that might have come before */
685	  inform_cancel_pending_events(service_p);
686	  /* ALIGN: txbuf is aligned to sizeof(uint32_t) bytes */
687	  inform->request = make_dhcp_request((struct dhcp *)(void *)inform->txbuf,
688					      sizeof(inform->txbuf),
689					      dhcp_msgtype_inform_e,
690					      if_link_address(if_p),
691					      if_link_arptype(if_p),
692					      if_link_length(if_p),
693					      NULL, 0,
694					      FALSE,
695					      &options);
696	  if (inform->request == NULL) {
697	      my_log(LOG_ERR, "INFORM %s: make_dhcp_request failed",
698		     if_name(if_p));
699	      status = ipconfig_status_allocation_failed_e;
700	      goto error;
701	  }
702	  inform->request->dp_ciaddr = service_requested_ip_addr(service_p);
703	  add_computer_name(&options);
704	  if (dhcpoa_add(&options, dhcptag_end_e, 0, 0)
705	      != dhcpoa_success_e) {
706	      my_log(LOG_ERR, "INFORM %s: failed to terminate options",
707		     if_name(if_p));
708	      status = ipconfig_status_allocation_failed_e;
709	      goto error;
710	  }
711	  inform->request_size = sizeof(*inform->request) + sizeof(G_rfc_magic)
712	      + dhcpoa_used(&options);
713	  if (inform->request_size < sizeof(struct bootp)) {
714	      /* pad out to BOOTP-sized packet */
715	      inform->request_size = sizeof(struct bootp);
716	  }
717	  inform->try = 0;
718	  inform->gathering = FALSE;
719	  inform->wait_secs = G_initial_wait_secs;
720	  bootp_client_enable_receive(inform->client,
721				      (bootp_receive_func_t *)inform_request,
722				      service_p, (void *)IFEventID_data_e);
723	  inform->saved.rating = 0;
724	  inform->xid++;
725	  /* FALL THROUGH */
726      }
727      case IFEventID_timeout_e: {
728	  struct timeval 	tv;
729	  if (inform->gathering == TRUE) {
730	      /* done gathering */
731	      inform_success(service_p);
732	      return;
733	  }
734	  inform->try++;
735	  if (inform->try > 1) {
736	      link_status_t	link_status = service_link_status(service_p);
737
738	      if (link_status.valid
739		  && link_status.active == FALSE) {
740		  inform_inactive(service_p);
741		  break; /* out of switch */
742	      }
743	  }
744	  if (inform->try > (G_max_retries + 1)) {
745	      inform_success(service_p);
746	      break;
747	  }
748	  inform->request->dp_xid = htonl(inform->xid);
749	  inform->request->dp_secs
750	      = htons((uint16_t)(current_time - inform->start_secs));
751
752	  /* send the packet */
753	  if (bootp_client_transmit(inform->client,
754				    G_ip_broadcast, G_ip_zeroes,
755				    G_server_port, G_client_port,
756				    inform->request,
757				    inform->request_size) < 0) {
758	      my_log(LOG_ERR,
759		     "INFORM %s: transmit failed", if_name(if_p));
760	  }
761	  /* wait for responses */
762	  tv.tv_sec = inform->wait_secs;
763	  tv.tv_usec = (suseconds_t)random_range(0, USECS_PER_SEC - 1);
764	  my_log(LOG_DEBUG, "INFORM %s: waiting at %d for %d.%06d",
765		 if_name(if_p),
766		 current_time - inform->start_secs,
767		 tv.tv_sec, tv.tv_usec);
768	  timer_set_relative(inform->timer, tv,
769			     (timer_func_t *)inform_request,
770			     service_p, (void *)IFEventID_timeout_e, NULL);
771	  /* next time wait twice as long */
772	  inform->wait_secs *= 2;
773	  if (inform->wait_secs > G_max_wait_secs) {
774	      inform->wait_secs = G_max_wait_secs;
775	  }
776	  break;
777      }
778      case IFEventID_data_e: {
779	  boolean_t 		is_dhcp = TRUE;
780	  bootp_receive_data_t *pkt = (bootp_receive_data_t *)event_data;
781	  dhcp_msgtype_t	reply_msgtype = dhcp_msgtype_none_e;
782	  struct in_addr	server_ip;
783
784	  if (verify_packet(pkt, inform->xid, if_p, &reply_msgtype,
785			    &server_ip, &is_dhcp) == FALSE) {
786	      /* reject the packet */
787	      break; /* out of switch */
788	  }
789	  if (is_dhcp == FALSE
790	      || (reply_msgtype == dhcp_msgtype_ack_e)) {
791	      int rating = 0;
792
793	      rating = dhcpol_count_params(&pkt->options,
794					   dhcp_params, n_dhcp_params);
795	      /*
796	       * The new packet is "better" than the saved
797	       * packet if:
798	       * - there was no saved packet, or
799	       * - the new packet is a DHCP packet and the saved
800	       *   one is a BOOTP packet or a DHCP packet with
801	       *   a lower rating, or
802	       * - the new packet and the saved packet are both
803	       *   BOOTP but the new one has a higher rating
804	       * All this to allow BOOTP/DHCP interoperability
805	       * ie. we accept a BOOTP response if it's
806	       * the only one we've got.  We expect/favour a DHCP
807	       * response.
808	       */
809	      if (inform->saved.pkt_size == 0
810		  || (is_dhcp == TRUE && (inform->saved.is_dhcp == FALSE
811					  || rating > inform->saved.rating))
812		  || (is_dhcp == FALSE && inform->saved.is_dhcp == FALSE
813		      && rating > inform->saved.rating)) {
814		  dhcpol_free(&inform->saved.options);
815		  bcopy(pkt->data, inform->saved.pkt, pkt->size);
816		  inform->saved.pkt_size = pkt->size;
817		  inform->saved.rating = rating;
818		  /* ALIGN: saved.pkt is at least sizeof(uint32_t) aligned,
819		   * cast ok. */
820		  (void)dhcpol_parse_packet(&inform->saved.options,
821					    (void *)inform->saved.pkt,
822					    inform->saved.pkt_size, NULL);
823		  inform->saved.server_ip = server_ip;
824		  inform->saved.is_dhcp = is_dhcp;
825		  if (is_dhcp && rating == n_dhcp_params) {
826		      inform_success(service_p);
827		      return;
828		  }
829		  if (inform->gathering == FALSE) {
830		      struct timeval t = {0,0};
831		      t.tv_sec = G_gather_secs;
832		      my_log(LOG_DEBUG, "INFORM %s: gathering began at %d",
833			     if_name(if_p),
834			     current_time - inform->start_secs);
835		      inform->gathering = TRUE;
836		      timer_set_relative(inform->timer, t,
837					 (timer_func_t *)inform_request,
838					 service_p, (void *)IFEventID_timeout_e,
839					 NULL);
840		  }
841	      }
842	  }
843	  break;
844      }
845      default:
846	  break;
847    }
848    return;
849 error:
850    inform_failed(service_p, status);
851    return;
852
853}
854
855static void
856inform_start(ServiceRef service_p, IFEventID_t event_id, void * event_data)
857{
858    interface_t *	if_p = service_interface(service_p);
859    Service_inform_t *	inform;
860
861    inform = (Service_inform_t *)ServiceGetPrivate(service_p);
862    switch (event_id) {
863      case IFEventID_start_e: {
864	  inform_cancel_pending_events(service_p);
865
866	  arp_client_probe(inform->arp,
867			   (arp_result_func_t *)inform_start, service_p,
868			   (void *)IFEventID_arp_e, G_ip_zeroes,
869			   service_requested_ip_addr(service_p));
870	  break;
871      }
872      case IFEventID_arp_e: {
873	  link_status_t		link_status;
874	  arp_result_t *	result = (arp_result_t *)event_data;
875
876	  if (result->error) {
877	      my_log(LOG_DEBUG, "INFORM %s: arp probe failed, %s",
878		     if_name(if_p),
879		     arp_client_errmsg(inform->arp));
880	      inform_failed(service_p, ipconfig_status_internal_error_e);
881	      break;
882	  }
883	  else {
884	      if (result->in_use) {
885		  struct in_addr	requested_ip;
886		  char			msg[128];
887		  struct timeval	tv;
888
889		  requested_ip = service_requested_ip_addr(service_p);
890		  snprintf(msg, sizeof(msg),
891			   IP_FORMAT " in use by " EA_FORMAT,
892			   IP_LIST(&requested_ip),
893			   EA_LIST(result->addr.target_hardware));
894		  if (inform->user_warned == FALSE) {
895		      inform->user_warned = TRUE;
896		      ServiceReportIPv4AddressConflict(service_p,
897						       requested_ip);
898		  }
899		  my_log(LOG_ERR, "INFORM %s: %s", if_name(if_p),
900			 msg);
901		  (void)service_remove_address(service_p);
902		  inform_failed(service_p, ipconfig_status_address_in_use_e);
903		  /* try again in a bit */
904		  if (G_manual_conflict_retry_interval_secs > 0) {
905		      tv.tv_sec = G_manual_conflict_retry_interval_secs;
906		      tv.tv_usec = 0;
907		      timer_set_relative(inform->timer, tv,
908					 (timer_func_t *)inform_start,
909					 service_p, IFEventID_start_e, NULL);
910		  }
911		  break;
912	      }
913	  }
914	  link_status = service_link_status(service_p);
915	  if (link_status.valid == TRUE
916	      && link_status.active == FALSE) {
917	      inform_inactive(service_p);
918	      break;
919	  }
920
921	  /* set the primary address */
922	  (void)service_set_address(service_p,
923				    service_requested_ip_addr(service_p),
924				    inform->our_mask,
925				    G_ip_zeroes);
926	  ServiceRemoveAddressConflict(service_p);
927	  inform_request(service_p, IFEventID_start_e, 0);
928	  break;
929      }
930      default: {
931	  break;
932      }
933    }
934    return;
935}
936
937PRIVATE_EXTERN ipconfig_status_t
938inform_thread(ServiceRef service_p, IFEventID_t event_id, void * event_data)
939{
940    interface_t *	if_p = service_interface(service_p);
941    Service_inform_t *	inform;
942    ipconfig_status_t	status = ipconfig_status_success_e;
943
944    inform = (Service_inform_t *)ServiceGetPrivate(service_p);
945    switch (event_id) {
946      case IFEventID_start_e: {
947	  ipconfig_method_data_t *	method_data;
948
949	  method_data = (ipconfig_method_data_t *)event_data;
950	  if (if_flags(if_p) & IFF_LOOPBACK) {
951	      status = ipconfig_status_invalid_operation_e;
952	      break;
953	  }
954	  if (inform != NULL) {
955	      my_log(LOG_ERR, "INFORM %s: re-entering start state",
956		     if_name(if_p));
957	      status = ipconfig_status_internal_error_e;
958	      break;
959	  }
960	  inform = malloc(sizeof(*inform));
961	  if (inform == NULL) {
962	      my_log(LOG_ERR, "INFORM %s: malloc failed",
963		     if_name(if_p));
964	      status = ipconfig_status_allocation_failed_e;
965	      break;
966	  }
967	  ServiceSetPrivate(service_p, inform);
968	  bzero(inform, sizeof(*inform));
969	  dhcpol_init(&inform->saved.options);
970	  service_set_requested_ip_addr(service_p, method_data->manual.addr);
971	  service_set_requested_ip_mask(service_p, method_data->manual.mask);
972	  inform->our_mask = service_requested_ip_mask(service_p);
973	  inform->timer = timer_callout_init();
974	  if (inform->timer == NULL) {
975	      my_log(LOG_ERR, "INFORM %s: timer_callout_init failed",
976		     if_name(if_p));
977	      status = ipconfig_status_allocation_failed_e;
978	      goto stop;
979	  }
980	  inform->client = bootp_client_init(G_bootp_session, if_p);
981	  if (inform->client == NULL) {
982	      my_log(LOG_ERR, "INFORM %s: bootp_client_init failed",
983		     if_name(if_p));
984	      status = ipconfig_status_allocation_failed_e;
985	      goto stop;
986	  }
987	  inform->arp = arp_client_init(G_arp_session, if_p);
988	  if (inform->arp == NULL) {
989	      my_log(LOG_ERR, "INFORM %s: arp_client_init failed",
990		     if_name(if_p));
991	      status = ipconfig_status_allocation_failed_e;
992	      goto stop;
993	  }
994	  my_log(LOG_DEBUG, "INFORM %s: start", if_name(if_p));
995	  inform->xid = arc4random();
996	  inform_start(service_p, IFEventID_start_e, NULL);
997	  break;
998      }
999      case IFEventID_stop_e: {
1000      stop:
1001	  my_log(LOG_DEBUG, "INFORM %s: stop", if_name(if_p));
1002	  if (inform == NULL) { /* already stopped */
1003	      break;
1004	  }
1005	  /* remove IP address */
1006	  service_remove_address(service_p);
1007
1008	  /* clean-up resources */
1009	  if (inform->timer) {
1010	      timer_callout_free(&inform->timer);
1011	  }
1012	  if (inform->client) {
1013	      bootp_client_free(&inform->client);
1014	  }
1015	  if (inform->arp) {
1016	      arp_client_free(&inform->arp);
1017	  }
1018	  dhcpol_free(&inform->saved.options);
1019	  if (inform)
1020	      free(inform);
1021	  ServiceSetPrivate(service_p, NULL);
1022	  break;
1023      }
1024      case IFEventID_change_e: {
1025	  change_event_data_t *   	change_event;
1026	  ipconfig_method_data_t * 	method_data;
1027
1028	  if (inform == NULL) {
1029	      my_log(LOG_DEBUG, "INFORM %s: private data is NULL",
1030		     if_name(if_p));
1031	      return (ipconfig_status_internal_error_e);
1032	  }
1033	  change_event = ((change_event_data_t *)event_data);
1034	  method_data = change_event->method_data;
1035	  change_event->needs_stop = FALSE;
1036	  if (method_data->manual.addr.s_addr
1037	      != service_requested_ip_addr(service_p).s_addr) {
1038	      change_event->needs_stop = TRUE;
1039	  }
1040	  else if (method_data->manual.mask.s_addr != 0
1041		   && (method_data->manual.mask.s_addr
1042		       != service_requested_ip_mask(service_p).s_addr)) {
1043	      service_set_requested_ip_mask(service_p,
1044					    method_data->manual.mask);
1045	      inform->our_mask = method_data->manual.mask;
1046	      (void)service_set_address(service_p,
1047					method_data->manual.addr,
1048					inform->our_mask, G_ip_zeroes);
1049	  }
1050	  return (ipconfig_status_success_e);
1051      }
1052      case IFEventID_arp_collision_e: {
1053	  arp_collision_data_t *	arpc;
1054	  char				msg[128];
1055
1056	  arpc = (arp_collision_data_t *)event_data;
1057	  if (inform == NULL) {
1058	      return (ipconfig_status_internal_error_e);
1059	  }
1060	  if (arpc->ip_addr.s_addr
1061	      != service_requested_ip_addr(service_p).s_addr) {
1062	      break;
1063	  }
1064	  /* defend our address, don't just give it up */
1065	  if (ServiceDefendIPv4Address(service_p, arpc)) {
1066	      break;
1067	  }
1068	  snprintf(msg, sizeof(msg),
1069		   IP_FORMAT " in use by " EA_FORMAT,
1070		   IP_LIST(&arpc->ip_addr),
1071		   EA_LIST(arpc->hwaddr));
1072	  if (inform->user_warned == FALSE) {
1073	      inform->user_warned = TRUE;
1074	      ServiceReportIPv4AddressConflict(service_p,
1075					       arpc->ip_addr);
1076	  }
1077	  my_log(LOG_ERR, "INFORM %s: %s", if_name(if_p),
1078		 msg);
1079	  break;
1080      }
1081      case IFEventID_renew_e:
1082      case IFEventID_link_status_changed_e: {
1083	  link_status_t	link_status;
1084
1085	  if (inform == NULL)
1086	      return (ipconfig_status_internal_error_e);
1087
1088	  inform->user_warned = FALSE;
1089	  link_status = service_link_status(service_p);
1090	  if (link_status.valid == TRUE) {
1091	      if (link_status.active == TRUE) {
1092		  inform_start(service_p, IFEventID_start_e, 0);
1093	      }
1094	      else if (event_id == IFEventID_link_status_changed_e) {
1095		  inform_cancel_pending_events(service_p);
1096	      }
1097	  }
1098	  break;
1099      }
1100      case IFEventID_link_timer_expired_e:
1101	  inform_inactive(service_p);
1102	  break;
1103      case IFEventID_get_dhcp_info_e: {
1104	  if (ServiceGetActiveIPAddress(service_p).s_addr == 0
1105	      || inform->saved.pkt_size == 0) {
1106	      break;
1107	  }
1108	  inform_set_dhcp_info(inform, (dhcp_info_t *)event_data);
1109	  break;
1110      }
1111      default:
1112	  break;
1113    } /* switch (event_id) */
1114    return (status);
1115}
1116
1117/**
1118 **
1119 ** DHCP Functions
1120 **
1121 */
1122
1123/*
1124 * Function: S_time_in_future
1125 * Purpose:
1126 *   Returns whether the given time is in the future by at least the
1127 *   time interval specified by 'time_interval'.
1128 */
1129INLINE boolean_t
1130S_time_in_future(absolute_time_t current_time,
1131		 absolute_time_t the_time,
1132		 uint32_t time_interval)
1133{
1134    return (current_time < the_time
1135	    && (the_time - current_time) >= time_interval);
1136}
1137
1138/*
1139 * Function: S_min_wake_ok
1140 * Purpose:
1141 *   Returns whether the given time is in the future by at least the
1142 *   the minimum wake interval.
1143 */
1144INLINE boolean_t
1145S_min_wake_ok(absolute_time_t current_time, absolute_time_t the_time)
1146{
1147    return (S_time_in_future(current_time, the_time,
1148			     G_min_wake_interval_secs));
1149}
1150
1151/*
1152 * Function: S_short_wake_ok
1153 * Purpose:
1154 *   Returns whether the given time is in the future by at least the minimum
1155 *   schedulable short wake.
1156 */
1157INLINE boolean_t
1158S_short_wake_ok(absolute_time_t current_time, absolute_time_t the_time)
1159{
1160    return (S_time_in_future(current_time, the_time,
1161			     G_min_short_wake_interval_secs));
1162}
1163
1164static void
1165dhcp_set_dhcp_info(Service_dhcp_t * dhcp, dhcp_info_t * dhcp_info_p)
1166{
1167    dhcp_info_p->pkt = (uint8_t *)dhcp->saved.pkt;
1168    dhcp_info_p->pkt_size = dhcp->saved.pkt_size;
1169    dhcp_info_p->options = &dhcp->saved.options;
1170    dhcp_info_p->lease_start = dhcp->lease.start;
1171    dhcp_info_p->lease_expiration = dhcp->lease.expiration;
1172    return;
1173}
1174
1175static void
1176dhcp_publish_success(ServiceRef service_p)
1177{
1178    Service_dhcp_t *	dhcp = (Service_dhcp_t *)ServiceGetPrivate(service_p);
1179    dhcp_info_t		dhcp_info;
1180
1181    dhcp_set_dhcp_info(dhcp, &dhcp_info);
1182    ServicePublishSuccessIPv4(service_p, &dhcp_info);
1183    return;
1184}
1185
1186static void
1187dhcp_cancel_pending_events(ServiceRef service_p)
1188{
1189    Service_dhcp_t *	dhcp = (Service_dhcp_t *)ServiceGetPrivate(service_p);
1190
1191    if (dhcp == NULL)
1192	return;
1193    timer_cancel(dhcp->timer);
1194    bootp_client_disable_receive(dhcp->client);
1195    arp_client_cancel(dhcp->arp);
1196    return;
1197}
1198
1199
1200static void
1201dhcp_failed(ServiceRef service_p, ipconfig_status_t status)
1202{
1203    Service_dhcp_t *	dhcp = (Service_dhcp_t *)ServiceGetPrivate(service_p);
1204
1205    dhcp_cancel_pending_events(service_p);
1206
1207    service_disable_autoaddr(service_p);
1208    dhcpol_free(&dhcp->saved.options);
1209    service_remove_address(service_p);
1210    service_publish_failure(service_p, status);
1211    dhcp->state = dhcp_cstate_none_e;
1212    dhcp->allow_wake_with_short_lease = FALSE;
1213    return;
1214}
1215
1216static void
1217dhcp_inactive(ServiceRef service_p)
1218{
1219    Service_dhcp_t * 	dhcp = (Service_dhcp_t *)ServiceGetPrivate(service_p);
1220
1221    dhcp_cancel_pending_events(service_p);
1222    /*
1223     * Set the status here so that the link-local service will disappear
1224     * when we call service_remove_address.
1225     */
1226    ServiceSetStatus(service_p, ipconfig_status_media_inactive_e);
1227    service_remove_address(service_p);
1228    service_disable_autoaddr(service_p);
1229    service_publish_failure(service_p, ipconfig_status_media_inactive_e);
1230    dhcp->state = dhcp_cstate_none_e;
1231    dhcp->allow_wake_with_short_lease = FALSE;
1232    return;
1233}
1234
1235static void
1236dhcp_set_lease_params(ServiceRef service_p, char * descr, boolean_t is_dhcp,
1237		      absolute_time_t lease_start,
1238		      dhcp_lease_time_t lease, dhcp_lease_time_t t1,
1239		      dhcp_lease_time_t t2)
1240{
1241    Service_dhcp_t * dhcp = (Service_dhcp_t *)ServiceGetPrivate(service_p);
1242    interface_t *    if_p = service_interface(service_p);
1243
1244    dhcp->lease.start = lease_start;
1245    if (is_dhcp == FALSE) {
1246	dhcp->lease.length = DHCP_INFINITE_LEASE;
1247    }
1248    else {
1249	dhcp->lease.length = lease;
1250    }
1251
1252    if (dhcp->lease.length == DHCP_INFINITE_LEASE) {
1253	dhcp->lease.t1 = dhcp->lease.t2 = dhcp->lease.expiration = 0;
1254    }
1255    else {
1256	dhcp->lease.expiration = dhcp->lease.start + dhcp->lease.length;
1257	dhcp->lease.t1 = dhcp->lease.start + t1;
1258	dhcp->lease.t2 = dhcp->lease.start + t2;
1259    }
1260    my_log(LOG_DEBUG,
1261	   "DHCP %s: %s "
1262	   "lease = { start 0x%lx, t1 0x%lx, t2 0x%lx, expiration 0x%lx }",
1263	   if_name(if_p), descr, dhcp->lease.start,
1264	   dhcp->lease.t1, dhcp->lease.t2, dhcp->lease.expiration);
1265    return;
1266}
1267
1268/*
1269 * Function: dhcp_adjust_lease_info
1270 * Purpose:
1271 *    Apply the delta to the lease information: start, t1, t2, and expiration.
1272 */
1273static void
1274dhcp_adjust_lease_info(ServiceRef service_p, absolute_time_t delta)
1275{
1276    Service_dhcp_t * 	dhcp = (Service_dhcp_t *)ServiceGetPrivate(service_p);
1277    interface_t *    	if_p = service_interface(service_p);
1278    lease_info_t *   	info_p = &dhcp->lease;
1279
1280    info_p->expiration += delta;
1281    info_p->start += delta;
1282    info_p->t1 += delta;
1283    info_p->t2 += delta;
1284    my_log(LOG_DEBUG,
1285	   "DHCP %s: adjusted lease by %d seconds", if_name(if_p), delta);
1286    return;
1287}
1288
1289
1290STATIC void
1291dhcp_log_lease(int level, const char * ifname, absolute_time_t current_time,
1292	       lease_info_t * lease_p)
1293
1294{
1295    my_log(level,
1296	   "DHCP %s: now = 0x%lx, "
1297	   "lease = { start 0x%lx, t1 0x%lx, t2 0x%lx, expiration 0x%lx }",
1298	   ifname, current_time,
1299	   lease_p->start, lease_p->t1, lease_p->t2, lease_p->expiration);
1300    return;
1301}
1302
1303static void
1304get_client_id(Service_dhcp_t * dhcp, interface_t * if_p, const void * * cid_p,
1305	      uint8_t * cid_type_p, int * cid_length_p)
1306{
1307    if (dhcp->client_id != NULL) {
1308	*cid_type_p = 0;
1309	*cid_p = dhcp->client_id;
1310	*cid_length_p = dhcp->client_id_len;
1311    }
1312    else {
1313	*cid_type_p = if_link_arptype(if_p);
1314	*cid_p = if_link_address(if_p);
1315	*cid_length_p = if_link_length(if_p);
1316    }
1317    return;
1318}
1319
1320static void
1321_dhcp_lease_save(ServiceRef service_p, absolute_time_t lease_start,
1322		 const uint8_t * pkt, int pkt_size, boolean_t write_lease)
1323{
1324    Service_dhcp_t *	dhcp = (Service_dhcp_t *)ServiceGetPrivate(service_p);
1325    interface_t *	if_p = service_interface(service_p);
1326    const void *	cid;
1327    uint8_t 		cid_type;
1328    int 		cid_length;
1329    struct in_addr	router_ip = { 0 };
1330    const uint8_t * 	router_hwaddr = NULL;
1331    int 		router_hwaddr_length = 0;
1332
1333    if (service_router_is_iaddr_valid(service_p)) {
1334	router_ip = service_router_iaddr(service_p);
1335	if (service_router_is_hwaddr_valid(service_p)) {
1336	    /* we have the router h/w address, save it too */
1337	    router_hwaddr = service_router_hwaddr(service_p);
1338	    router_hwaddr_length = if_link_length(if_p);
1339	}
1340    }
1341    get_client_id(dhcp, if_p, &cid, &cid_type, &cid_length);
1342    DHCPLeaseListUpdateLease(&dhcp->lease_list, dhcp->saved.our_ip,
1343			     router_ip, router_hwaddr, router_hwaddr_length,
1344			     dhcp->lease.start, dhcp->lease.length,
1345			     pkt, pkt_size, ServiceGetSSID(service_p));
1346    if (write_lease) {
1347	DHCPLeaseListWrite(&dhcp->lease_list,
1348			   if_name(if_p), cid_type, cid, cid_length);
1349    }
1350    return;
1351}
1352
1353static void
1354_dhcp_lease_clear(ServiceRef service_p, bool nak)
1355{
1356    Service_dhcp_t *	dhcp = (Service_dhcp_t *)ServiceGetPrivate(service_p);
1357    interface_t *	if_p = service_interface(service_p);
1358    struct in_addr	router_ip = { 0 };
1359    const uint8_t * 	router_hwaddr = NULL;
1360    int 		router_hwaddr_length = 0;
1361
1362    if (service_router_is_iaddr_valid(service_p)) {
1363	router_ip = service_router_iaddr(service_p);
1364	if (service_router_is_hwaddr_valid(service_p)) {
1365	    router_hwaddr = service_router_hwaddr(service_p);
1366	    router_hwaddr_length = if_link_length(if_p);
1367	}
1368    }
1369    if (nak == FALSE
1370	|| router_ip.s_addr == 0
1371	|| router_hwaddr_length == 0) {
1372	const void *	cid;
1373	uint8_t		cid_type;
1374	int 		cid_length;
1375
1376	get_client_id(dhcp, if_p, &cid, &cid_type, &cid_length);
1377	DHCPLeaseListRemoveLease(&dhcp->lease_list,
1378				 dhcp->saved.our_ip, router_ip,
1379				 router_hwaddr, router_hwaddr_length);
1380	DHCPLeaseListWrite(&dhcp->lease_list,
1381			   if_name(if_p), cid_type, cid, cid_length);
1382    }
1383    else {
1384	DHCPLeaseRef	lease_p;
1385	int		where;
1386
1387	where = DHCPLeaseListFindLease(&dhcp->lease_list,
1388				       dhcp->saved.our_ip,
1389				       router_ip,
1390				       router_hwaddr,
1391				       router_hwaddr_length);
1392	if (where != -1) {
1393	    lease_p = DHCPLeaseListElement(&dhcp->lease_list, where);
1394	    DHCPLeaseSetNAK(lease_p, TRUE);
1395	}
1396    }
1397    return;
1398}
1399
1400static boolean_t
1401switch_to_lease(ServiceRef service_p, DHCPLeaseRef lease_p)
1402{
1403    Service_dhcp_t *	dhcp = (Service_dhcp_t *)ServiceGetPrivate(service_p);
1404    interface_t *	if_p = service_interface(service_p);
1405    dhcp_lease_time_t	lease_time;
1406    dhcp_lease_time_t	t1_time;
1407    dhcp_lease_time_t	t2_time;
1408
1409    if (lease_p->our_ip.s_addr == dhcp->saved.our_ip.s_addr) {
1410	if (ip_is_private(lease_p->our_ip) == FALSE) {
1411	    /* same lease */
1412	    if (G_IPConfiguration_verbose) {
1413		my_log(LOG_DEBUG, "DHCP %s: switch_to_lease returns FALSE, "
1414		       "public IP is the same", if_name(if_p));
1415	    }
1416	    return (FALSE);
1417	}
1418	if (service_router_is_iaddr_valid(service_p)
1419	    && service_router_is_hwaddr_valid(service_p)
1420	    && (lease_p->router_ip.s_addr
1421		== service_router_iaddr(service_p).s_addr)
1422	    && (bcmp(lease_p->router_hwaddr,
1423		     service_router_hwaddr(service_p),
1424		     if_link_length(if_p)) == 0)) {
1425	    if (G_IPConfiguration_verbose) {
1426		my_log(LOG_DEBUG, "DHCP %s: switch_to_lease returns FALSE, "
1427		       "private IP has same router", if_name(if_p));
1428	    }
1429	    return (FALSE);
1430	}
1431    }
1432    /* make sure we stop using the old IP address */
1433    (void)service_remove_address(service_p);
1434    if (lease_p->pkt_length > sizeof(dhcp->saved.pkt)) {
1435	dhcp->saved.pkt_size = sizeof(dhcp->saved.pkt);
1436    }
1437    else {
1438	dhcp->saved.pkt_size = lease_p->pkt_length;
1439    }
1440    bcopy(lease_p->pkt, dhcp->saved.pkt, dhcp->saved.pkt_size);
1441    dhcpol_free(&dhcp->saved.options);
1442    /* ALIGN: saved.pkt is uint32_t aligned, cast ok */
1443    (void)dhcpol_parse_packet(&dhcp->saved.options,
1444			      (void *)dhcp->saved.pkt,
1445			      dhcp->saved.pkt_size, NULL);
1446    dhcp_get_lease_from_options(&dhcp->saved.options, &lease_time,
1447				&t1_time, &t2_time);
1448    dhcp_set_lease_params(service_p, "RECOVERED",
1449			  TRUE, lease_p->lease_start, lease_time,
1450			  t1_time, t2_time);
1451    dhcp->lease.valid = TRUE;
1452    dhcp->saved.rating = 0;
1453    dhcp->saved.our_ip = lease_p->our_ip;
1454    get_server_identifier(&dhcp->saved.options, &dhcp->saved.server_ip);
1455    dhcp->saved.is_dhcp = TRUE;
1456    service_router_clear(service_p);
1457    if (lease_p->router_ip.s_addr != 0) {
1458	service_router_set_iaddr(service_p, lease_p->router_ip);
1459	service_router_set_iaddr_valid(service_p);
1460	if (lease_p->router_hwaddr_length > 0) {
1461	    int		len;
1462
1463	    len = lease_p->router_hwaddr_length;
1464	    if (len > service_router_hwaddr_size(service_p)) {
1465		len = service_router_hwaddr_size(service_p);
1466	    }
1467	    bcopy(lease_p->router_hwaddr,
1468		  service_router_hwaddr(service_p), len);
1469	    service_router_set_hwaddr_valid(service_p);
1470	}
1471    }
1472    if (G_IPConfiguration_verbose) {
1473	my_log(LOG_DEBUG, "DHCP %s: switched to lease for IP " IP_FORMAT,
1474	       if_name(if_p), IP_LIST(&lease_p->our_ip));
1475    }
1476    return (TRUE);
1477}
1478
1479static boolean_t
1480recover_lease(ServiceRef service_p, struct in_addr * our_ip)
1481{
1482    const void *	cid;
1483    uint8_t		cid_type;
1484    int			cid_length;
1485    int			count;
1486    Service_dhcp_t *	dhcp = (Service_dhcp_t *)ServiceGetPrivate(service_p);
1487    interface_t *	if_p = service_interface(service_p);
1488    DHCPLeaseRef	lease_p;
1489
1490    /* read the DHCP lease */
1491    get_client_id(dhcp, if_p, &cid, &cid_type, &cid_length);
1492    DHCPLeaseListRead(&dhcp->lease_list, if_name(if_p),
1493		      if_is_wireless(if_p),
1494		      cid_type, cid, cid_length);
1495    count = DHCPLeaseListCount(&dhcp->lease_list);
1496    if (count == 0) {
1497	goto failed;
1498    }
1499    lease_p = DHCPLeaseListElement(&dhcp->lease_list, count - 1);
1500    (void)switch_to_lease(service_p, lease_p);
1501    *our_ip = lease_p->our_ip;
1502    if (G_IPConfiguration_verbose) {
1503	my_log(LOG_DEBUG, "DHCP %s: recovered lease for IP " IP_FORMAT,
1504	       if_name(if_p), IP_LIST(our_ip));
1505    }
1506    return (TRUE);
1507
1508 failed:
1509    return (FALSE);
1510}
1511
1512static boolean_t
1513dhcp_check_lease(ServiceRef service_p, absolute_time_t current_time)
1514{
1515    Service_dhcp_t *	dhcp = (Service_dhcp_t *)ServiceGetPrivate(service_p);
1516
1517    if (dhcp->lease.valid) {
1518	if (dhcp->lease.length != DHCP_INFINITE_LEASE
1519	    && (current_time >= dhcp->lease.expiration)) {
1520	    dhcp->lease.valid = FALSE;
1521	    service_router_clear(service_p);
1522	    (void)service_remove_address(service_p);
1523	    service_publish_failure(service_p,
1524				    ipconfig_status_media_inactive_e);
1525	}
1526    }
1527    return (dhcp->lease.valid);
1528}
1529
1530static void
1531dhcp_check_link(ServiceRef service_p, IFEventID_t event_id)
1532{
1533    absolute_time_t 	current_time = timer_current_secs();
1534    Service_dhcp_t *	dhcp = (Service_dhcp_t *)ServiceGetPrivate(service_p);
1535    link_status_t	link_status = service_link_status(service_p);
1536
1537    if (link_status.valid == FALSE
1538	|| link_status.active == TRUE) {
1539	dhcp->conflicting_address.s_addr = 0;
1540	dhcp->user_warned = FALSE;
1541	if (dhcp_check_lease(service_p, current_time)) {
1542	    /* try same address */
1543	    if (event_id == IFEventID_renew_e
1544		|| dhcp->state != dhcp_cstate_init_reboot_e
1545		|| dhcp->try != 1) {
1546		struct in_addr	our_ip;
1547
1548		our_ip = dhcp->saved.our_ip;
1549		dhcp_init_reboot(service_p, IFEventID_start_e,
1550				 &our_ip);
1551	    }
1552	    /* we're already in the init-reboot state */
1553	    return;
1554	}
1555	if (event_id == IFEventID_renew_e
1556	    || dhcp->state != dhcp_cstate_init_e
1557	    || dhcp->try != 1) {
1558	    dhcp_init(service_p, IFEventID_start_e, NULL);
1559	    return;
1560	}
1561    }
1562    else {
1563	/* ensure that we'll retry when the link goes back up */
1564	dhcp->try = 0;
1565	dhcp->state = dhcp_cstate_none_e;
1566	dhcp_cancel_pending_events(service_p);
1567    }
1568    return;
1569}
1570
1571INLINE boolean_t
1572S_dhcp_cstate_is_bound_renew_or_rebind(dhcp_cstate_t state)
1573{
1574    boolean_t	ret;
1575
1576    switch (state) {
1577    case dhcp_cstate_bound_e:
1578    case dhcp_cstate_renew_e:
1579    case dhcp_cstate_rebind_e:
1580	ret = TRUE;
1581	break;
1582    default:
1583	ret = FALSE;
1584	break;
1585    }
1586    return (ret);
1587}
1588
1589STATIC void
1590dhcp_handle_active_during_sleep(ServiceRef service_p,
1591				active_during_sleep_t * active_p)
1592{
1593    Service_dhcp_t *	dhcp = (Service_dhcp_t *)ServiceGetPrivate(service_p);
1594    interface_t *	if_p = service_interface(service_p);
1595    absolute_time_t	wake_time = 0;
1596
1597    if (active_p->requested
1598	&& ServiceGetActiveIPAddress(service_p).s_addr != 0
1599	&& dhcp->lease.valid
1600	&& dhcp->lease.expiration != 0) {
1601	/* try to schedule a wake */
1602	absolute_time_t 	current_time;
1603	const char *		reason = "";
1604
1605	current_time = timer_current_secs();
1606	if (current_time >= dhcp->lease.expiration) {
1607	    /* expired lease, this should not happen */
1608	    my_log_fl(LOG_ERR,
1609		      "DHCP %s: lease is already expired",
1610		      if_name(if_p));
1611	    reason = "lease is expired";
1612	}
1613	else if (S_min_wake_ok(current_time, dhcp->renew_rebind_time)) {
1614	    wake_time = dhcp->renew_rebind_time;
1615	    reason = "wake at renew_rebind_time";
1616	}
1617	else if (S_min_wake_ok(current_time, dhcp->lease.t2)) {
1618	    wake_time = dhcp->lease.t2;
1619	    reason = "wake at t2";
1620	}
1621	else if (dhcp->allow_wake_with_short_lease
1622		 && S_short_wake_ok(current_time, dhcp->lease.t2)) {
1623	    wake_time = dhcp->lease.t2;
1624	    reason = "wake at t2 (allow short first wake)";
1625	}
1626	else if (S_min_wake_ok(current_time, dhcp->lease.expiration)) {
1627	    wake_time = current_time + G_min_wake_interval_secs;
1628	    reason = "wake in min_interval";
1629	}
1630	else {
1631	    reason = "expiration is too soon";
1632	}
1633	if (wake_time == 0) {
1634	    my_log(LOG_NOTICE,
1635		   "DHCP %s: can't schedule wake-up, %s",
1636		   if_name(if_p), reason);
1637	    dhcp_log_lease(LOG_NOTICE, if_name(if_p),
1638			   current_time, &dhcp->lease);
1639	    active_p->supported = FALSE;
1640	    wake_time = 0;
1641	}
1642	else {
1643	    my_log(LOG_DEBUG,
1644		   "DHCP %s: next wake-up at 0x%lx, %s",
1645		   if_name(if_p), wake_time, reason);
1646	    dhcp_log_lease(LOG_DEBUG, if_name(if_p),
1647			   current_time, &dhcp->lease);
1648	}
1649    }
1650    if (wake_time != 0) {
1651	if (wake_time == dhcp->wake_time) {
1652	    /* wake already scheduled */
1653	    my_log(LOG_DEBUG,
1654		   "DHCP %s: wake at 0x%lx already scheduled",
1655		   if_name(if_p), wake_time);
1656	}
1657	else {
1658	    CFDateRef		date;
1659	    IOReturn		status;
1660	    CFStringRef		wake_id;
1661
1662	    wake_id = ServiceCopyWakeID(service_p);
1663	    if (dhcp->wake_time != 0) {
1664		/* unschedule old wake up time */
1665		date = CFDateCreate(NULL, dhcp->wake_time);
1666		(void)IOPMCancelScheduledPowerEvent(date, wake_id,
1667						    CFSTR(kIOPMAutoWake));
1668		my_log(LOG_DEBUG,
1669		       "DHCP %s: unscheduled old wake at %@ (0x%lx)",
1670		       if_name(if_p), date, dhcp->wake_time);
1671		CFRelease(date);
1672	    }
1673
1674	    /* schedule new wake up time */
1675	    date = CFDateCreate(NULL, wake_time);
1676	    status = IOPMSchedulePowerEvent(date, wake_id,
1677					    CFSTR(kIOPMAutoWake));
1678	    CFRelease(wake_id);
1679	    if (status != kIOReturnSuccess) {
1680		my_log(LOG_ERR,
1681		       "DHCP %s: failed to schedule wake at %@ (0x%lx)",
1682		       if_name(if_p), date, wake_time);
1683		dhcp->wake_time = 0;
1684		active_p->supported = FALSE;
1685	    }
1686	    else {
1687		my_log(LOG_DEBUG,
1688		       "DHCP %s: scheduled wake at %@ (0x%lx)",
1689		       if_name(if_p), date, wake_time);
1690		dhcp->wake_time = wake_time;
1691	    }
1692	    CFRelease(date);
1693	}
1694    }
1695    else if (dhcp->wake_time != 0) {
1696	CFDateRef			date;
1697	CFStringRef		wake_id;
1698
1699	/* unschedule existing wake time */
1700	wake_id = ServiceCopyWakeID(service_p);
1701	date = CFDateCreate(NULL, dhcp->wake_time);
1702	(void)IOPMCancelScheduledPowerEvent(date, wake_id,
1703					    CFSTR(kIOPMAutoWake));
1704	CFRelease(wake_id);
1705	my_log(LOG_DEBUG,
1706	       "DHCP %s: unscheduled old wake at %@ (0x%lx)",
1707	       if_name(if_p), date, dhcp->wake_time);
1708	CFRelease(date);
1709	dhcp->wake_time = 0;
1710    }
1711    return;
1712}
1713
1714PRIVATE_EXTERN ipconfig_status_t
1715dhcp_thread(ServiceRef service_p, IFEventID_t event_id, void * event_data)
1716{
1717    Service_dhcp_t *	dhcp = (Service_dhcp_t *)ServiceGetPrivate(service_p);
1718    interface_t *	if_p = service_interface(service_p);
1719    ipconfig_status_t	status = ipconfig_status_success_e;
1720
1721    switch (event_id) {
1722      case IFEventID_start_e: {
1723	  ipconfig_method_data_t *	method_data;
1724	  struct in_addr		our_ip = { 0 };
1725
1726	  if (if_flags(if_p) & IFF_LOOPBACK) {
1727	      status = ipconfig_status_invalid_operation_e;
1728	      break;
1729	  }
1730	  if (dhcp != NULL) {
1731	      my_log(LOG_ERR, "DHCP %s: re-entering start state",
1732		     if_name(if_p));
1733	      status = ipconfig_status_internal_error_e;
1734	      break;
1735	  }
1736	  dhcp = malloc(sizeof(*dhcp));
1737	  if (dhcp == NULL) {
1738	      my_log(LOG_ERR, "DHCP %s: malloc failed",
1739		     if_name(if_p));
1740	      status = ipconfig_status_allocation_failed_e;
1741	      break;
1742	  }
1743	  bzero(dhcp, sizeof(*dhcp));
1744	  dhcp->must_broadcast = (if_link_arptype(if_p) == ARPHRD_IEEE1394);
1745	  dhcpol_init(&dhcp->saved.options);
1746	  ServiceSetPrivate(service_p, dhcp);
1747
1748	  dhcp->lease.valid = FALSE;
1749	  service_router_clear(service_p);
1750	  dhcp->state = dhcp_cstate_none_e;
1751	  dhcp->timer = timer_callout_init();
1752	  if (dhcp->timer == NULL) {
1753	      my_log(LOG_ERR, "DHCP %s: timer_callout_init failed",
1754		     if_name(if_p));
1755	      status = ipconfig_status_allocation_failed_e;
1756	      goto stop;
1757	  }
1758	  (void)service_enable_autoaddr(service_p);
1759	  dhcp->client = bootp_client_init(G_bootp_session, if_p);
1760	  if (dhcp->client == NULL) {
1761	      my_log(LOG_ERR, "DHCP %s: bootp_client_init failed",
1762		     if_name(if_p));
1763	      status = ipconfig_status_allocation_failed_e;
1764	      goto stop;
1765	  }
1766	  dhcp->arp = arp_client_init(G_arp_session, if_p);
1767	  if (dhcp->arp == NULL) {
1768	      my_log(LOG_ERR, "DHCP %s: arp_client_init failed",
1769		     if_name(if_p));
1770	      status = ipconfig_status_allocation_failed_e;
1771	      goto stop;
1772	  }
1773	  method_data = (ipconfig_method_data_t *)event_data;
1774	  if (method_data != NULL && method_data->dhcp.client_id_len > 0) {
1775	      dhcp->client_id_len = method_data->dhcp.client_id_len;
1776	      dhcp->client_id = malloc(dhcp->client_id_len);
1777	      if (dhcp->client_id == NULL) {
1778		  my_log(LOG_ERR, "DHCP %s: malloc client ID failed",
1779			 if_name(if_p));
1780		  status = ipconfig_status_allocation_failed_e;
1781		  goto stop;
1782	      }
1783	      bcopy(method_data->dhcp.client_id,
1784		    dhcp->client_id, dhcp->client_id_len);
1785	  }
1786	  my_log(LOG_DEBUG, "DHCP %s: start", if_name(if_p));
1787	  dhcp->xid = arc4random();
1788	  DHCPLeaseListInit(&dhcp->lease_list);
1789	  if (ServiceIsNetBoot(service_p)
1790	      || recover_lease(service_p, &our_ip)) {
1791	      dhcp_init_reboot(service_p, IFEventID_start_e, &our_ip);
1792	  }
1793	  else {
1794	      dhcp_init(service_p, IFEventID_start_e, NULL);
1795	  }
1796	  break;
1797      }
1798      case IFEventID_stop_e: {
1799      stop:
1800	  my_log(LOG_DEBUG, "DHCP %s: stop", if_name(if_p));
1801	  if (dhcp == NULL) {
1802	      my_log(LOG_DEBUG, "DHCP %s: already stopped",
1803		     if_name(if_p));
1804	      status = ipconfig_status_internal_error_e; /* shouldn't happen */
1805	      break;
1806	  }
1807	  if (event_id == IFEventID_stop_e) {
1808	      (void)dhcp_release(service_p);
1809	  }
1810
1811	  /* remove IP address */
1812	  service_remove_address(service_p);
1813
1814	  service_disable_autoaddr(service_p);
1815
1816	  /* clean-up resources */
1817	  if (dhcp->timer) {
1818	      timer_callout_free(&dhcp->timer);
1819	  }
1820	  if (dhcp->client) {
1821	      bootp_client_free(&dhcp->client);
1822	  }
1823	  if (dhcp->arp) {
1824	      arp_client_free(&dhcp->arp);
1825	  }
1826	  if (dhcp->client_id) {
1827	      free(dhcp->client_id);
1828	      dhcp->client_id = NULL;
1829	  }
1830	  DHCPLeaseListFree(&dhcp->lease_list);
1831	  dhcpol_free(&dhcp->saved.options);
1832	  if (dhcp)
1833	      free(dhcp);
1834	  ServiceSetPrivate(service_p, NULL);
1835	  break;
1836      }
1837      case IFEventID_change_e: {
1838	  change_event_data_t *		change_event;
1839	  ipconfig_method_data_t *	method_data;
1840
1841	  if (dhcp == NULL) {
1842	      my_log(LOG_DEBUG, "DHCP %s: private data is NULL",
1843		     if_name(if_p));
1844	      return (ipconfig_status_internal_error_e);
1845	  }
1846	  change_event = (change_event_data_t *)event_data;
1847	  method_data = change_event->method_data;
1848	  change_event->needs_stop = FALSE;
1849	  if (method_data != NULL && method_data->dhcp.client_id_len > 0) {
1850	      if (dhcp->client_id == NULL
1851		  || dhcp->client_id_len != method_data->dhcp.client_id_len
1852		  || bcmp(dhcp->client_id, method_data->dhcp.client_id,
1853			  dhcp->client_id_len)) {
1854		  change_event->needs_stop = TRUE;
1855	      }
1856	  }
1857	  else {
1858	      if (dhcp->client_id != NULL) {
1859		  change_event->needs_stop = TRUE;
1860	      }
1861	  }
1862	  return (ipconfig_status_success_e);
1863      }
1864      case IFEventID_arp_collision_e: {
1865	  arp_collision_data_t *	arpc;
1866	  struct timeval 		tv;
1867
1868	  arpc = (arp_collision_data_t *)event_data;
1869
1870	  if (dhcp == NULL) {
1871	      return (ipconfig_status_internal_error_e);
1872	  }
1873	  if (dhcp->disable_arp_collision_detection
1874	      || (ServiceGetActiveIPAddress(service_p).s_addr
1875		  != dhcp->saved.our_ip.s_addr)
1876	      || arpc->ip_addr.s_addr != dhcp->saved.our_ip.s_addr) {
1877	      break;
1878	  }
1879
1880	  /* defend our address, don't just give it up */
1881	  if (ServiceDefendIPv4Address(service_p, arpc)) {
1882	      break;
1883	  }
1884
1885	  /* address conflict, give up the address */
1886	  my_log(LOG_ERR, "DHCP %s: "
1887		 IP_FORMAT " in use by " EA_FORMAT ", DHCP Server "
1888		 IP_FORMAT,
1889		 if_name(if_p),
1890		 IP_LIST(&dhcp->saved.our_ip),
1891		 EA_LIST(arpc->hwaddr),
1892		 IP_LIST(&dhcp->saved.server_ip));
1893	  if (dhcp->state != dhcp_cstate_init_reboot_e) {
1894	      /* don't bother reporting it */
1895	      ServiceReportIPv4AddressConflict(service_p,
1896					       dhcp->saved.our_ip);
1897	  }
1898	  _dhcp_lease_clear(service_p, FALSE);
1899	  service_publish_failure(service_p,
1900				  ipconfig_status_address_in_use_e);
1901	  if (dhcp->saved.is_dhcp) {
1902	      dhcp_decline(service_p, IFEventID_start_e, NULL);
1903	      break;
1904	  }
1905	  dhcp_cancel_pending_events(service_p);
1906	  (void)service_disable_autoaddr(service_p);
1907	  dhcp->saved.our_ip.s_addr = 0;
1908	  dhcp->lease.valid = FALSE;
1909	  service_router_clear(service_p);
1910	  tv.tv_sec = 10; /* retry in a bit */
1911	  tv.tv_usec = 0;
1912	  timer_set_relative(dhcp->timer, tv,
1913			     (timer_func_t *)dhcp_init,
1914			     service_p, (void *)IFEventID_start_e, NULL);
1915	  break;
1916      }
1917      case IFEventID_renew_e:
1918      case IFEventID_link_status_changed_e: {
1919	  void *	network_changed = event_data;
1920
1921	  if (dhcp == NULL) {
1922	      return (ipconfig_status_internal_error_e);
1923	  }
1924	  if (network_changed != NULL) {
1925	      /* switched networks, remove IP address to avoid IP collisions */
1926	      (void)service_remove_address(service_p);
1927	      service_publish_failure(service_p,
1928				      ipconfig_status_network_changed_e);
1929	      linklocal_service_change(service_p, LINKLOCAL_NO_ALLOCATE);
1930	  }
1931	  /* make sure to start DHCP again */
1932	  dhcp_check_link(service_p, event_id);
1933	  break;
1934      }
1935      case IFEventID_link_timer_expired_e:
1936	  dhcp_inactive(service_p);
1937	  break;
1938      case IFEventID_power_off_e:
1939      case IFEventID_sleep_e: {
1940	  if (dhcp == NULL) {
1941	      return (ipconfig_status_internal_error_e);
1942	  }
1943	  if (dhcp->lease.valid && dhcp->saved.is_dhcp) {
1944	      _dhcp_lease_save(service_p, dhcp->lease.start,
1945			       (uint8_t *)dhcp->saved.pkt, dhcp->saved.pkt_size,
1946			       TRUE);
1947	  }
1948	  break;
1949      }
1950      case IFEventID_wake_e: {
1951	  link_status_t		link_status;
1952	  wake_data_t *		wake_data = (wake_data_t *)event_data;
1953
1954	  if (ServiceIsNetBoot(service_p)) {
1955	      break;
1956	  }
1957	  /*
1958	   * While asleep, we could have switched networks without knowing it.
1959	   * Unless we know with some confidence that we're on the same network,
1960	   * we need to remove the IP address from the interface.
1961	   *
1962	   * We remove the IP address if any of the following are true:
1963	   * - we're not connected to a network (link status is inactive)
1964	   * - we woke from hibernation
1965	   * - we're on a different Wi-Fi network (the SSID changed)
1966	   * - we're not on the same ethernet network
1967	   */
1968	  link_status = service_link_status(service_p);
1969	  if ((link_status.valid && link_status.active == FALSE)
1970	      || (wake_data->flags & kWakeFlagsFromHibernation) != 0
1971	      || (if_is_wireless(if_p)
1972		  && (wake_data->flags & kWakeFlagsSSIDChanged) != 0)
1973	      || (if_is_wireless(if_p) == FALSE
1974		  && link_status.wake_on_same_network == FALSE)) {
1975	      /* make sure there's no IP address assigned when we wake */
1976	      service_remove_address(service_p);
1977	      service_publish_failure(service_p,
1978				      ipconfig_status_media_inactive_e);
1979	      if ((wake_data->flags & kWakeFlagsFromHibernation) != 0) {
1980		  /*
1981		   * wake from hibernation is the same as a reboot case, so
1982		   * simulate that by removing all but the last lease, and
1983		   * mark the lease as tentative.
1984		   */
1985		  my_log(LOG_DEBUG, "DHCP %s: wake from hibernation",
1986			 if_name(if_p));
1987		  DHCPLeaseListRemoveAllButLastLease(&dhcp->lease_list);
1988	      }
1989	      dhcp_check_link(service_p, event_id);
1990	  }
1991	  else {
1992	      absolute_time_t 	current_time;
1993
1994	      current_time = timer_current_secs();
1995	      if (dhcp_check_lease(service_p, current_time) == FALSE) {
1996		  /* no valid lease */
1997		  dhcp_init(service_p, IFEventID_start_e, NULL);
1998		  break;
1999	      }
2000
2001	      /* if not RENEW/REBIND/BOUND, do INIT-REBOOT */
2002	      if (S_dhcp_cstate_is_bound_renew_or_rebind(dhcp->state)
2003		  == FALSE) {
2004		  struct in_addr	our_ip;
2005
2006		  our_ip = dhcp->saved.our_ip;
2007
2008		  dhcp_init_reboot(service_p, IFEventID_start_e, &our_ip);
2009		  break;
2010	      }
2011
2012	      /* Check if the new bssid router is arp-able. */
2013	      if ((wake_data->flags & kWakeFlagsBSSIDChanged) != 0) {
2014		  arp_address_info_t info;
2015
2016		  if (service_populate_router_arpinfo(service_p,
2017						      &info)) {
2018		      dhcp_check_router(service_p, IFEventID_start_e,
2019					(void *)&info);
2020		      break;
2021		  }
2022	      }
2023
2024	      /* Check the lease time */
2025	      if (dhcp->lease.length == DHCP_INFINITE_LEASE) {
2026		  /* infinite lease, no need to do any maintenance */
2027		  break;
2028	      }
2029	      /*
2030	       * Check the timer we had scheduled. If it is sufficiently in the
2031	       * future, schedule a new timer to wakeup in RENEW/REBIND then.
2032	       * Otherwise, proceed to RENEW/REBIND.
2033	       */
2034	      if (S_time_in_future(current_time, dhcp->renew_rebind_time,
2035				   G_wake_skew_secs)) {
2036		  struct timeval	tv;
2037
2038		  tv.tv_sec = dhcp->renew_rebind_time - current_time;
2039		  tv.tv_usec = 0;
2040		  timer_set_relative(dhcp->timer, tv,
2041				     (timer_func_t *)dhcp_renew_rebind,
2042				     service_p, (void *)IFEventID_start_e,
2043				     NULL);
2044	      }
2045	      else {
2046		  dhcp_renew_rebind(service_p, IFEventID_start_e, NULL);
2047	      }
2048	  }
2049	  break;
2050      }
2051      case IFEventID_get_dhcp_info_e: {
2052	  dhcp_info_t * dhcp_info_p = ((dhcp_info_t *)event_data);
2053
2054	  if (ServiceGetActiveIPAddress(service_p).s_addr == 0
2055	      || dhcp->saved.pkt_size == 0) {
2056	      break;
2057	  }
2058	  dhcp_set_dhcp_info(dhcp, dhcp_info_p);
2059	  break;
2060      }
2061      case IFEventID_bssid_changed_e: {
2062	  arp_address_info_t info;
2063
2064	  if (S_dhcp_cstate_is_bound_renew_or_rebind(dhcp->state)
2065		== FALSE) {
2066	      break;
2067	  }
2068	  if (service_populate_router_arpinfo(service_p,
2069	      &info)) {
2070	      dhcp_check_router(service_p, IFEventID_start_e,
2071	          	        (void *)&info);
2072	  }
2073	  break;
2074      }
2075      case IFEventID_active_during_sleep_e:
2076	  dhcp_handle_active_during_sleep(service_p,
2077					  (active_during_sleep_t *)event_data);
2078	  break;
2079      default:
2080	  break;
2081    } /* switch (event_id) */
2082    return (status);
2083}
2084
2085static void
2086dhcp_init(ServiceRef service_p, IFEventID_t event_id, void * event_data)
2087{
2088    absolute_time_t 	current_time = timer_current_secs();
2089    Service_dhcp_t *	dhcp = (Service_dhcp_t *)ServiceGetPrivate(service_p);
2090    interface_t *	if_p = service_interface(service_p);
2091    ipconfig_status_t	status = ipconfig_status_success_e;
2092    struct timeval 	tv;
2093
2094    switch (event_id) {
2095      case IFEventID_start_e: {
2096	  dhcp_lease_time_t	lease_option = htonl(SUGGESTED_LEASE_LENGTH);
2097	  dhcpoa_t		options;
2098	  dhcp_cstate_t		prev_state = dhcp->state;
2099
2100	  my_log(LOG_DEBUG, "DHCP %s: INIT", if_name(if_p));
2101	  dhcp->state = dhcp_cstate_init_e;
2102
2103	  dhcp->allow_wake_with_short_lease = TRUE;
2104
2105	  /* clean-up anything that might have come before */
2106	  dhcp_cancel_pending_events(service_p);
2107
2108	  /* form the request */
2109	  /* ALIGN: txbuf is aligned to at least sizeof(uint32) bytes */
2110	  dhcp->request = make_dhcp_request((struct dhcp *)(void *)dhcp->txbuf,
2111					    sizeof(dhcp->txbuf),
2112					    dhcp_msgtype_discover_e,
2113					    if_link_address(if_p),
2114					    if_link_arptype(if_p),
2115					    if_link_length(if_p),
2116					    dhcp->client_id,
2117					    dhcp->client_id_len,
2118					    dhcp->must_broadcast,
2119					    &options);
2120	  if (dhcp->request == NULL) {
2121	      my_log(LOG_ERR, "DHCP %s: INIT make_dhcp_request failed",
2122		     if_name(if_p));
2123	      status = ipconfig_status_allocation_failed_e;
2124	      goto error;
2125	  }
2126	  if (dhcpoa_add(&options, dhcptag_lease_time_e,
2127			 sizeof(lease_option), &lease_option)
2128	      != dhcpoa_success_e) {
2129	      my_log(LOG_ERR, "DHCP %s: INIT dhcpoa_add lease time failed, %s",
2130		     if_name(if_p), dhcpoa_err(&options));
2131	      status = ipconfig_status_allocation_failed_e;
2132	      goto error;
2133	  }
2134	  add_computer_name(&options);
2135	  if (dhcpoa_add(&options, dhcptag_end_e, 0, 0)
2136	      != dhcpoa_success_e) {
2137	      my_log(LOG_ERR, "DHCP %s: INIT failed to terminate options",
2138		     if_name(if_p));
2139	      status = ipconfig_status_allocation_failed_e;
2140	      goto error;
2141	  }
2142	  dhcp->request_size = sizeof(*dhcp->request) + sizeof(G_rfc_magic)
2143	      + dhcpoa_used(&options);
2144	  if (dhcp->request_size < sizeof(struct bootp)) {
2145	      /* pad out to BOOTP-sized packet */
2146	      dhcp->request_size = sizeof(struct bootp);
2147	  }
2148	  if (prev_state != dhcp_cstate_init_reboot_e) {
2149	      dhcp->start_secs = current_time;
2150	  }
2151	  dhcp->wait_secs = G_initial_wait_secs;
2152	  dhcp->try = 0;
2153	  dhcp->xid++;
2154	  dhcp->gathering = FALSE;
2155	  dhcp->saved.rating = 0;
2156	  dhcp->got_nak = FALSE;
2157	  (void)service_enable_autoaddr(service_p);
2158	  bootp_client_enable_receive(dhcp->client,
2159				      (bootp_receive_func_t *)dhcp_init,
2160				      service_p, (void *)IFEventID_data_e);
2161	  /* FALL THROUGH */
2162      }
2163      case IFEventID_timeout_e: {
2164	  if (dhcp->gathering == TRUE) {
2165	      /* done gathering */
2166	      if (dhcp->saved.is_dhcp) {
2167		  dhcp_select(service_p, IFEventID_start_e, NULL);
2168		  break; /* out of switch */
2169	      }
2170	      dhcp_bound(service_p, IFEventID_start_e, NULL);
2171	      break; /* out of switch */
2172	  }
2173	  dhcp->try++;
2174	  if (dhcp->try > 1) {
2175	      link_status_t	link_status = service_link_status(service_p);
2176
2177	      if (link_status.valid
2178		  && link_status.active == FALSE) {
2179		  dhcp_inactive(service_p);
2180		  break;
2181	      }
2182	  }
2183	  if (dhcp->try >= (G_dhcp_router_arp_at_retry_count + 1)) {
2184	      /* try to confirm the router's address */
2185	      dhcp_arp_router(service_p, IFEventID_start_e, &current_time);
2186	  }
2187	  if (service_router_all_valid(service_p) == FALSE
2188	      && dhcp->try >= (G_dhcp_allocate_linklocal_at_retry_count + 1)) {
2189	      if (G_dhcp_failure_configures_linklocal) {
2190		  ServiceSetStatus(service_p,
2191					    ipconfig_status_no_server_e);
2192		  linklocal_service_change(service_p, LINKLOCAL_ALLOCATE);
2193	      }
2194	  }
2195	  if (dhcp->try > (G_max_retries + 1)) {
2196	      /* no server responded */
2197	      if (service_router_all_valid(service_p)) {
2198		  /* lease is still valid, router is still available */
2199		  dhcp_bound(service_p, IFEventID_start_e, NULL);
2200		  break;
2201	      }
2202	      /* DHCP server and router not responding, try again later */
2203	      dhcp_no_server(service_p, IFEventID_start_e, NULL);
2204	      break; /* out of switch */
2205	  }
2206	  dhcp->request->dp_xid = htonl(dhcp->xid);
2207	  dhcp->request->dp_secs
2208	      = htons((uint16_t)(current_time - dhcp->start_secs));
2209
2210	  /* send the packet */
2211	  if (bootp_client_transmit(dhcp->client,
2212				    G_ip_broadcast, G_ip_zeroes,
2213				    G_server_port, G_client_port,
2214				    dhcp->request, dhcp->request_size) < 0) {
2215	      my_log(LOG_ERR,
2216		     "DHCP %s: INIT transmit failed", if_name(if_p));
2217	  }
2218	  /* wait for responses */
2219	  tv.tv_sec = dhcp->wait_secs;
2220	  tv.tv_usec = (suseconds_t)random_range(0, USECS_PER_SEC - 1);
2221	  my_log(LOG_DEBUG, "DHCP %s: INIT waiting at %d for %d.%06d",
2222		 if_name(if_p),
2223		 current_time - dhcp->start_secs,
2224		 tv.tv_sec, tv.tv_usec);
2225	  timer_set_relative(dhcp->timer, tv,
2226			     (timer_func_t *)dhcp_init,
2227			     service_p, (void *)IFEventID_timeout_e, NULL);
2228	  /* next time wait twice as long */
2229	  dhcp->wait_secs *= 2;
2230	  if (dhcp->wait_secs > G_max_wait_secs)
2231	      dhcp->wait_secs = G_max_wait_secs;
2232	  break;
2233      }
2234      case IFEventID_data_e: {
2235	  boolean_t 		is_dhcp = TRUE;
2236	  dhcp_lease_time_t 	lease;
2237	  bootp_receive_data_t *pkt = (bootp_receive_data_t *)event_data;
2238	  dhcp_msgtype_t	reply_msgtype = dhcp_msgtype_none_e;
2239	  struct in_addr	server_ip;
2240	  dhcp_lease_time_t	t1;
2241	  dhcp_lease_time_t	t2;
2242
2243	  if (verify_packet(pkt, dhcp->xid, if_p, &reply_msgtype,
2244			    &server_ip, &is_dhcp) == FALSE
2245	      || server_ip.s_addr == 0
2246	      || ip_valid(pkt->data->dp_yiaddr) == FALSE) {
2247	      /* reject the packet */
2248	      break; /* out of switch */
2249	  }
2250	  if (is_dhcp == FALSE
2251	      || reply_msgtype == dhcp_msgtype_offer_e) {
2252	      int rating = 0;
2253	      int dhcp_packet_ideal_rating = n_dhcp_params
2254					     + DHCP_ADDRESS_RATING_GOOD;
2255
2256	      dhcp_get_lease_from_options(&pkt->options, &lease, &t1, &t2);
2257	      rating = dhcpol_count_params(&pkt->options,
2258					   dhcp_params, n_dhcp_params);
2259
2260	      /* We need to get an augmented rating to account for routable
2261	       * addresses (Ie, those which are not private or linklocal
2262	       */
2263	      rating +=
2264		 get_rating_for_ip_address(&pkt->data->dp_yiaddr);
2265
2266	      /*
2267	       * The new packet is "better" than the saved
2268	       * packet if:
2269	       * - there was no saved packet, or
2270	       * - the new packet is a DHCP packet and the saved
2271	       *   one is a BOOTP packet or a DHCP packet with
2272	       *   a lower rating, or
2273	       * - the new packet and the saved packet are both
2274	       *   BOOTP but the new one has a higher rating
2275	       * All this to allow BOOTP/DHCP interoperability
2276	       * ie. we accept a BOOTP response if it's
2277	       * the only one we've got.  We expect/favour a DHCP
2278	       * response.
2279	       */
2280	      if (dhcp->saved.pkt_size == 0
2281		  || (is_dhcp == TRUE && (dhcp->saved.is_dhcp == FALSE
2282					  || rating > dhcp->saved.rating))
2283		  || (is_dhcp == FALSE && dhcp->saved.is_dhcp == FALSE
2284		      && rating > dhcp->saved.rating)) {
2285		  service_router_clear(service_p);
2286		  dhcpol_free(&dhcp->saved.options);
2287		  bcopy(pkt->data, dhcp->saved.pkt, pkt->size);
2288		  dhcp->saved.pkt_size = pkt->size;
2289		  dhcp->saved.rating = rating;
2290		  /* ALIGN: saved.pkt is uint32_t aligned, cast ok */
2291		  (void)dhcpol_parse_packet(&dhcp->saved.options,
2292					    (void *)dhcp->saved.pkt,
2293					    dhcp->saved.pkt_size, NULL);
2294		  dhcp->saved.our_ip = pkt->data->dp_yiaddr;
2295		  dhcp->saved.server_ip = server_ip;
2296		  dhcp->saved.is_dhcp = is_dhcp;
2297		  dhcp_set_lease_params(service_p, "INIT", is_dhcp,
2298					current_time, lease, t1, t2);
2299		  if (is_dhcp && rating == dhcp_packet_ideal_rating) {
2300		      dhcp_select(service_p, IFEventID_start_e, NULL);
2301		      break; /* out of switch */
2302		  }
2303		  if (dhcp->gathering == FALSE) {
2304		      struct timeval t = {0,0};
2305		      t.tv_sec = G_gather_secs;
2306		      my_log(LOG_DEBUG, "DHCP %s: INIT gathering began at %d",
2307			     if_name(if_p),
2308			     current_time - dhcp->start_secs);
2309		      dhcp->gathering = TRUE;
2310		      timer_set_relative(dhcp->timer, t,
2311					 (timer_func_t *)dhcp_init,
2312					 service_p, (void *)IFEventID_timeout_e,
2313					 NULL);
2314		  }
2315	      }
2316	  }
2317	  break;
2318      }
2319      default:
2320	  break;
2321    }
2322    return;
2323
2324 error:
2325    dhcp_failed(service_p, status);
2326    return;
2327}
2328
2329
2330static void
2331dhcp_init_reboot(ServiceRef service_p, IFEventID_t evid, void * event_data)
2332{
2333    absolute_time_t 	current_time = timer_current_secs();
2334    Service_dhcp_t *	dhcp = (Service_dhcp_t *)ServiceGetPrivate(service_p);
2335    interface_t *	if_p = service_interface(service_p);
2336    struct in_addr	source_ip = G_ip_zeroes;
2337    ipconfig_status_t	status = ipconfig_status_success_e;
2338    struct timeval 	tv;
2339    struct in_addr 	server_ip = G_ip_broadcast;
2340
2341    if (ServiceIsNetBoot(service_p)) {
2342	netboot_addresses(&source_ip, NULL);
2343    }
2344
2345    switch (evid) {
2346      case IFEventID_start_e: {
2347	  struct in_addr 	our_ip;
2348	  dhcp_lease_time_t	lease_option = htonl(SUGGESTED_LEASE_LENGTH);
2349	  dhcpoa_t	 	options;
2350
2351	  dhcp->state = dhcp_cstate_init_reboot_e;
2352	  dhcp->allow_wake_with_short_lease = TRUE;
2353	  dhcp->start_secs = current_time;
2354	  dhcp->try = 0;
2355	  dhcp->wait_secs = G_initial_wait_secs;
2356	  if (source_ip.s_addr == 0) {
2357	      our_ip = *((struct in_addr *)event_data);
2358	  }
2359	  else {
2360	      our_ip = source_ip;
2361	  }
2362	  if (G_IPConfiguration_verbose) {
2363	      my_log(LOG_DEBUG, "DHCP %s: INIT-REBOOT (" IP_FORMAT ")",
2364		     if_name(if_p), IP_LIST(&our_ip));
2365	  }
2366
2367	  /* clean-up anything that might have come before */
2368	  dhcp_cancel_pending_events(service_p);
2369
2370	  /* form the request */
2371	  /* ALIGN: txbuf is aligned to at least sizeof(uint32_t) bytes */
2372	  dhcp->request = make_dhcp_request((struct dhcp *)(void *)dhcp->txbuf,
2373					    sizeof(dhcp->txbuf),
2374					    dhcp_msgtype_request_e,
2375					    if_link_address(if_p),
2376					    if_link_arptype(if_p),
2377					    if_link_length(if_p),
2378					    dhcp->client_id,
2379					    dhcp->client_id_len,
2380					    dhcp->must_broadcast,
2381					    &options);
2382	  if (dhcp->request == NULL) {
2383	      status = ipconfig_status_allocation_failed_e;
2384	      goto error;
2385	  }
2386	  if (dhcpoa_add(&options, dhcptag_requested_ip_address_e,
2387			 sizeof(our_ip), &our_ip)
2388	      != dhcpoa_success_e) {
2389	      my_log(LOG_ERR, "DHCP %s: INIT-REBOOT add request ip failed, %s",
2390		     if_name(if_p), dhcpoa_err(&options));
2391	      status = ipconfig_status_allocation_failed_e;
2392	      goto error;
2393	  }
2394	  if (dhcpoa_add(&options, dhcptag_lease_time_e,
2395			 sizeof(lease_option), &lease_option)
2396	      != dhcpoa_success_e) {
2397	      my_log(LOG_ERR, "DHCP %s: INIT-REBOOT add lease time failed, %s",
2398		     if_name(if_p), dhcpoa_err(&options));
2399	      status = ipconfig_status_allocation_failed_e;
2400	      goto error;
2401	  }
2402	  add_computer_name(&options);
2403	  if (dhcpoa_add(&options, dhcptag_end_e, 0, 0)
2404	      != dhcpoa_success_e) {
2405	      my_log(LOG_ERR, "DHCP %s: INIT-REBOOT failed to terminate options",
2406		     if_name(if_p));
2407	      status = ipconfig_status_allocation_failed_e;
2408	      goto error;
2409	  }
2410	  dhcp->request_size = sizeof(*dhcp->request) + sizeof(G_rfc_magic)
2411	      + dhcpoa_used(&options);
2412	  if (dhcp->request_size < sizeof(struct bootp)) {
2413	      /* pad out to BOOTP-sized packet */
2414	      dhcp->request_size = sizeof(struct bootp);
2415	  }
2416	  dhcp->got_nak = FALSE;
2417	  dhcp->gathering = FALSE;
2418	  dhcp->xid++;
2419	  dhcp->saved.our_ip = our_ip;
2420	  dhcp->saved.rating = 0;
2421	  (void)service_enable_autoaddr(service_p);
2422	  bootp_client_enable_receive(dhcp->client,
2423				      (bootp_receive_func_t *)dhcp_init_reboot,
2424				      service_p, (void *)IFEventID_data_e);
2425	  /* FALL THROUGH */
2426      }
2427      case IFEventID_timeout_e: {
2428	  if (dhcp->gathering == TRUE) {
2429	      /* done gathering */
2430	      dhcp_bound(service_p, IFEventID_start_e, NULL);
2431	      break; /* out of switch */
2432	  }
2433	  dhcp->try++;
2434	  if (dhcp->try > 1) {
2435	      link_status_t	link_status = service_link_status(service_p);
2436
2437	      if (link_status.valid
2438		  && link_status.active == FALSE) {
2439		  dhcp_inactive(service_p);
2440		  break;
2441	      }
2442	  }
2443	  /* try to confirm the router's address */
2444	  dhcp_arp_router(service_p, IFEventID_start_e, &current_time);
2445
2446	  if (service_router_all_valid(service_p) == FALSE
2447	      && dhcp->try >= (G_dhcp_allocate_linklocal_at_retry_count + 1)) {
2448	      if (G_dhcp_failure_configures_linklocal) {
2449		  ServiceSetStatus(service_p, ipconfig_status_no_server_e);
2450		  linklocal_service_change(service_p, LINKLOCAL_ALLOCATE);
2451	      }
2452	  }
2453	  if (dhcp->try > (G_dhcp_init_reboot_retry_count + 1)) {
2454	      if (G_IPConfiguration_verbose) {
2455		  my_log(LOG_DEBUG, "DHCP %s: INIT-REBOOT (" IP_FORMAT
2456			 ") timed out", if_name(if_p),
2457			 IP_LIST(&dhcp->saved.our_ip));
2458	      }
2459	      (void)service_remove_address(service_p);
2460	      service_publish_failure_sync(service_p,
2461					   ipconfig_status_server_not_responding_e,
2462					   FALSE);
2463	      dhcp->try--;
2464	      /* go back to the INIT state */
2465	      dhcp_init(service_p, IFEventID_start_e, NULL);
2466	      break; /* ouf of case */
2467	  }
2468
2469	  dhcp->request->dp_xid = htonl(dhcp->xid);
2470	  dhcp->request->dp_secs
2471	      = htons((uint16_t)(current_time - dhcp->start_secs));
2472	  /* send the packet */
2473	  if (bootp_client_transmit(dhcp->client,
2474				    server_ip, source_ip,
2475				    G_server_port, G_client_port,
2476				    dhcp->request, dhcp->request_size) < 0) {
2477	      my_log(LOG_ERR,
2478		     "DHCP %s: INIT-REBOOT transmit failed",
2479		     if_name(if_p));
2480	  }
2481	  /* wait for responses */
2482	  tv.tv_sec = dhcp->wait_secs;
2483	  tv.tv_usec = (suseconds_t)random_range(0, USECS_PER_SEC - 1);
2484
2485	  if (G_IPConfiguration_verbose) {
2486	      my_log(LOG_DEBUG,
2487		     "DHCP %s: INIT-REBOOT (" IP_FORMAT
2488		     ") waiting at %d for %d.%06d",
2489		     if_name(if_p),
2490		     IP_LIST(&dhcp->saved.our_ip),
2491		     current_time - dhcp->start_secs,
2492		     tv.tv_sec, tv.tv_usec);
2493	  }
2494	  timer_set_relative(dhcp->timer, tv,
2495			     (timer_func_t *)dhcp_init_reboot,
2496			     service_p, (void *)IFEventID_timeout_e, NULL);
2497	  /* next time wait twice as long */
2498	  dhcp->wait_secs *= 2;
2499	  if (dhcp->wait_secs > G_max_wait_secs)
2500	      dhcp->wait_secs = G_max_wait_secs;
2501	  break;
2502      }
2503      case IFEventID_data_e: {
2504	  boolean_t 		is_dhcp = TRUE;
2505	  dhcp_lease_time_t 	lease;
2506	  bootp_receive_data_t *pkt = (bootp_receive_data_t *)event_data;
2507	  dhcp_msgtype_t	reply_msgtype = dhcp_msgtype_none_e;
2508	  struct in_addr	server_ip;
2509	  dhcp_lease_time_t 	t1;
2510	  dhcp_lease_time_t 	t2;
2511
2512	  if (verify_packet(pkt, dhcp->xid, if_p, &reply_msgtype,
2513			    &server_ip, &is_dhcp) == FALSE) {
2514	      /* reject the packet */
2515	      break; /* out of switch */
2516	  }
2517	  if (ServiceIsNetBoot(service_p) == FALSE
2518	      && reply_msgtype == dhcp_msgtype_nak_e) {
2519	      if (G_IPConfiguration_verbose) {
2520		  my_log(LOG_DEBUG, "DHCP %s: got DHCP NAK",
2521			 if_name(if_p));
2522	      }
2523	      if (arp_client_is_active(dhcp->arp)) {
2524		  dhcp->got_nak = TRUE;
2525	      }
2526	      else {
2527		  service_publish_failure(service_p,
2528					  ipconfig_status_lease_terminated_e);
2529		  dhcp_unbound(service_p, IFEventID_start_e, (void *)TRUE);
2530		  break; /* out of switch */
2531	      }
2532	  }
2533	  if (server_ip.s_addr == 0
2534	      || pkt->data->dp_yiaddr.s_addr != dhcp->saved.our_ip.s_addr) {
2535	      /* reject the packet */
2536	      break; /* out of switch */
2537	  }
2538	  if (is_dhcp == FALSE
2539	      || reply_msgtype == dhcp_msgtype_ack_e) {
2540	      int rating = 0;
2541
2542	      dhcp_get_lease_from_options(&pkt->options, &lease, &t1, &t2);
2543
2544	      rating = dhcpol_count_params(&pkt->options,
2545					   dhcp_params, n_dhcp_params);
2546	      /*
2547	       * The new packet is "better" than the saved
2548	       * packet if:
2549	       * - there was no saved packet, or
2550	       * - the new packet is a DHCP packet and the saved
2551	       *   one is a BOOTP packet or a DHCP packet with
2552	       *   a lower rating, or
2553	       * - the new packet and the saved packet are both
2554	       *   BOOTP but the new one has a higher rating
2555	       * All this to allow BOOTP/DHCP interoperability
2556	       * ie. we accept a BOOTP response if it's
2557	       * the only one we've got.  We expect/favour a DHCP
2558	       * response.
2559	       */
2560	      if (dhcp->saved.pkt_size == 0
2561		  || (is_dhcp == TRUE && (dhcp->saved.is_dhcp == FALSE
2562					  || rating > dhcp->saved.rating))
2563		  || (is_dhcp == FALSE && dhcp->saved.is_dhcp == FALSE
2564		      && rating > dhcp->saved.rating)) {
2565		  service_router_clear(service_p);
2566		  dhcpol_free(&dhcp->saved.options);
2567		  bcopy(pkt->data, dhcp->saved.pkt, pkt->size);
2568		  dhcp->saved.pkt_size = pkt->size;
2569		  dhcp->saved.rating = rating;
2570		  /* ALIGN: saved.pkt is uint32_t aligned, cast ok */
2571		  (void)dhcpol_parse_packet(&dhcp->saved.options,
2572					    (void *)dhcp->saved.pkt,
2573					    dhcp->saved.pkt_size, NULL);
2574		  dhcp->saved.our_ip = pkt->data->dp_yiaddr;
2575		  dhcp->saved.server_ip = server_ip;
2576		  dhcp->saved.is_dhcp = is_dhcp;
2577		  dhcp_set_lease_params(service_p, "INIT-REBOOT", is_dhcp,
2578					current_time, lease, t1, t2);
2579		  if (is_dhcp && rating == n_dhcp_params) {
2580		      dhcp_bound(service_p, IFEventID_start_e, NULL);
2581		      break; /* out of switch */
2582		  }
2583		  if (dhcp->gathering == FALSE) {
2584		      struct timeval t = {0,0};
2585		      t.tv_sec = G_gather_secs;
2586		      if (G_IPConfiguration_verbose) {
2587			  my_log(LOG_DEBUG,
2588				 "DHCP %s: INIT-REBOOT ("
2589				 IP_FORMAT ") gathering began at %d",
2590				 if_name(if_p),
2591				 IP_LIST(&dhcp->saved.our_ip),
2592				 current_time - dhcp->start_secs);
2593		      }
2594                      /* don't bother trying to figure out what lease to use */
2595                      arp_client_cancel(dhcp->arp);
2596
2597		      dhcp->gathering = TRUE;
2598		      timer_set_relative(dhcp->timer, t,
2599					 (timer_func_t *)dhcp_init_reboot,
2600					 service_p,
2601					 (void *)IFEventID_timeout_e,
2602					 NULL);
2603		  }
2604	      }
2605	  }
2606	  break;
2607      }
2608      default:
2609	  break; /* shouldn't happen */
2610    }
2611    return;
2612
2613 error:
2614    dhcp_failed(service_p, status);
2615    return;
2616}
2617
2618static void
2619dhcp_select(ServiceRef service_p, IFEventID_t evid, void * event_data)
2620{
2621    absolute_time_t 	current_time = timer_current_secs();
2622    Service_dhcp_t *	dhcp = (Service_dhcp_t *)ServiceGetPrivate(service_p);
2623    interface_t *	if_p = service_interface(service_p);
2624    ipconfig_status_t	status = ipconfig_status_success_e;
2625    struct timeval 	tv;
2626
2627    switch (evid) {
2628      case IFEventID_start_e: {
2629	  dhcpoa_t	 	options;
2630
2631	  my_log(LOG_DEBUG, "DHCP %s: SELECT", if_name(if_p));
2632	  /* clean-up anything that might have come before */
2633	  dhcp_cancel_pending_events(service_p);
2634
2635	  dhcp->state = dhcp_cstate_select_e;
2636
2637	  /* form the request */
2638	  /* ALIGN: txbuf is uint32_t aligned, cast ok */
2639	  dhcp->request = make_dhcp_request((struct dhcp *)(void *)dhcp->txbuf,
2640					    sizeof(dhcp->txbuf),
2641					    dhcp_msgtype_request_e,
2642					    if_link_address(if_p),
2643					    if_link_arptype(if_p),
2644					    if_link_length(if_p),
2645					    dhcp->client_id,
2646					    dhcp->client_id_len,
2647					    dhcp->must_broadcast,
2648					    &options);
2649	  if (dhcp->request == NULL) {
2650	      my_log(LOG_ERR, "DHCP %s: SELECT make_dhcp_request failed",
2651		     if_name(if_p));
2652	      status = ipconfig_status_allocation_failed_e;
2653	      goto error;
2654	  }
2655	  /* insert server identifier and requested ip address */
2656	  if (dhcpoa_add(&options, dhcptag_requested_ip_address_e,
2657			 sizeof(dhcp->saved.our_ip), &dhcp->saved.our_ip)
2658	      != dhcpoa_success_e) {
2659	      my_log(LOG_ERR, "DHCP %s: SELECT add requested ip failed, %s",
2660		     if_name(if_p), dhcpoa_err(&options));
2661	      status = ipconfig_status_allocation_failed_e;
2662	      goto error;
2663	  }
2664	  if (dhcpoa_add(&options, dhcptag_server_identifier_e,
2665			 sizeof(dhcp->saved.server_ip), &dhcp->saved.server_ip)
2666	      != dhcpoa_success_e) {
2667	      my_log(LOG_ERR, "DHCP %s: SELECT add server ip failed, %s",
2668		     if_name(if_p), dhcpoa_err(&options));
2669	      status = ipconfig_status_allocation_failed_e;
2670	      goto error;
2671	  }
2672	  add_computer_name(&options);
2673	  if (dhcpoa_add(&options, dhcptag_end_e, 0, 0)
2674	      != dhcpoa_success_e) {
2675	      my_log(LOG_ERR, "DHCP %s: SELECT failed to terminate options",
2676		     if_name(if_p));
2677	      status = ipconfig_status_allocation_failed_e;
2678	      goto error;
2679	  }
2680	  dhcp->request_size = sizeof(*dhcp->request) + sizeof(G_rfc_magic)
2681	      + dhcpoa_used(&options);
2682	  if (dhcp->request_size < sizeof(struct bootp)) {
2683	      /* pad out to BOOTP-sized packet */
2684	      dhcp->request_size = sizeof(struct bootp);
2685	  }
2686	  dhcp->try = 0;
2687	  dhcp->gathering = FALSE;
2688	  dhcp->wait_secs = G_initial_wait_secs;
2689	  bootp_client_enable_receive(dhcp->client,
2690				      (bootp_receive_func_t *)dhcp_select,
2691				      service_p, (void *)IFEventID_data_e);
2692      }
2693      case IFEventID_timeout_e: {
2694	  dhcp->try++;
2695	  if (dhcp->try > (G_dhcp_select_retry_count + 1)) {
2696	      my_log(LOG_DEBUG, "DHCP %s: SELECT timed out", if_name(if_p));
2697	      /* go back to INIT and try again */
2698	      dhcp_init(service_p, IFEventID_start_e, NULL);
2699	      break; /* out of switch */
2700	  }
2701	  dhcp->request->dp_xid = htonl(dhcp->xid);
2702	  dhcp->request->dp_secs
2703	      = htons((uint16_t)(current_time - dhcp->start_secs));
2704
2705	  /* send the packet */
2706	  if (bootp_client_transmit(dhcp->client,
2707				    G_ip_broadcast, G_ip_zeroes,
2708				    G_server_port, G_client_port,
2709				    dhcp->request, dhcp->request_size) < 0) {
2710	      my_log(LOG_ERR,
2711		     "DHCP %s: SELECT transmit failed",
2712		     if_name(if_p));
2713	  }
2714	  /* wait for responses */
2715	  tv.tv_sec = dhcp->wait_secs;
2716	  tv.tv_usec = 0;
2717	  my_log(LOG_DEBUG, "DHCP %s: SELECT waiting at %d for %d.%06d",
2718		 if_name(if_p),
2719		 current_time - dhcp->start_secs,
2720		 tv.tv_sec, tv.tv_usec);
2721	  timer_set_relative(dhcp->timer, tv,
2722			     (timer_func_t *)dhcp_select,
2723			     service_p, (void *)IFEventID_timeout_e, NULL);
2724	  /* next time wait twice as long */
2725	  dhcp->wait_secs *= 2;
2726	  if (dhcp->wait_secs > G_max_wait_secs)
2727	      dhcp->wait_secs = G_max_wait_secs;
2728	  break;
2729      }
2730      case IFEventID_data_e: {
2731	  boolean_t 		is_dhcp = TRUE;
2732	  dhcp_lease_time_t 	lease = SUGGESTED_LEASE_LENGTH;
2733	  bootp_receive_data_t *pkt = (bootp_receive_data_t *)event_data;
2734	  dhcp_msgtype_t	reply_msgtype = dhcp_msgtype_none_e;
2735	  struct in_addr	server_ip = { 0 };
2736	  dhcp_lease_time_t 	t1;
2737	  dhcp_lease_time_t 	t2;
2738
2739	  if (verify_packet(pkt, dhcp->xid, if_p, &reply_msgtype,
2740			    &server_ip, &is_dhcp) == FALSE
2741	      || is_dhcp == FALSE) {
2742	      /* reject the packet */
2743	      break; /* out of switch */
2744	  }
2745
2746	  if (reply_msgtype == dhcp_msgtype_nak_e) {
2747	      if (server_ip.s_addr == 0
2748		  || server_ip.s_addr != dhcp->saved.server_ip.s_addr) {
2749		  /* reject the packet */
2750		  break; /* out of switch */
2751	      }
2752	      /* clean-up anything that might have come before */
2753	      dhcp_cancel_pending_events(service_p);
2754
2755	      /*
2756	       * wait to retry INIT just in case there's a misbehaving server
2757	       * and we get stuck in an INIT-SELECT-NAK infinite loop
2758	       */
2759	      tv.tv_sec = 10;
2760	      tv.tv_usec = 0;
2761	      timer_set_relative(dhcp->timer, tv,
2762				 (timer_func_t *)dhcp_init,
2763				 service_p, (void *)IFEventID_start_e, NULL);
2764	      break; /* out of switch */
2765	  }
2766	  if (reply_msgtype != dhcp_msgtype_ack_e
2767	      || ip_valid(pkt->data->dp_yiaddr) == FALSE) {
2768	      /* reject the packet */
2769	      break; /* out of switch */
2770	  }
2771	  dhcp_get_lease_from_options(&pkt->options, &lease, &t1, &t2);
2772	  dhcp_set_lease_params(service_p, "SELECT", is_dhcp,
2773				current_time, lease, t1, t2);
2774	  dhcpol_free(&dhcp->saved.options);
2775	  bcopy(pkt->data, dhcp->saved.pkt, pkt->size);
2776	  dhcp->saved.pkt_size = pkt->size;
2777	  dhcp->saved.rating = 0;
2778	  /* ALIGN: saved.pkt is uint32_t aligned, cast ok */
2779	  (void)dhcpol_parse_packet(&dhcp->saved.options,
2780				    (void *)dhcp->saved.pkt,
2781				    dhcp->saved.pkt_size, NULL);
2782	  dhcp->saved.our_ip = pkt->data->dp_yiaddr;
2783	  if (server_ip.s_addr != 0) {
2784	      dhcp->saved.server_ip = server_ip;
2785	  }
2786	  dhcp->saved.is_dhcp = TRUE;
2787	  dhcp_bound(service_p, IFEventID_start_e, NULL);
2788	  break;
2789      }
2790      default:
2791	  break;
2792    }
2793    return;
2794
2795 error:
2796    dhcp_failed(service_p, status);
2797    return;
2798}
2799
2800static void
2801dhcp_check_router(ServiceRef service_p,
2802                  IFEventID_t event_id, void * event_data)
2803{
2804    Service_dhcp_t *    dhcp = (Service_dhcp_t *)ServiceGetPrivate(service_p);
2805    interface_t *       if_p = service_interface(service_p);
2806
2807    switch (event_id) {
2808	case IFEventID_start_e: {
2809	    arp_address_info_t* info_p = (arp_address_info_t*) event_data;
2810
2811	    info_p->sender_ip = dhcp->saved.our_ip;
2812
2813	    arp_client_detect(dhcp->arp,
2814			      (arp_result_func_t *)
2815			      dhcp_check_router,
2816			      service_p,
2817			      (void *)IFEventID_arp_e,
2818			      info_p, 1, TRUE);
2819	    break;
2820	}
2821	case IFEventID_arp_e: {
2822	    arp_result_t *        result = (arp_result_t *)event_data;
2823
2824	    if (result->error || result->in_use == FALSE) {
2825		if (result->error) {
2826		    my_log(LOG_ERR, "DHCP %s: ARP default gateway failed, %s",
2827			   if_name(if_p),
2828			   arp_client_errmsg(dhcp->arp));
2829                }
2830		else {
2831		   my_log(LOG_DEBUG,
2832			  "DHCP %s: ARP detect default gateway got "
2833			  "no response", if_name(if_p));
2834		}
2835		/* Try DHCP */
2836	    	dhcp_check_link(service_p, event_id);
2837	    }
2838	    else {
2839		absolute_time_t     current_time = timer_current_secs();
2840
2841		my_log(LOG_DEBUG,  "DHCP %s: ARP detect default gateway got "
2842		       "a response", if_name(if_p));
2843
2844		/* Check the lease time */
2845		if (dhcp->lease.length == DHCP_INFINITE_LEASE) {
2846		    /* infinite lease, no need to do any maintenance */
2847		    break;
2848		}
2849		/*
2850		 * Check the timer we had scheduled.  If it is in the
2851		 * future, schedule a new timer to wakeup in RENEW/REBIND then.
2852		 * If it is in the past, proceed immediately to RENEW/REBIND.
2853		 */
2854		else if (current_time < dhcp->renew_rebind_time) {
2855		    struct timeval        tv;
2856
2857		    tv.tv_sec = dhcp->renew_rebind_time - current_time;
2858		    tv.tv_usec = 0;
2859		    timer_set_relative(dhcp->timer, tv,
2860				       (timer_func_t *)dhcp_renew_rebind,
2861				       service_p, (void *)IFEventID_start_e,
2862				       NULL);
2863		}
2864		else {
2865		    dhcp_renew_rebind(service_p, IFEventID_start_e, NULL);
2866		}
2867	    }
2868	    break;
2869	}
2870	default:
2871 	    break;
2872    }
2873}
2874
2875static void
2876dhcp_arp_router(ServiceRef service_p, IFEventID_t event_id, void * event_data)
2877{
2878    Service_dhcp_t *	dhcp = (Service_dhcp_t *)ServiceGetPrivate(service_p);
2879    interface_t *	if_p = service_interface(service_p);
2880
2881    switch (event_id) {
2882      case IFEventID_start_e: {
2883	  int			info_count;
2884	  arp_address_info_t *	info_p;
2885	  CFStringRef		ssid;
2886	  absolute_time_t	start_time_threshold;
2887	  absolute_time_t *	start_time_threshold_p;
2888	  bool			tentative_ok;
2889
2890	  if (G_router_arp == FALSE) {
2891	      /* don't ARP for the router */
2892	      break;
2893	  }
2894	  if (service_router_is_arp_verified(service_p)) {
2895	      /* nothing to be done */
2896	      break;
2897	  }
2898	  tentative_ok = (dhcp->state == dhcp_cstate_init_e);
2899	  if (if_is_wireless(if_p)) {
2900	      absolute_time_t *	current_time_p;
2901
2902	      ssid = ServiceGetSSID(service_p);
2903	      if (ssid == NULL) {
2904		  /* no SSID, no play */
2905		  my_log(LOG_NOTICE, "dhcp_arp_router: %s SSID unavailable",
2906			 if_name(if_p));
2907		  break;
2908	      }
2909	      current_time_p = (absolute_time_t *)event_data;
2910	      start_time_threshold = *current_time_p
2911		  - G_router_arp_wifi_lease_start_threshold_secs;
2912	      start_time_threshold_p = &start_time_threshold;
2913	  }
2914	  else {
2915	      start_time_threshold_p = NULL;
2916	      ssid = NULL;
2917	  }
2918	  info_p = DHCPLeaseListCopyARPAddressInfo(&dhcp->lease_list,
2919						   ssid,
2920						   start_time_threshold_p,
2921						   tentative_ok,
2922						   &info_count);
2923	  if (info_p == NULL) {
2924	      my_log(LOG_DEBUG,
2925		     "DHCP %s: ARP router: No leases to query for",
2926		     if_name(if_p));
2927	      break;
2928	  }
2929	  if (G_IPConfiguration_verbose) {
2930	      int	i;
2931
2932	      my_log(LOG_DEBUG, "DHCP %s: ARP detect router starting",
2933		     if_name(if_p));
2934	      for (i = 0; i < info_count; i++) {
2935		  my_log(LOG_DEBUG, "%d. sender "  IP_FORMAT
2936			 " target " IP_FORMAT "", i + 1,
2937			 IP_LIST(&info_p[i].sender_ip),
2938			 IP_LIST(&info_p[i].target_ip));
2939	      }
2940	  }
2941	  arp_client_detect(dhcp->arp,
2942			    (arp_result_func_t *)
2943			    dhcp_arp_router,
2944			    service_p,
2945			    (void *)IFEventID_arp_e,
2946			    info_p, info_count, FALSE);
2947	  free(info_p);
2948	  break;
2949      }
2950      case IFEventID_arp_e: {
2951	  arp_address_info_t *	info_p;
2952	  DHCPLeaseRef		lease_p;
2953	  void *		option;
2954	  struct in_addr	mask = {0};
2955	  arp_result_t *	result = (arp_result_t *)event_data;
2956	  int			where;
2957
2958	  if (result->error || result->in_use == FALSE) {
2959	      if (result->error) {
2960		  my_log(LOG_ERR, "DHCP %s: ARP detect ROUTER failed, %s",
2961			 if_name(if_p),
2962			 arp_client_errmsg(dhcp->arp));
2963	      }
2964	      else {
2965		  my_log(LOG_DEBUG,
2966			 "DHCP %s: ARP detect router got no response",
2967			 if_name(if_p));
2968	      }
2969	      if (dhcp->got_nak) {
2970		  service_publish_failure(service_p,
2971					  ipconfig_status_lease_terminated_e);
2972		  dhcp_unbound(service_p, IFEventID_start_e, (void *)TRUE);
2973		  break; /* out of switch */
2974	      }
2975	      break;
2976	  }
2977	  info_p = &result->addr;
2978	  if (G_IPConfiguration_verbose) {
2979	      my_log(LOG_DEBUG, "DHCP %s: got response for sender "
2980		     IP_FORMAT " target " IP_FORMAT,
2981		     if_name(if_p), IP_LIST(&info_p->sender_ip),
2982		     IP_LIST(&info_p->target_ip));
2983	  }
2984	  where = DHCPLeaseListFindLease(&dhcp->lease_list,
2985					 info_p->sender_ip,
2986					 info_p->target_ip,
2987					 info_p->target_hardware,
2988					 if_link_length(if_p));
2989	  if (where == -1) {
2990	      if (G_IPConfiguration_verbose) {
2991		  my_log(LOG_DEBUG, "DHCP %s: lease for "
2992			 IP_FORMAT " is no longer available",
2993			 if_name(if_p), IP_LIST(&info_p->sender_ip));
2994	      }
2995	      if (dhcp->got_nak) {
2996		  service_publish_failure(service_p,
2997					  ipconfig_status_lease_terminated_e);
2998		  dhcp_unbound(service_p, IFEventID_start_e, NULL);
2999	      }
3000	      break; /* out of switch */
3001	  }
3002	  lease_p = DHCPLeaseListElement(&dhcp->lease_list, where);
3003	  if (G_IPConfiguration_verbose) {
3004	      my_log(LOG_DEBUG, "DHCP %s: identified lease for " IP_FORMAT,
3005		     if_name(if_p), IP_LIST(&lease_p->our_ip));
3006	  }
3007	  if (dhcp->state == dhcp_cstate_init_e) {
3008	      if (dhcp->gathering == TRUE) {
3009		  /* we got a response, don't bother with old lease */
3010		  break;
3011	      }
3012	      switch_to_lease(service_p, lease_p);
3013	      service_router_set_all_valid(service_p);
3014	      break;
3015	  }
3016	  if (dhcp->state != dhcp_cstate_init_reboot_e) {
3017	      /* this can't really happen */
3018	      break;
3019	  }
3020	  if (dhcp->gathering == TRUE) {
3021	      struct in_addr *	router_p;
3022
3023	      if (lease_p->our_ip.s_addr != dhcp->saved.our_ip.s_addr) {
3024		  /* not the same lease */
3025		  break;
3026	      }
3027	      router_p = dhcp_get_router_from_options(&dhcp->saved.options,
3028						      dhcp->saved.our_ip);
3029	      if (router_p == NULL
3030		  || router_p->s_addr != lease_p->router_ip.s_addr) {
3031		  /* router changed or different, do full refresh */
3032		  break;
3033	      }
3034	  }
3035	  else if (switch_to_lease(service_p, lease_p)) {
3036	      dhcpol_t		options;
3037	      struct in_addr *	req_ip_p;
3038
3039	      dhcp->request->dp_xid = htonl(++dhcp->xid);
3040	      dhcpol_init(&options);
3041	      /* ALIGN: txbuf is uint32_t aligned, cast ok */
3042	      (void)dhcpol_parse_packet(&options,
3043					(struct dhcp *)(void *)dhcp->txbuf,
3044					sizeof(dhcp->txbuf), NULL);
3045	      req_ip_p
3046		  = dhcpol_find_with_length(&options,
3047					    dhcptag_requested_ip_address_e,
3048					    sizeof(*req_ip_p));
3049	      if (req_ip_p != NULL) {
3050		  /* switch to the new IP address */
3051		  *req_ip_p = lease_p->our_ip;
3052		  dhcp->try = 0;
3053	      }
3054	      dhcpol_free(&options);
3055	  }
3056	  else if (dhcp->got_nak) {
3057	      /* we got a nak and we didn't switch leases */
3058	      service_publish_failure(service_p,
3059				      ipconfig_status_lease_terminated_e);
3060	      dhcp_unbound(service_p, IFEventID_start_e, NULL);
3061	      break; /* out of switch */
3062	  }
3063	  service_router_set_all_valid(service_p);
3064
3065	  /* lease is still valid, router is still available */
3066	  option = dhcpol_find_with_length(&dhcp->saved.options,
3067					   dhcptag_subnet_mask_e,
3068					   sizeof(mask));
3069	  if (option != NULL) {
3070	      mask = *((struct in_addr *)option);
3071	  }
3072
3073	  /* allow user warning to appear */
3074	  dhcp->conflicting_address.s_addr = 0;
3075	  dhcp->user_warned = FALSE;
3076
3077	  /* set our address */
3078	  (void)service_set_address(service_p, dhcp->saved.our_ip,
3079				    mask, G_ip_zeroes);
3080	  dhcp_publish_success(service_p);
3081
3082	  /* stop link local if necessary */
3083	  if (G_dhcp_success_deconfigures_linklocal) {
3084	      linklocal_service_change(service_p, LINKLOCAL_NO_ALLOCATE);
3085	  }
3086	  break;
3087      }
3088      default:
3089	  break;
3090    }
3091    return;
3092}
3093
3094static void
3095dhcp_resolve_router_callback(ServiceRef service_p, router_arp_status_t status);
3096
3097
3098static void
3099dhcp_resolve_router_retry(void * arg0, void * arg1, void * arg2)
3100{
3101    Service_dhcp_t *	dhcp;
3102    ServiceRef 		service_p = (ServiceRef)arg0;
3103
3104    dhcp = (Service_dhcp_t *)ServiceGetPrivate(service_p);
3105    service_resolve_router(service_p, dhcp->arp,
3106			   dhcp_resolve_router_callback,
3107			   dhcp->saved.our_ip);
3108    return;
3109}
3110
3111
3112static void
3113dhcp_resolve_router_callback(ServiceRef service_p, router_arp_status_t status)
3114{
3115    Service_dhcp_t *	dhcp = (Service_dhcp_t *)ServiceGetPrivate(service_p);
3116    struct timeval	tv;
3117
3118    switch (status) {
3119    case router_arp_status_no_response_e:
3120	/* try again in 60 seconds */
3121	tv.tv_sec = 60;
3122	tv.tv_usec = 0;
3123	timer_set_relative(dhcp->timer, tv,
3124			   (timer_func_t *)dhcp_resolve_router_retry,
3125			   service_p, NULL, NULL);
3126	if (dhcp->resolve_router_timed_out) {
3127	    break;
3128	}
3129	/* publish what we have so far */
3130	dhcp->resolve_router_timed_out = TRUE;
3131	dhcp_publish_success(service_p);
3132	if (dhcp->saved.is_dhcp) {
3133	    _dhcp_lease_save(service_p, dhcp->lease.start,
3134			     (uint8_t *)dhcp->saved.pkt,
3135			     dhcp->saved.pkt_size,
3136			     TRUE);
3137	}
3138	break;
3139    case router_arp_status_success_e:
3140	if (dhcp->saved.is_dhcp) {
3141	    _dhcp_lease_save(service_p, dhcp->lease.start,
3142			     (uint8_t *)dhcp->saved.pkt,
3143			     dhcp->saved.pkt_size,
3144			     dhcp->lease.needs_write);
3145	    dhcp->lease.needs_write = FALSE;
3146	}
3147	dhcp_publish_success(service_p);
3148	break;
3149    default:
3150    case router_arp_status_failed_e:
3151	break;
3152    }
3153}
3154
3155static boolean_t
3156should_write_lease(Service_dhcp_t * dhcp, absolute_time_t current_time)
3157{
3158    if (dhcp->lease.t1 < current_time) {
3159	/* t1 is already in the past */
3160	return (FALSE);
3161    }
3162    if ((dhcp->lease.t1 - current_time)
3163	> G_dhcp_lease_write_t1_threshold_secs) {
3164	/* if T1 is sufficiently far enough into the future, write the lease */
3165	return (TRUE);
3166    }
3167    return (FALSE);
3168}
3169
3170static void
3171dhcp_bound(ServiceRef service_p, IFEventID_t event_id, void * event_data)
3172{
3173    absolute_time_t 	current_time = timer_current_secs();
3174    Service_dhcp_t *	dhcp = (Service_dhcp_t *)ServiceGetPrivate(service_p);
3175    boolean_t		do_arp = TRUE;
3176    interface_t *	if_p = service_interface(service_p);
3177    struct in_addr	mask = {0};
3178    void *		option;
3179    struct timeval 	tv = {0, 0};
3180
3181    switch (event_id) {
3182      case IFEventID_start_e: {
3183	  dhcp_cstate_t		prev_state = dhcp->state;
3184
3185	  my_log(LOG_DEBUG, "DHCP %s: BOUND", if_name(if_p));
3186	  dhcp->state = dhcp_cstate_bound_e;
3187	  dhcp->lease.needs_write = TRUE;
3188	  dhcp->disable_arp_collision_detection = TRUE;
3189
3190	  /* clean-up anything that might have come before */
3191	  dhcp_cancel_pending_events(service_p);
3192
3193	  switch (prev_state) {
3194	  case dhcp_cstate_renew_e:
3195	  case dhcp_cstate_rebind_e:
3196	      dhcp->lease.needs_write
3197		  = should_write_lease(dhcp, current_time);
3198	      break;
3199	  default:
3200	      /* make sure autoaddr is disabled */
3201	      service_disable_autoaddr(service_p);
3202	      break;
3203	  }
3204
3205	  /* For renew/rebind and netboot cases, we don't
3206	   * need to arp
3207	   */
3208	  if (prev_state == dhcp_cstate_rebind_e
3209	      || prev_state == dhcp_cstate_renew_e
3210	      || ServiceIsNetBoot(service_p)) {
3211	     break;
3212	  }
3213
3214	  /* Here we need to consider 3 prev_states:
3215           * Init, Select and Init-reboot
3216           */
3217	  if (prev_state == dhcp_cstate_select_e) {
3218              /* do an ARP probe of the supplied address */
3219              arp_client_probe(dhcp->arp,
3220                               (arp_result_func_t *)dhcp_bound, service_p,
3221                               (void *)IFEventID_arp_e, G_ip_zeroes,
3222                               dhcp->saved.our_ip);
3223              return;
3224	  }
3225
3226	  if (ServiceGetActiveIPAddress(service_p).s_addr
3227	      == dhcp->saved.our_ip.s_addr) {
3228	      /* no need to probe, we're already using it */
3229	      if (prev_state == dhcp_cstate_init_reboot_e
3230		  || prev_state == dhcp_cstate_init_e) {
3231                  arp_client_announce(dhcp->arp,
3232                                      (arp_result_func_t *)dhcp_bound,
3233				      service_p,
3234                                      (void *)IFEventID_arp_e, G_ip_zeroes,
3235                                      dhcp->saved.our_ip, TRUE);
3236                  return;
3237	      }
3238	  }
3239	  break;
3240	}
3241	case IFEventID_arp_e: {
3242	  arp_result_t *	result = (arp_result_t *)event_data;
3243
3244	  if (result->error) {
3245	      my_log(LOG_ERR, "DHCP %s: ARP probe failed, %s",
3246		     if_name(if_p),
3247		     arp_client_errmsg(dhcp->arp));
3248	      dhcp_failed(service_p, ipconfig_status_internal_error_e);
3249	      return;
3250	  }
3251	  if (result->in_use) {
3252	      char		msg[128];
3253	      struct timeval 	tv;
3254
3255	      snprintf(msg, sizeof(msg),
3256		       IP_FORMAT " in use by " EA_FORMAT
3257		       ", DHCP Server "
3258		       IP_FORMAT, IP_LIST(&dhcp->saved.our_ip),
3259		       EA_LIST(result->addr.target_hardware),
3260		       IP_LIST(&dhcp->saved.server_ip));
3261	      if (dhcp->conflicting_address.s_addr
3262		  != dhcp->saved.our_ip.s_addr) {
3263		  /* we got a different address, so allow warning again */
3264		  dhcp->user_warned = FALSE;
3265	      }
3266	      else if (dhcp->user_warned == FALSE) {
3267		  ServiceReportIPv4AddressConflict(service_p,
3268						   dhcp->saved.our_ip);
3269		  dhcp->user_warned = TRUE;
3270	      }
3271	      dhcp->conflicting_address = dhcp->saved.our_ip;
3272	      my_log(LOG_ERR, "DHCP %s: %s", if_name(if_p), msg);
3273	      _dhcp_lease_clear(service_p, FALSE);
3274	      dhcp->lease.valid = FALSE;
3275	      service_router_clear(service_p);
3276	      service_publish_failure(service_p,
3277				      ipconfig_status_address_in_use_e);
3278	      if (dhcp->saved.is_dhcp) {
3279		  dhcp_decline(service_p, IFEventID_start_e, NULL);
3280		  return;
3281	      }
3282	      dhcp_cancel_pending_events(service_p);
3283	      (void)service_disable_autoaddr(service_p);
3284	      dhcp->saved.our_ip.s_addr = 0;
3285	      tv.tv_sec = 10; /* retry in a bit */
3286	      tv.tv_usec = 0;
3287	      timer_set_relative(dhcp->timer, tv,
3288				 (timer_func_t *)dhcp_init,
3289				 service_p, (void *)IFEventID_start_e, NULL);
3290	      return;
3291	  }
3292	  dhcp_cancel_pending_events(service_p);
3293	  break;
3294	}
3295	default:
3296	  return;
3297    }
3298
3299    /* the lease is valid */
3300    dhcp->lease.valid = TRUE;
3301
3302    /* allow user warning to appear */
3303    dhcp->conflicting_address.s_addr = 0;
3304    dhcp->user_warned = FALSE;
3305
3306    /* set the interface's address and output the status */
3307    option = dhcpol_find_with_length(&dhcp->saved.options,
3308				     dhcptag_subnet_mask_e, sizeof(mask));
3309    if (option != NULL) {
3310	mask = *((struct in_addr *)option);
3311    }
3312
3313    /* set our address */
3314    if ((dhcp->saved.our_ip.s_addr
3315	 != ServiceGetActiveIPAddress(service_p).s_addr)
3316	|| (mask.s_addr
3317	    != ServiceGetActiveSubnetMask(service_p).s_addr)) {
3318	(void)service_set_address(service_p, dhcp->saved.our_ip,
3319				  mask, G_ip_zeroes);
3320    }
3321    /* stop link local if necessary */
3322    if (G_dhcp_success_deconfigures_linklocal) {
3323	linklocal_service_change(service_p, LINKLOCAL_NO_ALLOCATE);
3324    }
3325
3326    /* allow us to be called in the event of a subsequent collision */
3327    dhcp->disable_arp_collision_detection = FALSE;
3328
3329    if (dhcp->lease.length == DHCP_INFINITE_LEASE) {
3330	/* don't need to talk to server anymore */
3331	my_log(LOG_DEBUG, "DHCP %s: infinite lease",
3332	       if_name(if_p));
3333    }
3334    else {
3335	if (dhcp->lease.t1 < current_time) {
3336	    /* t1 is already in the past, wake up in RENEW momentarily */
3337	    tv.tv_sec = 0;
3338	    tv.tv_usec = 1;
3339	    do_arp = FALSE;
3340	}
3341	else {
3342	    /* wake up in RENEW at t1 */
3343	    tv.tv_sec = dhcp->lease.t1 - current_time;
3344	    tv.tv_usec = 0;
3345	}
3346	dhcp->renew_rebind_time = dhcp->lease.t1;
3347	timer_set_relative(dhcp->timer, tv,
3348			   (timer_func_t *)dhcp_renew_rebind,
3349			   service_p, (void *)IFEventID_start_e, NULL);
3350    }
3351
3352    /* get the router's MAC address */
3353    dhcp->resolve_router_timed_out = FALSE;
3354    if (do_arp
3355	&& service_update_router_address(service_p, &dhcp->saved.options,
3356					 dhcp->saved.our_ip)
3357	&& service_resolve_router(service_p, dhcp->arp,
3358				  dhcp_resolve_router_callback,
3359				  dhcp->saved.our_ip)) {
3360	/* resolving router's MAC address started */
3361    }
3362    else {
3363	if (dhcp->saved.is_dhcp) {
3364	    /* save the lease now since we won't ARP for the router */
3365	    _dhcp_lease_save(service_p, dhcp->lease.start,
3366			     (uint8_t *)dhcp->saved.pkt, dhcp->saved.pkt_size,
3367			     dhcp->lease.needs_write);
3368	    dhcp->lease.needs_write = FALSE;
3369	}
3370	dhcp_publish_success(service_p);
3371    }
3372
3373    return;
3374}
3375
3376static void
3377dhcp_no_server(ServiceRef service_p, IFEventID_t event_id, void * event_data)
3378{
3379    Service_dhcp_t *	dhcp = (Service_dhcp_t *)ServiceGetPrivate(service_p);
3380    struct timeval 	tv;
3381
3382    switch (event_id) {
3383      case IFEventID_start_e: {
3384	  if (G_dhcp_failure_configures_linklocal) {
3385	      linklocal_service_change(service_p, LINKLOCAL_ALLOCATE);
3386	  }
3387	  dhcp_cancel_pending_events(service_p);
3388	  service_publish_failure(service_p, ipconfig_status_no_server_e);
3389
3390#define INIT_RETRY_INTERVAL_SECS      (1 * 60)
3391	  tv.tv_sec = INIT_RETRY_INTERVAL_SECS;
3392	  tv.tv_usec = 0;
3393	  /* wake up in INIT state after a period of waiting */
3394	  timer_set_relative(dhcp->timer, tv,
3395			     (timer_func_t *)dhcp_init,
3396			     service_p, (void *)IFEventID_start_e, NULL);
3397	  break;
3398      }
3399      default: {
3400	break;
3401      }
3402    }
3403    return;
3404}
3405
3406static void
3407dhcp_decline(ServiceRef service_p, IFEventID_t event_id, void * event_data)
3408{
3409    Service_dhcp_t *	dhcp = (Service_dhcp_t *)ServiceGetPrivate(service_p);
3410    interface_t *	if_p = service_interface(service_p);
3411    ipconfig_status_t	status = ipconfig_status_success_e;
3412    struct timeval 	tv;
3413
3414    switch (event_id) {
3415      case IFEventID_start_e: {
3416	  dhcpoa_t	 	options;
3417
3418	  my_log(LOG_DEBUG, "DHCP %s: DECLINE", if_name(if_p));
3419
3420	  /* clean-up anything that might have come before */
3421	  dhcp_cancel_pending_events(service_p);
3422
3423	  /* decline the address */
3424	  dhcp->state = dhcp_cstate_decline_e;
3425	  /* ALIGN: txbuf is uint32_t aligned, cast ok */
3426	  dhcp->request = make_dhcp_request((struct dhcp *)(void *)dhcp->txbuf,
3427					    sizeof(dhcp->txbuf),
3428					    dhcp_msgtype_decline_e,
3429					    if_link_address(if_p),
3430					    if_link_arptype(if_p),
3431					    if_link_length(if_p),
3432					    dhcp->client_id,
3433					    dhcp->client_id_len,
3434					    FALSE,
3435					    &options);
3436	  if (dhcp->request == NULL) {
3437	      status = ipconfig_status_allocation_failed_e;
3438	      goto error;
3439	  }
3440	  if (dhcpoa_add(&options, dhcptag_requested_ip_address_e,
3441			 sizeof(dhcp->saved.our_ip), &dhcp->saved.our_ip)
3442	      != dhcpoa_success_e) {
3443	      my_log(LOG_ERR, "DHCP %s: DECLINE couldn't add our ip, %s",
3444		     if_name(if_p), dhcpoa_err(&options));
3445	      status = ipconfig_status_allocation_failed_e;
3446	      goto error;
3447	  }
3448	  if (dhcpoa_add(&options, dhcptag_server_identifier_e,
3449			 sizeof(dhcp->saved.server_ip), &dhcp->saved.server_ip)
3450	      != dhcpoa_success_e) {
3451	      my_log(LOG_ERR, "DHCP %s: DECLINE couldn't add server ip, %s",
3452		     if_name(if_p), dhcpoa_err(&options));
3453	      status = ipconfig_status_allocation_failed_e;
3454	      goto error;
3455	  }
3456	  if (dhcpoa_add(&options, dhcptag_end_e, 0, 0)
3457	      != dhcpoa_success_e) {
3458	      my_log(LOG_ERR, "DHCP %s: DECLINE failed to terminate options",
3459		     if_name(if_p));
3460	      status = ipconfig_status_allocation_failed_e;
3461	      goto error;
3462	  }
3463	  if (bootp_client_transmit(dhcp->client,
3464				    G_ip_broadcast, G_ip_zeroes,
3465				    G_server_port, G_client_port,
3466				    dhcp->request, dhcp->request_size) < 0) {
3467	      my_log(LOG_ERR,
3468		     "DHCP %s: DECLINE transmit failed",
3469		     if_name(if_p));
3470	  }
3471	  (void)service_remove_address(service_p);
3472	  dhcp->saved.our_ip.s_addr = 0;
3473	  dhcp->lease.valid = FALSE;
3474	  service_router_clear(service_p);
3475	  service_disable_autoaddr(service_p);
3476	  tv.tv_sec = 10; /* retry in a bit */
3477	  tv.tv_usec = 0;
3478	  timer_set_relative(dhcp->timer, tv,
3479			     (timer_func_t *)dhcp_init,
3480			     service_p, (void *)IFEventID_start_e, NULL);
3481	  break;
3482      }
3483      default:
3484	  break;
3485    }
3486    return;
3487 error:
3488    dhcp_failed(service_p, status);
3489    return;
3490}
3491
3492static void
3493dhcp_unbound(ServiceRef service_p, IFEventID_t event_id, void * event_data)
3494{
3495    Service_dhcp_t *	dhcp = (Service_dhcp_t *)ServiceGetPrivate(service_p);
3496    interface_t *	if_p = service_interface(service_p);
3497    struct timeval 	tv = {0,0};
3498
3499    switch (event_id) {
3500      case IFEventID_start_e: {
3501	  int		nak = (int)(intptr_t)event_data;
3502
3503	  my_log(LOG_DEBUG, "DHCP %s: UNBOUND%s", if_name(if_p),
3504		 nak ? " (NAK)" : "");
3505
3506	  /* clean-up anything that might have come before */
3507	  dhcp_cancel_pending_events(service_p);
3508	  dhcp->state = dhcp_cstate_unbound_e;
3509
3510	  /* stop using the IP address immediately */
3511	  _dhcp_lease_clear(service_p, nak);
3512	  (void)service_remove_address(service_p);
3513	  dhcp->saved.our_ip.s_addr = 0;
3514	  dhcp->lease.valid = FALSE;
3515	  dhcp->got_nak = FALSE;
3516	  service_router_clear(service_p);
3517
3518	  tv.tv_sec = 0;
3519	  tv.tv_usec = 1000;
3520	  timer_set_relative(dhcp->timer, tv,
3521			     (timer_func_t *)dhcp_init,
3522			     service_p, (void *)IFEventID_start_e, NULL);
3523	  break;
3524      }
3525      default:
3526	break;
3527    }
3528    return;
3529}
3530
3531static void
3532dhcp_renew_rebind(ServiceRef service_p, IFEventID_t event_id, void * event_data)
3533{
3534    absolute_time_t 	current_time = timer_current_secs();
3535    Service_dhcp_t *	dhcp = (Service_dhcp_t *)ServiceGetPrivate(service_p);
3536    interface_t *	if_p = service_interface(service_p);
3537    ipconfig_status_t	status = ipconfig_status_success_e;
3538    struct timeval 	tv;
3539
3540    switch (event_id) {
3541      case IFEventID_start_e: {
3542	  dhcp_lease_time_t	lease_option;
3543	  dhcpoa_t	 	options;
3544
3545	  /* clean-up anything that might have come before */
3546	  dhcp_cancel_pending_events(service_p);
3547	  dhcp->allow_wake_with_short_lease = FALSE;
3548	  dhcp->start_secs = current_time;
3549
3550	  dhcp->state = dhcp_cstate_renew_e;
3551	  my_log(LOG_DEBUG, "DHCP %s: RENEW/REBIND", if_name(if_p));
3552	  /* ALIGN: txbuf is uint32_t aligned, cast ok */
3553	  dhcp->request = make_dhcp_request((struct dhcp *)(void *)dhcp->txbuf,
3554					    sizeof(dhcp->txbuf),
3555					    dhcp_msgtype_request_e,
3556					    if_link_address(if_p),
3557					    if_link_arptype(if_p),
3558					    if_link_length(if_p),
3559					    dhcp->client_id,
3560					    dhcp->client_id_len,
3561					    FALSE,
3562					    &options);
3563	  if (dhcp->request == NULL) {
3564	      status = ipconfig_status_allocation_failed_e;
3565	      goto error;
3566	  }
3567	  dhcp->try = 0;
3568	  dhcp->request->dp_ciaddr = dhcp->saved.our_ip;
3569	  lease_option = htonl(SUGGESTED_LEASE_LENGTH);
3570	  if (dhcpoa_add(&options, dhcptag_lease_time_e, sizeof(lease_option),
3571			 &lease_option) != dhcpoa_success_e) {
3572	      my_log(LOG_ERR, "DHCP %s: RENEW/REBIND couldn't add"
3573		     " lease time: %s", if_name(if_p),
3574		     dhcpoa_err(&options));
3575	      status = ipconfig_status_allocation_failed_e;
3576	      goto error;
3577	  }
3578	  add_computer_name(&options);
3579	  if (dhcpoa_add(&options, dhcptag_end_e, 0, 0)
3580	      != dhcpoa_success_e) {
3581	      my_log(LOG_ERR, "DHCP %s: RENEW/REBIND failed to terminate options",
3582		     if_name(if_p));
3583	      status = ipconfig_status_allocation_failed_e;
3584	      goto error;
3585	  }
3586	  /* enable packet reception */
3587	  bootp_client_enable_receive(dhcp->client,
3588				      (bootp_receive_func_t *)dhcp_renew_rebind,
3589				      service_p, (void *)IFEventID_data_e);
3590
3591	  /* FALL THROUGH */
3592      }
3593      case IFEventID_timeout_e: {
3594	  struct in_addr	dest_ip = {0};
3595	  absolute_time_t	wakeup_time;
3596
3597	  /*
3598	   * If we're running as the result of our timer firing (i.e. not
3599	   * by the Wake code calling us), check whether the time has changed.
3600	   * If it has, assume that our timer fired accurately, and compute the
3601	   * difference between current_time and dhcp->renew_rebind_time.
3602	   * Assume that the entire delta is due to the time changing, and
3603	   * apply the delta to the lease information.
3604	   */
3605	  if (timer_still_pending(dhcp->timer) == FALSE
3606	      && timer_time_changed(dhcp->timer)) {
3607	      dhcp_adjust_lease_info(service_p,
3608				     (current_time - dhcp->renew_rebind_time));
3609	  }
3610
3611	  if (current_time >= dhcp->lease.expiration) {
3612	      /* server did not respond */
3613	      service_publish_failure(service_p,
3614				      ipconfig_status_server_not_responding_e);
3615	      dhcp_unbound(service_p, IFEventID_start_e, NULL);
3616	      return;
3617	  }
3618	  if (current_time < dhcp->lease.t2) {
3619	      dhcp->state = dhcp_cstate_renew_e;
3620	      wakeup_time = current_time + (dhcp->lease.t2 - current_time) / 2;
3621	      dest_ip = dhcp->saved.server_ip;
3622	  }
3623	  else { /* rebind */
3624	      dhcp->state = dhcp_cstate_rebind_e;
3625	      wakeup_time = current_time
3626		  + (dhcp->lease.expiration - current_time) / 2;
3627	      dest_ip = G_ip_broadcast;
3628	  }
3629	  dhcp->request->dp_xid = htonl(++dhcp->xid);
3630	  dhcp->request->dp_secs
3631	      = htons((uint16_t)(current_time - dhcp->start_secs));
3632
3633	  /* send the packet */
3634	  if (bootp_client_transmit(dhcp->client,
3635				    dest_ip, dhcp->saved.our_ip,
3636				    G_server_port, G_client_port,
3637				    dhcp->request, dhcp->request_size) < 0) {
3638	      my_log(LOG_ERR,
3639		     "DHCP %s: RENEW/REBIND transmit failed",
3640		     if_name(if_p));
3641	  }
3642	  /* wait for responses */
3643#define RENEW_REBIND_MIN_WAIT_SECS	60
3644	  if ((wakeup_time - current_time) < RENEW_REBIND_MIN_WAIT_SECS) {
3645	      tv.tv_sec = RENEW_REBIND_MIN_WAIT_SECS;
3646	      dhcp->renew_rebind_time = current_time + tv.tv_sec;
3647	  }
3648	  else {
3649	      tv.tv_sec = wakeup_time - current_time;
3650	      dhcp->renew_rebind_time = wakeup_time;
3651	  }
3652	  tv.tv_usec = 0;
3653	  my_log(LOG_DEBUG, "DHCP %s: RENEW/REBIND waiting at %d for %d.%06d",
3654		 if_name(if_p),
3655		 current_time - dhcp->start_secs,
3656		 tv.tv_sec, tv.tv_usec);
3657	  timer_set_relative(dhcp->timer, tv,
3658			     (timer_func_t *)dhcp_renew_rebind,
3659			     service_p, (void *)IFEventID_timeout_e, NULL);
3660	  if (dhcp->wake_time == 0
3661	      || current_time > dhcp->wake_time
3662	      || (dhcp->wake_time - current_time) < G_wake_skew_secs) {
3663	      ServiceSetActiveDuringSleepNeedsAttention(service_p);
3664	  }
3665	  break;
3666      }
3667      case IFEventID_data_e: {
3668	  boolean_t 		is_dhcp = TRUE;
3669	  dhcp_lease_time_t	lease = SUGGESTED_LEASE_LENGTH;
3670	  bootp_receive_data_t *pkt = (bootp_receive_data_t *)event_data;
3671	  dhcp_msgtype_t	reply_msgtype = dhcp_msgtype_none_e;
3672	  struct in_addr	server_ip;
3673	  dhcp_lease_time_t	t1;
3674	  dhcp_lease_time_t 	t2;
3675
3676	  if (verify_packet(pkt, dhcp->xid, if_p, &reply_msgtype,
3677			    &server_ip, &is_dhcp) == FALSE
3678	      || is_dhcp == FALSE) {
3679	      /* reject the packet */
3680	      return;
3681	  }
3682
3683	  if (reply_msgtype == dhcp_msgtype_nak_e) {
3684	      service_publish_failure(service_p,
3685				      ipconfig_status_lease_terminated_e);
3686	      dhcp_unbound(service_p, IFEventID_start_e, NULL);
3687	      return;
3688	  }
3689	  if (reply_msgtype != dhcp_msgtype_ack_e
3690	      || server_ip.s_addr == 0
3691	      || ip_valid(pkt->data->dp_yiaddr) == FALSE) {
3692	      /* reject the packet */
3693	      return;
3694	  }
3695	  dhcp_get_lease_from_options(&pkt->options, &lease, &t1, &t2);
3696
3697	  /* address has to match, otherwise start over */
3698	  if (pkt->data->dp_yiaddr.s_addr != dhcp->saved.our_ip.s_addr) {
3699	      service_publish_failure(service_p,
3700				      ipconfig_status_server_error_e);
3701	      dhcp_unbound(service_p, IFEventID_start_e, NULL);
3702	      return;
3703	  }
3704	  dhcp_set_lease_params(service_p, "RENEW/REBIND", is_dhcp,
3705				current_time, lease, t1, t2);
3706	  dhcpol_free(&dhcp->saved.options);
3707	  bcopy(pkt->data, dhcp->saved.pkt, pkt->size);
3708	  dhcp->saved.pkt_size = pkt->size;
3709	  dhcp->saved.rating = 0;
3710	  /* ALIGN: saved.pkt is uint32_t aligned, cast ok */
3711	  (void)dhcpol_parse_packet(&dhcp->saved.options,
3712				    (void *)dhcp->saved.pkt,
3713				    dhcp->saved.pkt_size, NULL);
3714	  dhcp->saved.server_ip = server_ip;
3715	  dhcp->saved.is_dhcp = TRUE;
3716	  dhcp_bound(service_p, IFEventID_start_e, NULL);
3717	  break;
3718      }
3719      default:
3720	  return;
3721    }
3722    return;
3723
3724 error:
3725    dhcp_failed(service_p, status);
3726    return;
3727}
3728
3729#include "arp.h"
3730
3731static void
3732dhcp_wait_until_arp_completes(ServiceRef service_p)
3733{
3734    struct in_addr		addr_wait;
3735    Service_dhcp_t *		dhcp;
3736    int				i;
3737    int				if_index;
3738    interface_t *		if_p = service_interface(service_p);
3739    route_msg			msg;
3740    struct in_addr		our_mask;
3741    boolean_t			resolved = FALSE;
3742    int				s;
3743    struct sockaddr_dl *	sdl;
3744    struct sockaddr_inarp *	sin;
3745    struct in_addr		subnet;
3746
3747    dhcp = (Service_dhcp_t *)ServiceGetPrivate(service_p);
3748    our_mask = ServiceGetActiveSubnetMask(service_p);
3749    subnet.s_addr = dhcp->saved.our_ip.s_addr & our_mask.s_addr;
3750    if (in_subnet(subnet, our_mask, dhcp->saved.server_ip)) {
3751	/* we'll ARP for the DHCP server */
3752	addr_wait = dhcp->saved.server_ip;
3753    }
3754    else {
3755	struct in_addr *	router_p;
3756
3757	router_p = dhcpol_find_with_length(&dhcp->saved.options,
3758					   dhcptag_router_e,
3759					   sizeof(*router_p));
3760	if (router_p == NULL
3761	    || router_p->s_addr == dhcp->saved.our_ip.s_addr) {
3762	    /* all subnet routes are local, so we'll ARP for the DHCP server */
3763	    addr_wait = dhcp->saved.server_ip;
3764	}
3765	else {
3766	    /* the router is the gateway to the DHCP server */
3767	    addr_wait = *router_p;
3768	}
3769    }
3770    s = arp_open_routing_socket();
3771    if (s == -1) {
3772	my_log(LOG_ERR, "DHCP %s: arp_open_routing_socket() failed, %s",
3773	       if_name(if_p),
3774	       strerror(errno));
3775	return;
3776    }
3777    /* ALIGN: msg_p->m_space is aligned sufficiently
3778     * to dereference sdl safely */
3779    sin = (struct sockaddr_inarp *)(void *)msg.m_space;
3780    if_index = if_link_index(if_p);
3781
3782#define N_ARP_GET_TRIES		5
3783
3784    i = 1;
3785    while (TRUE) {
3786	if (arp_get(s, &msg, addr_wait, if_index) != ARP_RETURN_SUCCESS) {
3787	    goto failed;
3788	}
3789	/* ALIGN: msg_p->m_space is aligned sufficiently
3790	 * to dereference sdl safely */
3791	sdl = (struct sockaddr_dl *)(void *)(sin->sin_len + (char *)sin);
3792	if (sdl->sdl_family == AF_LINK && sdl->sdl_alen != 0) {
3793	    resolved = TRUE;
3794	    break;
3795	}
3796	if (i == N_ARP_GET_TRIES) {
3797	    break;
3798	}
3799	i++;
3800	/* sleep for 1 millisecond, and try again */
3801	usleep(1000);
3802    }
3803    if (G_IPConfiguration_verbose) {
3804	if (resolved == FALSE) {
3805	    my_log(LOG_DEBUG, "DHCP %s:" IP_FORMAT " was NOT resolved",
3806		   if_name(if_p), IP_LIST(&addr_wait));
3807	}
3808	else  {
3809	    my_log(LOG_DEBUG,
3810		   "DHCP %s: " IP_FORMAT
3811		   " is resolved, %s after trying %d time(s)",
3812		   if_name(if_p), IP_LIST(&addr_wait),
3813		   link_ntoa(sdl), i);
3814	}
3815    }
3816 failed:
3817    close(s);
3818    return;
3819}
3820
3821static void
3822dhcp_release(ServiceRef service_p)
3823{
3824    interface_t *	if_p = service_interface(service_p);
3825    Service_dhcp_t *	dhcp = (Service_dhcp_t *)ServiceGetPrivate(service_p);
3826    link_status_t	link_status;
3827    dhcpoa_t	 	options;
3828
3829    if (dhcp->saved.is_dhcp == FALSE || dhcp->lease.valid == FALSE) {
3830	return;
3831    }
3832
3833    my_log(LOG_DEBUG, "DHCP %s: RELEASE", if_name(if_p));
3834    _dhcp_lease_clear(service_p, FALSE);
3835    dhcp->lease.valid = FALSE;
3836    service_router_clear(service_p);
3837
3838    /* clean-up anything that might have come before */
3839    dhcp_cancel_pending_events(service_p);
3840
3841    /* don't bother trying to transmit if the link is down */
3842    link_status = service_link_status(service_p);
3843    if (link_status.valid
3844	&& link_status.active == FALSE) {
3845	return;
3846    }
3847
3848    /* release the address */
3849    /* ALIGN: txbuf is aligned to at least sizeof(uint32_t) bytes */
3850    dhcp->request = make_dhcp_request((struct dhcp *)(void *)dhcp->txbuf,
3851				      sizeof(dhcp->txbuf),
3852				      dhcp_msgtype_release_e,
3853				      if_link_address(if_p),
3854				      if_link_arptype(if_p),
3855				      if_link_length(if_p),
3856				      dhcp->client_id, dhcp->client_id_len,
3857				      FALSE,
3858				      &options);
3859    if (dhcp->request == NULL) {
3860	return;
3861    }
3862    dhcp->request->dp_xid = htonl(++dhcp->xid);
3863    dhcp->request->dp_ciaddr = dhcp->saved.our_ip;
3864    if (dhcpoa_add(&options, dhcptag_server_identifier_e,
3865		   sizeof(dhcp->saved.server_ip), &dhcp->saved.server_ip)
3866	!= dhcpoa_success_e) {
3867	my_log(LOG_ERR, "DHCP %s: RELEASE couldn't add server ip, %s",
3868	       if_name(if_p), dhcpoa_err(&options));
3869	return;
3870    }
3871    if (dhcpoa_add(&options, dhcptag_end_e, 0, 0)
3872	!= dhcpoa_success_e) {
3873	my_log(LOG_ERR, "DHCP %s: RELEASE failed to terminate options",
3874	       if_name(if_p));
3875	return;
3876    }
3877    if (bootp_client_transmit(dhcp->client,
3878			      dhcp->saved.server_ip, dhcp->saved.our_ip,
3879			      G_server_port, G_client_port,
3880			      dhcp->request, dhcp->request_size) < 0) {
3881	my_log(LOG_ERR,
3882	       "DHCP %s: RELEASE transmit failed",
3883	       if_name(if_p));
3884	return;
3885    }
3886    dhcp_wait_until_arp_completes(service_p);
3887    dhcp->saved.our_ip.s_addr = 0;
3888    return;
3889}
3890
3891#define DEFAULT_LEASE_LENGTH		(60 * 60)		/* 1 hour */
3892#define MIN_LEASE_LENGTH		(3) 			/* 3 seconds */
3893#define MIN_T1_VAL			(2)			/* 2 seconds */
3894#define MIN_T2_VAL			(2)			/* 2 seconds */
3895
3896void
3897dhcp_get_lease_from_options(dhcpol_t * options, dhcp_lease_time_t * lease,
3898			    dhcp_lease_time_t * t1, dhcp_lease_time_t * t2)
3899{
3900    dhcp_lease_time_t *	lease_opt;
3901    dhcp_lease_time_t *	t1_opt;
3902    dhcp_lease_time_t *	t2_opt;
3903
3904    lease_opt = (dhcp_lease_time_t *)
3905	dhcpol_find_with_length(options, dhcptag_lease_time_e,
3906				sizeof(dhcp_lease_time_t));
3907    t1_opt = (dhcp_lease_time_t *)
3908	dhcpol_find_with_length(options, dhcptag_renewal_t1_time_value_e,
3909				sizeof(dhcp_lease_time_t));
3910    t2_opt = (dhcp_lease_time_t *)
3911	dhcpol_find_with_length(options, dhcptag_rebinding_t2_time_value_e,
3912				sizeof(dhcp_lease_time_t));
3913    if (lease_opt != NULL) {
3914	*lease = ntohl(*lease_opt);
3915	if (*lease < MIN_LEASE_LENGTH) {
3916	    *lease = MIN_LEASE_LENGTH;
3917	}
3918    }
3919    if (t1_opt != NULL) {
3920	*t1 = ntohl(*t1_opt);
3921	if (*t1 < MIN_T1_VAL) {
3922	    *t1 = MIN_T1_VAL;
3923	}
3924    }
3925    if (t2_opt != NULL) {
3926	*t2 = ntohl(*t2_opt);
3927	if (*t2 < MIN_T2_VAL) {
3928	    *t2 = MIN_T2_VAL;
3929	}
3930    }
3931    if (lease_opt == NULL) {
3932	if (t1_opt != NULL) {
3933	    *lease = *t1;
3934	}
3935	else if (t2_opt != NULL) {
3936	    *lease = *t2;
3937	}
3938	else {
3939	    *lease = DEFAULT_LEASE_LENGTH;
3940	}
3941    }
3942    if (*lease == DHCP_INFINITE_LEASE) {
3943	*t1 = *t2 = 0;
3944    }
3945    else if (t1_opt == NULL || *t1 >= *lease
3946	     || t2_opt == NULL || *t2 >= *lease
3947	     || *t2 < *t1) {
3948	*t1 = (*lease) / 2;
3949	*t2 = (dhcp_lease_time_t) ((double)(*lease) * 0.875);
3950    }
3951    return;
3952}
3953
3954struct in_addr *
3955dhcp_get_router_from_options(dhcpol_t * options_p, struct in_addr our_ip)
3956{
3957    struct in_addr *	router_p;
3958
3959    router_p = dhcpol_find_with_length(options_p, dhcptag_router_e,
3960				       sizeof(*router_p));
3961    if (router_p == NULL) {
3962	goto failed;
3963    }
3964    if (router_p->s_addr == our_ip.s_addr) {
3965	/* proxy arp, use DNS server instead */
3966	router_p = dhcpol_find_with_length(options_p,
3967					   dhcptag_domain_name_server_e,
3968					   sizeof(*router_p));
3969	if (router_p == NULL) {
3970	    goto failed;
3971	}
3972    }
3973    if (router_p->s_addr == 0 || router_p->s_addr == 0xffffffff) {
3974	goto failed;
3975    }
3976    return (router_p);
3977
3978 failed:
3979    return (NULL);
3980}
3981
3982