static void
manual_inactive(ServiceRef service_p)
{
    manual_cancel_pending_events(service_p);
    service_remove_address(service_p);
    service_publish_failure(service_p, ipconfig_status_media_inactive_e);
    return;
}
static void
failover_timed_out(ServiceRef service_p)
{
    my_log(LOG_DEBUG, "FAILOVER %s: address timer fired", 
	   if_name(service_interface(service_p)));
    failover_cancel_pending_events(service_p);
    service_remove_address(service_p);
    service_publish_failure(service_p, ipconfig_status_address_timed_out_e);
    return;
}
Esempio n. 3
0
static void
linklocal_failed(ServiceRef service_p, ipconfig_status_t status)
{
    Service_linklocal_t * linklocal;

    linklocal = (Service_linklocal_t *)ServiceGetPrivate(service_p);
    linklocal->enable_arp_collision_detection = FALSE;
    linklocal_cancel_pending_events(service_p);
    arp_linklocal_disable(if_name(service_interface(service_p)));
    service_remove_address(service_p);
    if (status != ipconfig_status_media_inactive_e) {
        linklocal->our_ip = G_ip_zeroes;
    }
    service_publish_failure(service_p, status);
    return;
}
Esempio n. 4
0
ipconfig_status_t
linklocal_thread(ServiceRef service_p, IFEventID_t event_id, void * event_data)
{
    interface_t *		if_p = service_interface(service_p);
    Service_linklocal_t *	linklocal;
    ipconfig_status_t		status = ipconfig_status_success_e;

    linklocal = (Service_linklocal_t *)ServiceGetPrivate(service_p);
    switch (event_id) {
    case IFEventID_start_e: {
        ipconfig_method_data_t * method_data;

        if (if_flags(if_p) & IFF_LOOPBACK) {
            status = ipconfig_status_invalid_operation_e;
            break;
        }
        if (linklocal != NULL) {
            my_log(LOG_ERR, "LINKLOCAL %s: re-entering start state",
                   if_name(if_p));
            status = ipconfig_status_internal_error_e;
            break;
        }
        my_log(LOG_DEBUG, "LINKLOCAL %s: start", if_name(if_p));
        linklocal = malloc(sizeof(*linklocal));
        if (linklocal == NULL) {
            my_log(LOG_ERR, "LINKLOCAL %s: malloc failed",
                   if_name(if_p));
            status = ipconfig_status_allocation_failed_e;
            break;
        }
        bzero(linklocal, sizeof(*linklocal));
        ServiceSetPrivate(service_p, linklocal);

        linklocal->timer = timer_callout_init();
        if (linklocal->timer == NULL) {
            my_log(LOG_ERR, "LINKLOCAL %s: timer_callout_init failed",
                   if_name(if_p));
            status = ipconfig_status_allocation_failed_e;
            goto stop;
        }
        linklocal->arp = arp_client_init(G_arp_session, if_p);
        if (linklocal->arp == NULL) {
            my_log(LOG_ERR, "LINKLOCAL %s: arp_client_init failed",
                   if_name(if_p));
            status = ipconfig_status_allocation_failed_e;
            goto stop;
        }
        /* ARP probes count as collisions for link-local address allocation */
        arp_client_set_probes_are_collisions(linklocal->arp, TRUE);
        linklocal->allocate = TRUE;

        method_data = (ipconfig_method_data_t *)event_data;
        if (method_data != NULL
                && method_data->linklocal.allocate == FALSE) {
            /* don't allocate an IP address, just set the subnet */
            linklocal->allocate = FALSE;
            linklocal_detect_proxy_arp(service_p, IFEventID_start_e, NULL);
            break;
        }
        linklocal->our_ip = S_find_linklocal_address(service_p);
        linklocal_allocate(service_p, IFEventID_start_e, NULL);
        break;
    }
    case IFEventID_stop_e: {
stop:
        my_log(LOG_DEBUG, "LINKLOCAL %s: stop", if_name(if_p));
        if (linklocal == NULL) {
            my_log(LOG_DEBUG, "LINKLOCAL %s: already stopped",
                   if_name(if_p));
            status = ipconfig_status_internal_error_e; /* shouldn't happen */
            break;
        }

        /* remove IP address */
        arp_linklocal_disable(if_name(if_p));
        service_remove_address(service_p);

        /* clean-up the published state */
        service_publish_failure(service_p,
                                ipconfig_status_success_e);

        /* clean-up resources */
        if (linklocal->timer) {
            timer_callout_free(&linklocal->timer);
        }
        if (linklocal->arp) {
            arp_client_free(&linklocal->arp);
        }
        if (linklocal) {
            free(linklocal);
        }
        ServiceSetPrivate(service_p, NULL);
        break;
    }
    case IFEventID_change_e: {
        boolean_t		  	allocate = TRUE;
        change_event_data_t *   	change_event;
        ipconfig_method_data_t * 	method_data;

        change_event = (change_event_data_t *)event_data;
        method_data = change_event->method_data;
        if (method_data != NULL
                && method_data->linklocal.allocate == FALSE) {
            /* don't allocate an IP address, just set the subnet */
            allocate = FALSE;
        }
        if (linklocal->allocate != allocate) {
            linklocal->allocate = allocate;
            if (allocate) {
                linklocal->our_ip = S_find_linklocal_address(service_p);
                linklocal_allocate(service_p, IFEventID_start_e, NULL);
            }
            else {
                linklocal_failed(service_p, ipconfig_status_success_e);
                linklocal_detect_proxy_arp(service_p,
                                           IFEventID_start_e, NULL);
            }
        }
        break;
    }
    case IFEventID_arp_collision_e: {
        arp_collision_data_t *	arpc;

        arpc = (arp_collision_data_t *)event_data;
        if (linklocal == NULL) {
            return (ipconfig_status_internal_error_e);
        }
        if (linklocal->allocate == FALSE) {
            break;
        }
        if (linklocal->enable_arp_collision_detection == FALSE
                || arpc->ip_addr.s_addr != linklocal->our_ip.s_addr) {
            break;
        }
        linklocal->our_ip = G_ip_zeroes;
        (void)service_remove_address(service_p);
        service_publish_failure(service_p,
                                ipconfig_status_address_in_use_e);
        linklocal_allocate(service_p, IFEventID_start_e, NULL);
        break;
    }
    case IFEventID_link_status_changed_e: {
        link_status_t	   link_status;

        if (linklocal == NULL) {
            return (ipconfig_status_internal_error_e);
        }
        link_status = service_link_status(service_p);
        if (link_status.valid == TRUE) {
            linklocal_cancel_pending_events(service_p);
            if (link_status.active == TRUE) {
                linklocal_start(service_p);
            }
            else {
                linklocal->enable_arp_collision_detection = FALSE;
            }
        }
        break;
    }
    case IFEventID_link_timer_expired_e:
        linklocal_inactive(service_p);
        break;

    case IFEventID_renew_e: {
        break;
    }
    default:
        break;
    } /* switch (event_id) */
    return (status);
}
Esempio n. 5
0
static void
linklocal_allocate(ServiceRef service_p, IFEventID_t event_id,
                   void * event_data)
{
    interface_t *	  if_p = service_interface(service_p);
    Service_linklocal_t * linklocal;

    linklocal = (Service_linklocal_t *)ServiceGetPrivate(service_p);
    switch (event_id) {
    case IFEventID_start_e: {
        linklocal->enable_arp_collision_detection = FALSE;

        arp_linklocal_disable(if_name(if_p));

        /* clean-up anything that might have come before */
        linklocal_cancel_pending_events(service_p);

        linklocal->current = 1;
        if (linklocal->our_ip.s_addr) {
            /* try to keep the same address */
            linklocal->probe = linklocal->our_ip;
        }
        else {
            linklocal->probe.s_addr
                = htonl(LINKLOCAL_FIRST_USEABLE
                        + random_range(0, LINKLOCAL_RANGE));
        }
        my_log(LOG_DEBUG, "LINKLOCAL %s: probing " IP_FORMAT,
               if_name(if_p), IP_LIST(&linklocal->probe));
        arp_client_probe(linklocal->arp,
                         (arp_result_func_t *)linklocal_allocate, service_p,
                         (void *)IFEventID_arp_e, G_ip_zeroes,
                         linklocal->probe);
        /* wait for the results */
        return;
    }
    case IFEventID_arp_e: {
        arp_result_t *	result = (arp_result_t *)event_data;

        if (result->error) {
            my_log(LOG_DEBUG, "LINKLOCAL %s: ARP probe failed, %s",
                   if_name(if_p),
                   arp_client_errmsg(linklocal->arp));
            break;
        }
        if (result->in_use
                || service_is_using_ip(service_p, linklocal->probe)) {
            if (result->in_use) {
                my_log(LOG_DEBUG, "LINKLOCAL %s: IP address "
                       IP_FORMAT " is in use by " EA_FORMAT,
                       if_name(if_p),
                       IP_LIST(&linklocal->probe),
                       EA_LIST(result->addr.target_hardware));
            }
            else {
                my_log(LOG_DEBUG, "LINKLOCAL %s: IP address "
                       IP_FORMAT " is no longer unique",
                       if_name(if_p));
            }
            if (linklocal->our_ip.s_addr == linklocal->probe.s_addr) {
                linklocal->our_ip = G_ip_zeroes;
                (void)service_remove_address(service_p);
                service_publish_failure(service_p,
                                        ipconfig_status_address_in_use_e);
            }
        }
        else {
            link_status_t	   link_status = service_link_status(service_p);
            const struct in_addr linklocal_mask = { htonl(LINKLOCAL_MASK) };

            if (link_status.valid == TRUE
                    && link_status.active == FALSE) {
                linklocal_failed(service_p,
                                 ipconfig_status_media_inactive_e);
                break;
            }

            /* ad-hoc IP address is not in use, so use it */
            (void)service_set_address(service_p, linklocal->probe,
                                      linklocal_mask, G_ip_zeroes);
            linklocal_set_address(service_p, linklocal->probe);
            arp_linklocal_enable(if_name(if_p));
            linklocal_cancel_pending_events(service_p);
            linklocal->our_ip = linklocal->probe;
            ServicePublishSuccessIPv4(service_p, NULL);
            linklocal->enable_arp_collision_detection = TRUE;
            /* we're done */
            break; /* out of switch */
        }
        if (linklocal->current >= MAX_LINKLOCAL_INITIAL_TRIES) {
            struct timeval tv;
            /* initial tries threshold reached, try again after a timeout */
            tv.tv_sec = LINKLOCAL_RETRY_TIME_SECS;
            tv.tv_usec = 0;
            timer_set_relative(linklocal->timer, tv,
                               (timer_func_t *)linklocal_allocate,
                               service_p, (void *)IFEventID_timeout_e, NULL);
            /* don't fall through, wait for timer */
            break;
        }
        linklocal->current++;
    /* FALL THROUGH */
        case IFEventID_timeout_e:
            /* try the next address */
            linklocal->probe.s_addr
                = htonl(LINKLOCAL_FIRST_USEABLE
                        + random_range(0, LINKLOCAL_RANGE));
            arp_client_probe(linklocal->arp,
                             (arp_result_func_t *)linklocal_allocate, service_p,
                             (void *)IFEventID_arp_e, G_ip_zeroes,
                             linklocal->probe);
            my_log(LOG_DEBUG, "LINKLOCAL %s probing " IP_FORMAT,
                   if_name(if_p), IP_LIST(&linklocal->probe));
            /* wait for the results */
            break;
        }
    default:
        break;
    }

    return;
}
PRIVATE_EXTERN ipconfig_status_t
manual_thread(ServiceRef service_p, IFEventID_t evid, void * event_data)
{
    interface_t *	if_p = service_interface(service_p);
    Service_manual_t *	manual;
    ipconfig_status_t	status = ipconfig_status_success_e;

    manual = (Service_manual_t *)ServiceGetPrivate(service_p);
    switch (evid) {
      case IFEventID_start_e: {
	  ipconfig_method_data_t * method_data;

	  method_data = (ipconfig_method_data_t *)event_data;
	  if (manual) {
	      my_log(LOG_DEBUG, "MANUAL %s: re-entering start state", 
		     if_name(if_p));
	      return (ipconfig_status_internal_error_e);
	  }
	  manual = malloc(sizeof(*manual));
	  if (manual == NULL) {
	      my_log(LOG_ERR, "MANUAL %s: malloc failed", 
		     if_name(if_p));
	      status = ipconfig_status_allocation_failed_e;
	      break;
	  }
	  ServiceSetPrivate(service_p, manual);
	  bzero(manual, sizeof(*manual));
	  manual->ignore_link_status = method_data->manual.ignore_link_status;
	  service_set_requested_ip_addr(service_p, method_data->manual.addr);
	  service_set_requested_ip_mask(service_p, method_data->manual.mask);
	  if (if_flags(if_p) & IFF_LOOPBACK) {
	      /* set the new address */
	      (void)service_set_address(service_p, 
					service_requested_ip_addr(service_p),
					service_requested_ip_mask(service_p),
					G_ip_zeroes);
	      ServicePublishSuccessIPv4(service_p, NULL);
	      break;
	  }
	  if (method_data->manual.router.s_addr != 0
	      && (method_data->manual.router.s_addr 
		  != method_data->manual.addr.s_addr)) {
	      service_router_set_iaddr(service_p, method_data->manual.router);
	      service_router_set_iaddr_valid(service_p);
	  }
	  manual->timer = timer_callout_init();
	  if (manual->timer == NULL) {
	      my_log(LOG_ERR, "MANUAL %s: timer_callout_init failed", 
		     if_name(if_p));
	      status = ipconfig_status_allocation_failed_e;
	      goto stop;
	  }
	  manual->arp = arp_client_init(G_arp_session, if_p);
	  if (manual->arp == NULL) {
	      my_log(LOG_NOTICE, "MANUAL %s: arp_client_init failed", 
		     if_name(if_p));
	  }
	  my_log(LOG_DEBUG, "MANUAL %s: starting", 
		 if_name(if_p));
	  manual_start(service_p, IFEventID_start_e, NULL);
	  break;
      }
      stop:
      case IFEventID_stop_e: {
	  my_log(LOG_DEBUG, "MANUAL %s: stop", if_name(if_p));
	  if (manual == NULL) {
	      break;
	  }

	  /* remove IP address */
	  service_remove_address(service_p);

	  /* clean-up resources */
	  if (manual->arp) {
	      arp_client_free(&manual->arp);
	  }
	  if (manual->timer) {
	      timer_callout_free(&manual->timer);
	  }
	  if (manual) {
	      free(manual);
	  }
	  ServiceSetPrivate(service_p, NULL);
	  break;
      }
      case IFEventID_change_e: {
	  change_event_data_t *   	change_event;
	  ipconfig_method_data_t * 	method_data;

	  if (manual == NULL) {
	      my_log(LOG_DEBUG, "MANUAL %s: private data is NULL", 
		     if_name(if_p));
	      status = ipconfig_status_internal_error_e;
	      break;
	  }
	  change_event = ((change_event_data_t *)event_data);
	  method_data = change_event->method_data;
	  change_event->needs_stop = FALSE;
	  if (method_data->manual.addr.s_addr
	      != service_requested_ip_addr(service_p).s_addr
	      || (service_router_is_iaddr_valid(service_p)
		  && (method_data->manual.router.s_addr 
		      != service_router_iaddr(service_p).s_addr))
	      || (method_data->manual.ignore_link_status
		  != manual->ignore_link_status)) {
	      change_event->needs_stop = TRUE;
	  }
	  else if (method_data->manual.mask.s_addr
		   != service_requested_ip_mask(service_p).s_addr) {
	      service_set_requested_ip_mask(service_p,
					    method_data->manual.mask);
	      (void)service_set_address(service_p, 
					method_data->manual.addr,
					method_data->manual.mask,
					G_ip_zeroes);
	      /* publish new mask */
	      ServicePublishSuccessIPv4(service_p, NULL);
	  }
	  break;
      }
      case IFEventID_arp_collision_e: {
	  arp_collision_data_t *	arpc;
	  char				msg[128];

	  arpc = (arp_collision_data_t *)event_data;

	  if (manual == NULL) {
	      return (ipconfig_status_internal_error_e);
	  }
	  if (arpc->ip_addr.s_addr 
	      != service_requested_ip_addr(service_p).s_addr) {
	      break;
	  }
	  snprintf(msg, sizeof(msg), 
		   IP_FORMAT " in use by " EA_FORMAT,
		   IP_LIST(&arpc->ip_addr), 
		   EA_LIST(arpc->hwaddr));
	  if (manual->user_warned == FALSE) {
	      manual->user_warned = TRUE;
	      ServiceReportIPv4AddressConflict(service_p,
					       arpc->ip_addr);
	  }
	  my_log(LOG_ERR, "MANUAL %s: %s", 
		 if_name(if_p), msg);
	  break;
      }
      case IFEventID_link_status_changed_e: {
	  link_status_t		link_status;

	  if (manual == NULL) {
	      return (ipconfig_status_internal_error_e);
	  }
	  if (manual->ignore_link_status) {
	      break;
	  }
	  manual->user_warned = FALSE;
	  link_status = service_link_status(service_p);
	  if (link_status.valid == TRUE) {
	      if (link_status.active == TRUE) {
		  manual_start(service_p, IFEventID_start_e, NULL);
	      }
	      else {
		  manual_cancel_pending_events(service_p);
	      }
	  }
	  break;
      }
      case IFEventID_link_timer_expired_e:
	  if (manual->ignore_link_status) {
	      break;
	  }
	  manual_inactive(service_p);
	  break;
      default:
	  break;
    } /* switch */

    return (status);
}
static void
manual_start(ServiceRef service_p, IFEventID_t evid, void * event_data)
{
    interface_t *	if_p = service_interface(service_p);
    Service_manual_t *	manual;

    manual = (Service_manual_t *)ServiceGetPrivate(service_p);
    switch (evid) {
      case IFEventID_start_e: {
	  if (manual->arp == NULL) {
	      link_status_t	link_status = service_link_status(service_p);

	      /* if the link is up, just assign the IP */
	      if (manual->ignore_link_status == FALSE
		  && link_status.valid == TRUE 
		  && link_status.active == FALSE) {
		  manual_inactive(service_p);
		  break;
	      }
	      (void)service_set_address(service_p,
					service_requested_ip_addr(service_p),
					service_requested_ip_mask(service_p),
					G_ip_zeroes);
	      ServicePublishSuccessIPv4(service_p, NULL);
	      break;
	  }
	  manual_cancel_pending_events(service_p);
	  arp_client_probe(manual->arp,
			   (arp_result_func_t *)manual_start, service_p,
			   (void *)IFEventID_arp_e, G_ip_zeroes,
			   service_requested_ip_addr(service_p));
	  break;
      }
      case IFEventID_arp_e: {
	  link_status_t		link_status;
	  arp_result_t *	result = (arp_result_t *)event_data;

	  if (result->error) {
	      my_log(LOG_ERR, "MANUAL %s: arp probe failed, %s", 
		     if_name(if_p), arp_client_errmsg(manual->arp));
	      break;
	  }
	  else {
	      if (result->in_use) {
		  char			msg[128];
		  struct in_addr	requested_ip;
		  struct timeval	tv;

		  requested_ip = service_requested_ip_addr(service_p);
		  snprintf(msg, sizeof(msg), 
			   IP_FORMAT " in use by " EA_FORMAT,
			   IP_LIST(&requested_ip),
			   EA_LIST(result->addr.target_hardware));
		  if (manual->user_warned == FALSE) {
		      manual->user_warned = TRUE;
		      ServiceReportIPv4AddressConflict(service_p,
						       requested_ip);
		  }
		  my_log(LOG_ERR, "MANUAL %s: %s", 
			 if_name(if_p), msg);
		  service_remove_address(service_p);
		  service_publish_failure(service_p, 
					  ipconfig_status_address_in_use_e);
		  if (G_manual_conflict_retry_interval_secs > 0) {
		      /* try again in a bit */
		      tv.tv_sec = G_manual_conflict_retry_interval_secs;
		      tv.tv_usec = 0;
		      timer_set_relative(manual->timer, tv, 
					 (timer_func_t *)manual_start,
					 service_p, IFEventID_start_e, NULL);
		  }
		  break;
	      }
	  }
	  link_status = service_link_status(service_p);
	  if (manual->ignore_link_status == FALSE
	      && link_status.valid == TRUE 
	      && link_status.active == FALSE) {
	      manual_inactive(service_p);
	      break;
	  }

	  /* set the new address */
	  (void)service_set_address(service_p,
				    service_requested_ip_addr(service_p),
				    service_requested_ip_mask(service_p),
				    G_ip_zeroes);
	  ServiceRemoveAddressConflict(service_p);
	  if (service_router_is_iaddr_valid(service_p)
	      && service_resolve_router(service_p, manual->arp,
					manual_resolve_router_callback,
					service_requested_ip_addr(service_p))) {
	      /* router ARP resolution started */
	      manual->resolve_router_timed_out = FALSE;
	  }
	  else {
	      ServicePublishSuccessIPv4(service_p, NULL);
	  }
	  break;
      }
      default: {
	  break;
      }
    }
    return;
}
ipconfig_status_t
failover_thread(ServiceRef service_p, IFEventID_t evid, void * event_data)
{
    interface_t *	if_p = service_interface(service_p);
    Service_failover_t *failover;
    ipconfig_status_t	status = ipconfig_status_success_e;

    failover = (Service_failover_t *)ServiceGetPrivate(service_p);
    switch (evid) {
      case IFEventID_start_e: {
	  ipconfig_method_data_t * method_data;

	  method_data = (ipconfig_method_data_t *)event_data;
	  if (failover != NULL) {
	      my_log(LOG_DEBUG, "FAILOVER %s: re-entering start state", 
		     if_name(if_p));
	      return (ipconfig_status_internal_error_e);
	  }
	  if (if_flags(if_p) & IFF_LOOPBACK) {
	      return (ipconfig_status_invalid_parameter_e);
	  }
	  failover = malloc(sizeof(*failover));
	  if (failover == NULL) {
	      my_log(LOG_ERR, "FAILOVER %s: malloc failed", 
		     if_name(if_p));
	      status = ipconfig_status_allocation_failed_e;
	      break;
	  }
	  ServiceSetPrivate(service_p, failover);
	  bzero(failover, sizeof(*failover));
	  service_set_requested_ip_addr(service_p, method_data->manual.addr);
	  service_set_requested_ip_mask(service_p, method_data->manual.mask);
	  failover->timer = timer_callout_init();
	  if (failover->timer == NULL) {
	      my_log(LOG_ERR, "FAILOVER %s: timer_callout_init failed", 
		     if_name(if_p));
	      status = ipconfig_status_allocation_failed_e;
	      goto stop;
	  }
	  failover->arp = arp_client_init(G_arp_session, if_p);
	  if (failover->arp == NULL) {
	      my_log(LOG_NOTICE, "FAILOVER %s: arp_client_init failed", 
		     if_name(if_p));
	      goto stop;
	  }
	  failover->address_timeout_secs = method_data->manual.failover_timeout;
	  my_log(LOG_DEBUG, "FAILOVER %s: starting", 
		 if_name(if_p));
	  failover_start(service_p, IFEventID_start_e, NULL);
	  break;
      }
      stop:
      case IFEventID_stop_e: {
	  my_log(LOG_DEBUG, "FAILOVER %s: stop", if_name(if_p));

	  if (failover == NULL) {
	      break;
	  }

	  /* remove IP address */
	  service_remove_address(service_p);

	  /* clean-up resources */
	  if (failover->arp) {
	      arp_client_free(&failover->arp);
	  }
	  if (failover->timer) {
	      timer_callout_free(&failover->timer);
	  }
	  if (failover) {
	      free(failover);
	  }
	  ServiceSetPrivate(service_p, NULL);
	  break;
      }
      case IFEventID_change_e: {
	  change_event_data_t *   	change_event;
	  ipconfig_method_data_t * 	method_data;

	  if (failover == NULL) {
	      my_log(LOG_DEBUG, "FAILOVER %s: private data is NULL", 
		     if_name(if_p));
	      status = ipconfig_status_internal_error_e;
	      break;
	  }
	  change_event = ((change_event_data_t *)event_data);
	  method_data = change_event->method_data;
	  change_event->needs_stop = FALSE;
	  if ((method_data->manual.addr.s_addr
	       != service_requested_ip_addr(service_p).s_addr)
	      || (method_data->manual.mask.s_addr
		  != service_requested_ip_mask(service_p).s_addr)) {
	      change_event->needs_stop = TRUE;
	  }
	  else {
	      failover->address_timeout_secs
		  = method_data->manual.failover_timeout;
	      if (service_is_address_set(service_p)
		  && failover->address_is_verified) {
		  if (failover->address_timeout_secs != 0) {
		      struct timeval	tv;

		      tv.tv_sec = failover->address_timeout_secs;
		      tv.tv_usec = 0;
		      timer_set_relative(failover->timer, tv, 
					 (timer_func_t *)failover_timed_out,
					 service_p, IFEventID_start_e, NULL);
		  }
		  else {
		      timer_cancel(failover->timer);
		  }
	      }
	      else {
		  failover_start(service_p, IFEventID_start_e, NULL);
	      }
	  }
	  break;
      }
      case IFEventID_arp_collision_e: {
	  arp_collision_data_t *	arpc;
	  char				msg[128];

	  arpc = (arp_collision_data_t *)event_data;

	  if (failover == NULL) {
	      return (ipconfig_status_internal_error_e);
	  }
	  if (arpc->ip_addr.s_addr 
	      != service_requested_ip_addr(service_p).s_addr) {
	      break;
	  }
	  snprintf(msg, sizeof(msg), 
		   IP_FORMAT " in use by " EA_FORMAT,
		   IP_LIST(&arpc->ip_addr), 
		   EA_LIST(arpc->hwaddr));
	  my_log(LOG_NOTICE, "FAILOVER %s: %s", 
		 if_name(if_p), msg);
	  service_remove_address(service_p);
	  service_publish_failure(service_p, 
				  ipconfig_status_address_in_use_e);
	  failover_start(service_p, IFEventID_start_e, NULL);
	  break;
      }
      case IFEventID_link_status_changed_e: {
	  link_status_t		link_status;

	  if (failover == NULL) {
	      return (ipconfig_status_internal_error_e);
	  }
	  link_status = service_link_status(service_p);
	  if (link_status.valid == TRUE) {
	      if (link_status.active == TRUE) {
		  failover_start(service_p, IFEventID_start_e, NULL);
	      }
	      else {
		  failover->address_is_verified = FALSE;
		  failover_cancel_pending_events(service_p);
	      }
	  }
	  break;
      }
      case IFEventID_link_timer_expired_e:
	  failover_inactive(service_p);
	  break;
      default:
	  break;
    } /* switch */

    return (status);
}
static void
failover_start(ServiceRef service_p, IFEventID_t evid, void * event_data)
{
    Service_failover_t * failover;
    interface_t *	if_p = service_interface(service_p);
    struct timeval	tv;

    failover = (Service_failover_t *)ServiceGetPrivate(service_p);
    switch (evid) {
      case IFEventID_start_e: {
	  failover->address_is_verified = FALSE;
	  failover_cancel_pending_events(service_p);
	  tv.tv_sec = random_range(0, 4);
	  tv.tv_usec = random_range(0, USECS_PER_SEC - 1);
	  timer_set_relative(failover->timer, tv, 
			     (timer_func_t *)failover_start,
			     service_p, (void *)IFEventID_timeout_e, NULL);
	  break;
      }
      case IFEventID_timeout_e: {
	  arp_client_probe(failover->arp, 
			   (arp_result_func_t *)failover_start, service_p,
			   (void *)IFEventID_arp_e, G_ip_zeroes,
			   service_requested_ip_addr(service_p));
	  break;
      }
      case IFEventID_arp_e: {
	  link_status_t		link_status;
	  arp_result_t *	result = (arp_result_t *)event_data;

	  if (result->error) {
	      my_log(LOG_ERR, "FAILOVER %s: arp probe failed, %s", 
		     if_name(if_p), arp_client_errmsg(failover->arp));
	      break;
	  }
	  else {
	      if (result->in_use) {
		  char			msg[128];
		  struct in_addr	requested_ip;

		  requested_ip = service_requested_ip_addr(service_p);
		  snprintf(msg, sizeof(msg), 
			   IP_FORMAT " in use by " EA_FORMAT,
			   IP_LIST(&requested_ip),
			   EA_LIST(result->addr.target_hardware));
		  my_log(LOG_NOTICE, "FAILOVER %s: %s", 
			 if_name(if_p), msg);
		  service_remove_address(service_p);
		  service_publish_failure(service_p, 
					  ipconfig_status_address_in_use_e);
		  tv.tv_sec = 10;
		  tv.tv_usec = 0;
		  timer_set_relative(failover->timer, tv, 
				     (timer_func_t *)failover_start,
				     service_p, IFEventID_start_e, NULL);
		  break;
	      }
	  }
	  link_status = service_link_status(service_p);
	  if (link_status.valid == TRUE 
	      && link_status.active == FALSE) {
	      failover_inactive(service_p);
	      break;
	  }

	  /* set the new address */
	  (void)service_set_address(service_p,
				    service_requested_ip_addr(service_p),
				    service_requested_ip_mask(service_p),
				    G_ip_zeroes);
	  ServicePublishSuccessIPv4(service_p, NULL);
	  failover->address_is_verified = TRUE;
	  if (failover->address_timeout_secs != 0) {
	      tv.tv_sec = failover->address_timeout_secs;
	      tv.tv_usec = 0;
	      timer_set_relative(failover->timer, tv, 
				 (timer_func_t *)failover_timed_out,
				 service_p, IFEventID_start_e, NULL);
	  }
	  break;
      }
      default: {
	  break;
      }
    }
    return;
}