1/*
2 * Copyright (c) 2000-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
25/*
26 * DHCPLease.c
27 * - routines to handle the DHCP in-memory lease list and the lease
28 *   stored in the filesystem
29 */
30
31/*
32 * Modification History
33 *
34 * June 11, 2009		Dieter Siegmund (dieter@apple.com)
35 * - split out from ipconfigd.c
36 */
37
38#include <CoreFoundation/CFString.h>
39#include <SystemConfiguration/SCValidation.h>
40#include <SystemConfiguration/SCPrivate.h>
41#include "DHCPLease.h"
42#include "util.h"
43#include "host_identifier.h"
44#include "globals.h"
45#include "cfutil.h"
46#include "dhcp_thread.h"
47#include "cfutil.h"
48
49#define DHCPCLIENT_LEASE_FILE_FMT	DHCPCLIENT_LEASES_DIR "/%s-%s"
50
51/* required properties: */
52#define kLeaseStartDate			CFSTR("LeaseStartDate")
53#define kPacketData			CFSTR("PacketData")
54#define kRouterHardwareAddress		CFSTR("RouterHardwareAddress")
55#define kSSID				CFSTR("SSID") /* wi-fi only */
56
57/* informative properties: */
58#define kLeaseLength			CFSTR("LeaseLength")
59#define kIPAddress			CFSTR("IPAddress")
60#define kRouterIPAddress		CFSTR("RouterIPAddress")
61
62/*
63 * Function: DHCPLeaseCreateWithDictionary
64 * Purpose:
65 *   Instantiate a new DHCPLease structure corresponding to the given
66 *   dictionary.  Validates that required properties are present,
67 *   returns NULL if those checks fail.
68 */
69static DHCPLeaseRef
70DHCPLeaseCreateWithDictionary(CFDictionaryRef dict, bool is_wifi)
71{
72    CFDataRef			hwaddr_data;
73    dhcp_lease_time_t		lease_time;
74    DHCPLeaseRef		lease_p;
75    CFDataRef			pkt_data;
76    CFRange			pkt_data_range;
77    struct in_addr *		router_p;
78    CFStringRef			ssid = NULL;
79    CFDateRef			start_date;
80    dhcp_lease_time_t		t1_time;
81    dhcp_lease_time_t		t2_time;
82
83    /* get the lease start time */
84    start_date = CFDictionaryGetValue(dict, kLeaseStartDate);
85    if (isA_CFDate(start_date) == NULL) {
86	goto failed;
87    }
88    /* get the packet data */
89    pkt_data = CFDictionaryGetValue(dict, kPacketData);
90    if (isA_CFData(pkt_data) == NULL) {
91	goto failed;
92    }
93    /* if Wi-Fi, get the SSID */
94    if (is_wifi) {
95	ssid = CFDictionaryGetValue(dict, kSSID);
96	if (isA_CFString(ssid) == NULL) {
97	    goto failed;
98	}
99    }
100
101    pkt_data_range.location = 0;
102    pkt_data_range.length = CFDataGetLength(pkt_data);
103    if (pkt_data_range.length < sizeof(struct dhcp)) {
104	goto failed;
105    }
106    lease_p = (DHCPLeaseRef)
107	malloc(offsetof(DHCPLease, pkt) + pkt_data_range.length);
108    bzero(lease_p, offsetof(DHCPLease, pkt));
109
110    /* copy the packet data */
111    CFDataGetBytes(pkt_data, pkt_data_range, lease_p->pkt);
112    lease_p->pkt_length = (int)pkt_data_range.length;
113
114    /* get the lease information and router IP address */
115    lease_p->lease_start = (absolute_time_t)CFDateGetAbsoluteTime(start_date);
116    { /* parse/retrieve options */
117	dhcpol_t			options;
118
119	(void)dhcpol_parse_packet(&options, (void *)lease_p->pkt,
120				  (int)pkt_data_range.length, NULL);
121	dhcp_get_lease_from_options(&options, &lease_time, &t1_time, &t2_time);
122	router_p = dhcp_get_router_from_options(&options, lease_p->our_ip);
123	dhcpol_free(&options);
124    }
125    lease_p->lease_length = lease_time;
126
127    /* get the IP address */
128    /* ALIGN: lease_p->pkt is aligned, cast ok. */
129    lease_p->our_ip = ((struct dhcp *)(void *)lease_p->pkt)->dp_yiaddr;
130
131    /* get the router information */
132    if (router_p != NULL) {
133	CFRange		hwaddr_range;
134
135	lease_p->router_ip = *router_p;
136	/* get the router hardware address */
137	hwaddr_data = CFDictionaryGetValue(dict, kRouterHardwareAddress);
138	hwaddr_range.length = 0;
139	if (isA_CFData(hwaddr_data) != NULL) {
140	    hwaddr_range.length = CFDataGetLength(hwaddr_data);
141	}
142	if (hwaddr_range.length > 0) {
143	    hwaddr_range.location = 0;
144	    if (hwaddr_range.length > sizeof(lease_p->router_hwaddr)) {
145		hwaddr_range.length = sizeof(lease_p->router_hwaddr);
146	    }
147	    lease_p->router_hwaddr_length = (int)hwaddr_range.length;
148	    CFDataGetBytes(hwaddr_data, hwaddr_range, lease_p->router_hwaddr);
149	}
150    }
151    if (ssid != NULL) {
152	CFRetain(ssid);
153	lease_p->ssid = ssid;
154    }
155    return (lease_p);
156
157 failed:
158    return (NULL);
159}
160
161static void
162DHCPLeaseDeallocate(void * arg)
163{
164    DHCPLeaseRef lease_p = (DHCPLeaseRef)arg;
165
166    if (lease_p->ssid != NULL) {
167	CFRelease(lease_p->ssid);
168    }
169    free(lease_p);
170    return;
171}
172
173void
174DHCPLeaseSetNAK(DHCPLeaseRef lease_p, int nak)
175{
176    lease_p->nak = nak;
177    return;
178}
179
180/*
181 * Function: DHCPLeaseCreate
182 * Purpose:
183 *   Instantiate a new DHCPLease structure corresponding to the given
184 *   information.
185 */
186static DHCPLeaseRef
187DHCPLeaseCreate(struct in_addr our_ip, struct in_addr router_ip,
188		const uint8_t * router_hwaddr, int router_hwaddr_length,
189		absolute_time_t lease_start,
190		dhcp_lease_time_t lease_length,
191		const uint8_t * pkt, int pkt_size,
192		CFStringRef ssid)
193{
194    DHCPLeaseRef		lease_p = NULL;
195
196    lease_p = (DHCPLeaseRef)
197	malloc(offsetof(DHCPLease, pkt) + pkt_size);
198    bzero(lease_p, offsetof(DHCPLease, pkt));
199    lease_p->our_ip = our_ip;
200    lease_p->router_ip = router_ip;
201    lease_p->lease_start = lease_start;
202    lease_p->lease_length = lease_length;
203    bcopy(pkt, lease_p->pkt, pkt_size);
204    lease_p->pkt_length = pkt_size;
205    if (router_hwaddr != NULL && router_hwaddr_length != 0) {
206	if (router_hwaddr_length > sizeof(lease_p->router_hwaddr)) {
207	    router_hwaddr_length = sizeof(lease_p->router_hwaddr);
208	}
209	lease_p->router_hwaddr_length = router_hwaddr_length;
210	bcopy(router_hwaddr, lease_p->router_hwaddr, router_hwaddr_length);
211    }
212    if (ssid != NULL) {
213	CFRetain(ssid);
214	lease_p->ssid = ssid;
215    }
216    return (lease_p);
217}
218
219static CFDictionaryRef
220DHCPLeaseCopyDictionary(DHCPLeaseRef lease_p)
221{
222    CFDataRef			data;
223    CFDateRef			date;
224    CFMutableDictionaryRef	dict;
225    CFNumberRef			num;
226    CFStringRef			str;
227
228    dict = CFDictionaryCreateMutable(NULL, 0,
229				     &kCFTypeDictionaryKeyCallBacks,
230				     &kCFTypeDictionaryValueCallBacks);
231    /* set the IP address */
232    str = CFStringCreateWithFormat(NULL, NULL, CFSTR(IP_FORMAT),
233				   IP_LIST(&lease_p->our_ip));
234    CFDictionarySetValue(dict, kIPAddress, str);
235    CFRelease(str);
236
237    /* set the lease start date */
238    date = CFDateCreate(NULL, lease_p->lease_start);
239    CFDictionarySetValue(dict, kLeaseStartDate, date);
240    CFRelease(date);
241
242    /* set the lease length */
243    num = CFNumberCreate(NULL, kCFNumberSInt32Type, &lease_p->lease_length);
244    CFDictionarySetValue(dict, kLeaseLength, num);
245    CFRelease(num);
246
247    /* set the SSID */
248    if (lease_p->ssid != NULL) {
249	CFDictionarySetValue(dict, kSSID, lease_p->ssid);
250    }
251
252    /* set the packet data */
253    data = CFDataCreateWithBytesNoCopy(NULL, lease_p->pkt, lease_p->pkt_length,
254				       kCFAllocatorNull);
255    CFDictionarySetValue(dict, kPacketData, data);
256    CFRelease(data);
257
258    if (lease_p->router_ip.s_addr != 0) {
259	/* set the router IP address */
260	str = CFStringCreateWithFormat(NULL, NULL, CFSTR(IP_FORMAT),
261				       IP_LIST(&lease_p->router_ip));
262	CFDictionarySetValue(dict, kRouterIPAddress, str);
263	CFRelease(str);
264
265	if (lease_p->router_hwaddr_length > 0) {
266	    /* set the router hardware address */
267	    data = CFDataCreateWithBytesNoCopy(NULL, lease_p->router_hwaddr,
268					       lease_p->router_hwaddr_length,
269					       kCFAllocatorNull);
270	    CFDictionarySetValue(dict, kRouterHardwareAddress, data);
271	    CFRelease(data);
272	}
273    }
274    return (dict);
275}
276
277static void
278DHCPLeasePrintToString(CFMutableStringRef str, DHCPLeaseRef lease_p)
279{
280    STRING_APPEND(str, "IP " IP_FORMAT " Start %d Length",
281		  IP_LIST(&lease_p->our_ip), (int)lease_p->lease_start);
282    if (lease_p->lease_length == DHCP_INFINITE_LEASE) {
283	STRING_APPEND(str, " infinite");
284    }
285    else {
286	STRING_APPEND(str, " %d", (int)lease_p->lease_length);
287    }
288
289    if (lease_p->router_ip.s_addr != 0) {
290	STRING_APPEND(str, " Router IP " IP_FORMAT,
291		      IP_LIST(&lease_p->router_ip));
292	if (lease_p->router_hwaddr_length > 0) {
293	    char	link_string[MAX_LINK_ADDR_LEN * 3];
294
295	    link_addr_to_string(link_string, sizeof(link_string),
296				lease_p->router_hwaddr,
297				lease_p->router_hwaddr_length);
298	    STRING_APPEND(str, " MAC %s", link_string);
299	}
300    }
301    if (lease_p->ssid != NULL) {
302	STRING_APPEND(str, " SSID '%@'", lease_p->ssid);
303    }
304    return;
305}
306
307static void
308DHCPLeaseListLog(DHCPLeaseListRef list_p)
309{
310    int			count;
311    int			i;
312    CFMutableStringRef	str;
313
314    str = CFStringCreateMutable(NULL, 0);
315    count = dynarray_count(list_p);
316    for (i = 0; i < count; i++) {
317	DHCPLeaseRef	lease_p = dynarray_element(list_p, i);
318
319	STRING_APPEND(str, "\n%d. ", i + 1);
320	DHCPLeasePrintToString(str, lease_p);
321    }
322    my_log(~LOG_DEBUG, "DHCPLeaseList has %d element(s)%@", count, str);
323    CFRelease(str);
324    return;
325}
326
327static bool
328DHCPLeaseListGetPath(const char * ifname,
329		     uint8_t cid_type, const void * cid, int cid_length,
330		     char * filename, int filename_size)
331{
332    char *			idstr;
333    char			idstr_scratch[128];
334
335    idstr = identifierToStringWithBuffer(cid_type, cid, cid_length,
336					 idstr_scratch, sizeof(idstr_scratch));
337    if (idstr == NULL) {
338	return (FALSE);
339    }
340    snprintf(filename, filename_size, DHCPCLIENT_LEASE_FILE_FMT, ifname,
341	     idstr);
342    if (idstr != idstr_scratch) {
343	free(idstr);
344    }
345    return (TRUE);
346}
347
348void
349DHCPLeaseListInit(DHCPLeaseListRef list_p)
350{
351    dynarray_init(list_p, DHCPLeaseDeallocate, NULL);
352    return;
353}
354
355void
356DHCPLeaseListFree(DHCPLeaseListRef list_p)
357{
358    dynarray_free(list_p);
359}
360
361/*
362 * Function: DHCPLeaseListRemoveStaleLeases
363 * Purpose:
364 *   Scans the list of leases removing any that are no longer valid.
365 */
366static void
367DHCPLeaseListRemoveStaleLeases(DHCPLeaseListRef list_p)
368{
369    int				count;
370    absolute_time_t 		current_time;
371    int				i;
372
373    count = dynarray_count(list_p);
374    if (count == 0) {
375	return;
376    }
377    current_time = timer_current_secs();
378    i = 0;
379    while (i < count) {
380	DHCPLeaseRef	lease_p = dynarray_element(list_p, i);
381
382	/* check the lease expiration */
383	if (lease_p->lease_length != DHCP_INFINITE_LEASE
384	    && current_time >= (lease_p->lease_start + lease_p->lease_length)) {
385	    /* lease is expired */
386	    if (G_IPConfiguration_verbose) {
387		my_log(LOG_DEBUG, "Removing Stale Lease "
388		       IP_FORMAT " Router " IP_FORMAT,
389		       IP_LIST(&lease_p->our_ip),
390		       IP_LIST(&lease_p->router_ip));
391	    }
392	    dynarray_free_element(list_p, i);
393	    count--;
394	}
395	else {
396	    i++;
397	}
398    }
399    return;
400}
401
402/*
403 * Function: DHCPLeaseListRead
404 *
405 * Purpose:
406 *   Read the single DHCP lease for the given interface/client_id into a
407 *   DHCPLeaseList structure.  This lease is marked as "tentative" because
408 *   we have no idea whether the lease is still good or not, since another
409 *   version of the OS (or another OS) could have had additional communication
410 *   with the DHCP server, invalidating our notion of the lease.  It also
411 *   affords a simple, self-cleansing mechanism to clear out the set of
412 *   leases we keep track of.
413 */
414void
415DHCPLeaseListRead(DHCPLeaseListRef list_p,
416		  const char * ifname, bool is_wifi,
417		  uint8_t cid_type, const void * cid, int cid_length)
418{
419    char			filename[PATH_MAX];
420    CFDictionaryRef		lease_dict = NULL;
421    DHCPLeaseRef		lease_p;
422    struct in_addr		lease_ip;
423
424    DHCPLeaseListInit(list_p);
425    if (DHCPLeaseListGetPath(ifname, cid_type, cid, cid_length,
426			     filename, sizeof(filename)) == FALSE) {
427	goto done;
428    }
429    lease_dict = my_CFPropertyListCreateFromFile(filename);
430    if (isA_CFDictionary(lease_dict) == NULL) {
431	goto done;
432    }
433    lease_p = DHCPLeaseCreateWithDictionary(lease_dict, is_wifi);
434    if (lease_p == NULL) {
435	goto done;
436    }
437    lease_p->tentative = TRUE;
438    dynarray_add(list_p, lease_p);
439    if (G_IPConfiguration_verbose) {
440	DHCPLeaseListLog(list_p);
441    }
442    lease_ip = lease_p->our_ip;
443    DHCPLeaseListRemoveStaleLeases(list_p);
444    if (DHCPLeaseListCount(list_p) == 0) {
445	remove_unused_ip(ifname, lease_ip);
446    }
447
448 done:
449    my_CFRelease(&lease_dict);
450    return;
451}
452
453/*
454 * Function: DHCPLeaseListWrite
455 *
456 * Purpose:
457 *   Write the last DHCP lease in the list for the given interface/client_id.
458 *   We only save the last (current) lease.  See the comments for
459 *   DHCPLeaseListRead above for more information.
460 */
461void
462DHCPLeaseListWrite(DHCPLeaseListRef list_p,
463		   const char * ifname,
464		   uint8_t cid_type, const void * cid, int cid_length)
465{
466    int			count;
467    char		filename[PATH_MAX];
468    CFDictionaryRef	lease_dict;
469    DHCPLeaseRef	lease_p;
470
471    if (DHCPLeaseListGetPath(ifname, cid_type, cid, cid_length,
472			     filename, sizeof(filename)) == FALSE) {
473	return;
474    }
475    DHCPLeaseListRemoveStaleLeases(list_p);
476    count = dynarray_count(list_p);
477    if (count == 0) {
478	unlink(filename);
479	return;
480    }
481    lease_p = dynarray_element(list_p, count - 1);
482    lease_dict = DHCPLeaseCopyDictionary(lease_p);
483    if (my_CFPropertyListWriteFile(lease_dict, filename, 0644) < 0) {
484	/*
485	 * An ENOENT error is expected on a read-only filesystem.  All
486	 * other errors should be reported.
487	 */
488	if (errno != ENOENT) {
489	    my_log(LOG_ERR, "my_CFPropertyListWriteFile(%s) failed, %s",
490		   filename, strerror(errno));
491	}
492    }
493    my_CFRelease(&lease_dict);
494    return;
495}
496
497/*
498 * Function: DHCPLeaseListCopyARPAddressInfo
499 * Purpose:
500 *   Returns a list of arp_address_info_t's corresponding to each
501 *   discoverable lease.
502 */
503arp_address_info_t *
504DHCPLeaseListCopyARPAddressInfo(DHCPLeaseListRef list_p,
505				CFStringRef ssid,
506				absolute_time_t * start_time_threshold_p,
507				bool tentative_ok,
508				int * ret_count)
509{
510    int				arp_info_count;
511    arp_address_info_t *	arp_info_p;
512    int				count;
513    int				i;
514    arp_address_info_t *	info_p;
515
516    DHCPLeaseListRemoveStaleLeases(list_p);
517    count = dynarray_count(list_p);
518    if (count == 0) {
519	*ret_count = 0;
520	return (NULL);
521    }
522    arp_info_p = (arp_address_info_t *)malloc(sizeof(*arp_info_p) * count);
523    arp_info_count = 0;
524    info_p = arp_info_p;
525    for (i = 0; i < count; i++) {
526	DHCPLeaseRef	lease_p = dynarray_element(list_p, i);
527
528	if (ssid != NULL) {
529	    if (lease_p->ssid == NULL || !CFEqual(lease_p->ssid, ssid)) {
530		if (G_IPConfiguration_verbose) {
531		    my_log(LOG_DEBUG,
532			   "ignoring lease with SSID %@",
533			   lease_p->ssid);
534		    continue;
535		}
536	    }
537
538	}
539	if (lease_p->router_ip.s_addr == 0
540	    || lease_p->router_hwaddr_length == 0) {
541	    /* can't use this with ARP discovery */
542	    if (G_IPConfiguration_verbose) {
543		my_log(LOG_DEBUG, "ignoring lease for " IP_FORMAT,
544		       IP_LIST(&lease_p->our_ip));
545	    }
546	    continue;
547	}
548	if (lease_p->tentative && tentative_ok == FALSE) {
549	    /* ignore tentative lease */
550	    continue;
551	}
552	if (start_time_threshold_p != NULL
553	    && lease_p->lease_start < *start_time_threshold_p) {
554	    if (G_IPConfiguration_verbose) {
555		my_log(LOG_DEBUG,
556		       "start time on lease " IP_FORMAT " too old (%ld < %ld)",
557		       IP_LIST(&lease_p->our_ip),
558		       lease_p->lease_start, *start_time_threshold_p);
559	    }
560	    continue;
561	}
562	info_p->sender_ip = lease_p->our_ip;
563	info_p->target_ip = lease_p->router_ip;
564	bcopy(lease_p->router_hwaddr, info_p->target_hardware,
565	      lease_p->router_hwaddr_length);
566	arp_info_count++;
567	info_p++;
568    }
569    if (arp_info_count == 0) {
570	free(arp_info_p);
571	arp_info_p = NULL;
572    }
573    *ret_count = arp_info_count;
574    return (arp_info_p);
575}
576
577/*
578 * Function: DHCPLeaseListFindLease
579 * Purpose:
580 *   Find a lease corresponding to the supplied information.
581 */
582int
583DHCPLeaseListFindLease(DHCPLeaseListRef list_p, struct in_addr our_ip,
584		       struct in_addr router_ip,
585		       const uint8_t * router_hwaddr, int router_hwaddr_length)
586{
587    int			count;
588    int			i;
589    bool		private_ip = ip_is_private(our_ip);
590
591    count = dynarray_count(list_p);
592    for (i = 0; i < count; i++) {
593	DHCPLeaseRef	lease_p = dynarray_element(list_p, i);
594
595	if (lease_p->our_ip.s_addr != our_ip.s_addr) {
596	    /* IP doesn't match */
597	    continue;
598	}
599	if (private_ip == FALSE) {
600	    /* lease for public IP is unique */
601	    return (i);
602	}
603	if (lease_p->router_ip.s_addr != router_ip.s_addr) {
604	    /* router IP doesn't match (or one is set the other isn't)*/
605	    continue;
606	}
607	if (router_ip.s_addr == 0) {
608	    /* found lease with no router information */
609	    return (i);
610	}
611	if (lease_p->router_hwaddr_length != router_hwaddr_length) {
612	    /* one has router hwaddr, other doesn't */
613	    continue;
614	}
615	if (router_hwaddr == NULL || router_hwaddr_length == 0) {
616	    /* found lease with router IP but no router hwaddr */
617	    return (i);
618	}
619	if (bcmp(lease_p->router_hwaddr, router_hwaddr, router_hwaddr_length)
620	    == 0) {
621	    /* exact match on IP, router IP, router hwaddr */
622	    return (i);
623	}
624    }
625    return (-1);
626}
627
628/*
629 * Function: DHCPLeaseShouldBeRemoved
630 * Purpose:
631 *   Given an existing lease entry 'existing_p' and the one to be added 'new_p',
632 *   determine whether the existing lease entry should be removed.
633 *
634 *   The criteria for removing the lease entry:
635 *   1) No router information is specified.  This entry is useless for lease
636 *      detection.   If such a lease exists, it only makes sense to have
637 *      one of them, the most recently used lease.  It will be added to the
638 *      end of the list in that case.
639 *   2) Lease was NAK'd.  The DHCP server NAK'd this lease and it's for the
640 *      same network i.e. same router MAC.
641 *   3) Lease is for a public IP and is the same as the new lease.
642 *   4) Lease has the same router IP/MAC address as an existing lease.  We
643 *      can only sensibly have a single lease for a particular network, so
644 *      eliminate redundant ones.
645 */
646static boolean_t
647DHCPLeaseShouldBeRemoved(DHCPLeaseRef existing_p, DHCPLeaseRef new_p,
648			 boolean_t private_ip)
649{
650    if (existing_p->router_ip.s_addr == 0
651	|| existing_p->router_hwaddr_length == 0) {
652	if (G_IPConfiguration_verbose) {
653	    my_log(LOG_DEBUG,
654		   "Removing lease with no router for IP address "
655		   IP_FORMAT, IP_LIST(&existing_p->our_ip));
656	}
657	return (TRUE);
658    }
659    if (existing_p->nak) {
660	boolean_t		ignore = FALSE;
661
662	existing_p->nak = FALSE;
663	if (ip_is_private(existing_p->our_ip) != private_ip) {
664	    /* one IP is private, the other is public, ignore NAK */
665	    ignore = TRUE;
666	}
667	else if (new_p->router_hwaddr_length != 0
668		 && bcmp(existing_p->router_hwaddr, new_p->router_hwaddr,
669			 existing_p->router_hwaddr_length) != 0) {
670	    /* router MAC on NAK'd lease is different, so ignore NAK */
671	    ignore = TRUE;
672	}
673	if (ignore) {
674	    if (G_IPConfiguration_verbose) {
675		my_log(LOG_DEBUG, "Ignoring NAK on IP address "
676		       IP_FORMAT, IP_LIST(&existing_p->our_ip));
677	    }
678	}
679	else {
680	    if (G_IPConfiguration_verbose) {
681		my_log(LOG_DEBUG, "Removing NAK'd lease for IP address "
682		       IP_FORMAT, IP_LIST(&existing_p->our_ip));
683	    }
684	    return (TRUE);
685	}
686    }
687    if (private_ip == FALSE
688	&& new_p->our_ip.s_addr == existing_p->our_ip.s_addr) {
689	/* public IP's are the same, remove it */
690	if (G_IPConfiguration_verbose) {
691	    my_log(LOG_DEBUG, "Removing lease for public IP address "
692		   IP_FORMAT, IP_LIST(&existing_p->our_ip));
693	}
694	return (TRUE);
695    }
696    if (new_p->router_ip.s_addr == 0
697	|| new_p->router_hwaddr_length == 0) {
698	/* new lease doesn't have a router */
699	return (FALSE);
700    }
701    if (bcmp(new_p->router_hwaddr, existing_p->router_hwaddr,
702	     new_p->router_hwaddr_length) == 0) {
703	if (G_IPConfiguration_verbose) {
704	    if (new_p->our_ip.s_addr == existing_p->our_ip.s_addr) {
705		my_log(LOG_DEBUG,
706		       "Removing lease with same router for IP address "
707		       IP_FORMAT, IP_LIST(&existing_p->our_ip));
708	    }
709	    else {
710		my_log(LOG_DEBUG,
711		       "Removing lease with same router, old IP "
712		       IP_FORMAT " new IP " IP_FORMAT,
713		       IP_LIST(&existing_p->our_ip),
714		       IP_LIST(&new_p->our_ip));
715	    }
716	}
717	return (TRUE);
718    }
719    return (FALSE);
720}
721
722/*
723 * Function: DHCPLeaseListUpdateLease
724 *
725 * Purpose:
726 *   Update the lease entry for the given lease in the in-memory lease database.
727 */
728void
729DHCPLeaseListUpdateLease(DHCPLeaseListRef list_p, struct in_addr our_ip,
730			 struct in_addr router_ip,
731			 const uint8_t * router_hwaddr,
732			 int router_hwaddr_length,
733			 absolute_time_t lease_start,
734			 dhcp_lease_time_t lease_length,
735			 const uint8_t * pkt, int pkt_size,
736			 CFStringRef ssid)
737{
738    int			count;
739    int			i;
740    DHCPLeaseRef	lease_p;
741    boolean_t		private_ip = ip_is_private(our_ip);
742
743    lease_p = DHCPLeaseCreate(our_ip, router_ip,
744			      router_hwaddr, router_hwaddr_length,
745			      lease_start, lease_length, pkt, pkt_size,
746			      ssid);
747    /* scan lease list to eliminate NAK'd, incomplete, and duplicate leases */
748    count = dynarray_count(list_p);
749    for (i = 0; i < count; i++) {
750	DHCPLeaseRef	scan_p = dynarray_element(list_p, i);
751
752	if (DHCPLeaseShouldBeRemoved(scan_p, lease_p, private_ip)) {
753	    dynarray_free_element(list_p, i);
754	    i--;
755	    count--;
756	}
757    }
758    dynarray_add(list_p, lease_p);
759    if (G_IPConfiguration_verbose) {
760	my_log(LOG_DEBUG, "Saved lease for " IP_FORMAT,
761	       IP_LIST(&lease_p->our_ip));
762	if (G_IPConfiguration_verbose) {
763	    DHCPLeaseListLog(list_p);
764	}
765    }
766    return;
767}
768
769/*
770 * Function: DHCPLeaseListRemoveLease
771 *
772 * Purpose:
773 *   Remove the lease entry for the given lease.
774 */
775void
776DHCPLeaseListRemoveLease(DHCPLeaseListRef list_p,
777			 struct in_addr our_ip,
778			 struct in_addr router_ip,
779			 const uint8_t * router_hwaddr,
780			 int router_hwaddr_length)
781{
782    int		where;
783
784    /* remove the old information if it's there */
785    where = DHCPLeaseListFindLease(list_p, our_ip, router_ip,
786				   router_hwaddr, router_hwaddr_length);
787    if (where != -1) {
788	if (G_IPConfiguration_verbose) {
789	    DHCPLeaseRef lease_p = DHCPLeaseListElement(list_p, where);
790
791	    my_log(LOG_DEBUG, "Removing lease for " IP_FORMAT,
792		   IP_LIST(&lease_p->our_ip));
793	}
794	dynarray_free_element(list_p, where);
795    }
796    return;
797}
798
799/*
800 * Function: DHCPLeaseListRemoveAllButLastLease
801 * Purpose:
802 *   Remove all leases except the last one (the most recently used one),
803 *   and mark it tentative.
804 */
805void
806DHCPLeaseListRemoveAllButLastLease(DHCPLeaseListRef list_p)
807{
808    int			count;
809    int			i;
810    DHCPLeaseRef	lease_p;
811
812    count = DHCPLeaseListCount(list_p);
813    if (count == 0) {
814	return;
815    }
816    for (i = 0; i < (count - 1); i++) {
817	if (G_IPConfiguration_verbose) {
818	    lease_p = DHCPLeaseListElement(list_p, 0);
819	    my_log(LOG_DEBUG, "Removing lease #%d for IP address "
820		   IP_FORMAT, i + 1, IP_LIST(&lease_p->our_ip));
821	}
822	dynarray_free_element(list_p, 0);
823    }
824    lease_p = DHCPLeaseListElement(list_p, 0);
825    lease_p->tentative = TRUE;
826    return;
827}
828
829