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