gsplus/src/atbridge/aarp.c

291 lines
7.3 KiB
C

/*
GSPLUS - Advanced Apple IIGS Emulator Environment
Based on the KEGS emulator written by Kent Dickey
See COPYRIGHT.txt for Copyright information
See LICENSE.txt for license (GPL v2)
*/
/** This module implements AARP, a necessary protocol for ELAP communication. **/
#include <stdbool.h>
#include <time.h>
#include "../defc.h"
#include "atbridge.h"
#include "elap.h"
#include "port.h"
#include "elap_defs.h"
#include "aarp.h"
#ifdef WIN32
#include <winsock.h>
#elif __linux__
#include <netinet/in.h>
#endif
struct amt_entry_t
{
struct at_addr_t protocol;
struct ether_addr_t hardware;
struct amt_entry_t* next;
};
typedef struct amt_entry_t* amt_t;
static amt_t amt = 0;
static unsigned int retry_count;
static clock_t retry_timer;
void aarp_init()
{
aarp_retry_reset();
}
void aarp_shutdown()
{
struct amt_entry_t* entry = amt;
while (entry)
{
struct amt_entry_t* next = entry->next;
free(entry);
entry = next;
}
}
////
static void aarp_send_packet(enum AARP_FUNCTION function, const struct at_addr_t* source_at_addr, const struct at_addr_t* dest_at_addr, const struct ether_addr_t* dest_hw_addr)
{
if (source_at_addr && dest_at_addr && dest_hw_addr)
{
struct aarp_header_t response;
response.hardware_type = htons(AARP_HARDWARE_ETHER);
response.protocol_type = htons(AARP_PROTOCOL_TYPE);
response.hw_addr_len = AARP_HW_ADDR_LEN;
response.protocol_addr_len = AARP_PROTOCOL_ADDR_LEN;
response.function = htons(function);
memcpy(&response.source_proto_addr.addr, source_at_addr, sizeof(response.source_proto_addr.addr));
response.source_proto_addr.addr.network = htons(response.source_proto_addr.addr.network);
response.source_proto_addr.zero = 0x00;
memcpy(&response.dest_proto_addr.addr, dest_at_addr, sizeof(response.dest_proto_addr.addr));
response.dest_proto_addr.addr.network = htons(response.dest_proto_addr.addr.network);
response.dest_proto_addr.zero = 0x00;
memcpy(response.source_hw_addr.mac, elap_get_mac()->mac, sizeof(response.source_hw_addr.mac));
memcpy(response.dest_hw_addr.mac, &dest_hw_addr->mac, sizeof(response.dest_hw_addr.mac));
if (dest_hw_addr == &HW_ZERO)
elap_send(&HW_APPLETALK_BROADCAST, &SNAP_AARP, sizeof(struct aarp_header_t), (byte*)&response);
else
elap_send(&response.dest_hw_addr, &SNAP_AARP, sizeof(struct aarp_header_t), (byte*)&response);
}
}
void aarp_probe(const struct at_addr_t* addr)
{
if (addr)
{
aarp_send_packet(AARP_FUNCTION_PROBE, addr, addr, &HW_ZERO);
}
}
static void aarp_request(const struct at_addr_t* addr)
{
if (addr)
{
aarp_send_packet(AARP_FUNCTION_REQUEST, atbridge_get_addr(), addr, &HW_ZERO);
}
}
////
static struct amt_entry_t* amt_lookup_entry_hardware(const struct ether_addr_t* hardware)
{
if (hardware)
{
struct amt_entry_t* entry = amt;
while (entry)
{
if (memcmp(&entry->hardware, hardware, sizeof(entry->hardware)) == 0)
return entry;
entry = entry->next;
}
}
return 0;
}
static struct amt_entry_t* amt_lookup_entry_protocol(const struct at_addr_t* protocol)
{
if (protocol)
{
struct amt_entry_t* entry = amt;
while (entry)
{
if (memcmp(&entry->protocol, protocol, sizeof(entry->protocol)) == 0)
return entry;
entry = entry->next;
}
}
return 0;
}
static void amt_delete_entry_protocol(const struct at_addr_t* protocol)
{
if (protocol)
{
struct amt_entry_t* entry = amt;
struct amt_entry_t* previous = amt;
while (entry)
{
if (memcmp(&entry->protocol, protocol, sizeof(entry->protocol)) == 0)
{
previous->next = entry->next;
free(entry);
break;
}
previous = entry;
entry = entry->next;
}
}
}
static void amt_add(const struct at_addr_t* protocol, const struct ether_addr_t* hardware)
{
// Does an entry matching one of the protocol or hardware addresses exist? If so, update it.
struct amt_entry_t* entry = amt_lookup_entry_protocol(protocol);
if (entry)
{
memcpy(&entry->hardware, hardware, sizeof(entry->hardware));
return;
}
entry = amt_lookup_entry_hardware(hardware);
if (entry)
{
memcpy(&entry->protocol, protocol, sizeof(entry->protocol));
return;
}
// Otherwise, add a new entry.
entry = (struct amt_entry_t*)malloc(sizeof(struct amt_entry_t));
memcpy(&entry->hardware, hardware, sizeof(entry->hardware));
memcpy(&entry->protocol, protocol, sizeof(entry->protocol));
entry->next = amt;
amt = entry;
}
const struct ether_addr_t* aarp_request_hardware(const struct at_addr_t* protocol)
{
struct amt_entry_t* entry = amt_lookup_entry_protocol(protocol);
if (entry)
{
aarp_retry_reset();
return (const struct ether_addr_t*)&entry->hardware;
}
else
{
// The AMT doesn't have this protocol address so issue a request at no more than the AARP_PROBE_INTERVAL period.
if (((clock() - retry_timer) >= (AARP_REQUEST_INTERVAL * CLOCKS_PER_SEC / 1000)) &&
(retry_count > 0))
{
aarp_request(protocol);
retry_count--;
retry_timer = clock();
//atbridge_printf("AARP request count %d timer %d.\n", retry_count, retry_timer);
}
return 0;
}
}
const struct at_addr_t* aarp_request_protocol(const struct ether_addr_t* hardware)
{
struct amt_entry_t* entry = amt_lookup_entry_hardware(hardware);
if (entry)
return (const struct at_addr_t*)&entry->protocol;
else
return 0;
}
bool aarp_retry()
{
return retry_count > 0;
}
void aarp_retry_reset()
{
retry_count = AARP_REQUEST_COUNT;
retry_timer = clock();
}
void aarp_glean(const struct at_addr_t* protocol, const struct ether_addr_t* hardware)
{
amt_add(protocol, hardware);
}
bool aarp_address_used(const struct at_addr_t* protocol)
{
// reference 2-8
if (protocol)
{
// Check for reserved node numbers, per reference 3-9.
if (protocol->node == 0x00 || protocol->node == 0xfe || protocol->node == 0xff)
return true;
// Look for the address in the AMT. If it's there, another node is using this address.
struct amt_entry_t* entry = amt_lookup_entry_protocol(protocol);
if (entry)
return true;
// Try a probe. If this address is in use, another node will reply with an AARP RESPONSE packet.
// Return true to advise the caller that the address is not known to be in use. The caller should
// retry aarp_try_address() every 200 ms (AARP_PROBE_INTERVAL) and 10 times (AARP_PROBE_COUNT),
// per the AARP protocol definition, before choosing this address.
aarp_probe(protocol);
return false;
}
return false;
}
////
void aarp_handle_packet(const struct aarp_header_t* aarp)
{
if (aarp &&
aarp->hardware_type == AARP_HARDWARE_ETHER &&
aarp->protocol_type == AARP_PROTOCOL_TYPE &&
aarp->hw_addr_len == AARP_HW_ADDR_LEN &&
aarp->protocol_addr_len == AARP_PROTOCOL_ADDR_LEN)
{
switch (aarp->function)
{
case AARP_FUNCTION_REQUEST:
if (((aarp->dest_proto_addr.addr.network == atbridge_get_net()) ||
(aarp->dest_proto_addr.addr.network == 0x00 /* reference 4-6 */)) &&
(aarp->dest_proto_addr.addr.node == atbridge_get_node()))
{
// Generate a response for the AARP request.
aarp_send_packet(AARP_FUNCTION_RESPONSE, &aarp->dest_proto_addr.addr, &aarp->source_proto_addr.addr, &aarp->source_hw_addr);
}
break;
case AARP_FUNCTION_RESPONSE:
aarp_glean(&aarp->source_proto_addr.addr, &aarp->source_hw_addr);
aarp_glean(&aarp->dest_proto_addr.addr, &aarp->dest_hw_addr);
break;
case AARP_FUNCTION_PROBE:
// AMT entry aging, method 2, reference 2-11
amt_delete_entry_protocol(&aarp->dest_proto_addr.addr);
break;
default:
break;
}
}
}