void
ssh_virtual_adapter_ip_ether_address(SshIpAddr ip, unsigned char *buffer)
{
  memset(buffer, 0, SSH_ETHERH_ADDRLEN);

  if (SSH_IP_IS4(ip))
    {
      buffer[1] = 2;
      SSH_IP4_ENCODE(ip, buffer + 2);
    }
#if defined (WITH_IPV6)
  else
    {
      SshUInt32 value;

      value = SSH_IP6_WORD0_TO_INT(ip);
      value ^= SSH_IP6_WORD1_TO_INT(ip);
      value ^= SSH_IP6_WORD2_TO_INT(ip);
      value ^= SSH_IP6_WORD3_TO_INT(ip);

      buffer[1] = 2;
      SSH_PUT_32BIT(buffer + 2, value);
    }
#endif /* WITH_IPV6 */
}
SshOperationHandle
ssh_pm_cfgmode_client_store_register(SshPm pm,
				     SshPmTunnel tunnel,
                                     SshPmActiveCfgModeClient client,
                                     SshIpAddr address,
				     void *address_context,
                                     SshPmRemoteAccessAttrsFreeCB free_cb,
                                     void *free_cb_context,
				     SshPmStatusCB status_cb,
				     void *status_cb_context)
{
  SSH_DEBUG(SSH_D_LOWOK,
	    ("Registering address `%@'", ssh_ipaddr_render, address));

  SSH_ASSERT(client->status_cb == NULL_FNPTR);

  if (client->flags & SSH_PM_CFGMODE_CLIENT_ADDING_ARP)
    goto error;

  if (!SSH_IP_DEFINED(address))
    goto error;
  
  if (SSH_IP_IS4(address))
    {
      SSH_ASSERT(client->ip4 == NULL);
      client->ip4 = ssh_memdup(address, sizeof(*address));
      if (client->ip4 == NULL)
	goto error;
      client->ip4_address_context = address_context;
    }
  else
    {
      SSH_ASSERT(client->ip6 == NULL);
      client->ip6 = ssh_memdup(address, sizeof(*address));
      if (client->ip6 == NULL)
	goto error;
      client->ip6_address_context = address_context;
    }

  client->free_cb = free_cb;
  client->free_cb_context = free_cb_context;
  
  /* Check if we should add a proxy ARP entry for the remote access client. */
  if (tunnel->flags & SSH_PM_TR_PROXY_ARP)
    {
      unsigned char media_addr[SSH_ETHERH_ADDRLEN];
      SshUInt32 flags;
      
      /* Create a fake ethernet address. */
      memset(media_addr, 0, sizeof(media_addr));
      if (SSH_IP_IS4(address))
	{
	  media_addr[1] = 2;
	  SSH_IP4_ENCODE(address, media_addr + 2);

	  client->flags |= SSH_PM_CFGMODE_CLIENT_IPV4_PROXY_ARP;
	}
      else
	{
	  SshUInt32 value;
	  
	  value = SSH_IP6_WORD0_TO_INT(address);
	  value ^= SSH_IP6_WORD1_TO_INT(address);
	  value ^= SSH_IP6_WORD2_TO_INT(address);
	  value ^= SSH_IP6_WORD3_TO_INT(address);
	  
	  media_addr[1] = 2;
	  SSH_PUT_32BIT(media_addr + 2, value);

	  client->flags |= SSH_PM_CFGMODE_CLIENT_IPV6_PROXY_ARP;
	}
      
      /* Flags for ARP entry. */
      flags = SSH_PME_ARP_PERMANENT | SSH_PME_ARP_GLOBAL | SSH_PME_ARP_PROXY;

      /* Store status_cb. */
      client->status_cb = status_cb;
      client->status_cb_context = status_cb_context;
      if (SSH_IP_IS4(address))
	client->flags |= SSH_PM_CFGMODE_CLIENT_IPV4_REGISTERING;
      else
	client->flags |= SSH_PM_CFGMODE_CLIENT_IPV6_REGISTERING;

      /* Register an abort callback for the engine operation. */
      ssh_operation_register_no_alloc(&client->operation,
				      pm_cfgmode_client_store_arp_abort,
				      client);

      /* Take a reference to the client and mark ARP ongoing. */
      ssh_pm_cfgmode_client_store_take_reference(pm, client);
      client->flags |= SSH_PM_CFGMODE_CLIENT_ADDING_ARP;

      /* Add ARP entry. */
      SSH_DEBUG(SSH_D_LOWSTART, ("Adding ARP entry"));
      ssh_pme_arp_add(pm->engine, address, 0,
		      media_addr, sizeof(media_addr),
		      flags, pm_cfgmode_client_store_arp_cb, client);

      return &client->operation;
    }

  if (status_cb) 
    (*status_cb)(pm, TRUE, status_cb_context);
  
  return NULL;

 error:
  if (status_cb) 
    (*status_cb)(pm, FALSE, status_cb_context);

  return NULL;
}