/* 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 #include #include "../defc.h" #include "atbridge.h" #include "elap.h" #include "port.h" #include "elap_defs.h" #include "aarp.h" #ifdef WIN32 #include #elif __linux__ #include #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; } } }