363 lines
11 KiB
C
363 lines
11 KiB
C
|
/*
|
||
|
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;
|
||
|
}
|