/* 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 the ELAP port of the bridge. **/ #include #include "../defc.h" #include "atbridge.h" #include "elap.h" #include "port.h" #include "aarp.h" #include "elap_defs.h" #include "pcap_delay.h" #ifdef __CYGWIN__ #include #include #endif #ifdef WIN32 #include #include #endif #ifdef __linux__ #include #include #endif extern int g_ethernet_interface; static pcap_t* pcap_session; static struct packet_port_t elap_port; static struct ether_addr_t HW_LOCAL; /*static void dump_device_list(pcap_if_t* devices) { int i = 0; for(pcap_if_t* device = devices; device; device = device->next) { printf("%d. %s", ++i, device->name); if (device->description) printf(" (%s)\n", device->description); else printf(" (No description available)\n"); } }*/ static void elap_clone_host_mac(pcap_if_t* device) { if (!device) return; #ifdef WIN32 //// // Extract the device GUID, which Windows uses to identify the device. char* name = device->name; while (name && *name != '{') name++; size_t guidLen = strlen(name); //// // Find and copy the device MAC address. ULONG size = sizeof(IP_ADAPTER_ADDRESSES) * 15; IP_ADAPTER_ADDRESSES* addresses = malloc(size); ULONG result = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER, NULL, addresses, &size); if (result == ERROR_BUFFER_OVERFLOW) { // The addresses buffer is too small. Allocate a bigger buffer. free(addresses); addresses = malloc(size); result = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER, NULL, addresses, &size); } if (result == NO_ERROR) { // Search for the desired adapter address. IP_ADAPTER_ADDRESSES* current = addresses; while (current) { if (current->PhysicalAddressLength == ETHER_ADDR_LEN && memcmp(current->AdapterName, name, guidLen) == 0) { memcpy(&HW_LOCAL.mac, ¤t->PhysicalAddress, sizeof(HW_LOCAL.mac)); break; } current = current->Next; } } else { halt_printf("ATBridge: Failed to find host MAC address (%d).", result); } free(addresses); #else #ifdef AF_PACKET struct pcap_addr* address; for (address = device->addresses; address != 0; address = address->next) if (address->addr->sa_family == AF_PACKET) { struct sockaddr_ll* ll = (struct sockaddr_ll*)address->addr; memcpy(&HW_LOCAL.mac, ll->sll_addr, sizeof(HW_LOCAL.mac)); } #endif #endif } const struct ether_addr_t* elap_get_mac() { return &HW_LOCAL; } bool elap_init() { port_init(&elap_port); memcpy(&HW_LOCAL, &HW_LOCAL_DEFAULT, sizeof(HW_LOCAL)); pcap_if_t* device; pcap_if_t* alldevs; int i = 0; char errbuf[PCAP_ERRBUF_SIZE]; // Load the PCAP library. if (!pcapdelay_load()) { halt_printf("ATBridge: PCAP not available.\n"); return false; } // Retrieve the device list. if(pcapdelay_findalldevs(&alldevs, errbuf) == -1) { atbridge_printf("ATBridge: Error enumerating PCAP devices: %s\n", errbuf); return false; } //dump_device_list(alldevs); // Jump to the selected adapter. for (device = alldevs, i = 0; i < g_ethernet_interface; device = device->next, i++); if (!device) { halt_printf("ATBridge: PCAP device not found. Check interface number in settings.\n"); return false; } // Clone the MAC address of the underlying interface. In certain configurations (e.g. Windows with an MS Loopback // interface), the interface, even in promiscous mode, filters foreign MAC addresses. elap_clone_host_mac(device); // Open the adapter, if ((pcap_session = pcapdelay_open_live(device->name, // name of the device 65536, // portion of the packet to capture. // 65536 grants that the whole packet will be captured on all the MACs. 1, // promiscuous mode (nonzero means promiscuous) 1, // read timeout errbuf // error buffer )) == NULL) { halt_printf("ATBridge: Unable to open the adapter. Pcap does not support %s.\n", device->name); pcapdelay_freealldevs(alldevs); return false; } // The device must support Ethernet because the bridge "speaks" EtherTalk. if (pcapdelay_datalink(pcap_session) == DLT_EN10MB) { pcapdelay_setnonblock(pcap_session, 1, errbuf); atbridge_printf("ATBridge: AppleTalk bridging using network device '%s' with Ethernet address %.2X:%.2X:%.2X:%.2X:%.2X:%.2X.\n", device->description, HW_LOCAL.mac[0], HW_LOCAL.mac[1], HW_LOCAL.mac[2], HW_LOCAL.mac[3], HW_LOCAL.mac[4], HW_LOCAL.mac[5]); pcapdelay_freealldevs(alldevs); return true; } else { pcapdelay_close(pcap_session); pcap_session = 0; halt_printf("ATBridge: Selected network device %s must support Ethernet.\n", device->description); pcapdelay_freealldevs(alldevs); return false; } } void elap_shutdown() { port_shutdown(&elap_port); if (pcap_session) { pcapdelay_close(pcap_session); pcap_session = 0; } pcapdelay_unload(); } void elap_enqueue_out(struct packet_t* packet) { enqueue_packet(&elap_port.out, packet); } struct packet_t* elap_dequeue_in() { return dequeue(&elap_port.in); } void elap_send(const struct ether_addr_t* dest, const struct snap_discriminator_t* discriminator, size_t size, byte data[]) { if (pcap_session && dest && discriminator) { // Allocate heap space for the frame. const size_t frame_size = sizeof(struct ethernet_header_t) + sizeof(struct snap_header_t) + size; u_char* frame_data; size_t pad = 0; if (frame_size < ETHER_MIN_SIZE) pad = ETHER_MIN_SIZE - frame_size; frame_data = (u_char*)malloc(frame_size + pad); // Build the 802.3 header. struct ethernet_header_t* ether = (struct ethernet_header_t*)frame_data; memcpy(ether->dest.mac, dest, sizeof(ether->dest.mac)); memcpy(ether->source.mac, HW_LOCAL.mac, sizeof(ether->source.mac)); ether->length = htons(frame_size - sizeof(struct ethernet_header_t)); // Build the 802.2 header. struct snap_header_t* snap = (struct snap_header_t*)(frame_data + sizeof(struct ethernet_header_t)); snap->dsap = SNAP_DSAP; snap->ssap = SNAP_SSAP; snap->control = SNAP_CONTROL; memcpy(&snap->discriminator, discriminator, sizeof(snap->discriminator)); // Add the data payload. struct snap_header_t* payload = (struct snap_header_t*)(frame_data + sizeof(struct ethernet_header_t) + sizeof(struct snap_header_t)); memcpy(payload, data, size); // Add padding to meet minimum Ethernet frame size. if (pad > 0) memset(frame_data + frame_size, 0, pad); pcapdelay_sendpacket(pcap_session, frame_data, frame_size + pad); } } static bool elap_send_one_queued() { // Attempt to send one packet out the host network interface. struct packet_t* packet = queue_peek(&elap_port.out); if (packet) { // Find the MAC address. const struct ether_addr_t* dest; if (packet->dest.node == at_broadcast_node) dest = &HW_APPLETALK_BROADCAST; else dest = aarp_request_hardware(&packet->dest); // Send it. if (dest) { elap_send(dest, &SNAP_APPLETALK, packet->size, packet->data); dequeue(&elap_port.out); free(packet->data); free(packet); } else { // AARP does not have the needed hardware address. Give AARP time to obtain the address and keep the current packet. if (!aarp_retry()) { // However, if AARP has reached the retry limit, the packet is undeliverable. Discard the packet and move on. atbridge_printf("ATBridge: AARP failed to find MAC address for network %d node %d. Dropping packet.\n", packet->dest.network, packet->dest.node); aarp_retry_reset(); dequeue(&elap_port.out); free(packet->data); free(packet); } } return true; } else return false; } static void elap_send_all_queued() { while (elap_send_one_queued()); } static bool elap_receive_one() { if (!pcap_session) return false; struct pcap_pkthdr header; const u_char* packet = pcapdelay_next(pcap_session, &header); if (packet) { // Receive and process one packet from the host network interface. //// // Check the Ethernet 802.3 header. const struct ethernet_header_t* ether = (struct ethernet_header_t*)packet; if (header.len > sizeof(struct ethernet_header_t) && ntohs(ether->length) <= ETHER_MAX_SIZE && ntohs(ether->length) > sizeof(struct snap_header_t) && (memcmp(ðer->source, &HW_LOCAL, sizeof(ether->source)) != 0) && /* Ignore packets sent from our node. */ (memcmp(ðer->dest, &HW_LOCAL, sizeof(ether->dest)) == 0 || /* Accept packets destined for our node ... */ memcmp(ðer->dest, &HW_APPLETALK_BROADCAST, sizeof(ether->dest)) == 0 /* ... or for broadcast. */) ) { // Check the 802.2 SNAP header. const struct snap_header_t* snap = (struct snap_header_t*)(packet + sizeof(struct ethernet_header_t)); if (snap->dsap == SNAP_DSAP && snap->ssap == SNAP_SSAP && snap->control == SNAP_CONTROL) { if (memcmp(&snap->discriminator, &SNAP_APPLETALK, sizeof(snap->discriminator)) == 0) { // Handle an AppleTalk packet. const size_t payload_size = ntohs(ether->length) - sizeof(struct snap_header_t); const u_char* payload = packet + sizeof(struct ethernet_header_t) + sizeof(struct snap_header_t); byte* copy = (byte*)malloc(payload_size); memcpy(copy, payload, payload_size); // ELAP does not support short-form DDP, so this must be a long-form DDP packet. struct at_addr_t source, dest; if (payload_size >= sizeof(struct DDP_LONG)) { // Extract the protocol address from the header. // // ELAP really shouldn't be looking at the header for the next level of the protocol stack, // but this is a convenient place to glean addresses for the AMT, a process that needs both // hardware and protocol addresses. struct DDP_LONG* header = (struct DDP_LONG*)copy; dest.network = (at_network_t)ntohs(header->dest_net); source.network = (at_network_t)ntohs(header->source_net); dest.node = header->dest_node; source.node = header->source_node; enqueue(&elap_port.in, dest, source, LAP_DDP_LONG, payload_size, copy); aarp_glean(&source, ðer->source); } else atbridge_printf("ATBridge: Dropping invalid short ELAP frame.\n"); } else if (memcmp(&snap->discriminator, &SNAP_AARP, sizeof(snap->discriminator)) == 0) { // Handle an AARP packet. struct aarp_header_t* aarp = (struct aarp_header_t*)(packet + sizeof(struct ethernet_header_t) + sizeof(struct snap_header_t)); aarp->dest_proto_addr.addr.network = ntohs(aarp->dest_proto_addr.addr.network); aarp->source_proto_addr.addr.network = ntohs(aarp->source_proto_addr.addr.network); aarp->function = ntohs(aarp->function); aarp->hardware_type = ntohs(aarp->hardware_type); aarp->protocol_type = ntohs(aarp->protocol_type); aarp_handle_packet(aarp); } } } return true; } else return false; } static void elap_receive_all() { while (elap_receive_one()); } void elap_process() { elap_receive_all(); elap_send_all_queued(); }