#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/errno.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/sockio.h>
#include <net/if.h>
#include <net/ethernet.h>
#include <netinet/in.h>
#include <netinet/udp.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <net/if_arp.h>
#include <net/firewire.h>
#include <netinet/if_ether.h>
#include <arpa/inet.h>
#include <net/if_types.h>
#include <net/bpf.h>
#include <CoreFoundation/CFRunLoop.h>
#include "util.h"
#include <syslog.h>
#include "bpflib.h"
#include "util.h"
#include "dprintf.h"
#include "dynarray.h"
#include "timer.h"
#include "FDSet.h"
#include "ipconfigd_globals.h"
#include "arp_session.h"
#include "ioregpath.h"
#include "ipconfigd_threads.h"
struct firewire_arp {
struct arphdr fw_hdr;
uint8_t arp_sha[FIREWIRE_ADDR_LEN];
uint8_t arp_spa[4];
uint8_t arp_tpa[4];
};
struct probe_info {
struct timeval retry_interval;
int probe_count;
int gratuitous_count;
boolean_t skip_first;
};
struct arp_session {
int debug;
struct probe_info default_probe_info;
int default_detect_count;
struct timeval default_detect_retry;
int default_conflict_retry_count;
struct timeval default_conflict_delay;
struct timeval default_resolve_retry;
arp_our_address_func_t * is_our_address;
dynarray_t if_sessions;
#ifdef TEST_ARP_SESSION
int next_client_index;
#endif
};
struct arp_if_session {
arp_session_t * session;
interface_t * if_p;
dynarray_t clients;
char * receive_buf;
int receive_bufsize;
FDCalloutRef read_fd;
int read_fd_refcount;
struct firewire_address fw_addr;
};
typedef struct arp_if_session arp_if_session_t;
typedef enum {
arp_client_command_none_e = 0,
arp_client_command_probe_e = 1,
arp_client_command_resolve_e = 2,
arp_client_command_detect_e = 3
} arp_client_command_t;
typedef enum {
arp_status_none_e = 0,
arp_status_not_in_use_e = 1,
arp_status_in_use_e = 2,
arp_status_error_e = 3,
arp_status_unknown_e = 4,
} arp_status_t;
struct arp_client {
#ifdef TEST_ARP_SESSION
int client_index;
#endif
arp_client_command_t command;
arp_status_t command_status;
boolean_t fd_open;
arp_if_session_t * if_session;
arp_result_func_t * func;
void * arg1;
void * arg2;
struct in_addr sender_ip;
struct in_addr target_ip;
int try;
int conflict_count;
timer_callout_t * timer_callout;
arp_address_info_t in_use_addr;
char errmsg[128];
struct probe_info probe_info;
boolean_t probes_are_collisions;
uint32_t resolve_secs;
arp_address_info_t * detect_list;
int detect_list_count;
CFRunLoopObserverRef callback_rls;
};
#ifdef TEST_ARP_SESSION
#define my_log arp_session_log
static void arp_session_log(int priority, const char * message, ...);
#define G_IPConfiguration_verbose TRUE
#endif
#include <CoreFoundation/CFDictionary.h>
#include <CoreFoundation/CFRunLoop.h>
#include <SystemConfiguration/SCValidation.h>
#define ARP_STR "ARP "
static Boolean
getFireWireAddress(const char * ifname, struct firewire_address * addr_p)
{
CFDictionaryRef dict = NULL;
CFDataRef data;
Boolean found = FALSE;
dict = myIORegistryEntryBSDNameMatchingCopyValue(ifname, TRUE);
if (dict == NULL) {
return (FALSE);
}
data = CFDictionaryGetValue(dict, CFSTR("IOFWHWAddr"));
if (isA_CFData(data) == NULL || CFDataGetLength(data) != sizeof(*addr_p)) {
goto done;
}
CFDataGetBytes(data, CFRangeMake(0, sizeof(*addr_p)), (void *)addr_p);
addr_p->unicastFifoHi = htons(addr_p->unicastFifoHi);
addr_p->unicastFifoLo = htonl(addr_p->unicastFifoLo);
found = TRUE;
done:
if (dict != NULL) {
CFRelease(dict);
}
return (found);
}
static arp_if_session_t *
arp_session_new_if_session(arp_session_t * session, interface_t * if_p);
static void
arp_client_free_element(void * arg);
static void
arp_client_probe_retransmit(void * arg1, void * arg2, void * arg3);
static void
arp_client_probe_start(void * arg1, void * arg2, void * arg3);
static void
arp_client_resolve_retransmit(void * arg1, void * arg2, void * arg3);
static boolean_t
arp_client_open_fd(arp_client_t * client);
static void
arp_client_close_fd(arp_client_t * client);
static void
arp_if_session_free(arp_if_session_t * * if_session_p);
static void
arp_if_session_free_element(void * arg);
static void
arp_if_session_read(void * arg1, void * arg2);
static boolean_t
arp_is_our_address(interface_t * if_p, int hwtype, void * hwaddr, int hwlen);
static __inline__ char *
arpop_name(u_int16_t op)
{
switch (op) {
case ARPOP_REQUEST:
return "ARP REQUEST";
case ARPOP_REPLY:
return "ARP REPLY";
case ARPOP_REVREQUEST:
return "REVARP REQUEST";
case ARPOP_REVREPLY:
return "REVARP REPLY";
default:
break;
}
return ("<unknown>");
}
static void
dump_arp(struct arphdr * arp_p)
{
int arphrd = ntohs(arp_p->ar_hrd);
printf("\n");
printf("%s type=0x%x proto=0x%x\n", arpop_name(ntohs(arp_p->ar_op)),
arphrd, ntohs(arp_p->ar_pro));
switch (arphrd) {
case ARPHRD_ETHER:
{
struct ether_arp * earp = (struct ether_arp *)arp_p;
if (arp_p->ar_hln == sizeof(earp->arp_sha)) {
printf("Sender H/W\t%s\n",
ether_ntoa((const struct ether_addr *)earp->arp_sha));
printf("Target H/W\t%s\n",
ether_ntoa((const struct ether_addr *)earp->arp_tha));
}
printf("Sender IP\t%s\n",
inet_ntoa(*((struct in_addr *)earp->arp_spa)));
printf("Target IP\t%s\n",
inet_ntoa(*((struct in_addr *)earp->arp_tpa)));
}
break;
case ARPHRD_IEEE1394:
{
struct firewire_arp * farp = (struct firewire_arp *)arp_p;
if (arp_p->ar_hln == sizeof(farp->arp_sha)) {
printf("Sender H/W\t" FWA_FORMAT "\n",
FWA_LIST(farp->arp_sha));
}
printf("Sender IP\t%s\n",
inet_ntoa(*((struct in_addr *)farp->arp_spa)));
printf("Target IP\t%s\n",
inet_ntoa(*((struct in_addr *)farp->arp_tpa)));
}
break;
}
fflush(stdout);
return;
}
static void
arp_client_close_fd(arp_client_t * client)
{
arp_if_session_t * if_session = client->if_session;
if (client->fd_open == FALSE) {
return;
}
if (if_session->read_fd_refcount <= 0) {
my_log(LOG_INFO, "arp_client_close_fd(%s): bpf open fd count is %d",
if_name(if_session->if_p), if_session->read_fd_refcount);
return;
}
if_session->read_fd_refcount--;
my_log(LOG_DEBUG, "arp_client_close_fd(%s): bpf open fd count is %d",
if_name(if_session->if_p), if_session->read_fd_refcount);
client->fd_open = FALSE;
if (if_session->read_fd_refcount == 0) {
if (if_session->read_fd != NULL) {
my_log(LOG_DEBUG, "arp_client_close_fd(%s): closing bpf fd %d",
if_name(if_session->if_p),
FDCalloutGetFD(if_session->read_fd));
FDCalloutRelease(&if_session->read_fd);
}
if (if_session->receive_buf != NULL) {
free(if_session->receive_buf);
if_session->receive_buf = NULL;
}
}
return;
}
boolean_t
arp_client_is_active(arp_client_t * client)
{
return (client->func != NULL);
}
static void
arp_client_cancel_callback(arp_client_t * client)
{
if (client->callback_rls != NULL) {
CFRunLoopObserverInvalidate(client->callback_rls);
CFRelease(client->callback_rls);
client->callback_rls = NULL;
}
return;
}
static void
arp_client_callback(arp_client_t * client)
{
void * c_arg1;
void * c_arg2;
arp_result_func_t * func;
arp_result_t result;
c_arg1 = client->arg1;
c_arg2 = client->arg2;
func = client->func;
client->func = client->arg1 = client->arg2 = NULL;
arp_client_close_fd(client);
timer_cancel(client->timer_callout);
bzero(&result, sizeof(result));
switch (client->command_status) {
default:
case arp_status_none_e:
printf("No result for %s?\n",
if_name(client->if_session->if_p));
break;
case arp_status_error_e:
result.error = TRUE;
break;
case arp_status_not_in_use_e:
break;
case arp_status_in_use_e:
result.in_use = TRUE;
result.addr = client->in_use_addr;
break;
}
result.client = client;
(*func)(c_arg1, c_arg2, &result);
return;
}
static void
arp_client_do_callback(CFRunLoopObserverRef observer,
CFRunLoopActivity activity,
void * info)
{
arp_client_t * client = (arp_client_t *)info;
arp_client_cancel_callback(client);
arp_client_callback(client);
return;
}
static void
arp_client_schedule_callback(arp_client_t * client)
{
CFRunLoopObserverContext context = { 0, client, NULL, NULL, NULL };
arp_client_cancel_callback(client);
client->callback_rls
= CFRunLoopObserverCreate(NULL, kCFRunLoopAllActivities,
TRUE, 0, arp_client_do_callback, &context);
CFRunLoopAddObserver(CFRunLoopGetCurrent(), client->callback_rls,
kCFRunLoopDefaultMode);
return;
}
static boolean_t
arp_is_our_address(interface_t * if_p, int hwtype, void * hwaddr, int hwlen)
{
int link_length = if_link_length(if_p);
if (hwlen != link_length || hwtype != if_link_arptype(if_p)) {
return (FALSE);
}
if (bcmp(hwaddr, if_link_address(if_p), link_length) == 0) {
return (TRUE);
}
return (FALSE);
}
static void
arp_if_session_update_hardware_address(arp_if_session_t * if_session)
{
if (if_link_type(if_session->if_p) != IFT_IEEE1394) {
return;
}
if (getFireWireAddress(if_name(if_session->if_p),
&if_session->fw_addr) == FALSE) {
my_log(LOG_NOTICE,
"arp_if_session_update_hardware_address(%s):"
"could not retrieve firewire address",
if_name(if_session->if_p));
}
return;
}
static void
arp_if_session_read(void * arg1, void * arg2)
{
arp_client_t * client;
int client_count;
boolean_t debug;
char errmsg[128];
int hwlen = 0;
int hwtype;
int i;
arp_if_session_t * if_session;
int link_header_size;
int link_arp_size;
int link_length;
int n;
char * offset;
arp_session_t * session;
if_session = (arp_if_session_t *)arg1;
session = if_session->session;
errmsg[0] = '\0';
if (if_session->read_fd_refcount == 0) {
my_log(LOG_ERR, "arp_if_session_read: no pending clients?");
return;
}
debug = session->debug;
client_count = dynarray_count(&if_session->clients);
link_length = if_link_length(if_session->if_p);
hwtype = if_link_arptype(if_session->if_p);
switch (hwtype) {
default:
case ARPHRD_ETHER:
link_header_size = sizeof(struct ether_header);
link_arp_size = sizeof(struct ether_arp);
hwlen = ETHER_ADDR_LEN;
break;
case ARPHRD_IEEE1394:
link_header_size = sizeof(struct firewire_header);
link_arp_size = sizeof(struct firewire_arp);
hwlen = FIREWIRE_ADDR_LEN;
break;
}
n = read(FDCalloutGetFD(if_session->read_fd), if_session->receive_buf,
if_session->receive_bufsize);
if (n < 0) {
if (errno == EAGAIN) {
return;
}
my_log(LOG_ERR, "arp_if_session_read: read(%s) failed, %s (%d)",
if_name(if_session->if_p), strerror(errno), errno);
snprintf(errmsg, sizeof(errmsg),
"arp_if_session_read: read(%s) failed, %s (%d)",
if_name(if_session->if_p), strerror(errno), errno);
goto failed;
}
for (offset = if_session->receive_buf; n > 0; ) {
struct arphdr * arp_p;
struct bpf_hdr * bpf = (struct bpf_hdr *)offset;
void * hwaddr;
boolean_t is_our_address;
short op;
char * pkt_start;
struct in_addr * source_ip_p;
struct in_addr * target_ip_p;
int skip;
dprintf(("bpf remaining %d header %d captured %d\n", n,
bpf->bh_hdrlen, bpf->bh_caplen));
pkt_start = offset + bpf->bh_hdrlen;
arp_p = (struct arphdr *)(pkt_start + link_header_size);
if (debug) {
dump_arp(arp_p);
}
op = ntohs(arp_p->ar_op);
if (bpf->bh_caplen < (link_header_size + link_arp_size)
|| arp_p->ar_hln != hwlen
|| (op != ARPOP_REPLY && op != ARPOP_REQUEST)
|| ntohs(arp_p->ar_hrd) != hwtype
|| ntohs(arp_p->ar_pro) != ETHERTYPE_IP) {
goto next_packet;
}
switch (hwtype) {
default:
case ARPHRD_ETHER:
{
struct ether_arp * earp;
earp = (struct ether_arp *)arp_p;
source_ip_p = (struct in_addr *)earp->arp_spa;
target_ip_p = (struct in_addr *)earp->arp_tpa;
hwaddr = earp->arp_sha;
}
break;
case ARPHRD_IEEE1394:
{
struct firewire_arp * farp;
farp = (struct firewire_arp *)arp_p;
source_ip_p = (struct in_addr *)farp->arp_spa;
target_ip_p = (struct in_addr *)farp->arp_tpa;
hwaddr = farp->arp_sha;
}
break;
}
is_our_address
= (*session->is_our_address)(if_session->if_p,
hwtype,
hwaddr,
link_length);
for (i = 0; i < client_count; i++) {
int addr_index;
arp_client_t * client;
boolean_t got_match;
client = dynarray_element(&if_session->clients, i);
if (client->func == NULL) {
continue;
}
if (client->command_status == arp_status_in_use_e) {
continue;
}
got_match = FALSE;
switch (client->command) {
case arp_client_command_probe_e:
if (is_our_address) {
}
else if (client->target_ip.s_addr == source_ip_p->s_addr
|| (client->probes_are_collisions
&& op == ARPOP_REQUEST
&& source_ip_p->s_addr == 0
&& client->target_ip.s_addr == target_ip_p->s_addr)) {
client->in_use_addr.sender_ip = client->sender_ip;
client->in_use_addr.target_ip = client->target_ip;
bcopy(hwaddr, client->in_use_addr.target_hardware,
link_length);
got_match = TRUE;
}
break;
case arp_client_command_resolve_e:
if (client->target_ip.s_addr == source_ip_p->s_addr
&& op == ARPOP_REPLY) {
client->in_use_addr.sender_ip = client->sender_ip;
client->in_use_addr.target_ip = client->target_ip;
bcopy(hwaddr, client->in_use_addr.target_hardware,
link_length);
got_match = TRUE;
}
break;
case arp_client_command_detect_e:
if (op != ARPOP_REPLY) {
break;
}
for (addr_index = 0; addr_index < client->detect_list_count;
addr_index++) {
arp_address_info_t * info_p;
info_p = client->detect_list + addr_index;
if (info_p->sender_ip.s_addr == target_ip_p->s_addr
&& info_p->target_ip.s_addr == source_ip_p->s_addr
&& (bcmp(info_p->target_hardware, hwaddr, link_length)
== 0)) {
client->in_use_addr = *info_p;
got_match = TRUE;
break;
}
}
break;
default:
break;
}
if (got_match) {
client->command_status = arp_status_in_use_e;
if (client->command == arp_client_command_probe_e
&& client->probes_are_collisions == FALSE) {
client->conflict_count++;
my_log(LOG_DEBUG,
"arp_session: encountered conflict,"
" trying again %d (of %d)",
client->conflict_count,
session->default_conflict_retry_count + 1);
if (client->conflict_count
<= session->default_conflict_retry_count) {
timer_set_relative(client->timer_callout,
session->default_conflict_delay,
(timer_func_t *)
arp_client_probe_start,
client, NULL, NULL);
goto next_packet;
}
}
arp_client_schedule_callback(client);
}
}
next_packet:
skip = BPF_WORDALIGN(bpf->bh_caplen + bpf->bh_hdrlen);
if (skip == 0) {
break;
}
offset += skip;
n -= skip;
}
return;
failed:
for (i = 0; i < client_count; i++) {
client = dynarray_element(&if_session->clients, i);
if (client->func == NULL) {
continue;
}
strncpy(client->errmsg, errmsg, sizeof(client->errmsg));
client->command_status = arp_status_error_e;
arp_client_schedule_callback(client);
}
return;
}
static boolean_t
arp_client_open_fd(arp_client_t * client)
{
int bpf_fd;
arp_if_session_t * if_session = client->if_session;
int opt;
int status;
if (client->fd_open) {
return (TRUE);
}
if_session->read_fd_refcount++;
my_log(LOG_DEBUG, "arp_client_open_fd (%s): refcount %d",
if_name(if_session->if_p), if_session->read_fd_refcount);
client->fd_open = TRUE;
if (if_session->read_fd_refcount > 1) {
return (TRUE);
}
bpf_fd = bpf_new();
if (bpf_fd < 0) {
my_log(LOG_ERR, "arp_client_open_fd: bpf_new(%s) failed, %s (%d)",
if_name(if_session->if_p), strerror(errno), errno);
snprintf(client->errmsg, sizeof(client->errmsg),
"arp_client_open_fd: bpf_new(%s) failed, %s (%d)",
if_name(if_session->if_p), strerror(errno), errno);
goto failed;
}
opt = 1;
status = ioctl(bpf_fd, FIONBIO, &opt);
if (status < 0) {
my_log(LOG_ERR, "ioctl FIONBIO failed %s", strerror(errno));
goto failed;
}
status = bpf_setif(bpf_fd, if_name(if_session->if_p));
if (status < 0) {
my_log(LOG_ERR, "arp_client_open_fd: bpf_setif(%s) failed: %s (%d)",
if_name(if_session->if_p), strerror(errno), errno);
snprintf(client->errmsg, sizeof(client->errmsg),
"arp_client_open_fd: bpf_setif(%s) failed: %s (%d)",
if_name(if_session->if_p), strerror(errno), errno);
goto failed;
}
bpf_set_immediate(bpf_fd, 1);
switch (if_link_type(if_session->if_p)) {
default:
case IFT_ETHER:
status = bpf_arp_filter(bpf_fd, 12, ETHERTYPE_ARP,
sizeof(struct ether_arp)
+ sizeof(struct ether_header));
break;
case IFT_IEEE1394:
status = bpf_arp_filter(bpf_fd, 16, ETHERTYPE_ARP,
sizeof(struct firewire_arp)
+ sizeof(struct firewire_header));
break;
}
if (status < 0) {
my_log(LOG_ERR,
"arp_client_open_fd: bpf_arp_filter(%s) failed: %s (%d)",
if_name(if_session->if_p), strerror(errno), errno);
snprintf(client->errmsg, sizeof(client->errmsg),
"arp_client_open_fd: bpf_arp_filter(%s) failed: %s (%d)",
if_name(if_session->if_p), strerror(errno), errno);
goto failed;
}
status = bpf_get_blen(bpf_fd, &if_session->receive_bufsize);
if (status < 0) {
my_log(LOG_ERR,
"arp_client_open_fd: bpf_get_blen(%s) failed, %s (%d)",
if_name(if_session->if_p), strerror(errno), errno);
snprintf(client->errmsg, sizeof(client->errmsg),
"arp_client_open_fd: bpf_get_blen(%s) failed, %s (%d)",
if_name(if_session->if_p), strerror(errno), errno);
goto failed;
}
if_session->receive_buf = malloc(if_session->receive_bufsize);
if_session->read_fd
= FDCalloutCreate(bpf_fd,
arp_if_session_read, if_session, NULL);
if (if_session->read_fd == NULL) {
goto failed;
}
my_log(LOG_DEBUG, "arp_client_open_fd (%s): opened bpf fd %d\n",
if_name(if_session->if_p), bpf_fd);
return (TRUE);
failed:
if (bpf_fd >= 0) {
close(bpf_fd);
}
arp_client_close_fd(client);
return (FALSE);
}
static char link_broadcast[8] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
static boolean_t
arp_client_transmit(arp_client_t * client, boolean_t send_gratuitous,
const arp_address_info_t * info_p)
{
arp_if_session_t * if_session = client->if_session;
struct arphdr * hdr;
int status = 0;
int txbuf_aligned[32];
char * txbuf = (char *)txbuf_aligned;
int size;
bzero(txbuf_aligned, sizeof(txbuf_aligned));
switch (if_link_arptype(if_session->if_p)) {
case ARPHRD_ETHER:
{
struct ether_header * eh_p;
struct ether_arp * earp;
eh_p = (struct ether_header *)txbuf;
eh_p->ether_type = htons(ETHERTYPE_ARP);
if (info_p != NULL) {
bcopy(info_p->target_hardware, eh_p->ether_dhost,
sizeof(eh_p->ether_dhost));
}
else {
bcopy(link_broadcast, eh_p->ether_dhost,
sizeof(eh_p->ether_dhost));
}
earp = (struct ether_arp *)(txbuf + sizeof(*eh_p));
hdr = &earp->ea_hdr;
hdr->ar_hrd = htons(ARPHRD_ETHER);
hdr->ar_pro = htons(ETHERTYPE_IP);
hdr->ar_hln = sizeof(earp->arp_sha);;
hdr->ar_pln = sizeof(struct in_addr);
hdr->ar_op = htons(ARPOP_REQUEST);
bcopy(if_link_address(if_session->if_p), earp->arp_sha,
sizeof(earp->arp_sha));
if (info_p != NULL) {
*((struct in_addr *)earp->arp_spa) = info_p->sender_ip;
*((struct in_addr *)earp->arp_tpa) = info_p->target_ip;
}
else {
if (send_gratuitous == TRUE
&& client->sender_ip.s_addr == 0) {
*((struct in_addr *)earp->arp_spa) = client->target_ip;
}
else {
*((struct in_addr *)earp->arp_spa) = client->sender_ip;
}
*((struct in_addr *)earp->arp_tpa) = client->target_ip;
}
size = sizeof(*eh_p) + sizeof(*earp);
}
break;
case ARPHRD_IEEE1394:
{
struct firewire_header * fh_p;
struct firewire_arp * farp;
fh_p = (struct firewire_header *)txbuf;
fh_p->firewire_type = htons(ETHERTYPE_ARP);
if (info_p != NULL) {
bcopy(info_p->target_hardware, fh_p->firewire_dhost,
sizeof(fh_p->firewire_dhost));
}
else {
bcopy(link_broadcast, fh_p->firewire_dhost,
sizeof(fh_p->firewire_dhost));
}
farp = (struct firewire_arp *)(txbuf + sizeof(*fh_p));
hdr = &farp->fw_hdr;
hdr->ar_hrd = htons(ARPHRD_IEEE1394);
hdr->ar_pro = htons(ETHERTYPE_IP);
hdr->ar_hln = sizeof(farp->arp_sha);;
hdr->ar_pln = sizeof(struct in_addr);
hdr->ar_op = htons(ARPOP_REQUEST);
bcopy(&if_session->fw_addr, farp->arp_sha,
sizeof(farp->arp_sha));
if (info_p != NULL) {
*((struct in_addr *)farp->arp_spa) = info_p->sender_ip;
*((struct in_addr *)farp->arp_tpa) = info_p->target_ip;
}
else {
if (send_gratuitous == TRUE
&& client->sender_ip.s_addr == 0) {
*((struct in_addr *)farp->arp_spa) = client->target_ip;
}
else {
*((struct in_addr *)farp->arp_spa) = client->sender_ip;
}
*((struct in_addr *)farp->arp_tpa) = client->target_ip;
}
size = sizeof(*fh_p) + sizeof(*farp);
}
break;
default:
snprintf(client->errmsg, sizeof(client->errmsg),
"arp_client_transmit(%s): "
"interface hardware type not yet known",
if_name(if_session->if_p));
goto failed;
}
status = bpf_write(FDCalloutGetFD(if_session->read_fd), txbuf, size);
if (status < 0) {
my_log(LOG_ERR, "arp_client_transmit(%s) failed, %s (%d)",
if_name(if_session->if_p), strerror(errno), errno);
snprintf(client->errmsg, sizeof(client->errmsg),
"arp_client_transmit(%s) failed, %s (%d)",
if_name(if_session->if_p), strerror(errno), errno);
goto failed;
}
return (TRUE);
failed:
return (FALSE);
}
static void
arp_client_probe_start(void * arg1, void * arg2, void * arg3)
{
arp_client_t * client = (arp_client_t *)arg1;
client->try = 0;
client->command_status = arp_status_unknown_e;
arp_client_probe_retransmit(arg1, arg2, arg3);
return;
}
static void
arp_client_probe_retransmit(void * arg1, void * arg2, void * arg3)
{
arp_client_t * client = (arp_client_t *)arg1;
struct probe_info * probe_info = &client->probe_info;
int tries_left;
arp_if_session_t * if_session = client->if_session;
tries_left = (probe_info->probe_count + probe_info->gratuitous_count)
- client->try;
if (tries_left <= 0) {
client->command_status = arp_status_not_in_use_e;
arp_client_schedule_callback(client);
return;
}
client->try++;
if (client->probe_info.skip_first ||
arp_client_transmit(client,
(tries_left <= probe_info->gratuitous_count),
NULL)) {
if (G_IPConfiguration_verbose) {
if (client->probe_info.skip_first) {
my_log(LOG_DEBUG, ARP_STR
"(%s): skipping the first arp announcement.",
if_name(if_session->if_p));
}
else if (tries_left <= probe_info->gratuitous_count) {
my_log(LOG_NOTICE,
ARP_STR
"(%s): sending (%d of %d) arp announcements ",
if_name(if_session->if_p),
probe_info->gratuitous_count - tries_left + 1,
probe_info->gratuitous_count);
}
else {
my_log(LOG_DEBUG, ARP_STR "(%s): sending (%d of %d) "
"arp probes ", if_name(if_session->if_p),
client->try, probe_info->probe_count);
}
}
timer_set_relative(client->timer_callout,
probe_info->retry_interval,
(timer_func_t *)arp_client_probe_retransmit,
client, NULL, NULL);
client->probe_info.skip_first = FALSE;
}
else {
client->command_status = arp_status_error_e;
arp_client_schedule_callback(client);
}
}
static void
arp_client_resolve_retransmit(void * arg1, void * arg2, void * arg3)
{
arp_client_t * client = (arp_client_t *)arg1;
int tries_left;
tries_left = client->resolve_secs - client->try;
if (tries_left <= 0) {
client->command_status = arp_status_not_in_use_e;
arp_client_schedule_callback(client);
return;
}
client->try++;
if (arp_client_transmit(client, FALSE, NULL)) {
struct timeval t;
#define ONE_SECOND 1
t.tv_sec = ONE_SECOND;
t.tv_usec = 0;
timer_set_relative(client->timer_callout, t,
(timer_func_t *)arp_client_resolve_retransmit,
client, NULL, NULL);
}
else {
client->command_status = arp_status_error_e;
arp_client_schedule_callback(client);
}
return;
}
static void
arp_client_detect_retransmit(void * arg1, void * arg2, void * arg3)
{
arp_client_t * client = (arp_client_t *)arg1;
int i;
boolean_t keep_going = TRUE;
arp_session_t * session = client->if_session->session;
int tries_left;
struct timeval * timeout_p;
boolean_t resolve = (boolean_t) (uintptr_t) arg2;
tries_left = session->default_detect_count - client->try;
if (tries_left <= 0) {
client->command_status = arp_status_not_in_use_e;
arp_client_schedule_callback(client);
return;
}
client->try++;
for (i = 0; i < client->detect_list_count; i++) {
if (arp_client_transmit(client, FALSE, client->detect_list + i)
== FALSE) {
keep_going = FALSE;
break;
}
}
if (keep_going) {
timeout_p = resolve ? &session->default_resolve_retry :
&session->default_detect_retry;
timer_set_relative(client->timer_callout, *timeout_p,
(timer_func_t *)
arp_client_detect_retransmit,
client, arg2, NULL);
}
else {
client->command_status = arp_status_error_e;
arp_client_schedule_callback(client);
}
return;
}
static arp_client_t *
arp_if_session_new_client(arp_if_session_t * if_session)
{
arp_client_t * client;
client = malloc(sizeof(*client));
if (client == NULL) {
return (NULL);
}
bzero(client, sizeof(*client));
if (dynarray_add(&if_session->clients, client) == FALSE) {
free(client);
return (NULL);
}
#ifdef TEST_ARP_SESSION
client->client_index = if_session->session->next_client_index++;
#endif
client->if_session = if_session;
client->probe_info = if_session->session->default_probe_info;
client->timer_callout = timer_callout_init();
return (client);
}
static arp_client_t *
arp_session_new_client(arp_session_t * session, interface_t * if_p)
{
arp_if_session_t * if_session;
if_session = arp_session_new_if_session(session, if_p);
if (if_session == NULL) {
return (NULL);
}
return (arp_if_session_new_client(if_session));
}
#ifdef TEST_ARP_SESSION
static arp_client_t *
arp_session_find_client_with_index(arp_session_t * session, int index)
{
int if_sessions_count;
int i;
int j;
if_sessions_count = dynarray_count(&session->if_sessions);
for (i = 0; i < if_sessions_count; i++) {
int clients_count;
arp_if_session_t * if_session;
if_session = dynarray_element(&session->if_sessions, i);
clients_count = dynarray_count(&if_session->clients);
for (j = 0; j < clients_count; j++) {
arp_client_t * client;
client = dynarray_element(&if_session->clients, j);
if (client->client_index == index) {
return (client);
}
}
}
return (NULL);
}
#endif
void
arp_client_set_probe_info(arp_client_t * client,
const struct timeval * retry_interval,
const int * probe_count,
const int * gratuitous_count)
{
struct probe_info * probe_info = &client->probe_info;
if (retry_interval != NULL) {
probe_info->retry_interval = *retry_interval;
}
if (probe_count != NULL) {
probe_info->probe_count = *probe_count;
}
if (gratuitous_count != NULL) {
probe_info->gratuitous_count = *gratuitous_count;
}
return;
}
void
arp_client_restore_default_probe_info(arp_client_t * client)
{
client->probe_info = client->if_session->session->default_probe_info;
return;
}
arp_client_t *
arp_client_init(arp_session_t * session, interface_t * if_p)
{
return (arp_session_new_client(session, if_p));
}
static void
arp_client_free_element(void * arg)
{
arp_client_t * client = (arp_client_t *)arg;
arp_client_cancel(client);
timer_callout_free(&client->timer_callout);
free(client);
return;
}
void
arp_client_free(arp_client_t * * client_p)
{
arp_client_t * client = NULL;
int i;
arp_if_session_t * if_session;
if (client_p != NULL) {
client = *client_p;
}
if (client == NULL) {
return;
}
if_session = client->if_session;
i = dynarray_index(&if_session->clients, client);
if (i != -1) {
dynarray_remove(&if_session->clients, i, NULL);
}
else {
my_log(LOG_ERR, "arp_client_free(%s) not in list?",
if_name(if_session->if_p));
}
arp_client_free_element(client);
*client_p = NULL;
if (dynarray_count(&if_session->clients) == 0) {
arp_if_session_free(&if_session);
}
return;
}
void
arp_client_set_probes_are_collisions(arp_client_t * client,
boolean_t probes_are_collisions)
{
client->probes_are_collisions = probes_are_collisions;
return;
}
static inline void
arp_client_setup_context(arp_client_t * client,
arp_result_func_t * func, void * arg1, void * arg2,
struct in_addr sender_ip, struct in_addr target_ip,
boolean_t skip)
{
arp_if_session_t * if_session = client->if_session;
arp_client_cancel(client);
arp_if_session_update_hardware_address(if_session);
client->sender_ip = sender_ip;
client->target_ip = target_ip;
client->func = func;
client->arg1 = arg1;
client->arg2 = arg2;
client->errmsg[0] = '\0';
client->try = 0;
client->conflict_count = 0;
client->probe_info.skip_first = skip;
}
void
arp_client_announce(arp_client_t * client,
arp_result_func_t * func, void * arg1, void * arg2,
struct in_addr sender_ip, struct in_addr target_ip,
boolean_t skip)
{
arp_client_setup_context(client, func, arg1, arg2,
sender_ip, target_ip, skip);
client->try = client->probe_info.probe_count;
if (!arp_client_open_fd(client)) {
client->command_status = arp_status_error_e;
arp_client_schedule_callback(client);
return;
}
client->command_status = arp_status_unknown_e;
client->command = arp_client_command_probe_e;
arp_client_probe_retransmit(client, NULL, NULL);
}
void
arp_client_probe(arp_client_t * client,
arp_result_func_t * func, void * arg1, void * arg2,
struct in_addr sender_ip, struct in_addr target_ip)
{
arp_client_setup_context(client, func, arg1, arg2,
sender_ip, target_ip, FALSE);
if (!arp_client_open_fd(client)) {
client->command_status = arp_status_error_e;
arp_client_schedule_callback(client);
return;
}
client->command_status = arp_status_unknown_e;
client->command = arp_client_command_probe_e;
arp_client_probe_retransmit(client, NULL, NULL);
return;
}
void
arp_client_resolve(arp_client_t * client,
arp_result_func_t * func, void * arg1, void * arg2,
struct in_addr sender_ip, struct in_addr target_ip,
uint32_t resolve_secs)
{
arp_if_session_t * if_session = client->if_session;
arp_client_cancel(client);
arp_if_session_update_hardware_address(if_session);
client->sender_ip = sender_ip;
client->target_ip = target_ip;
client->func = func;
client->arg1 = arg1;
client->arg2 = arg2;
client->errmsg[0] = '\0';
client->try = 0;
client->conflict_count = 0;
if (!arp_client_open_fd(client)) {
client->command_status = arp_status_error_e;
arp_client_schedule_callback(client);
return;
}
client->command_status = arp_status_unknown_e;
#define DEFAULT_RESOLVE_SECS 16
client->resolve_secs
= (resolve_secs > 0) ? resolve_secs : DEFAULT_RESOLVE_SECS;
client->command = arp_client_command_resolve_e;
arp_client_resolve_retransmit(client, NULL, NULL);
return;
}
void
arp_client_detect(arp_client_t * client,
arp_result_func_t * func, void * arg1, void * arg2,
const arp_address_info_t * list, int list_count,
boolean_t resolve)
{
arp_if_session_t * if_session = client->if_session;
int list_size;
arp_client_cancel(client);
arp_if_session_update_hardware_address(if_session);
client->func = func;
client->arg1 = arg1;
client->arg2 = arg2;
client->errmsg[0] = '\0';
client->try = 0;
client->conflict_count = 0;
if (list_count == 0 || !arp_client_open_fd(client)) {
client->command_status = arp_status_error_e;
arp_client_schedule_callback(client);
return;
}
list_size = sizeof(*client->detect_list) * list_count;
client->detect_list = (arp_address_info_t *)malloc(list_size);
bcopy(list, client->detect_list, list_size);
client->detect_list_count = list_count;
client->command_status = arp_status_unknown_e;
client->command = arp_client_command_detect_e;
arp_client_detect_retransmit(client, (void*) (uintptr_t)resolve,
NULL);
return;
}
const char *
arp_client_errmsg(arp_client_t * client)
{
return ((const char *)client->errmsg);
}
void
arp_client_cancel(arp_client_t * client)
{
client->errmsg[0] = '\0';
client->func = client->arg1 = client->arg2 = NULL;
client->command_status = arp_status_none_e;
arp_client_close_fd(client);
timer_cancel(client->timer_callout);
if (client->detect_list != NULL) {
free(client->detect_list);
client->detect_list = NULL;
}
arp_client_cancel_callback(client);
return;
}
void
arp_client_defend(arp_client_t * client, struct in_addr our_ip)
{
arp_if_session_t * if_session = client->if_session;
arp_client_cancel(client);
arp_if_session_update_hardware_address(if_session);
if (!arp_client_open_fd(client)) {
my_log(LOG_ERR, "arp_client_defend(%s): open fd failed",
if_name(if_session->if_p));
}
else {
client->target_ip = client->sender_ip = our_ip;
if (!arp_client_transmit(client, FALSE, NULL)) {
my_log(LOG_ERR, "arp_client_defend(%s): transmit failed",
if_name(if_session->if_p));
}
arp_client_close_fd(client);
}
return;
}
arp_session_t *
arp_session_init(arp_our_address_func_t * func,
arp_session_values_t * values)
{
arp_session_t * session;
session = malloc(sizeof(*session));
if (session == NULL) {
return (NULL);
}
bzero(session, sizeof(*session));
dynarray_init(&session->if_sessions, arp_if_session_free_element, NULL);
if (func == NULL) {
session->is_our_address = arp_is_our_address;
}
else {
session->is_our_address = func;
}
if (values->probe_interval != NULL) {
session->default_probe_info.retry_interval = *values->probe_interval;
}
else {
session->default_probe_info.retry_interval.tv_sec = ARP_RETRY_SECS;
session->default_probe_info.retry_interval.tv_usec = ARP_RETRY_USECS;
}
if (values->probe_count != NULL) {
session->default_probe_info.probe_count = *values->probe_count;
}
else {
session->default_probe_info.probe_count = ARP_PROBE_COUNT;
}
if (values->probe_gratuitous_count != NULL) {
session->default_probe_info.gratuitous_count
= *values->probe_gratuitous_count;
}
else {
session->default_probe_info.gratuitous_count = ARP_GRATUITOUS_COUNT;
}
if (values->detect_count != NULL) {
session->default_detect_count = *values->detect_count;
}
else {
session->default_detect_count = ARP_DETECT_COUNT;
}
if (values->detect_interval != NULL) {
session->default_detect_retry = *values->detect_interval;
}
else {
session->default_detect_retry.tv_sec = ARP_DETECT_RETRY_SECS;
session->default_detect_retry.tv_usec = ARP_DETECT_RETRY_USECS;
}
if (values->resolve_interval != NULL) {
session->default_resolve_retry = *values->resolve_interval;
}
else {
session->default_resolve_retry.tv_sec = ARP_RESOLVE_RETRY_SECS;
session->default_resolve_retry.tv_usec = ARP_RESOLVE_RETRY_USECS;
}
if (values->conflict_retry_count != NULL) {
session->default_conflict_retry_count = *values->conflict_retry_count;
}
else {
session->default_conflict_retry_count = ARP_CONFLICT_RETRY_COUNT;
}
if (values->conflict_delay_interval != NULL) {
session->default_conflict_delay
= *values->conflict_delay_interval;
}
else {
session->default_conflict_delay.tv_sec
= ARP_CONFLICT_RETRY_DELAY_SECS;
session->default_conflict_delay.tv_usec
= ARP_CONFLICT_RETRY_DELAY_USECS;
}
#ifdef TEST_ARP_SESSION
session->next_client_index = 1;
#endif
return (session);
}
void
arp_session_free(arp_session_t * * session_p)
{
arp_session_t * session = *session_p;
dynarray_free(&session->if_sessions);
bzero(session, sizeof(*session));
free(session);
*session_p = NULL;
return;
}
static arp_if_session_t *
arp_session_find_if_session(arp_session_t * session, const char * ifn)
{
int count;
int i;
count = dynarray_count(&session->if_sessions);
for (i = 0; i < count; i++) {
arp_if_session_t * if_session;
if_session = dynarray_element(&session->if_sessions, i);
if (strcmp(if_name(if_session->if_p), ifn) == 0) {
return (if_session);
}
}
return (NULL);
}
static void
arp_if_session_free_element(void * arg)
{
arp_if_session_t * if_session = (arp_if_session_t *)arg;
dynarray_free(&if_session->clients);
free(if_session);
return;
}
static void
arp_if_session_free(arp_if_session_t * * if_session_p)
{
arp_if_session_t * if_session = NULL;
int i;
arp_session_t * session;
if (if_session_p != NULL) {
if_session = *if_session_p;
}
if (if_session == NULL) {
return;
}
session = if_session->session;
i = dynarray_index(&session->if_sessions, if_session);
if (i != -1) {
dynarray_remove(&session->if_sessions, i, NULL);
}
else {
my_log(LOG_ERR, "arp_if_session_free(%s) not in list?",
if_name(if_session->if_p));
}
arp_if_session_free_element(if_session);
*if_session_p = NULL;
return;
}
static arp_if_session_t *
arp_session_new_if_session(arp_session_t * session, interface_t * if_p)
{
struct firewire_address fw_addr;
arp_if_session_t * if_session;
if_session = arp_session_find_if_session(session, if_name(if_p));
if (if_session != NULL) {
return (if_session);
}
switch (if_link_type(if_p)) {
case IFT_ETHER:
break;
case IFT_IEEE1394:
if (getFireWireAddress(if_name(if_p), &fw_addr) == FALSE) {
my_log(LOG_INFO,
"arp_client_init(%s): could not retrieve firewire address",
if_name(if_p));
return (NULL);
}
break;
default:
my_log(LOG_INFO, "arp_client_init(%s): unsupported network type",
if_name(if_p));
return (NULL);
}
if_session = (arp_if_session_t *)malloc(sizeof(*if_session));
bzero(if_session, sizeof(*if_session));
dynarray_init(&if_session->clients, arp_client_free_element, NULL);
if (if_link_type(if_p) == IFT_IEEE1394) {
if_session->fw_addr = fw_addr;
}
if_session->if_p = if_p;
if_session->session = session;
dynarray_add(&session->if_sessions, if_session);
return (if_session);
}
void
arp_session_set_debug(arp_session_t * session, int debug)
{
session->debug = debug;
return;
}
#ifdef TEST_ARP_SESSION
#include <stdarg.h>
typedef boolean_t func_t(int argc, const char * * argv);
typedef func_t * funcptr_t;
static arp_session_t * S_arp_session;
static boolean_t S_debug = FALSE;
static func_t S_do_probe;
static func_t S_do_resolve;
static func_t S_do_detect;
static func_t S_cancel_probe;
static func_t S_toggle_debug;
static func_t S_new_client;
static func_t S_free_client;
static func_t S_list;
static func_t S_quit;
static func_t S_client_params;
static interface_list_t * S_interfaces;
#define BASE_16 16
static uint8_t *
hexstrtobin(const char * str, int * len)
{
int buf_pos;
uint8_t * buf = NULL;
boolean_t done = FALSE;
int max_decoded_len;
const char * scan = str;
int slen = strlen(str);
*len = 0;
max_decoded_len = (slen / 2) + 1;
buf = (uint8_t *)malloc(max_decoded_len);
if (buf == NULL) {
return (buf);
}
for (buf_pos = 0; buf_pos < max_decoded_len && !done; buf_pos++) {
char tmp[4];
const char * colon;
colon = strchr(scan, ':');
if (colon == NULL) {
done = TRUE;
colon = str + slen;
}
if ((colon - scan) > (sizeof(tmp) - 1)) {
goto err;
}
strncpy(tmp, scan, colon - scan);
tmp[colon - scan] = '\0';
buf[buf_pos] = (u_char)strtol(tmp, NULL, BASE_16);
scan = colon + 1;
}
*len = buf_pos;
return (buf);
err:
if (buf) {
free(buf);
}
return (NULL);
}
static void
arp_session_log(int priority, const char * message, ...)
{
va_list ap;
if (priority == LOG_DEBUG) {
if (S_arp_session->debug == FALSE)
return;
}
va_start(ap, message);
vfprintf(stderr, message, ap);
fprintf(stderr, "\n");
fflush(stderr);
return;
}
static const struct command_info {
char * command;
funcptr_t func;
int argc;
char * usage;
int display;
} commands[] = {
{ "new", S_new_client, 1, "<ifname>", 1 },
{ "free", S_free_client, 1, "<client_index>", 1 },
{ "probe", S_do_probe, 3, "<client_index> <sender_ip> <target_ip>", 1 },
{ "resolve", S_do_resolve, 3, "<client_index> <sender_ip> <target_ip>", 1 },
{ "detect", S_do_detect, 4,
"<client_index> [ <sender_ip> <target_ip> <target_hardware> ]+", 1 },
{ "cancel", S_cancel_probe, 1, "<client_index>", 1 },
{ "params", S_client_params, 1, "<client_index> [ default | <interval> <probes> [ <gratuitous> ] ]", 1 },
{ "debug", S_toggle_debug, 0, NULL, 1 },
{ "list", S_list, 0, NULL, 1 },
{ "quit", S_quit, 0, NULL, 1 },
{ NULL, NULL, 0 }
};
struct arg_info {
char * * argv;
int argc;
int argv_size;
};
static void
arg_info_init(struct arg_info * args)
{
args->argv = NULL;
args->argv_size = 0;
args->argc = 0;
return;
}
static void
arg_info_free(struct arg_info * args)
{
if (args->argv != NULL) {
free(args->argv);
}
arg_info_init(args);
return;
}
static void
arg_info_add(struct arg_info * args, char * new_arg)
{
if (args->argv == NULL) {
args->argv_size = 6;
args->argv = (char * *)malloc(sizeof(*args->argv) * args->argv_size);
}
else if (args->argc == args->argv_size) {
args->argv_size *= 2;
args->argv = (char * *)realloc(args->argv,
sizeof(*args->argv) * args->argv_size);
}
args->argv[args->argc++] = new_arg;
return;
}
void
my_CFRelease(void * t)
{
void * * obj = (void * *)t;
if (obj && *obj) {
CFRelease(*obj);
*obj = NULL;
}
return;
}
static int
arp_link_length(interface_t * if_p)
{
int len;
if (if_link_arptype(if_p) == ARPHRD_IEEE1394) {
len = sizeof(struct firewire_eui64);
}
else {
len = ETHER_ADDR_LEN;
}
return (len);
}
static void
arp_test(void * arg1, void * arg2, const arp_result_t * result)
{
arp_client_t * client = (arp_client_t *)arg1;
if (result->error) {
printf("ARP probe failed: '%s'\n",
client->errmsg);
}
else if (result->in_use) {
int i;
int len = arp_link_length(client->if_session->if_p);
const u_char * addr = result->addr.target_hardware;
printf("ip address " IP_FORMAT " in use by",
IP_LIST(&client->target_ip));
for (i = 0; i < len; i++) {
printf("%c%02x", i == 0 ? ' ' : ':', addr[i]);
}
printf("\n");
}
else {
printf("ip address " IP_FORMAT " is not in use\n",
IP_LIST(&client->target_ip));
}
}
static void
arp_detect_callback(void * arg1, void * arg2, const arp_result_t * result)
{
arp_client_t * client = (arp_client_t *)arg1;
if (result->error) {
printf("ARP detect failed: '%s'\n",
client->errmsg);
}
else if (result->in_use) {
int i;
int len = arp_link_length(client->if_session->if_p);
const u_char * addr = result->addr.target_hardware;
printf("ARP detected Sender IP " IP_FORMAT " Target IP " IP_FORMAT
" Target Hardware",
IP_LIST(&result->addr.sender_ip),
IP_LIST(&result->addr.target_ip));
for (i = 0; i < len; i++) {
printf("%c%02x", i == 0 ? ' ' : ':', addr[i]);
}
printf("\n");
}
else {
printf("Did not detect any IP\n");
}
}
static boolean_t
S_toggle_debug(int argc, const char * * argv)
{
S_debug = !S_debug;
arp_session_set_debug(S_arp_session, S_debug);
if (S_debug) {
printf("debug mode enabled\n");
}
else {
printf("debug mode disabled\n");
}
return (TRUE);
}
static boolean_t
S_quit(int argc, const char * * argv)
{
exit(0);
return (TRUE);
}
static boolean_t
S_new_client(int argc, const char * * argv)
{
arp_client_t * client;
interface_t * if_p;
if_p = ifl_find_name(S_interfaces, argv[1]);
if (if_p == NULL) {
fprintf(stderr, "interface %s does not exist\n", argv[1]);
goto done;
}
client = arp_client_init(S_arp_session, if_p);
if (client == NULL) {
fprintf(stderr, "Could not create a new client over %s\n",
if_name(if_p));
goto done;
}
printf("%d\n", client->client_index);
done:
return (client != NULL);
}
static boolean_t
get_int_param(const char * arg, int * ret_int)
{
char * endptr;
int val;
val = strtol(arg, &endptr, 0);
if (endptr == arg || (val == 0 && errno != 0)) {
return (FALSE);
}
*ret_int = val;
return (TRUE);
}
static boolean_t
get_client_index(const char * arg, int * client_index)
{
if (get_int_param(arg, client_index)) {
return (TRUE);
}
if (strcmp(arg, ".") == 0) {
*client_index = S_arp_session->next_client_index - 1;
return (TRUE);
}
return (FALSE);
}
static boolean_t
S_do_probe(int argc, const char * * argv)
{
arp_client_t * client;
int client_index;
struct in_addr sender_ip;
struct in_addr target_ip;
if (get_client_index(argv[1], &client_index) == FALSE) {
fprintf(stderr, "invalid client index\n");
goto done;
}
client = arp_session_find_client_with_index(S_arp_session, client_index);
if (client == NULL) {
fprintf(stderr, "no such client index\n");
goto done;
}
if (inet_aton(argv[2], &sender_ip) == 0) {
fprintf(stderr, "invalid sender ip address %s\n", argv[2]);
client = NULL;
goto done;
}
if (inet_aton(argv[3], &target_ip) == 0) {
fprintf(stderr, "invalid target ip address %s\n", argv[3]);
client = NULL;
goto done;
}
arp_client_probe(client, arp_test, client, NULL, sender_ip, target_ip);
done:
return (client != NULL);
}
static boolean_t
S_do_resolve(int argc, const char * * argv)
{
arp_client_t * client;
int client_index;
struct in_addr sender_ip;
struct in_addr target_ip;
if (get_client_index(argv[1], &client_index) == FALSE) {
fprintf(stderr, "invalid client index\n");
goto done;
}
client = arp_session_find_client_with_index(S_arp_session, client_index);
if (client == NULL) {
fprintf(stderr, "no such client index\n");
goto done;
}
if (inet_aton(argv[2], &sender_ip) == 0) {
fprintf(stderr, "invalid sender ip address %s\n", argv[2]);
client = NULL;
goto done;
}
if (inet_aton(argv[3], &target_ip) == 0) {
fprintf(stderr, "invalid target ip address %s\n", argv[3]);
client = NULL;
goto done;
}
arp_client_resolve(client, arp_test, client, NULL, sender_ip, target_ip, 0);
done:
return (client != NULL);
}
static boolean_t
S_do_detect(int argc, const char * * argv)
{
arp_client_t * client;
int client_index;
uint8_t * hwaddr = NULL;
int hwaddr_len;
int i;
arp_address_info_t *info = NULL;
int info_count;
if (get_client_index(argv[1], &client_index) == FALSE) {
fprintf(stderr, "invalid client index\n");
goto done;
}
client = arp_session_find_client_with_index(S_arp_session, client_index);
if (client == NULL) {
fprintf(stderr, "no such client index\n");
goto done;
}
if (((argc - 2) % 3) != 0) {
fprintf(stderr, "incorrect number of arguments\n");
client = NULL;
goto done;
}
info_count = (argc - 2) / 3;
info = malloc(sizeof(*info) * info_count);
for (i = 0; i < info_count; i++) {
if (inet_aton(argv[3 * i + 2], &info[i].sender_ip) == 0) {
fprintf(stderr, "invalid sender ip address %s\n", argv[3 * i + 2]);
client = NULL;
goto done;
}
if (inet_aton(argv[3 * i + 3], &info[i].target_ip) == 0) {
fprintf(stderr, "invalid target ip address %s\n", argv[3 * i + 3]);
client = NULL;
goto done;
}
hwaddr = hexstrtobin(argv[3 * i + 4], &hwaddr_len);
if (hwaddr == NULL
|| (hwaddr_len != ETHER_ADDR_LEN
&& hwaddr_len != FIREWIRE_EUI64_LEN)) {
fprintf(stderr, "invalid hardware address %s\n", argv[3 * i + 4]);
client = NULL;
if (hwaddr != NULL) {
free(hwaddr);
}
goto done;
}
bcopy(hwaddr, info[i].target_hardware, hwaddr_len);
free(hwaddr);
}
arp_client_detect(client,
arp_detect_callback, client, NULL,
info, info_count);
done:
if (info != NULL) {
free(info);
}
return (client != NULL);
}
static boolean_t
S_free_client(int argc, const char * * argv)
{
arp_client_t * client;
int client_index;
if (get_client_index(argv[1], &client_index) == FALSE) {
fprintf(stderr, "invalid client index\n");
goto done;
}
client = arp_session_find_client_with_index(S_arp_session, client_index);
if (client == NULL) {
fprintf(stderr, "no such client index\n");
goto done;
}
arp_client_free(&client);
done:
return (client != NULL);
}
static boolean_t
S_cancel_probe(int argc, const char * * argv)
{
arp_client_t * client;
int client_index;
if (get_client_index(argv[1], &client_index) == FALSE) {
fprintf(stderr, "invalid client index\n");
goto done;
}
client = arp_session_find_client_with_index(S_arp_session, client_index);
if (client == NULL) {
fprintf(stderr, "no such client index\n");
goto done;
}
arp_client_cancel(client);
done:
return (client != NULL);
}
static boolean_t
S_client_params(int argc, const char * * argv)
{
arp_client_t * client;
int client_index;
struct probe_info * probe_info;
if (get_client_index(argv[1], &client_index) == FALSE) {
fprintf(stderr, "invalid client index\n");
goto done;
}
client = arp_session_find_client_with_index(S_arp_session, client_index);
if (client == NULL) {
fprintf(stderr, "no such client index\n");
goto done;
}
if (argc == 2) {
}
else if (strcmp(argv[2], "default") == 0) {
if (argc != 3) {
fprintf(stderr, "Too many parameters specified\n");
client = NULL;
goto done;
}
arp_client_restore_default_probe_info(client);
}
else if (argc < 4) {
fprintf(stderr, "insufficient args\n");
client = NULL;
goto done;
}
else {
char * endptr;
float interval;
int gratuitous_count = 0;
int probe_count;
struct timeval tv;
if (argc > 5) {
fprintf(stderr, "Too many parameters specified\n");
client = NULL;
goto done;
}
interval = strtof(argv[2], &endptr);
if (endptr == argv[2] || (interval == 0 && errno != 0)) {
fprintf(stderr, "Invalid probe interval specified\n");
client = NULL;
goto done;
}
tv.tv_sec = (int)interval;
tv.tv_usec = (interval - tv.tv_sec) * 1000 * 1000;
if (get_int_param(argv[3], &probe_count) == FALSE) {
fprintf(stderr, "Invalid probe count specified\n");
client = NULL;
goto done;
}
if (argc == 5) {
if (get_int_param(argv[4], &gratuitous_count) == FALSE) {
fprintf(stderr, "Invalid gratuitous count specified\n");
client = NULL;
goto done;
}
}
arp_client_set_probe_info(client, &tv, &probe_count,
&gratuitous_count);
}
probe_info = &client->probe_info;
printf("Probe interval %d.%06d probes %d gratuitous %d\n",
(int)probe_info->retry_interval.tv_sec,
(int)probe_info->retry_interval.tv_usec,
probe_info->probe_count, probe_info->gratuitous_count);
done:
return (client != NULL);
}
static void
dump_bytes(const unsigned char * buf, int buf_len)
{
int i;
for (i = 0; i < buf_len; i++) {
printf("%c%02x", i == 0 ? ' ' : ':', buf[i]);
}
return;
}
static boolean_t
S_list(int argc, const char * * argv)
{
int if_sessions_count;
int i;
int j;
if_sessions_count = dynarray_count(&S_arp_session->if_sessions);
for (i = 0; i < if_sessions_count; i++) {
int clients_count;
arp_if_session_t * if_session;
int len;
if_session = dynarray_element(&S_arp_session->if_sessions, i);
clients_count = dynarray_count(&if_session->clients);
len = arp_link_length(if_session->if_p);
for (j = 0; j < clients_count; j++) {
arp_client_t * client;
client = dynarray_element(&if_session->clients, j);
printf("%d. %s", client->client_index, if_name(if_session->if_p));
switch (client->command_status) {
case arp_status_none_e:
printf(" idle");
break;
case arp_status_unknown_e:
switch (client->command) {
case arp_client_command_probe_e:
printf(" probing %s", inet_ntoa(client->target_ip));
break;
case arp_client_command_resolve_e:
printf(" resolving %s", inet_ntoa(client->target_ip));
break;
case arp_client_command_detect_e:
printf(" detecting");
break;
default:
break;
}
break;
case arp_status_in_use_e:
printf(" %s in use by",
inet_ntoa(client->in_use_addr.target_ip));
dump_bytes((unsigned char *)client->in_use_addr.target_hardware,
len);
break;
case arp_status_not_in_use_e:
printf(" %s not in use", inet_ntoa(client->target_ip));
break;
case arp_status_error_e:
printf(" %s error encountered", inet_ntoa(client->target_ip));
break;
}
printf("\n");
}
}
return (TRUE);
}
static void
parse_command(char * buf, struct arg_info * args)
{
char * arg_start = NULL;
char * scan;
for (scan = buf; *scan != '\0'; scan++) {
char ch = *scan;
switch (ch) {
case ' ':
case '\n':
case '\t':
*scan = '\0';
if (arg_start != NULL) {
arg_info_add(args, arg_start);
arg_start = NULL;
}
break;
default:
if (arg_start == NULL) {
arg_start = scan;
}
break;
}
}
if (arg_start != NULL) {
arg_info_add(args, arg_start);
}
return;
}
void
usage()
{
int i;
fprintf(stderr, "Available commands: ");
for (i = 0; commands[i].command; i++) {
if (commands[i].display) {
fprintf(stderr, "%s%s", i == 0 ? "" : ", ",
commands[i].command);
}
}
fprintf(stderr, "\n");
return;
}
static const struct command_info *
S_lookup_command(char * cmd)
{
int i;
for (i = 0; commands[i].command; i++) {
if (strcmp(cmd, commands[i].command) == 0) {
return commands + i;
}
}
return (NULL);
}
static void
process_command(struct arg_info * args)
{
boolean_t ok = TRUE;
const struct command_info * cmd_info;
cmd_info = S_lookup_command(args->argv[0]);
if (cmd_info != NULL) {
if (cmd_info->argc >= args->argc) {
ok = FALSE;
if (cmd_info->display) {
fprintf(stderr, "insufficient args\nusage:\n\t%s %s\n",
args->argv[0],
cmd_info->usage ? cmd_info->usage : "");
}
}
}
else {
ok = FALSE;
usage();
}
if (ok) {
if ((*cmd_info->func)(args->argc, (const char * *)args->argv)
== FALSE) {
fprintf(stderr, "usage:\n\t%s %s\n",
args->argv[0],
cmd_info->usage ? cmd_info->usage : "");
}
}
return;
}
static void
display_prompt(FILE * f)
{
fprintf(f, "# ");
fflush(f);
return;
}
static void
user_input(CFSocketRef s, CFSocketCallBackType type,
CFDataRef address, const void *data, void *info)
{
struct arg_info args;
char choice[1024 * 10];
if (fgets(choice, sizeof(choice), stdin) == NULL) {
exit(1);
}
arg_info_init(&args);
parse_command(choice, &args);
if (args.argv == NULL) {
goto done;
}
process_command(&args);
done:
arg_info_free(&args);
display_prompt(stdout);
return;
}
static void
initialize_input()
{
CFSocketContext context = { 0, NULL, NULL, NULL, NULL };
CFRunLoopSourceRef rls = NULL;
CFSocketRef socket = NULL;
socket = CFSocketCreateWithNative(NULL, fileno(stdin),
kCFSocketReadCallBack,
user_input, &context);
if (socket == NULL) {
fprintf(stderr, "CFSocketCreateWithNative failed\n");
exit(1);
}
rls = CFSocketCreateRunLoopSource(NULL, socket, 0);
if (rls == NULL) {
fprintf(stderr, "CFSocketCreateRunLoopSource failed\n");
exit(1);
}
CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
my_CFRelease(&rls);
my_CFRelease(&socket);
display_prompt(stdout);
return;
}
int
main(int argc, char * argv[])
{
arp_session_values_t arp_values;
int gratuitous = 0;
S_interfaces = ifl_init(FALSE);
if (S_interfaces == NULL) {
fprintf(stderr, "couldn't get interface list\n");
exit(1);
}
bzero(&arp_values, sizeof(arp_values));
arp_values.probe_gratuitous_count = &gratuitous;
S_arp_session = arp_session_init(NULL, &arp_values);
if (S_arp_session == NULL) {
fprintf(stderr, "arp_session_init failed\n");
exit(1);
}
initialize_input();
CFRunLoopRun();
exit(0);
}
#endif