mirror of
https://github.com/digarok/gsplus.git
synced 2024-11-28 04:49:18 +00:00
303 lines
8.2 KiB
Plaintext
303 lines
8.2 KiB
Plaintext
|
/*
|
||
|
GSport - an Apple //gs Emulator
|
||
|
Copyright (C) 2013-2014 by Peter Neubauer
|
||
|
|
||
|
This program is free software; you can redistribute it and/or modify it
|
||
|
under the terms of the GNU General Public License as published by the
|
||
|
Free Software Foundation; either version 2 of the License, or (at your
|
||
|
option) any later version.
|
||
|
|
||
|
This program is distributed in the hope that it will be useful, but
|
||
|
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||
|
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||
|
for more details.
|
||
|
|
||
|
You should have received a copy of the GNU General Public License along
|
||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||
|
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||
|
*/
|
||
|
|
||
|
/** 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;
|
||
|
}
|
||
|
}
|
||
|
}
|