/* handle ARP Usage #1: At startup, we make a synchronous request for the local router. We'll wait several seconds for a response, but abort the program if we don't receive a response. Usage #2: While running, we'll need to respond to ARPs. That's because we may be bypassing the stack of the local machine with a "spoofed" IP address. Every so often, the local router may drop it's route entry and re-request our address. */ #include "rawsock.h" #include "rawsock-adapter.h" #include "stack-src.h" #include "stack-arpv4.h" #include "stack-queue.h" #include "util-safefunc.h" #include "util-logger.h" #include "pixie-timer.h" #include "proto-preprocess.h" #include "util-checksum.h" #define VERIFY_REMAINING(n) if (offset+(n) > max) return; /** * A structure representing the information parsed from an incoming * ARP packet. Note: unlike normal programming style, this isn't * overlayed on the incoming ARP header, but instead each field * is parsed one-by-one and converted into this internal structure. */ struct ARP_IncomingRequest { unsigned is_valid; unsigned opcode; unsigned hardware_type; unsigned protocol_type; unsigned hardware_length; unsigned protocol_length; unsigned ip_src; unsigned ip_dst; const unsigned char *mac_src; const unsigned char *mac_dst; }; /**************************************************************************** ****************************************************************************/ static void proto_arp_parse(struct ARP_IncomingRequest *arp, const unsigned char px[], unsigned offset, unsigned max) { /* * parse the header */ VERIFY_REMAINING(8); arp->is_valid = 0; /* not valid yet */ arp->hardware_type = px[offset]<<8 | px[offset+1]; arp->protocol_type = px[offset+2]<<8 | px[offset+3]; arp->hardware_length = px[offset+4]; arp->protocol_length = px[offset+5]; arp->opcode = px[offset+6]<<8 | px[offset+7]; offset += 8; /* We only support IPv4 and Ethernet addresses */ if (arp->protocol_length != 4 && arp->hardware_length != 6) return; if (arp->protocol_type != 0x0800) return; if (arp->hardware_type != 1 && arp->hardware_type != 6) return; /* * parse the addresses */ VERIFY_REMAINING(2 * arp->hardware_length + 2 * arp->protocol_length); arp->mac_src = px+offset; offset += arp->hardware_length; arp->ip_src = px[offset+0]<<24 | px[offset+1]<<16 | px[offset+2]<<8 | px[offset+3]; offset += arp->protocol_length; arp->mac_dst = px+offset; offset += arp->hardware_length; arp->ip_dst = px[offset+0]<<24 | px[offset+1]<<16 | px[offset+2]<<8 | px[offset+3]; //offset += arp->protocol_length; arp->is_valid = 1; } /**************************************************************************** * Resolve the IP address into a MAC address. Do this synchronously, meaning, * we'll stop and wait for the response. This is done at program startup, * but not during then normal asynchronous operation during the scan. ****************************************************************************/ int stack_arp_resolve(struct Adapter *adapter, ipv4address_t my_ipv4, macaddress_t my_mac_address, ipv4address_t your_ipv4, macaddress_t *your_mac_address) { unsigned char xarp_packet[64]; unsigned char *arp_packet = &xarp_packet[0]; unsigned i; time_t start; unsigned is_arp_notice_given = 0; struct ARP_IncomingRequest response; int is_delay_reported = 0; /* * [KLUDGE] * If this is a VPN connection */ if (stack_if_datalink(adapter) == 12) { memcpy(your_mac_address->addr, "\0\0\0\0\0\2", 6); return 0; /* success */ } memset(&response, 0, sizeof(response)); /* zero out bytes in packet to avoid leaking stuff in the padding * (ARP is 42 byte packet, Ethernet is 60 byte minimum) */ memset(arp_packet, 0, sizeof(xarp_packet)); /* * Create the request packet */ memcpy(arp_packet + 0, "\xFF\xFF\xFF\xFF\xFF\xFF", 6); memcpy(arp_packet + 6, my_mac_address.addr, 6); if (adapter->is_vlan) { memcpy(arp_packet + 12, "\x81\x00", 2); arp_packet[14] = (unsigned char)(adapter->vlan_id>>8); arp_packet[15] = (unsigned char)(adapter->vlan_id&0xFF); arp_packet += 4; } memcpy(arp_packet + 12, "\x08\x06", 2); memcpy(arp_packet + 14, "\x00\x01" /* hardware = Ethernet */ "\x08\x00" /* protocol = IPv4 */ "\x06\x04" /* MAC length = 6, IPv4 length = 4 */ "\x00\x01" /* opcode = request */ , 8); memcpy(arp_packet + 22, my_mac_address.addr, 6); arp_packet[28] = (unsigned char)(my_ipv4 >> 24); arp_packet[29] = (unsigned char)(my_ipv4 >> 16); arp_packet[30] = (unsigned char)(my_ipv4 >> 8); arp_packet[31] = (unsigned char)(my_ipv4 >> 0); memcpy(arp_packet + 32, "\x00\x00\x00\x00\x00\x00", 6); arp_packet[38] = (unsigned char)(your_ipv4 >> 24); arp_packet[39] = (unsigned char)(your_ipv4 >> 16); arp_packet[40] = (unsigned char)(your_ipv4 >> 8); arp_packet[41] = (unsigned char)(your_ipv4 >> 0); /* Kludge: handle VLNA header if it exists. This is probably * the wrong way to handle this. */ if (adapter->is_vlan) arp_packet -= 4; /* * Now loop for a few seconds looking for the response */ rawsock_send_packet(adapter, arp_packet, 60, 1); start = time(0); i = 0; for (;;) { unsigned length; unsigned secs; unsigned usecs; const unsigned char *px; int err; if (time(0) != start) { start = time(0); rawsock_send_packet(adapter, arp_packet, 60, 1); if (i++ >= 10) break; /* timeout */ /* It's taking too long, so notify the user */ if (!is_delay_reported) { ipaddress_formatted_t fmt = ipv4address_fmt(your_ipv4); LOG(0, "[+] resolving router %s with ARP (may take some time)...\n", fmt.string); is_delay_reported = 1; } } /* If we aren't getting a response back to our ARP, then print a * status message */ if (time(0) > start+1 && !is_arp_notice_given) { ipaddress_formatted_t fmt = ipv4address_fmt(your_ipv4); LOG(0, "[+] arping local router %s\n", fmt.string); is_arp_notice_given = 1; } err = rawsock_recv_packet( adapter, &length, &secs, &usecs, &px); if (err != 0) continue; if (adapter->is_vlan && px[17] != 6) continue; if (!adapter->is_vlan && px[13] != 6) continue; /* * Parse the response as an ARP packet */ if (adapter->is_vlan) proto_arp_parse(&response, px, 18, length); else proto_arp_parse(&response, px, 14, length); /* Is this an ARP packet? */ if (!response.is_valid) { LOG(2, "[-] arp: etype=0x%04x, not ARP\n", px[12]*256 + px[13]); continue; } /* Is this an ARP "reply"? */ if (response.opcode != 2) { LOG(2, "[-] arp: opcode=%u, not reply(2)\n", response.opcode); continue; } /* Is this response directed at us? */ if (response.ip_dst != my_ipv4) { LOG(2, "[-] arp: dst=%08x, not my ip 0x%08x\n", response.ip_dst, my_ipv4); continue; } if (memcmp(response.mac_dst, my_mac_address.addr, 6) != 0) continue; /* Is this the droid we are looking for? */ if (response.ip_src != your_ipv4) { ipaddress_formatted_t fmt1 = ipv4address_fmt(response.ip_src); ipaddress_formatted_t fmt2 = ipv4address_fmt(your_ipv4); LOG(2, "[-] arp: target=%s, not desired %s\n", fmt1.string, fmt2.string); continue; } /* * GOT IT! * we've got a valid response, so save the results and * return. */ memcpy(your_mac_address->addr, response.mac_src, 6); { ipaddress_formatted_t fmt1 = ipv4address_fmt(response.ip_src); ipaddress_formatted_t fmt2 = macaddress_fmt(*your_mac_address); LOG(1, "[+] arp: %s == %s\n", fmt1.string, fmt2.string); } return 0; } return 1; } /**************************************************************************** * Handle an incoming ARP request. ****************************************************************************/ int stack_arp_incoming_request( struct stack_t *stack, ipv4address_t my_ip, macaddress_t my_mac, const unsigned char *px, unsigned length) { struct PacketBuffer *response = 0; struct ARP_IncomingRequest request; memset(&request, 0, sizeof(request)); /* Get a buffer for sending the response packet. This thread doesn't * send the packet itself. Instead, it formats a packet, then hands * that packet off to a transmit thread for later transmission. */ response = stack_get_packetbuffer(stack); if (response == NULL) return -1; /* ARP packets are too short, so increase the packet size to * the Ethernet minimum */ response->length = 60; /* Fill the padded area with zeroes to avoid leaking data */ memset(response->px, 0, response->length); /* * Parse the response as an ARP packet */ proto_arp_parse(&request, px, 14, length); /* Is this an ARP packet? */ if (!request.is_valid) { LOG(2, "arp: etype=0x%04x, not ARP\n", px[12]*256 + px[13]); return -1; } /* Is this an ARP "request"? */ if (request.opcode != 1) { LOG(2, "arp: opcode=%u, not request(1)\n", request.opcode); return -1; } /* Is this response directed at us? */ if (request.ip_dst != my_ip) { LOG(2, "arp: dst=%08x, not my ip 0x%08x\n", request.ip_dst, my_ip); return -1; } /* * Create the response packet */ memcpy(response->px + 0, request.mac_src, 6); memcpy(response->px + 6, my_mac.addr, 6); memcpy(response->px + 12, "\x08\x06", 2); memcpy(response->px + 14, "\x00\x01" /* hardware = Ethernet */ "\x08\x00" /* protocol = IPv4 */ "\x06\x04" /* MAC length = 6, IPv4 length = 4 */ "\x00\x02" /* opcode = reply(2) */ , 8); memcpy(response->px + 22, my_mac.addr, 6); response->px[28] = (unsigned char)(my_ip >> 24); response->px[29] = (unsigned char)(my_ip >> 16); response->px[30] = (unsigned char)(my_ip >> 8); response->px[31] = (unsigned char)(my_ip >> 0); memcpy(response->px + 32, request.mac_src, 6); response->px[38] = (unsigned char)(request.ip_src >> 24); response->px[39] = (unsigned char)(request.ip_src >> 16); response->px[40] = (unsigned char)(request.ip_src >> 8); response->px[41] = (unsigned char)(request.ip_src >> 0); /* * Now queue the packet up for transmission */ stack_transmit_packetbuffer(stack, response); return 0; }