1/*
2 * Copyright (c) 2000-2014 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24/*
25 * arp_session.c
26 */
27/*
28 * Modification History
29 *
30 * May 11, 2000		Dieter Siegmund (dieter@apple.com)
31 * - created
32 *
33 * March 21, 2001	Dieter Siegmund (dieter@apple.com)
34 * - process multiple ARP responses from one bpf read instead of
35 *   assuming there's just one response per read
36 *
37 * June 16, 2003	Dieter Siegmund (dieter@apple.com)
38 * - added support for firewire
39 *
40 * December 22, 2003	Dieter Siegmund (dieter@apple.com)
41 * - handle multiple arp client probe requests over multiple
42 *   interfaces concurrently
43 */
44
45#include <stdlib.h>
46#include <unistd.h>
47#include <string.h>
48#include <stdio.h>
49#include <sys/types.h>
50#include <sys/wait.h>
51#include <sys/errno.h>
52#include <sys/socket.h>
53#include <sys/ioctl.h>
54#include <sys/time.h>
55#include <sys/sockio.h>
56#include <net/if.h>
57#include <net/ethernet.h>
58#include <netinet/in.h>
59#include <netinet/udp.h>
60#include <netinet/in_systm.h>
61#include <netinet/ip.h>
62#include <net/if_arp.h>
63#include <net/firewire.h>
64#include <netinet/if_ether.h>
65#include <arpa/inet.h>
66#include <net/if_types.h>
67#include <net/bpf.h>
68#include <CoreFoundation/CFRunLoop.h>
69
70#include "util.h"
71#include <syslog.h>
72#include "bpflib.h"
73#include "util.h"
74#include "dynarray.h"
75#include "timer.h"
76#include "FDSet.h"
77#include "ipconfigd_globals.h"
78#include "arp_session.h"
79#include "ioregpath.h"
80#include "ipconfigd_threads.h"
81#include "symbol_scope.h"
82
83struct firewire_arp {
84    struct arphdr 	fw_hdr;	/* fixed-size header */
85    uint8_t		arp_sha[FIREWIRE_ADDR_LEN];
86    uint8_t		arp_spa[4];
87    uint8_t		arp_tpa[4];
88};
89
90struct probe_info {
91    struct timeval		retry_interval;
92    int				probe_count;
93    int				gratuitous_count;
94    boolean_t			skip_first;
95};
96
97struct arp_session {
98    int				debug;
99    struct probe_info		default_probe_info;
100    int				default_detect_count;
101    struct timeval		default_detect_retry;
102    int				default_conflict_retry_count;
103    struct timeval		default_conflict_delay;
104    struct timeval		default_resolve_retry;
105    arp_our_address_func_t *	is_our_address;
106    dynarray_t			if_sessions;
107#ifdef TEST_ARP_SESSION
108    int				next_client_index;
109#endif /* TEST_ARP_SESSION */
110};
111
112struct arp_if_session {
113    arp_session_t *		session;
114    interface_t *		if_p;
115    dynarray_t			clients;
116    char *			receive_buf;
117    int				receive_bufsize;
118    FDCalloutRef		read_fd;
119    int				read_fd_refcount;
120    struct firewire_address	fw_addr;
121};
122
123typedef struct arp_if_session arp_if_session_t;
124
125typedef enum {
126    arp_client_command_none_e = 0,
127    arp_client_command_probe_e = 1,
128    arp_client_command_resolve_e = 2,
129    arp_client_command_detect_e = 3
130} arp_client_command_t;
131
132typedef enum {
133    arp_status_none_e = 0,
134    arp_status_not_in_use_e = 1,
135    arp_status_in_use_e = 2,
136    arp_status_error_e = 3,
137    arp_status_unknown_e = 4,
138} arp_status_t;
139
140struct arp_client {
141#ifdef TEST_ARP_SESSION
142    int				client_index; /* unique ID */
143#endif /* TEST_ARP_SESSION */
144    arp_client_command_t	command;
145    arp_status_t		command_status;
146    boolean_t			fd_open;
147    arp_if_session_t *		if_session;
148    arp_result_func_t *		func;
149    void *			arg1;
150    void *			arg2;
151    struct in_addr		sender_ip;
152    struct in_addr		target_ip;
153    int				try;
154    int				conflict_count;
155    timer_callout_t *		timer_callout;
156    arp_address_info_t		in_use_addr;
157    char			errmsg[128];
158    struct probe_info		probe_info;
159    boolean_t			probes_are_collisions;
160    uint32_t			resolve_secs;
161    arp_address_info_t *	detect_list;
162    int				detect_list_count;
163    CFRunLoopObserverRef	callback_rls;
164};
165
166#ifdef TEST_ARP_SESSION
167#define my_log		arp_session_log
168static void arp_session_log(int priority, const char * message, ...);
169#define G_IPConfiguration_verbose TRUE
170#endif /* TEST_ARP_SESSION */
171
172#include <CoreFoundation/CFDictionary.h>
173#include <CoreFoundation/CFRunLoop.h>
174#include <SystemConfiguration/SCValidation.h>
175
176#define ARP_STR "ARP "
177
178static Boolean
179getFireWireAddress(const char * ifname, struct firewire_address * addr_p)
180{
181    CFDictionaryRef	dict = NULL;
182    CFDataRef		data;
183    Boolean		found = FALSE;
184
185    dict = myIORegistryEntryBSDNameMatchingCopyValue(ifname, TRUE);
186    if (dict == NULL) {
187	return (FALSE);
188    }
189    data = CFDictionaryGetValue(dict, CFSTR("IOFWHWAddr"));
190    if (isA_CFData(data) == NULL || CFDataGetLength(data) != sizeof(*addr_p)) {
191	goto done;
192    }
193    CFDataGetBytes(data, CFRangeMake(0, sizeof(*addr_p)), (void *)addr_p);
194
195    /* put it in network byte order */
196    addr_p->unicastFifoHi = htons(addr_p->unicastFifoHi);
197    addr_p->unicastFifoLo = htonl(addr_p->unicastFifoLo);
198    found = TRUE;
199
200 done:
201    if (dict != NULL) {
202	CFRelease(dict);
203    }
204    return (found);
205}
206
207/* forward-declarations: */
208
209static arp_if_session_t *
210arp_session_new_if_session(arp_session_t * session, interface_t * if_p);
211
212static void
213arp_client_free_element(void * arg);
214
215static void
216arp_client_probe_retransmit(void * arg1, void * arg2, void * arg3);
217
218static void
219arp_client_probe_start(void * arg1, void * arg2, void * arg3);
220
221static void
222arp_client_resolve_retransmit(void * arg1, void * arg2, void * arg3);
223
224static boolean_t
225arp_client_open_fd(arp_client_t * client);
226
227static void
228arp_client_close_fd(arp_client_t * client);
229
230static void
231arp_if_session_free(arp_if_session_t * * if_session_p);
232
233static void
234arp_if_session_free_element(void * arg);
235
236static void
237arp_if_session_read(void * arg1, void * arg2);
238
239static boolean_t
240arp_is_our_address(interface_t * if_p, int hwtype, void * hwaddr, int hwlen);
241
242static __inline__ char *
243arpop_name(u_int16_t op)
244{
245    switch (op) {
246    case ARPOP_REQUEST:
247	return "ARP REQUEST";
248    case ARPOP_REPLY:
249	return "ARP REPLY";
250    case ARPOP_REVREQUEST:
251	return "REVARP REQUEST";
252    case ARPOP_REVREPLY:
253	return "REVARP REPLY";
254    default:
255	break;
256    }
257    return ("<unknown>");
258}
259
260/* NOTE: caller should make sure arp_p pointed structure
261 * be at least 4 byte aligned */
262static void
263dump_arp(struct arphdr * arp_p)
264
265{
266    int arphrd = ntohs(arp_p->ar_hrd);
267
268    printf("\n");
269    printf("%s type=0x%x proto=0x%x\n", arpop_name(ntohs(arp_p->ar_op)),
270	   arphrd, ntohs(arp_p->ar_pro));
271
272    switch (arphrd) {
273    case ARPHRD_ETHER:
274	{
275	    /* ALIGN: alignment not assumed, using bcopy */
276	    struct ether_arp * earp = (struct ether_arp *)(void *)arp_p;
277	    struct in_addr iaddr;
278
279	    if (arp_p->ar_hln == sizeof(earp->arp_sha)) {
280		struct ether_addr eaddr;
281
282		bcopy(earp->arp_sha, &eaddr, sizeof(eaddr));
283		printf("Sender H/W\t%s\n",
284		       ether_ntoa((const struct ether_addr *)&eaddr));
285
286		bcopy(earp->arp_tha, &eaddr, sizeof(eaddr));
287		printf("Target H/W\t%s\n",
288		       ether_ntoa((const struct ether_addr *)&eaddr));
289	    }
290	    bcopy(earp->arp_spa, &iaddr, sizeof(iaddr));
291	    printf("Sender IP\t%s\n",
292		   inet_ntoa(iaddr));
293
294	    bcopy(earp->arp_tpa, &iaddr, sizeof(iaddr));
295	    printf("Target IP\t%s\n",
296		   inet_ntoa(iaddr));
297	}
298	break;
299    case ARPHRD_IEEE1394:
300	{
301	    /* ALIGN: arp_p is aligned, cast ok. */
302	    struct firewire_arp * farp = (struct firewire_arp *)(void *)arp_p;
303
304	    if (arp_p->ar_hln == sizeof(farp->arp_sha)) {
305		printf("Sender H/W\t" FWA_FORMAT "\n",
306		       FWA_LIST(farp->arp_sha));
307	    }
308	    /* ALIGN: arp_p is aligned, cast ok. */
309	    printf("Sender IP\t%s\n",
310		   inet_ntoa(*((struct in_addr *)(void *)farp->arp_spa)));
311	    /* ALIGN: arp_p is aligned, cast ok. */
312	    printf("Target IP\t%s\n",
313		   inet_ntoa(*((struct in_addr *)(void *)farp->arp_tpa)));
314	}
315	break;
316    }
317    fflush(stdout);
318    return;
319}
320
321static void
322arp_client_close_fd(arp_client_t * client)
323{
324    arp_if_session_t * 	if_session = client->if_session;
325
326    if (client->fd_open == FALSE) {
327	return;
328    }
329    if (if_session->read_fd_refcount <= 0) {
330	my_log(LOG_ERR, "arp_client_close_fd(%s): bpf open fd count is %d",
331	       if_name(if_session->if_p), if_session->read_fd_refcount);
332	return;
333    }
334    if_session->read_fd_refcount--;
335    my_log(LOG_DEBUG, "arp_client_close_fd(%s): bpf open fd count is %d",
336	   if_name(if_session->if_p), if_session->read_fd_refcount);
337    client->fd_open = FALSE;
338    if (if_session->read_fd_refcount == 0) {
339	if (if_session->read_fd != NULL) {
340	    my_log(LOG_DEBUG, "arp_client_close_fd(%s): closing bpf fd %d",
341		   if_name(if_session->if_p),
342		   FDCalloutGetFD(if_session->read_fd));
343	    /* this closes the file descriptor */
344	    FDCalloutRelease(&if_session->read_fd);
345	}
346	if (if_session->receive_buf != NULL) {
347	    free(if_session->receive_buf);
348	    if_session->receive_buf = NULL;
349	}
350    }
351    return;
352}
353
354/*
355 * Function: arp_client_is_active
356 * Purpose:
357 *   Returns whether the arp_client is active.
358 */
359PRIVATE_EXTERN boolean_t
360arp_client_is_active(arp_client_t * client)
361{
362    return (client->func != NULL);
363}
364
365/*
366 * Function: arp_client_cancel_callback
367 * Purpose:
368 *   Invalidate/release the callback function.
369 */
370static void
371arp_client_cancel_callback(arp_client_t * client)
372{
373    if (client->callback_rls != NULL) {
374	CFRunLoopObserverInvalidate(client->callback_rls);
375	CFRelease(client->callback_rls);
376	client->callback_rls = NULL;
377    }
378    return;
379}
380
381/*
382 * Function: arp_client_callback
383 *
384 * Purpose:
385 *   Call the supplied function with the appropriate result.
386 */
387static void
388arp_client_callback(arp_client_t * client)
389{
390    void *		c_arg1;
391    void * 		c_arg2;
392    arp_result_func_t *	func;
393    arp_result_t	result;
394
395    /* remember the client parameters, then clear them */
396    c_arg1 = client->arg1;
397    c_arg2 = client->arg2;
398    func = client->func;
399    client->func = client->arg1 = client->arg2 = NULL;
400
401    arp_client_close_fd(client);
402    timer_cancel(client->timer_callout);
403
404    /* return the results */
405    bzero(&result, sizeof(result));
406    switch (client->command_status) {
407    default:
408    case arp_status_none_e:
409	/* not possible */
410	printf("No result for %s?\n",
411	       if_name(client->if_session->if_p));
412	break;
413    case arp_status_error_e:
414	result.error = TRUE;
415	break;
416    case arp_status_not_in_use_e:
417	break;
418    case arp_status_in_use_e:
419	result.in_use = TRUE;
420	result.addr = client->in_use_addr;
421	break;
422    }
423
424    /* return the results to the client */
425    result.client = client;
426    (*func)(c_arg1, c_arg2, &result);
427    return;
428}
429
430/*
431 * Function: arp_client_do_callback
432 * Purpose:
433 *   Invalidate the runloop observer then invoke arp_client_callback().
434 */
435static void
436arp_client_do_callback(CFRunLoopObserverRef observer,
437		       CFRunLoopActivity activity,
438		       void * info)
439{
440    arp_client_t * 	client = (arp_client_t *)info;
441
442    /* de-activate the observer */
443    arp_client_cancel_callback(client);
444    arp_client_callback(client);
445    return;
446}
447
448/*
449 * Function: arp_client_schedule_callback
450 * Purpose:
451 *   Call the arp_client_callback via a runloop observer.
452 */
453static void
454arp_client_schedule_callback(arp_client_t * client)
455{
456    CFRunLoopObserverContext	context = { 0, client, NULL, NULL, NULL };
457
458    arp_client_cancel_callback(client);
459    client->callback_rls
460	= CFRunLoopObserverCreate(NULL, kCFRunLoopAllActivities,
461				  TRUE, 0, arp_client_do_callback, &context);
462
463    CFRunLoopAddObserver(CFRunLoopGetCurrent(), client->callback_rls,
464			 kCFRunLoopDefaultMode);
465    return;
466}
467
468/*
469 * Function: arp_is_our_address
470 *
471 * Purpose:
472 *   Returns whether the given hardware address matches the given
473 *   network interface.
474 */
475static boolean_t
476arp_is_our_address(interface_t * if_p, int hwtype, void * hwaddr, int hwlen)
477{
478    int		link_length = if_link_length(if_p);
479
480    if (hwlen != link_length || hwtype != if_link_arptype(if_p)) {
481	return (FALSE);
482    }
483    if (bcmp(hwaddr, if_link_address(if_p), link_length) == 0) {
484	return (TRUE);
485    }
486    return (FALSE);
487}
488
489static void
490arp_if_session_update_hardware_address(arp_if_session_t * if_session)
491{
492    if (if_link_type(if_session->if_p) != IFT_IEEE1394) {
493	return;
494    }
495    /* copy in the latest firewire address */
496    if (getFireWireAddress(if_name(if_session->if_p),
497			   &if_session->fw_addr) == FALSE) {
498	my_log(LOG_ERR,
499	       "arp_if_session_update_hardware_address(%s):"
500	       "could not retrieve firewire address",
501	       if_name(if_session->if_p));
502    }
503    return;
504}
505
506/*
507 * Function: arp_if_session_read
508 * Purpose:
509 *   Called when data is available on the bpf fd.
510 *   Check the arp packet, and see if it matches
511 *   any of the clients' probe criteria.  If it does,
512 *   call the client with an in_use result structure.
513 */
514static void
515arp_if_session_read(void * arg1, void * arg2)
516{
517    arp_client_t *	client;
518    int			client_count;
519    boolean_t		debug;
520    char		errmsg[128];
521    int			hwlen = 0;
522    int			hwtype;
523    int			i;
524    arp_if_session_t * 	if_session;
525    int			link_header_size;
526    int			link_arp_size;
527    int			link_length;
528    ssize_t		n;
529    char *		offset;
530    arp_session_t *	session;
531
532    if_session = (arp_if_session_t *)arg1;
533    session = if_session->session;
534
535    errmsg[0] = '\0';
536
537    if (if_session->read_fd_refcount == 0) {
538	my_log(LOG_ERR, "arp_if_session_read: no pending clients?");
539	return;
540    }
541
542    debug = session->debug;
543    client_count = dynarray_count(&if_session->clients);
544
545    link_length = if_link_length(if_session->if_p);
546    hwtype = if_link_arptype(if_session->if_p);
547    switch (hwtype) {
548    default:
549	/* default clause will never match */
550    case ARPHRD_ETHER:
551	link_header_size = sizeof(struct ether_header);
552	link_arp_size = sizeof(struct ether_arp);
553	hwlen = ETHER_ADDR_LEN;
554	break;
555    case ARPHRD_IEEE1394:
556	link_header_size = sizeof(struct firewire_header);
557	link_arp_size = sizeof(struct firewire_arp);
558	hwlen = FIREWIRE_ADDR_LEN;
559	break;
560    }
561    n = read(FDCalloutGetFD(if_session->read_fd), if_session->receive_buf,
562	     if_session->receive_bufsize);
563    if (n < 0) {
564	if (errno == EAGAIN) {
565	    return;
566	}
567	my_log(LOG_ERR, "arp_if_session_read: read(%s) failed, %s (%d)",
568	       if_name(if_session->if_p), strerror(errno), errno);
569	snprintf(errmsg, sizeof(errmsg),
570		 "arp_if_session_read: read(%s) failed, %s (%d)",
571		 if_name(if_session->if_p), strerror(errno), errno);
572	goto failed;
573    }
574    for (offset = if_session->receive_buf; n > 0; ) {
575	struct arphdr *		arp_p;
576	struct bpf_hdr * 	bpf = (struct bpf_hdr *)(void *)offset;
577	void *			hwaddr;
578	boolean_t		is_our_address;
579	short			op;
580	char *			pkt_start;
581	struct in_addr		source_ip_aligned;
582	struct in_addr *	source_ip_p;
583	struct in_addr		target_ip_aligned;
584	struct in_addr *	target_ip_p;
585	int			skip;
586
587	/* ALIGN: offset is aligned to sizeof(int) bytes */
588	pkt_start = offset + bpf->bh_hdrlen;
589	arp_p = (struct arphdr *)(void *)(pkt_start + link_header_size);
590	if (debug) {
591	    dump_arp(arp_p);
592	}
593	op = ntohs(arp_p->ar_op);
594	if (bpf->bh_caplen < (link_header_size + link_arp_size)
595	    || arp_p->ar_hln != hwlen
596	    || (op != ARPOP_REPLY && op != ARPOP_REQUEST)
597	    || ntohs(arp_p->ar_hrd) != hwtype
598	    || ntohs(arp_p->ar_pro) != ETHERTYPE_IP) {
599	    goto next_packet;
600	}
601	switch (hwtype) {
602	default:
603	case ARPHRD_ETHER:
604	    {
605		struct ether_arp * earp;
606
607		earp = (struct ether_arp *)arp_p;
608
609		/* ALIGN: don't assume fields in earp are aligned */
610		source_ip_p = &source_ip_aligned;
611		target_ip_p = &target_ip_aligned;
612		bcopy(earp->arp_spa, source_ip_p, sizeof(struct in_addr));
613		bcopy(earp->arp_tpa, target_ip_p, sizeof(struct in_addr));
614		hwaddr = earp->arp_sha;
615	    }
616	    break;
617	case ARPHRD_IEEE1394:
618	    {
619		struct firewire_arp * farp;
620
621		farp = (struct firewire_arp *)arp_p;
622		/* ALIGN: arp_p aligned, cast ok. */
623		source_ip_p = (struct in_addr *)(void *)farp->arp_spa;
624		target_ip_p = (struct in_addr *)(void *)farp->arp_tpa;
625		hwaddr = farp->arp_sha;
626	    }
627	    break;
628	}
629	is_our_address
630	    = (*session->is_our_address)(if_session->if_p,
631					 hwtype,
632					 hwaddr,
633					 link_length);
634	for (i = 0; i < client_count; i++) {
635	    int			addr_index;
636	    arp_client_t *	client;
637	    boolean_t		got_match;
638
639	    client = dynarray_element(&if_session->clients, i);
640	    if (client->func == NULL) {
641		continue;
642	    }
643	    if (client->command_status == arp_status_in_use_e) {
644		/* we already found a match for this client */
645		continue;
646	    }
647	    got_match = FALSE;
648	    switch (client->command) {
649	    case arp_client_command_probe_e:
650		if (is_our_address) {
651		    /* don't report conflicts against our own h/w addresses */
652		}
653		/* IP is in use by some other host */
654		else if (client->target_ip.s_addr == source_ip_p->s_addr
655		    || (client->probes_are_collisions
656			&& op == ARPOP_REQUEST
657			&& source_ip_p->s_addr == 0
658			&& client->target_ip.s_addr == target_ip_p->s_addr)) {
659		    client->in_use_addr.sender_ip = client->sender_ip;
660		    client->in_use_addr.target_ip = client->target_ip;
661		    bcopy(hwaddr, client->in_use_addr.target_hardware,
662			  link_length);
663		    got_match = TRUE;
664		}
665		break;
666	    case arp_client_command_resolve_e:
667		if (client->target_ip.s_addr == source_ip_p->s_addr
668		    && op == ARPOP_REPLY) {
669		    client->in_use_addr.sender_ip = client->sender_ip;
670		    client->in_use_addr.target_ip = client->target_ip;
671		    bcopy(hwaddr, client->in_use_addr.target_hardware,
672			  link_length);
673		    got_match = TRUE;
674		}
675		break;
676	    case arp_client_command_detect_e:
677		if (op != ARPOP_REPLY) {
678		    break;
679		}
680		for (addr_index = 0; addr_index < client->detect_list_count;
681		     addr_index++) {
682		    arp_address_info_t *	info_p;
683
684		    info_p = client->detect_list + addr_index;
685		    if (info_p->sender_ip.s_addr == target_ip_p->s_addr
686			&& info_p->target_ip.s_addr == source_ip_p->s_addr
687			&& (bcmp(info_p->target_hardware, hwaddr, link_length)
688			    == 0)) {
689			client->in_use_addr = *info_p;
690			got_match = TRUE;
691			break;
692		    }
693		}
694		break;
695	    default:
696		break;
697	    }
698	    if (got_match) {
699		client->command_status = arp_status_in_use_e;
700		if (client->command == arp_client_command_probe_e
701		    && client->probes_are_collisions == FALSE) {
702		    client->conflict_count++;
703		    my_log(LOG_DEBUG,
704			   "arp_session: encountered conflict,"
705			   " trying again %d (of %d)",
706			   client->conflict_count,
707			   session->default_conflict_retry_count + 1);
708		    if (client->conflict_count
709			<= session->default_conflict_retry_count) {
710			/* schedule another probe cycle */
711			timer_set_relative(client->timer_callout,
712					   session->default_conflict_delay,
713					   (timer_func_t *)
714					   arp_client_probe_start,
715					   client, NULL, NULL);
716			goto next_packet;
717		    }
718		}
719		/* match found, provide results via callback */
720		arp_client_schedule_callback(client);
721	    }
722	}
723    next_packet:
724	skip = BPF_WORDALIGN(bpf->bh_caplen + bpf->bh_hdrlen);
725	if (skip == 0) {
726	    break;
727	}
728	offset += skip;
729	n -= skip;
730    }
731    return;
732 failed:
733    for (i = 0; i < client_count; i++) {
734	client = dynarray_element(&if_session->clients, i);
735	if (client->func == NULL) {
736	    continue;
737	}
738	strncpy(client->errmsg, errmsg, sizeof(client->errmsg));
739	/* report back an error to the caller */
740	client->command_status = arp_status_error_e;
741	arp_client_schedule_callback(client);
742    }
743    return;
744}
745
746static boolean_t
747arp_client_open_fd(arp_client_t * client)
748{
749    int			bpf_fd;
750    arp_if_session_t *	if_session = client->if_session;
751    int 		opt;
752    int 		status;
753
754    if (client->fd_open) {
755	return (TRUE);
756    }
757    if_session->read_fd_refcount++;
758    my_log(LOG_DEBUG, "arp_client_open_fd (%s): refcount %d",
759	   if_name(if_session->if_p), if_session->read_fd_refcount);
760    client->fd_open = TRUE;
761    if (if_session->read_fd_refcount > 1) {
762	/* already open */
763	return (TRUE);
764    }
765    bpf_fd = bpf_new();
766    if (bpf_fd < 0) {
767	my_log(LOG_ERR, "arp_client_open_fd: bpf_new(%s) failed, %s (%d)",
768	       if_name(if_session->if_p), strerror(errno), errno);
769	snprintf(client->errmsg, sizeof(client->errmsg),
770		 "arp_client_open_fd: bpf_new(%s) failed, %s (%d)",
771		 if_name(if_session->if_p), strerror(errno), errno);
772	goto failed;
773    }
774    opt = 1;
775    status = ioctl(bpf_fd, FIONBIO, &opt);
776    if (status < 0) {
777	my_log(LOG_ERR, "ioctl FIONBIO failed %s", strerror(errno));
778	goto failed;
779    }
780
781    /* associate it with the given interface */
782    status = bpf_setif(bpf_fd, if_name(if_session->if_p));
783    if (status < 0) {
784	my_log(LOG_ERR, "arp_client_open_fd: bpf_setif(%s) failed: %s (%d)",
785	       if_name(if_session->if_p), strerror(errno), errno);
786	snprintf(client->errmsg, sizeof(client->errmsg),
787		 "arp_client_open_fd: bpf_setif(%s) failed: %s (%d)",
788		 if_name(if_session->if_p), strerror(errno), errno);
789	goto failed;
790    }
791
792    /* don't wait for packets to be buffered */
793    bpf_set_immediate(bpf_fd, 1);
794
795    /* set the filter to return only ARP packets */
796    switch (if_link_type(if_session->if_p)) {
797    default:
798    case IFT_ETHER:
799	status = bpf_arp_filter(bpf_fd, 12, ETHERTYPE_ARP,
800				sizeof(struct ether_arp)
801				+ sizeof(struct ether_header));
802	break;
803    case IFT_IEEE1394:
804	status = bpf_arp_filter(bpf_fd, 16, ETHERTYPE_ARP,
805				sizeof(struct firewire_arp)
806				+ sizeof(struct firewire_header));
807	break;
808    }
809    if (status < 0) {
810	my_log(LOG_ERR,
811	       "arp_client_open_fd: bpf_arp_filter(%s) failed: %s (%d)",
812	       if_name(if_session->if_p), strerror(errno), errno);
813	snprintf(client->errmsg, sizeof(client->errmsg),
814		 "arp_client_open_fd: bpf_arp_filter(%s) failed: %s (%d)",
815		 if_name(if_session->if_p), strerror(errno), errno);
816	goto failed;
817    }
818    /* get the receive buffer size */
819    status = bpf_get_blen(bpf_fd, &if_session->receive_bufsize);
820    if (status < 0) {
821	my_log(LOG_ERR,
822	       "arp_client_open_fd: bpf_get_blen(%s) failed, %s (%d)",
823	       if_name(if_session->if_p), strerror(errno), errno);
824	snprintf(client->errmsg, sizeof(client->errmsg),
825		 "arp_client_open_fd: bpf_get_blen(%s) failed, %s (%d)",
826		 if_name(if_session->if_p), strerror(errno), errno);
827	goto failed;
828    }
829    if_session->receive_buf = malloc(if_session->receive_bufsize);
830    if_session->read_fd
831	= FDCalloutCreate(bpf_fd,
832			  arp_if_session_read, if_session, NULL);
833    if (if_session->read_fd == NULL) {
834	goto failed;
835    }
836    my_log(LOG_DEBUG, "arp_client_open_fd (%s): opened bpf fd %d",
837	   if_name(if_session->if_p), bpf_fd);
838    return (TRUE);
839
840 failed:
841    if (bpf_fd >= 0) {
842	close(bpf_fd);
843    }
844    arp_client_close_fd(client);
845    return (FALSE);
846}
847
848static char			link_broadcast[8] = {
849    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
850};
851
852static boolean_t
853arp_client_transmit(arp_client_t * client, boolean_t send_gratuitous,
854		    const arp_address_info_t * info_p)
855{
856    arp_if_session_t *		if_session = client->if_session;
857    struct arphdr *		hdr;
858    int 			status = 0;
859    /*
860     * txbuf is cast to some struct types containing short fields;
861     * force it to be aligned as much as an int
862     */
863    int				txbuf_aligned[32];
864    char *			txbuf = (char *)txbuf_aligned;
865    int				size;
866
867    bzero(txbuf_aligned, sizeof(txbuf_aligned));
868
869    /* fill in the ethernet header */
870    switch (if_link_arptype(if_session->if_p)) {
871    case ARPHRD_ETHER:
872	{
873	    struct ether_header *	eh_p;
874	    struct ether_arp *		earp;
875
876	    /* fill in the ethernet header */
877	    /* ALIGN: txbuf is aligned to sizeof(int) bytes */
878	    eh_p = (struct ether_header *)(void *)txbuf;
879	    eh_p->ether_type = htons(ETHERTYPE_ARP);
880	    if (info_p != NULL) {
881		bcopy(info_p->target_hardware, eh_p->ether_dhost,
882		      sizeof(eh_p->ether_dhost));
883	    }
884	    else {
885		bcopy(link_broadcast, eh_p->ether_dhost,
886		      sizeof(eh_p->ether_dhost));
887	    }
888	    /* fill in the arp packet contents */
889	    /* ALIGN: txbuf is aligned to sizeof(int) bytes */
890	    earp = (struct ether_arp *)(void *)(txbuf + sizeof(*eh_p));
891	    hdr = &earp->ea_hdr;
892	    hdr->ar_hrd = htons(ARPHRD_ETHER);
893	    hdr->ar_pro = htons(ETHERTYPE_IP);
894	    hdr->ar_hln = sizeof(earp->arp_sha);;
895	    hdr->ar_pln = sizeof(struct in_addr);
896	    hdr->ar_op = htons(ARPOP_REQUEST);
897	    bcopy(if_link_address(if_session->if_p), earp->arp_sha,
898		  sizeof(earp->arp_sha));
899	    if (info_p != NULL) {
900		*((struct in_addr *)(void *)earp->arp_spa) = info_p->sender_ip;
901		*((struct in_addr *)(void *)earp->arp_tpa) = info_p->target_ip;
902	    }
903	    else {
904		if (send_gratuitous == TRUE
905		    && client->sender_ip.s_addr == 0) {
906		    *((struct in_addr *)(void *)earp->arp_spa) = client->target_ip;
907		}
908		else {
909		    *((struct in_addr *)(void *)earp->arp_spa) = client->sender_ip;
910		}
911		*((struct in_addr *)(void *)earp->arp_tpa) = client->target_ip;
912	    }
913	    size = sizeof(*eh_p) + sizeof(*earp);
914	}
915	break;
916    case ARPHRD_IEEE1394:
917	{
918	    struct firewire_header *	fh_p;
919	    struct firewire_arp *	farp;
920
921	    /* fill in the firewire header */
922	    /* ALIGN: txbuf is aligned to sizeof(int) bytes */
923	    fh_p = (struct firewire_header *)(void *)txbuf;
924	    fh_p->firewire_type = htons(ETHERTYPE_ARP);
925	    if (info_p != NULL) {
926		bcopy(info_p->target_hardware, fh_p->firewire_dhost,
927		      sizeof(fh_p->firewire_dhost));
928	    }
929	    else {
930		bcopy(link_broadcast, fh_p->firewire_dhost,
931		      sizeof(fh_p->firewire_dhost));
932	    }
933
934	    /* fill in the arp packet contents */
935	    /* ALIGN: txbuf is aligned to sizeof(int) bytes */
936	    farp = (struct firewire_arp *)(void *)(txbuf + sizeof(*fh_p));
937	    hdr = &farp->fw_hdr;
938	    hdr->ar_hrd = htons(ARPHRD_IEEE1394);
939	    hdr->ar_pro = htons(ETHERTYPE_IP);
940	    hdr->ar_hln = sizeof(farp->arp_sha);;
941	    hdr->ar_pln = sizeof(struct in_addr);
942	    hdr->ar_op = htons(ARPOP_REQUEST);
943	    bcopy(&if_session->fw_addr, farp->arp_sha,
944		  sizeof(farp->arp_sha));
945	    if (info_p != NULL) {
946		*((struct in_addr *)(void *)farp->arp_spa) = info_p->sender_ip;
947		*((struct in_addr *)(void *)farp->arp_tpa) = info_p->target_ip;
948	    }
949	    else {
950		if (send_gratuitous == TRUE
951		    && client->sender_ip.s_addr == 0) {
952		    *((struct in_addr *)(void *)farp->arp_spa) = client->target_ip;
953		}
954		else {
955		    *((struct in_addr *)(void *)farp->arp_spa) = client->sender_ip;
956		}
957		*((struct in_addr *)(void *)farp->arp_tpa) = client->target_ip;
958	    }
959	    size = sizeof(*fh_p) + sizeof(*farp);
960	}
961	break;
962    default:
963	snprintf(client->errmsg, sizeof(client->errmsg),
964		 "arp_client_transmit(%s): "
965		 "interface hardware type not yet known",
966		 if_name(if_session->if_p));
967	goto failed;
968    }
969
970    status = bpf_write(FDCalloutGetFD(if_session->read_fd), txbuf, size);
971    if (status < 0) {
972	my_log(LOG_ERR, "arp_client_transmit(%s) failed, %s (%d)",
973	       if_name(if_session->if_p), strerror(errno), errno);
974	snprintf(client->errmsg, sizeof(client->errmsg),
975		 "arp_client_transmit(%s) failed, %s (%d)",
976		 if_name(if_session->if_p), strerror(errno), errno);
977	goto failed;
978    }
979    return (TRUE);
980
981 failed:
982    return (FALSE);
983}
984
985static void
986arp_client_probe_start(void * arg1, void * arg2, void * arg3)
987{
988    arp_client_t * 	client = (arp_client_t *)arg1;
989
990    client->try = 0;
991    client->command_status = arp_status_unknown_e;
992    arp_client_probe_retransmit(arg1, arg2, arg3);
993    return;
994}
995
996/*
997 * Function: arp_client_probe_retransmit
998 *
999 * Purpose:
1000 *   Transmit an ARP packet with timeout retry.
1001 *   Uses callback to invoke arp_client_report_error if the transmit failed.
1002 *   When we've tried often enough, call the client function with a result
1003 *   structure indicating no errors and the IP is not in use.
1004 */
1005static void
1006arp_client_probe_retransmit(void * arg1, void * arg2, void * arg3)
1007{
1008    arp_client_t *      client = (arp_client_t *)arg1;
1009    struct probe_info * probe_info = &client->probe_info;
1010    int                 tries_left;
1011    arp_if_session_t *  if_session = client->if_session;
1012
1013    tries_left = (probe_info->probe_count + probe_info->gratuitous_count)
1014        - client->try;
1015
1016    if (tries_left <= 0) {
1017        /* not in use */
1018        client->command_status = arp_status_not_in_use_e;
1019        arp_client_schedule_callback(client);
1020        return;
1021    }
1022
1023    client->try++;
1024
1025    if (client->probe_info.skip_first ||
1026	arp_client_transmit(client,
1027                            (tries_left <= probe_info->gratuitous_count),
1028                            NULL)) {
1029	if (G_IPConfiguration_verbose) {
1030	    if (client->probe_info.skip_first) {
1031	        my_log(LOG_DEBUG, ARP_STR
1032	         	  "(%s): skipping the first arp announcement.",
1033			   if_name(if_session->if_p));
1034	    }
1035	    else if (tries_left <= probe_info->gratuitous_count) {
1036	        my_log(LOG_DEBUG,
1037		       ARP_STR
1038                       "(%s): sending (%d of %d) arp announcements ",
1039	               if_name(if_session->if_p),
1040		       probe_info->gratuitous_count - tries_left + 1,
1041		       probe_info->gratuitous_count);
1042	    }
1043	    else {
1044		my_log(LOG_DEBUG, ARP_STR "(%s): sending (%d of %d) "
1045		       "arp probes ", if_name(if_session->if_p),
1046                       client->try, probe_info->probe_count);
1047	    }
1048	}
1049        timer_set_relative(client->timer_callout,
1050                           probe_info->retry_interval,
1051                           (timer_func_t *)arp_client_probe_retransmit,
1052                           client, NULL, NULL);
1053        client->probe_info.skip_first = FALSE;
1054    }
1055    else {
1056        /* report back an error to the caller */
1057        client->command_status = arp_status_error_e;
1058        arp_client_schedule_callback(client);
1059    }
1060}
1061/*
1062 * Function: arp_client_resolve_retransmit
1063 *
1064 * Purpose:
1065 *   Transmit an ARP request packet in an attempt to resolve the IP address.
1066 *   Uses callback to invoke arp_client_report_error if the transmit failed.
1067 *   If we can't resolve the address, call the callback indicating the IP
1068 *   address is not in use.
1069 */
1070static void
1071arp_client_resolve_retransmit(void * arg1, void * arg2, void * arg3)
1072{
1073    arp_client_t * 	client = (arp_client_t *)arg1;
1074    int			tries_left;
1075
1076    tries_left = client->resolve_secs - client->try;
1077    if (tries_left <= 0) {
1078	/* not in use */
1079	client->command_status = arp_status_not_in_use_e;
1080	arp_client_schedule_callback(client);
1081	return;
1082    }
1083    client->try++;
1084    if (arp_client_transmit(client, FALSE, NULL)) {
1085	struct timeval	t;
1086
1087#define ONE_SECOND		1
1088	t.tv_sec = ONE_SECOND;
1089	t.tv_usec = 0;
1090	timer_set_relative(client->timer_callout, t,
1091			   (timer_func_t *)arp_client_resolve_retransmit,
1092			   client, NULL, NULL);
1093    }
1094    else {
1095	/* report back an error to the caller */
1096	client->command_status = arp_status_error_e;
1097	arp_client_schedule_callback(client);
1098    }
1099    return;
1100}
1101
1102/*
1103 * Function: arp_client_detect_retransmit
1104 *
1105 * Purpose:
1106 *   Transmit a set of ARP requests, one request for each host of interest,
1107 *   in an attempt to detect which one is present.  The ARP requests are sent
1108 *   using unicast to a specific hardware address and should only be visible
1109 *   received/processed by that specific host.
1110 */
1111static void
1112arp_client_detect_retransmit(void * arg1, void * arg2, void * arg3)
1113{
1114    arp_client_t * 	client = (arp_client_t *)arg1;
1115    int			i;
1116    boolean_t		keep_going = TRUE;
1117    arp_session_t *	session = client->if_session->session;
1118    int			tries_left;
1119    struct timeval *	timeout_p;
1120    boolean_t 		resolve = (boolean_t) (uintptr_t) arg2;
1121
1122    tries_left = session->default_detect_count - client->try;
1123    if (tries_left <= 0) {
1124	/* not in use */
1125	client->command_status = arp_status_not_in_use_e;
1126	arp_client_schedule_callback(client);
1127	return;
1128    }
1129    client->try++;
1130    for (i = 0; i < client->detect_list_count; i++) {
1131	if (arp_client_transmit(client, FALSE, client->detect_list + i)
1132	    == FALSE) {
1133	    keep_going = FALSE;
1134	    break;
1135	}
1136    }
1137    if (keep_going) {
1138	timeout_p = resolve ? &session->default_resolve_retry :
1139		    &session->default_detect_retry;
1140
1141	timer_set_relative(client->timer_callout, *timeout_p,
1142			   (timer_func_t *)
1143			   arp_client_detect_retransmit,
1144			   client, arg2, NULL);
1145    }
1146    else {
1147	/* report back an error to the caller */
1148	client->command_status = arp_status_error_e;
1149	arp_client_schedule_callback(client);
1150    }
1151    return;
1152}
1153
1154static arp_client_t *
1155arp_if_session_new_client(arp_if_session_t * if_session)
1156{
1157    arp_client_t *		client;
1158
1159    client = malloc(sizeof(*client));
1160    if (client == NULL) {
1161	return (NULL);
1162    }
1163    bzero(client, sizeof(*client));
1164    if (dynarray_add(&if_session->clients, client) == FALSE) {
1165	free(client);
1166	return (NULL);
1167    }
1168#ifdef TEST_ARP_SESSION
1169    client->client_index = if_session->session->next_client_index++;
1170#endif /* TEST_ARP_SESSION */
1171    client->if_session = if_session;
1172    client->probe_info = if_session->session->default_probe_info;
1173    client->timer_callout = timer_callout_init();
1174    return (client);
1175
1176}
1177
1178static arp_client_t *
1179arp_session_new_client(arp_session_t * session, interface_t * if_p)
1180{
1181    arp_if_session_t *		if_session;
1182
1183    if_session = arp_session_new_if_session(session, if_p);
1184    if (if_session == NULL) {
1185	return (NULL);
1186    }
1187    return (arp_if_session_new_client(if_session));
1188}
1189
1190#ifdef TEST_ARP_SESSION
1191static arp_client_t *
1192arp_session_find_client_with_index(arp_session_t * session, int index)
1193{
1194    int		if_sessions_count;
1195    int		i;
1196    int		j;
1197
1198    if_sessions_count = dynarray_count(&session->if_sessions);
1199    for (i = 0; i < if_sessions_count; i++) {
1200	int			clients_count;
1201	arp_if_session_t *	if_session;
1202
1203	if_session = dynarray_element(&session->if_sessions, i);
1204	clients_count = dynarray_count(&if_session->clients);
1205	for (j = 0; j < clients_count; j++) {
1206	    arp_client_t *	client;
1207
1208	    client = dynarray_element(&if_session->clients, j);
1209	    if (client->client_index == index) {
1210		return (client);
1211	    }
1212	}
1213    }
1214    return (NULL);
1215}
1216
1217#endif /* TEST_ARP_SESSION */
1218
1219PRIVATE_EXTERN void
1220arp_client_set_probe_info(arp_client_t * client,
1221			  const struct timeval * retry_interval,
1222			  const int * probe_count,
1223			  const int * gratuitous_count)
1224{
1225    struct probe_info *		probe_info = &client->probe_info;
1226
1227    if (retry_interval != NULL) {
1228	probe_info->retry_interval = *retry_interval;
1229    }
1230    if (probe_count != NULL) {
1231	probe_info->probe_count = *probe_count;
1232    }
1233    if (gratuitous_count != NULL) {
1234	probe_info->gratuitous_count = *gratuitous_count;
1235    }
1236    return;
1237}
1238
1239PRIVATE_EXTERN void
1240arp_client_restore_default_probe_info(arp_client_t * client)
1241{
1242    client->probe_info = client->if_session->session->default_probe_info;
1243    return;
1244}
1245
1246PRIVATE_EXTERN arp_client_t *
1247arp_client_init(arp_session_t * session, interface_t * if_p)
1248{
1249    return (arp_session_new_client(session, if_p));
1250}
1251
1252static void
1253arp_client_free_element(void * arg)
1254{
1255    arp_client_t * 	client = (arp_client_t *)arg;
1256
1257    arp_client_cancel(client);
1258    timer_callout_free(&client->timer_callout);
1259    free(client);
1260    return;
1261}
1262
1263PRIVATE_EXTERN void
1264arp_client_free(arp_client_t * * client_p)
1265{
1266    arp_client_t * 		client = NULL;
1267    int 			i;
1268    arp_if_session_t * 		if_session;
1269
1270    if (client_p != NULL) {
1271	client = *client_p;
1272    }
1273    if (client == NULL) {
1274	return;
1275    }
1276
1277    /* remove from list of clients */
1278    if_session = client->if_session;
1279    i = dynarray_index(&if_session->clients, client);
1280    if (i != -1) {
1281	dynarray_remove(&if_session->clients, i, NULL);
1282    }
1283    else {
1284	my_log(LOG_ERR, "arp_client_free(%s) not in list?",
1285	       if_name(if_session->if_p));
1286    }
1287
1288    /* free resources */
1289    arp_client_free_element(client);
1290    *client_p = NULL;
1291
1292    /* if we're the last client, if_session can go too */
1293    if (dynarray_count(&if_session->clients) == 0) {
1294	arp_if_session_free(&if_session);
1295    }
1296    return;
1297}
1298
1299PRIVATE_EXTERN void
1300arp_client_set_probes_are_collisions(arp_client_t * client,
1301				     boolean_t probes_are_collisions)
1302{
1303    client->probes_are_collisions = probes_are_collisions;
1304    return;
1305}
1306
1307static inline void
1308arp_client_setup_context(arp_client_t * client,
1309                         arp_result_func_t * func, void * arg1, void * arg2,
1310                    	 struct in_addr sender_ip, struct in_addr target_ip,
1311                    	 boolean_t skip)
1312{
1313    arp_if_session_t *  if_session = client->if_session;
1314
1315    arp_client_cancel(client);
1316    arp_if_session_update_hardware_address(if_session);
1317    client->sender_ip = sender_ip;
1318    client->target_ip = target_ip;
1319    client->func = func;
1320    client->arg1 = arg1;
1321    client->arg2 = arg2;
1322    client->errmsg[0] = '\0';
1323    client->try = 0;
1324    client->conflict_count = 0;
1325
1326    /* We might need to skip the first arp announcement since it
1327     * may have already been sent. */
1328    client->probe_info.skip_first = skip;
1329}
1330
1331PRIVATE_EXTERN void
1332arp_client_announce(arp_client_t * client,
1333                    arp_result_func_t * func, void * arg1, void * arg2,
1334                    struct in_addr sender_ip, struct in_addr target_ip,
1335		    boolean_t skip)
1336{
1337    arp_client_setup_context(client, func, arg1, arg2,
1338 			     sender_ip, target_ip, skip);
1339
1340    /* Send announce only, get rid of the probe count. */
1341    client->try = client->probe_info.probe_count;
1342    if (!arp_client_open_fd(client)) {
1343        /* report back an error to the caller */
1344        client->command_status = arp_status_error_e;
1345        arp_client_schedule_callback(client);
1346        return;
1347    }
1348
1349    client->command_status = arp_status_unknown_e;
1350    client->command = arp_client_command_probe_e;
1351    arp_client_probe_retransmit(client, NULL, NULL);
1352}
1353
1354PRIVATE_EXTERN void
1355arp_client_probe(arp_client_t * client,
1356		 arp_result_func_t * func, void * arg1, void * arg2,
1357		 struct in_addr sender_ip, struct in_addr target_ip)
1358{
1359    arp_client_setup_context(client, func, arg1, arg2,
1360			     sender_ip, target_ip, FALSE);
1361
1362    if (!arp_client_open_fd(client)) {
1363	/* report back an error to the caller */
1364	client->command_status = arp_status_error_e;
1365	arp_client_schedule_callback(client);
1366	return;
1367    }
1368    client->command_status = arp_status_unknown_e;
1369    client->command = arp_client_command_probe_e;
1370    arp_client_probe_retransmit(client, NULL, NULL);
1371    return;
1372}
1373
1374PRIVATE_EXTERN void
1375arp_client_resolve(arp_client_t * client,
1376		   arp_result_func_t * func, void * arg1, void * arg2,
1377		   struct in_addr sender_ip, struct in_addr target_ip,
1378		   uint32_t resolve_secs)
1379{
1380
1381    arp_if_session_t * 	if_session = client->if_session;
1382
1383    arp_client_cancel(client);
1384    arp_if_session_update_hardware_address(if_session);
1385    client->sender_ip = sender_ip;
1386    client->target_ip = target_ip;
1387    client->func = func;
1388    client->arg1 = arg1;
1389    client->arg2 = arg2;
1390    client->errmsg[0] = '\0';
1391    client->try = 0;
1392    client->conflict_count = 0;
1393    if (!arp_client_open_fd(client)) {
1394	/* report back an error to the caller */
1395	client->command_status = arp_status_error_e;
1396	arp_client_schedule_callback(client);
1397	return;
1398    }
1399    client->command_status = arp_status_unknown_e;
1400#define DEFAULT_RESOLVE_SECS	16
1401    client->resolve_secs
1402	= (resolve_secs > 0) ? resolve_secs : DEFAULT_RESOLVE_SECS;
1403    client->command = arp_client_command_resolve_e;
1404    arp_client_resolve_retransmit(client, NULL, NULL);
1405    return;
1406}
1407
1408PRIVATE_EXTERN void
1409arp_client_detect(arp_client_t * client,
1410		  arp_result_func_t * func, void * arg1, void * arg2,
1411		  const arp_address_info_t * list, int list_count,
1412		  boolean_t resolve)
1413{
1414    arp_if_session_t * 	if_session = client->if_session;
1415    int			list_size;
1416
1417    arp_client_cancel(client);
1418    arp_if_session_update_hardware_address(if_session);
1419    client->func = func;
1420    client->arg1 = arg1;
1421    client->arg2 = arg2;
1422    client->errmsg[0] = '\0';
1423    client->try = 0;
1424    client->conflict_count = 0;
1425    if (list_count == 0 || !arp_client_open_fd(client)) {
1426	/* report back an error to the caller */
1427	client->command_status = arp_status_error_e;
1428	arp_client_schedule_callback(client);
1429	return;
1430    }
1431    list_size = sizeof(*client->detect_list) * list_count;
1432    client->detect_list = (arp_address_info_t *)malloc(list_size);
1433    bcopy(list, client->detect_list, list_size);
1434    client->detect_list_count = list_count;
1435    client->command_status = arp_status_unknown_e;
1436    client->command = arp_client_command_detect_e;
1437    arp_client_detect_retransmit(client, (void *)(uintptr_t)resolve,
1438				 NULL);
1439    return;
1440}
1441
1442PRIVATE_EXTERN const char *
1443arp_client_errmsg(arp_client_t * client)
1444{
1445    return ((const char *)client->errmsg);
1446}
1447
1448PRIVATE_EXTERN void
1449arp_client_cancel(arp_client_t * client)
1450{
1451    client->errmsg[0] = '\0';
1452    client->func = client->arg1 = client->arg2 = NULL;
1453    client->command_status = arp_status_none_e;
1454    arp_client_close_fd(client);
1455    timer_cancel(client->timer_callout);
1456    if (client->detect_list != NULL) {
1457	free(client->detect_list);
1458	client->detect_list = NULL;
1459    }
1460    arp_client_cancel_callback(client);
1461    return;
1462}
1463
1464PRIVATE_EXTERN boolean_t
1465arp_client_defend(arp_client_t * client, struct in_addr our_ip)
1466{
1467    boolean_t		defended = FALSE;
1468    arp_if_session_t * 	if_session = client->if_session;
1469
1470    arp_client_cancel(client);
1471    arp_if_session_update_hardware_address(if_session);
1472
1473    if (!arp_client_open_fd(client)) {
1474	my_log(LOG_ERR, "arp_client_defend(%s): open fd failed",
1475	       if_name(if_session->if_p));
1476    }
1477    else {
1478	client->target_ip = client->sender_ip = our_ip;
1479	if (!arp_client_transmit(client, FALSE, NULL)) {
1480	    my_log(LOG_ERR, "arp_client_defend(%s): transmit failed",
1481		   if_name(if_session->if_p));
1482	}
1483	else {
1484	    defended = TRUE;
1485	}
1486	arp_client_close_fd(client);
1487    }
1488    return (defended);
1489}
1490
1491PRIVATE_EXTERN arp_session_t *
1492arp_session_init(arp_our_address_func_t * func,
1493		 arp_session_values_t * values)
1494{
1495    arp_session_t * 	session;
1496
1497    session = malloc(sizeof(*session));
1498    if (session == NULL) {
1499	return (NULL);
1500    }
1501    bzero(session, sizeof(*session));
1502    dynarray_init(&session->if_sessions, arp_if_session_free_element, NULL);
1503    if (func == NULL) {
1504	session->is_our_address = arp_is_our_address;
1505    }
1506    else {
1507	session->is_our_address = func;
1508    }
1509    if (values->probe_interval != NULL) {
1510	session->default_probe_info.retry_interval = *values->probe_interval;
1511    }
1512    else {
1513	session->default_probe_info.retry_interval.tv_sec = ARP_RETRY_SECS;
1514	session->default_probe_info.retry_interval.tv_usec = ARP_RETRY_USECS;
1515    }
1516    if (values->probe_count != NULL) {
1517	session->default_probe_info.probe_count = *values->probe_count;
1518    }
1519    else {
1520	session->default_probe_info.probe_count = ARP_PROBE_COUNT;
1521    }
1522    if (values->probe_gratuitous_count != NULL) {
1523	session->default_probe_info.gratuitous_count
1524	    = *values->probe_gratuitous_count;
1525    }
1526    else {
1527	session->default_probe_info.gratuitous_count = ARP_GRATUITOUS_COUNT;
1528    }
1529    if (values->detect_count != NULL) {
1530	session->default_detect_count = *values->detect_count;
1531    }
1532    else {
1533	session->default_detect_count = ARP_DETECT_COUNT;
1534    }
1535    if (values->detect_interval != NULL) {
1536	session->default_detect_retry = *values->detect_interval;
1537    }
1538    else {
1539	session->default_detect_retry.tv_sec = ARP_DETECT_RETRY_SECS;
1540	session->default_detect_retry.tv_usec = ARP_DETECT_RETRY_USECS;
1541    }
1542    if (values->resolve_interval != NULL) {
1543	session->default_resolve_retry = *values->resolve_interval;
1544    }
1545    else {
1546	session->default_resolve_retry.tv_sec = ARP_RESOLVE_RETRY_SECS;
1547	session->default_resolve_retry.tv_usec = ARP_RESOLVE_RETRY_USECS;
1548    }
1549    if (values->conflict_retry_count != NULL) {
1550	session->default_conflict_retry_count = *values->conflict_retry_count;
1551    }
1552    else {
1553	session->default_conflict_retry_count = ARP_CONFLICT_RETRY_COUNT;
1554    }
1555    if (values->conflict_delay_interval != NULL) {
1556	session->default_conflict_delay
1557	    = *values->conflict_delay_interval;
1558    }
1559    else {
1560	session->default_conflict_delay.tv_sec
1561	    = ARP_CONFLICT_RETRY_DELAY_SECS;
1562	session->default_conflict_delay.tv_usec
1563	    = ARP_CONFLICT_RETRY_DELAY_USECS;
1564    }
1565#ifdef TEST_ARP_SESSION
1566    session->next_client_index = 1;
1567#endif /* TEST_ARP_SESSION */
1568    return (session);
1569}
1570
1571PRIVATE_EXTERN void
1572arp_session_free(arp_session_t * * session_p)
1573{
1574    arp_session_t * session = *session_p;
1575
1576    dynarray_free(&session->if_sessions);
1577    bzero(session, sizeof(*session));
1578    free(session);
1579    *session_p = NULL;
1580    return;
1581}
1582
1583static arp_if_session_t *
1584arp_session_find_if_session(arp_session_t * session, const char * ifn)
1585{
1586    int			count;
1587    int			i;
1588
1589    count = dynarray_count(&session->if_sessions);
1590    for (i = 0; i < count; i++) {
1591	arp_if_session_t * 	if_session;
1592
1593	if_session = dynarray_element(&session->if_sessions, i);
1594	if (strcmp(if_name(if_session->if_p), ifn) == 0) {
1595	    return (if_session);
1596	}
1597    }
1598    return (NULL);
1599}
1600
1601static void
1602arp_if_session_free_element(void * arg)
1603{
1604    arp_if_session_t * if_session = (arp_if_session_t *)arg;
1605
1606    /* free all of the clients, close file descriptor */
1607    dynarray_free(&if_session->clients);
1608
1609    free(if_session);
1610    return;
1611}
1612
1613static void
1614arp_if_session_free(arp_if_session_t * * if_session_p)
1615{
1616    arp_if_session_t * 		if_session = NULL;
1617    int 			i;
1618    arp_session_t * 		session;
1619
1620    if (if_session_p != NULL) {
1621	if_session = *if_session_p;
1622    }
1623    if (if_session == NULL) {
1624	return;
1625    }
1626
1627    /* remove from the list of if_sessions */
1628    session = if_session->session;
1629    i = dynarray_index(&session->if_sessions, if_session);
1630    if (i != -1) {
1631	dynarray_remove(&session->if_sessions, i, NULL);
1632    }
1633    else {
1634	my_log(LOG_ERR, "arp_if_session_free(%s) not in list?",
1635	       if_name(if_session->if_p));
1636    }
1637
1638    /* release resources */
1639    arp_if_session_free_element(if_session);
1640
1641    *if_session_p = NULL;
1642    return;
1643}
1644
1645static arp_if_session_t *
1646arp_session_new_if_session(arp_session_t * session, interface_t * if_p)
1647{
1648    struct firewire_address	fw_addr;
1649    arp_if_session_t * 		if_session;
1650
1651    if_session = arp_session_find_if_session(session, if_name(if_p));
1652    if (if_session != NULL) {
1653	return (if_session);
1654    }
1655    switch (if_link_type(if_p)) {
1656    case IFT_ETHER:
1657	break;
1658    case IFT_IEEE1394:
1659	/* copy in the firewire address */
1660	if (getFireWireAddress(if_name(if_p), &fw_addr) == FALSE) {
1661	    my_log(LOG_ERR,
1662		   "arp_client_init(%s): could not retrieve firewire address",
1663		   if_name(if_p));
1664	    return (NULL);
1665	}
1666	break;
1667    default:
1668	my_log(LOG_ERR, "arp_client_init(%s): unsupported network type",
1669	       if_name(if_p));
1670	return (NULL);
1671    }
1672    if_session = (arp_if_session_t *)malloc(sizeof(*if_session));
1673    bzero(if_session, sizeof(*if_session));
1674    dynarray_init(&if_session->clients, arp_client_free_element, NULL);
1675    if (if_link_type(if_p) == IFT_IEEE1394) {
1676	/* copy in the fw address */
1677	if_session->fw_addr = fw_addr;
1678    }
1679    if_session->if_p = if_p;
1680    if_session->session = session;
1681    dynarray_add(&session->if_sessions, if_session);
1682    return (if_session);
1683}
1684
1685PRIVATE_EXTERN void
1686arp_session_set_debug(arp_session_t * session, int debug)
1687{
1688    session->debug = debug;
1689    return;
1690}
1691
1692#ifdef TEST_ARP_SESSION
1693#include <stdarg.h>
1694typedef boolean_t		func_t(int argc, const char * * argv);
1695typedef func_t * 		funcptr_t;
1696
1697static arp_session_t *		S_arp_session;
1698static boolean_t 		S_debug = FALSE;
1699static func_t			S_do_probe;
1700static func_t			S_do_resolve;
1701static func_t			S_do_detect;
1702static func_t			S_cancel_probe;
1703static func_t			S_toggle_debug;
1704static func_t			S_new_client;
1705static func_t			S_free_client;
1706static func_t			S_list;
1707static func_t			S_quit;
1708static func_t			S_client_params;
1709static interface_list_t *	S_interfaces;
1710
1711#define BASE_16		16
1712
1713static uint8_t *
1714hexstrtobin(const char * str, int * len)
1715{
1716    int		buf_pos;
1717    uint8_t * 	buf = NULL;
1718    boolean_t	done = FALSE;
1719    int		max_decoded_len;
1720    const char * scan = str;
1721    int 	slen = strlen(str);
1722
1723    *len = 0;
1724    /* the worst case we turn "1:2:3:4:5:6" into 6 bytes
1725     * strlen("1:2:3:4:5:6") = 11
1726     * so to get the approximate decoded length,
1727     * we want strlen(str) / 2 + 1
1728     */
1729    max_decoded_len = (slen / 2) + 1;
1730    buf = (uint8_t *)malloc(max_decoded_len);
1731    if (buf == NULL) {
1732	return (buf);
1733    }
1734    for (buf_pos = 0; buf_pos < max_decoded_len && !done; buf_pos++) {
1735	char		tmp[4];
1736	const char *	colon;
1737
1738	colon = strchr(scan, ':');
1739	if (colon == NULL) {
1740	    done = TRUE;
1741	    colon = str + slen;
1742	}
1743	if ((colon - scan) > (sizeof(tmp) - 1)) {
1744	    goto err;
1745	}
1746	strncpy(tmp, scan, colon - scan);
1747	tmp[colon - scan] = '\0';
1748	buf[buf_pos] = (u_char)strtol(tmp, NULL, BASE_16);
1749	scan = colon + 1;
1750    }
1751    *len = buf_pos;
1752    return (buf);
1753  err:
1754    if (buf) {
1755	 free(buf);
1756    }
1757    return (NULL);
1758}
1759
1760static void
1761arp_session_log(int priority, const char * message, ...)
1762{
1763    va_list 		ap;
1764
1765    if (priority == LOG_DEBUG) {
1766	if (S_arp_session->debug == FALSE)
1767	    return;
1768    }
1769    va_start(ap, message);
1770    vfprintf(stderr, message, ap);
1771    fprintf(stderr, "\n");
1772    fflush(stderr);
1773    return;
1774}
1775
1776static const struct command_info {
1777    char *	command;
1778    funcptr_t	func;
1779    int		argc;
1780    char *	usage;
1781    int		display;
1782} commands[] = {
1783    { "new", S_new_client, 1, "<ifname>", 1 },
1784    { "free", S_free_client, 1, "<client_index>", 1 },
1785    { "probe", S_do_probe, 3, "<client_index> <sender_ip> <target_ip>", 1 },
1786    { "resolve", S_do_resolve, 3, "<client_index> <sender_ip> <target_ip>", 1 },
1787    { "detect", S_do_detect, 4,
1788      "<client_index> [ <sender_ip> <target_ip> <target_hardware> ]+", 1 },
1789    { "cancel", S_cancel_probe, 1, "<client_index>", 1 },
1790    { "params", S_client_params, 1, "<client_index> [ default | <interval> <probes> [ <gratuitous> ] ]", 1 },
1791    { "debug", S_toggle_debug, 0, NULL, 1 },
1792    { "list", S_list, 0, NULL, 1 },
1793    { "quit", S_quit, 0, NULL, 1 },
1794    { NULL, NULL, 0 }
1795};
1796
1797struct arg_info {
1798    char * *	argv;
1799    int		argc;
1800    int		argv_size;
1801};
1802
1803static void
1804arg_info_init(struct arg_info * args)
1805{
1806    args->argv = NULL;
1807    args->argv_size = 0;
1808    args->argc = 0;
1809    return;
1810}
1811
1812static void
1813arg_info_free(struct arg_info * args)
1814{
1815    if (args->argv != NULL) {
1816	free(args->argv);
1817    }
1818    arg_info_init(args);
1819    return;
1820}
1821
1822/*
1823static void
1824arg_info_print(struct arg_info * args)
1825{
1826    int	i;
1827
1828    for (i = 0; i < args->argc; i++) {
1829	printf("%2d. '%s'\n", i, args->argv[i]);
1830    }
1831    return;
1832}
1833*/
1834
1835static void
1836arg_info_add(struct arg_info * args, char * new_arg)
1837{
1838    if (args->argv == NULL) {
1839	args->argv_size = 6;
1840	args->argv = (char * *)malloc(sizeof(*args->argv) * args->argv_size);
1841    }
1842    else if (args->argc == args->argv_size) {
1843	args->argv_size *= 2;
1844	args->argv = (char * *)realloc(args->argv,
1845				       sizeof(*args->argv) * args->argv_size);
1846    }
1847    args->argv[args->argc++] = new_arg;
1848    return;
1849}
1850
1851void
1852my_CFRelease(void * t)
1853{
1854    void * * obj = (void * *)t;
1855    if (obj && *obj) {
1856	CFRelease(*obj);
1857	*obj = NULL;
1858    }
1859    return;
1860}
1861
1862static int
1863arp_link_length(interface_t * if_p)
1864{
1865    int len;
1866
1867    if (if_link_arptype(if_p) == ARPHRD_IEEE1394) {
1868	len = sizeof(struct firewire_eui64);
1869    }
1870    else {
1871	len = ETHER_ADDR_LEN;
1872    }
1873    return (len);
1874}
1875
1876
1877static void
1878arp_test(void * arg1, void * arg2, const arp_result_t * result)
1879{
1880    arp_client_t *	client = (arp_client_t *)arg1;
1881
1882    if (result->error) {
1883	printf("ARP probe failed: '%s'\n",
1884	       client->errmsg);
1885    }
1886    else if (result->in_use) {
1887	int i;
1888	int len = arp_link_length(client->if_session->if_p);
1889	const u_char * addr = result->addr.target_hardware;
1890
1891	printf("ip address " IP_FORMAT " in use by",
1892	       IP_LIST(&client->target_ip));
1893	for (i = 0; i < len; i++) {
1894	    printf("%c%02x", i == 0 ? ' ' : ':', addr[i]);
1895	}
1896	printf("\n");
1897    }
1898    else {
1899	printf("ip address " IP_FORMAT " is not in use\n",
1900	       IP_LIST(&client->target_ip));
1901    }
1902}
1903
1904static void
1905arp_detect_callback(void * arg1, void * arg2, const arp_result_t * result)
1906{
1907    arp_client_t *	client = (arp_client_t *)arg1;
1908
1909    if (result->error) {
1910	printf("ARP detect failed: '%s'\n",
1911	       client->errmsg);
1912    }
1913    else if (result->in_use) {
1914	int i;
1915	int len = arp_link_length(client->if_session->if_p);
1916	const u_char * addr = result->addr.target_hardware;
1917
1918	printf("ARP detected Sender IP " IP_FORMAT " Target IP " IP_FORMAT
1919	       " Target Hardware",
1920	       IP_LIST(&result->addr.sender_ip),
1921	       IP_LIST(&result->addr.target_ip));
1922	for (i = 0; i < len; i++) {
1923	    printf("%c%02x", i == 0 ? ' ' : ':', addr[i]);
1924	}
1925	printf("\n");
1926    }
1927    else {
1928	printf("Did not detect any IP\n");
1929    }
1930}
1931
1932static boolean_t
1933S_toggle_debug(int argc, const char * * argv)
1934{
1935    S_debug = !S_debug;
1936    arp_session_set_debug(S_arp_session, S_debug);
1937    if (S_debug) {
1938	printf("debug mode enabled\n");
1939    }
1940    else {
1941	printf("debug mode disabled\n");
1942    }
1943    return (TRUE);
1944}
1945
1946static boolean_t
1947S_quit(int argc, const char * * argv)
1948{
1949    exit(0);
1950    return (TRUE);
1951}
1952
1953static boolean_t
1954S_new_client(int argc, const char * * argv)
1955{
1956    arp_client_t *	client;
1957    interface_t *	if_p;
1958
1959    if_p = ifl_find_name(S_interfaces, argv[1]);
1960    if (if_p == NULL) {
1961	fprintf(stderr, "interface %s does not exist\n", argv[1]);
1962	goto done;
1963    }
1964    client = arp_client_init(S_arp_session, if_p);
1965    if (client == NULL) {
1966	fprintf(stderr, "Could not create a new client over %s\n",
1967		if_name(if_p));
1968	goto done;
1969    }
1970    printf("%d\n", client->client_index);
1971 done:
1972    return (client != NULL);
1973}
1974
1975static boolean_t
1976get_int_param(const char * arg, int * ret_int)
1977{
1978    char *	endptr;
1979    int		val;
1980
1981    val = strtol(arg, &endptr, 0);
1982    if (endptr == arg || (val == 0 && errno != 0)) {
1983	return (FALSE);
1984    }
1985    *ret_int = val;
1986    return (TRUE);
1987}
1988
1989static boolean_t
1990get_client_index(const char * arg, int * client_index)
1991{
1992    if (get_int_param(arg, client_index)) {
1993	return (TRUE);
1994    }
1995    if (strcmp(arg, ".") == 0) {
1996	*client_index = S_arp_session->next_client_index - 1;
1997	return (TRUE);
1998    }
1999    return (FALSE);
2000}
2001
2002static boolean_t
2003S_do_probe(int argc, const char * * argv)
2004{
2005    arp_client_t *	client;
2006    int			client_index;
2007    struct in_addr 	sender_ip;
2008    struct in_addr     	target_ip;
2009
2010    if (get_client_index(argv[1], &client_index) == FALSE) {
2011	fprintf(stderr, "invalid client index\n");
2012	goto done;
2013    }
2014    client = arp_session_find_client_with_index(S_arp_session, client_index);
2015    if (client == NULL) {
2016	fprintf(stderr, "no such client index\n");
2017	goto done;
2018    }
2019    if (inet_aton(argv[2], &sender_ip) == 0) {
2020	fprintf(stderr, "invalid sender ip address %s\n", argv[2]);
2021	client = NULL;
2022	goto done;
2023    }
2024    if (inet_aton(argv[3], &target_ip) == 0) {
2025	fprintf(stderr, "invalid target ip address %s\n", argv[3]);
2026	client = NULL;
2027	goto done;
2028    }
2029    arp_client_probe(client, arp_test, client, NULL, sender_ip, target_ip);
2030 done:
2031    return (client != NULL);
2032}
2033
2034static boolean_t
2035S_do_resolve(int argc, const char * * argv)
2036{
2037    arp_client_t *	client;
2038    int			client_index;
2039    struct in_addr 	sender_ip;
2040    struct in_addr     	target_ip;
2041
2042    if (get_client_index(argv[1], &client_index) == FALSE) {
2043	fprintf(stderr, "invalid client index\n");
2044	goto done;
2045    }
2046    client = arp_session_find_client_with_index(S_arp_session, client_index);
2047    if (client == NULL) {
2048	fprintf(stderr, "no such client index\n");
2049	goto done;
2050    }
2051    if (inet_aton(argv[2], &sender_ip) == 0) {
2052	fprintf(stderr, "invalid sender ip address %s\n", argv[2]);
2053	client = NULL;
2054	goto done;
2055    }
2056    if (inet_aton(argv[3], &target_ip) == 0) {
2057	fprintf(stderr, "invalid target ip address %s\n", argv[3]);
2058	client = NULL;
2059	goto done;
2060    }
2061    arp_client_resolve(client, arp_test, client, NULL, sender_ip, target_ip, 0);
2062 done:
2063    return (client != NULL);
2064}
2065
2066static boolean_t
2067S_do_detect(int argc, const char * * argv)
2068{
2069    arp_client_t *	client;
2070    int			client_index;
2071    uint8_t *		hwaddr = NULL;
2072    int			hwaddr_len;
2073    int			i;
2074    arp_address_info_t *info = NULL;
2075    int			info_count;
2076
2077    if (get_client_index(argv[1], &client_index) == FALSE) {
2078	fprintf(stderr, "invalid client index\n");
2079	goto done;
2080    }
2081    client = arp_session_find_client_with_index(S_arp_session, client_index);
2082    if (client == NULL) {
2083	fprintf(stderr, "no such client index\n");
2084	goto done;
2085    }
2086    if (((argc - 2) % 3) != 0) {
2087	fprintf(stderr, "incorrect number of arguments\n");
2088	client = NULL;
2089	goto done;
2090    }
2091    info_count = (argc - 2) / 3;
2092    info = malloc(sizeof(*info) * info_count);
2093    for (i = 0; i < info_count; i++) {
2094	if (inet_aton(argv[3 * i + 2], &info[i].sender_ip) == 0) {
2095	    fprintf(stderr, "invalid sender ip address %s\n", argv[3 * i + 2]);
2096	    client = NULL;
2097	    goto done;
2098	}
2099	if (inet_aton(argv[3 * i + 3], &info[i].target_ip) == 0) {
2100	    fprintf(stderr, "invalid target ip address %s\n", argv[3 * i + 3]);
2101	    client = NULL;
2102	    goto done;
2103	}
2104	/* get the hardware address */
2105	hwaddr = hexstrtobin(argv[3 * i + 4], &hwaddr_len);
2106	if (hwaddr == NULL
2107	    || (hwaddr_len != ETHER_ADDR_LEN
2108		&& hwaddr_len != FIREWIRE_EUI64_LEN)) {
2109	    fprintf(stderr, "invalid hardware address %s\n", argv[3 * i + 4]);
2110	    client = NULL;
2111	    if (hwaddr != NULL) {
2112		free(hwaddr);
2113	    }
2114	    goto done;
2115	}
2116	bcopy(hwaddr, info[i].target_hardware, hwaddr_len);
2117	free(hwaddr);
2118    }
2119    arp_client_detect(client,
2120		      arp_detect_callback, client, NULL,
2121		      info, info_count);
2122 done:
2123    if (info != NULL) {
2124	free(info);
2125    }
2126    return (client != NULL);
2127}
2128
2129static boolean_t
2130S_free_client(int argc, const char * * argv)
2131{
2132    arp_client_t *	client;
2133    int			client_index;
2134
2135    if (get_client_index(argv[1], &client_index) == FALSE) {
2136	fprintf(stderr, "invalid client index\n");
2137	goto done;
2138    }
2139    client = arp_session_find_client_with_index(S_arp_session, client_index);
2140    if (client == NULL) {
2141	fprintf(stderr, "no such client index\n");
2142	goto done;
2143    }
2144    arp_client_free(&client);
2145 done:
2146    return (client != NULL);
2147}
2148
2149static boolean_t
2150S_cancel_probe(int argc, const char * * argv)
2151{
2152    arp_client_t *	client;
2153    int			client_index;
2154
2155    if (get_client_index(argv[1], &client_index) == FALSE) {
2156	fprintf(stderr, "invalid client index\n");
2157	goto done;
2158    }
2159    client = arp_session_find_client_with_index(S_arp_session, client_index);
2160    if (client == NULL) {
2161	fprintf(stderr, "no such client index\n");
2162	goto done;
2163    }
2164    arp_client_cancel(client);
2165
2166 done:
2167    return (client != NULL);
2168}
2169
2170static boolean_t
2171S_client_params(int argc, const char * * argv)
2172{
2173    arp_client_t *	client;
2174    int			client_index;
2175    struct probe_info *	probe_info;
2176
2177    if (get_client_index(argv[1], &client_index) == FALSE) {
2178	fprintf(stderr, "invalid client index\n");
2179	goto done;
2180    }
2181    client = arp_session_find_client_with_index(S_arp_session, client_index);
2182    if (client == NULL) {
2183	fprintf(stderr, "no such client index\n");
2184	goto done;
2185    }
2186    if (argc == 2) {
2187	/* display only */
2188    }
2189    else if (strcmp(argv[2], "default") == 0) {
2190	if (argc != 3) {
2191	    fprintf(stderr, "Too many parameters specified\n");
2192	    client = NULL;
2193	    goto done;
2194	}
2195	arp_client_restore_default_probe_info(client);
2196    }
2197    else if (argc < 4) {
2198	fprintf(stderr, "insufficient args\n");
2199	client = NULL;
2200	goto done;
2201    }
2202    else {
2203	char *		endptr;
2204	float		interval;
2205	int		gratuitous_count = 0;
2206	int		probe_count;
2207	struct timeval	tv;
2208
2209	if (argc > 5) {
2210	    fprintf(stderr, "Too many parameters specified\n");
2211	    client = NULL;
2212	    goto done;
2213	}
2214	interval = strtof(argv[2], &endptr);
2215	if (endptr == argv[2] || (interval == 0 && errno != 0)) {
2216	    fprintf(stderr, "Invalid probe interval specified\n");
2217	    client = NULL;
2218	    goto done;
2219	}
2220	tv.tv_sec = (int)interval;
2221	tv.tv_usec = (interval - tv.tv_sec) * 1000 * 1000;
2222	if (get_int_param(argv[3], &probe_count) == FALSE) {
2223	    fprintf(stderr, "Invalid probe count specified\n");
2224	    client = NULL;
2225	    goto done;
2226	}
2227	if (argc == 5) {
2228	    if (get_int_param(argv[4], &gratuitous_count) == FALSE) {
2229		fprintf(stderr, "Invalid gratuitous count specified\n");
2230		client = NULL;
2231		goto done;
2232	    }
2233	}
2234	arp_client_set_probe_info(client, &tv, &probe_count,
2235				  &gratuitous_count);
2236    }
2237    probe_info = &client->probe_info;
2238    printf("Probe interval %d.%06d probes %d gratuitous %d\n",
2239	   (int)probe_info->retry_interval.tv_sec,
2240	   (int)probe_info->retry_interval.tv_usec,
2241	   probe_info->probe_count, probe_info->gratuitous_count);
2242
2243 done:
2244    return (client != NULL);
2245}
2246
2247static void
2248dump_bytes(const unsigned char * buf, int buf_len)
2249{
2250    int i;
2251
2252    for (i = 0; i < buf_len; i++) {
2253	printf("%c%02x", i == 0 ? ' ' : ':', buf[i]);
2254    }
2255    return;
2256}
2257
2258static boolean_t
2259S_list(int argc, const char * * argv)
2260{
2261    int		if_sessions_count;
2262    int		i;
2263    int		j;
2264
2265    if_sessions_count = dynarray_count(&S_arp_session->if_sessions);
2266    for (i = 0; i < if_sessions_count; i++) {
2267	int			clients_count;
2268	arp_if_session_t *	if_session;
2269	int			len;
2270
2271	if_session = dynarray_element(&S_arp_session->if_sessions, i);
2272	clients_count = dynarray_count(&if_session->clients);
2273	len = arp_link_length(if_session->if_p);
2274	for (j = 0; j < clients_count; j++) {
2275	    arp_client_t *	client;
2276
2277	    client = dynarray_element(&if_session->clients, j);
2278	    printf("%d. %s", client->client_index, if_name(if_session->if_p));
2279	    switch (client->command_status) {
2280	    case arp_status_none_e:
2281		printf(" idle");
2282		break;
2283	    case arp_status_unknown_e:
2284		switch (client->command) {
2285		case arp_client_command_probe_e:
2286		    printf(" probing %s", inet_ntoa(client->target_ip));
2287		    break;
2288		case arp_client_command_resolve_e:
2289		    printf(" resolving %s", inet_ntoa(client->target_ip));
2290		    break;
2291		case arp_client_command_detect_e:
2292		    printf(" detecting");
2293		    break;
2294		default:
2295		    break;
2296		}
2297		break;
2298	    case arp_status_in_use_e:
2299		printf(" %s in use by",
2300		       inet_ntoa(client->in_use_addr.target_ip));
2301		dump_bytes((unsigned char *)client->in_use_addr.target_hardware,
2302			   len);
2303		break;
2304	    case arp_status_not_in_use_e:
2305		printf(" %s not in use", inet_ntoa(client->target_ip));
2306		break;
2307	    case arp_status_error_e:
2308		printf(" %s error encountered", inet_ntoa(client->target_ip));
2309		break;
2310	    }
2311	    printf("\n");
2312	}
2313    }
2314    return (TRUE);
2315}
2316
2317static void
2318parse_command(char * buf, struct arg_info * args)
2319{
2320    char *		arg_start = NULL;
2321    char *		scan;
2322
2323    for (scan = buf; *scan != '\0'; scan++) {
2324	char ch = *scan;
2325
2326	switch (ch) {
2327	case ' ':
2328	case '\n':
2329	case '\t':
2330	    *scan = '\0';
2331	    if (arg_start != NULL) {
2332		arg_info_add(args, arg_start);
2333		arg_start = NULL;
2334	    }
2335	    break;
2336	default:
2337	    if (arg_start == NULL) {
2338		arg_start = scan;
2339	    }
2340	    break;
2341	}
2342    }
2343    if (arg_start != NULL) {
2344	arg_info_add(args, arg_start);
2345    }
2346    return;
2347}
2348
2349void
2350usage()
2351{
2352    int i;
2353
2354    fprintf(stderr, "Available commands: ");
2355    for (i = 0; commands[i].command; i++) {
2356	if (commands[i].display) {
2357	    fprintf(stderr, "%s%s",  i == 0 ? "" : ", ",
2358		    commands[i].command);
2359	}
2360    }
2361    fprintf(stderr, "\n");
2362    return;
2363}
2364
2365static const struct command_info *
2366S_lookup_command(char * cmd)
2367{
2368    int i;
2369
2370    for (i = 0; commands[i].command; i++) {
2371	if (strcmp(cmd, commands[i].command) == 0) {
2372	    return commands + i;
2373	}
2374    }
2375    return (NULL);
2376}
2377
2378static void
2379process_command(struct arg_info * args)
2380{
2381    boolean_t			ok = TRUE;
2382    const struct command_info *	cmd_info;
2383
2384    cmd_info = S_lookup_command(args->argv[0]);
2385    if (cmd_info != NULL) {
2386	if (cmd_info->argc >= args->argc) {
2387	    ok = FALSE;
2388	    if (cmd_info->display) {
2389		fprintf(stderr, "insufficient args\nusage:\n\t%s %s\n",
2390			args->argv[0],
2391			cmd_info->usage ? cmd_info->usage : "");
2392	    }
2393	}
2394    }
2395    else {
2396	ok = FALSE;
2397	usage();
2398    }
2399
2400    if (ok) {
2401	if ((*cmd_info->func)(args->argc, (const char * *)args->argv)
2402	    == FALSE) {
2403	    fprintf(stderr, "usage:\n\t%s %s\n",
2404		    args->argv[0],
2405		    cmd_info->usage ? cmd_info->usage : "");
2406	}
2407    }
2408    return;
2409}
2410
2411static void
2412display_prompt(FILE * f)
2413{
2414    fprintf(f, "# ");
2415    fflush(f);
2416    return;
2417}
2418
2419static void
2420user_input(CFSocketRef s, CFSocketCallBackType type,
2421	   CFDataRef address, const void *data, void *info)
2422{
2423    struct arg_info	args;
2424    char 		choice[1024 * 10];
2425
2426    if (fgets(choice, sizeof(choice), stdin) == NULL) {
2427	exit(1);
2428    }
2429    arg_info_init(&args);
2430    parse_command(choice, &args);
2431    if (args.argv == NULL) {
2432	goto done;
2433    }
2434    process_command(&args);
2435
2436 done:
2437    arg_info_free(&args);
2438    display_prompt(stdout);
2439    return;
2440}
2441
2442static void
2443initialize_input()
2444{
2445    CFSocketContext	context = { 0, NULL, NULL, NULL, NULL };
2446    CFRunLoopSourceRef	rls = NULL;
2447    CFSocketRef		socket = NULL;
2448
2449    /* context.info = NULL; */
2450    socket = CFSocketCreateWithNative(NULL, fileno(stdin),
2451				      kCFSocketReadCallBack,
2452				      user_input, &context);
2453    if (socket == NULL) {
2454	fprintf(stderr, "CFSocketCreateWithNative failed\n");
2455	exit(1);
2456    }
2457    rls = CFSocketCreateRunLoopSource(NULL, socket, 0);
2458    if (rls == NULL) {
2459	fprintf(stderr, "CFSocketCreateRunLoopSource failed\n");
2460	exit(1);
2461    }
2462    CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
2463    my_CFRelease(&rls);
2464    my_CFRelease(&socket);
2465    display_prompt(stdout);
2466    return;
2467}
2468
2469int
2470main(int argc, char * argv[])
2471{
2472    arp_session_values_t	arp_values;
2473    int				gratuitous = 0;
2474
2475    S_interfaces = ifl_init(FALSE);
2476    if (S_interfaces == NULL) {
2477	fprintf(stderr, "couldn't get interface list\n");
2478	exit(1);
2479    }
2480
2481    /* initialize the default values structure */
2482    bzero(&arp_values, sizeof(arp_values));
2483    arp_values.probe_gratuitous_count = &gratuitous;
2484    S_arp_session = arp_session_init(NULL, &arp_values);
2485    if (S_arp_session == NULL) {
2486	fprintf(stderr, "arp_session_init failed\n");
2487	exit(1);
2488    }
2489    initialize_input();
2490    CFRunLoopRun();
2491    exit(0);
2492}
2493#endif /* TEST_ARP_SESSION */
2494