Last active
November 14, 2019 02:40
-
-
Save nuew/f50160a14f97532369ada78fbf5b9e3d to your computer and use it in GitHub Desktop.
arp/rarp debugging utility
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include<assert.h> | |
#include<errno.h> | |
#include<getopt.h> | |
#include<linux/if_packet.h> | |
#include<net/ethernet.h> | |
#include<net/if.h> | |
#include<net/if_arp.h> | |
#include<netinet/ip.h> | |
#include<stdbool.h> | |
#include<stdint.h> | |
#include<stdio.h> | |
#include<stdlib.h> | |
#include<string.h> | |
#include<sys/ioctl.h> | |
#include<sys/socket.h> | |
#include<unistd.h> | |
#define atons(string) htons((uint16_t) atoi(string)) | |
#define die_main(string) { perror(string); return EXIT_FAILURE; } | |
struct txarp_cfg { | |
const struct arphdr *arphdr; | |
struct sockaddr_ll *sockaddr_ll; | |
struct ifreq *ifreq; | |
}; | |
bool txarp(int fd, struct txarp_cfg txarp_cfg) { | |
const size_t queryoff = sizeof(struct arphdr); | |
const size_t targetoff = queryoff + txarp_cfg.arphdr->ar_hln + txarp_cfg.arphdr->ar_pln; | |
size_t addrslen = 2 * (txarp_cfg.arphdr->ar_hln + txarp_cfg.arphdr->ar_pln); | |
size_t pktlen = sizeof(txarp_cfg.arphdr) + addrslen; | |
uint8_t arppkt[pktlen]; | |
// retreive addresses, interface index, etc., from ioctls | |
if(ioctl(fd, SIOCGIFINDEX, txarp_cfg.ifreq) == -1) return false; | |
txarp_cfg.sockaddr_ll->sll_ifindex = txarp_cfg.ifreq->ifr_ifindex; | |
if(ioctl(fd, SIOCGIFHWADDR, txarp_cfg.ifreq) == -1) return false; | |
assert(txarp_cfg.ifreq->ifr_hwaddr.sa_family == ntohs(txarp_cfg.arphdr->ar_hrd)); | |
memcpy(arppkt + queryoff, txarp_cfg.ifreq->ifr_hwaddr.sa_data, | |
txarp_cfg.arphdr->ar_hln); | |
memcpy(arppkt + targetoff, txarp_cfg.ifreq->ifr_hwaddr.sa_data, | |
txarp_cfg.arphdr->ar_hln); | |
// FIXME this is more or less hardcoded for ipv4 rarp requests because laziness | |
txarp_cfg.sockaddr_ll->sll_halen = ETH_ALEN; | |
memset(txarp_cfg.sockaddr_ll->sll_addr, 0xFF, ETH_ALEN); | |
memset(arppkt + queryoff + txarp_cfg.arphdr->ar_hln, 0, txarp_cfg.arphdr->ar_pln); | |
memset(arppkt + targetoff + txarp_cfg.arphdr->ar_hln, 0, txarp_cfg.arphdr->ar_pln); | |
// send [r]arp request | |
memcpy(arppkt, txarp_cfg.arphdr, sizeof(struct arphdr)); | |
const struct sockaddr *sockaddr = (const struct sockaddr *) txarp_cfg.sockaddr_ll; | |
if(sendto(fd, &arppkt, pktlen, 0, sockaddr, sizeof(struct sockaddr_ll)) == -1) { | |
return false; | |
} | |
return true; | |
} | |
#define OPTIONS "ad:e:Ehi:lo:p:rRxyY46" | |
static const struct option LONG_OPTIONS[] = { | |
{ "arp", false, NULL, 'a' }, | |
{ "ethernet", false, NULL, 'E' }, | |
{ "ethertype", true, NULL, 'e' }, | |
{ "hardware", true, NULL, 'd' }, | |
{ "help", false, NULL, 'h' }, | |
{ "interface", true, NULL, 'i' }, | |
{ "ipv4", false, NULL, '4' }, | |
{ "ipv6", false, NULL, '6' }, | |
{ "operation", true, NULL, 'o' }, | |
{ "protocol", true, NULL, 'p' }, | |
{ "rarp", false, NULL, 'r' }, | |
{ "request", false, NULL, 'R' }, | |
{ "reply", false, NULL, 'y' }, | |
{ "reverse", false, NULL, 'x' }, | |
{ "reverse-reply", false, NULL, 'Y' }, | |
{ NULL, 0, NULL, 0 } | |
}; | |
const char USAGE[] = | |
"ARP TOOLS (HELP)\n" | |
"\t-h --help\t\t\t\tthis documentation\n" | |
"\nHARDWARE\n" | |
"\t-E --ethernet\t\t\t\tuse ethernet hardware address type\n" | |
"\t-d --hardware HARDWARE_ADDR_TYPE:LEN\tset hardware address type and length\n" | |
"\nOPERATION\n" | |
"\t-o --operation OPERATION\t\tARP opcode\n" | |
"\t-R --request\t\t\t\tARP request\n" | |
"\t-x --reverse\t\t\t\tRARP reverse request\n" | |
"\t-y --reply\t\t\t\tARP reply\n" | |
"\t-Y --reverse-reply\t\t\tRARP reverse reply\n" | |
"\nPROTOCOL\n" | |
"\t-4 --ipv4\t\t\t\tuse ipv4 protocol address\n" | |
"\t-6 --ipv6\t\t\t\tuse ipv6 protocol address\n" | |
"\t-p --protocol PROTOCOL_ADDR_TYPE:LEN\tset protocol address type and length\n" | |
"\nTRANSMIT PROTOCOL\n" | |
"\t-a --arp\t\t\t\tsend over ARP ethertype\n" | |
"\t-e --ethertype ETHERTYPE\t\tset ethertype\n" | |
"\t-r --rarp\t\t\t\tsend over RARP ethertype\n" | |
"\nINTERFACE\n" | |
"\t-i --interface INTERFACE_NAME\t\ttx interface name\n"; | |
#define INIT_HRD (0x01) | |
#define INIT_OP (0x02) | |
#define INIT_PRO (0x04) | |
#define INIT_XPROTO (0x08) | |
#define INIT_IF (0x10) | |
#define INIT_FULL (INIT_HRD | INIT_OP | INIT_PRO | INIT_XPROTO | INIT_IF) | |
int main(int argc, char **argv) { | |
int fd, option; | |
struct arphdr arphdr; | |
struct ifreq ifreq; | |
struct sockaddr_ll sockaddr_ll = { .sll_family = AF_PACKET }; | |
uint8_t initted = 0; | |
struct txarp_cfg txarp_cfg = { | |
.sockaddr_ll = &sockaddr_ll, | |
.arphdr = &arphdr, | |
.ifreq = &ifreq | |
}; | |
// parse options | |
while((option = getopt_long(argc, argv, OPTIONS, LONG_OPTIONS, NULL)) != -1) { | |
switch(option) { | |
case 'a': | |
sockaddr_ll.sll_protocol = htons(ETH_P_ARP); | |
initted |= INIT_XPROTO; | |
break; | |
case 'd': { | |
char *ar_hrd = strtok(optarg, ":"); | |
char *ar_hln = strtok(NULL, ":"); | |
if(ar_hrd != NULL && ar_hln != NULL) { | |
arphdr.ar_hrd = atons(ar_hrd); | |
arphdr.ar_hln = (uint8_t) atoi(ar_hln); | |
initted |= INIT_HRD; | |
} else fprintf(stderr, "bad hardware: %s\n", optarg); | |
} break; | |
case 'e': | |
sockaddr_ll.sll_protocol = atons(optarg); | |
initted |= INIT_XPROTO; | |
break; | |
case 'E': | |
arphdr.ar_hrd = htons(ARPHRD_ETHER); | |
arphdr.ar_hln = ETH_ALEN; | |
initted |= INIT_HRD; | |
break; | |
case 'h': | |
puts(USAGE); | |
return EXIT_SUCCESS; | |
case 'i': { | |
size_t iflen = strlen(optarg); | |
if(iflen < IFNAMSIZ) { | |
strncpy(ifreq.ifr_name, optarg, IFNAMSIZ); | |
initted |= INIT_IF; | |
} else { | |
fprintf(stderr, "interface name too long\n"); | |
return EXIT_FAILURE; | |
} | |
} break; | |
case 'r': | |
sockaddr_ll.sll_protocol = htons(ETH_P_RARP); | |
initted |= INIT_XPROTO; | |
break; | |
case 'R': | |
arphdr.ar_op = htons(ARPOP_REQUEST); | |
initted |= INIT_OP; | |
break; | |
case 'o': | |
arphdr.ar_op = atons(optarg); | |
initted |= INIT_OP; | |
break; | |
case 'p': { | |
char *ar_pro = strtok(optarg, ":"); | |
char *ar_pln = strtok(NULL, ":"); | |
if(ar_pro != NULL && ar_pln != NULL) { | |
arphdr.ar_pro = atons(ar_pro); | |
arphdr.ar_pln = (uint8_t) atoi(ar_pln); | |
initted |= INIT_PRO; | |
} else fprintf(stderr, "bad protocol: %s\n", optarg); | |
} break; | |
case 'x': | |
arphdr.ar_op = htons(ARPOP_RREQUEST); | |
initted |= INIT_OP; | |
break; | |
case 'y': | |
arphdr.ar_op = htons(ARPOP_REPLY); | |
initted |= INIT_OP; | |
break; | |
case 'Y': | |
arphdr.ar_op = htons(ARPOP_RREPLY); | |
initted |= INIT_OP; | |
break; | |
case '4': | |
arphdr.ar_pro = htons(ETH_P_IP); | |
arphdr.ar_pln = sizeof(struct in_addr); | |
initted |= INIT_PRO; | |
break; | |
case '6': | |
arphdr.ar_pro = htons(ETH_P_IPV6); | |
arphdr.ar_pln = sizeof(struct in6_addr); | |
initted |= INIT_PRO; | |
break; | |
default: | |
return EXIT_FAILURE; | |
} | |
} | |
// open tx/rx raw socket | |
if((fd = socket(AF_PACKET, SOCK_DGRAM, sockaddr_ll.sll_protocol)) == -1) { | |
die_main("couldn't create raw socket"); | |
} | |
// ensure everything is configured | |
if(initted != INIT_FULL) { | |
fputs("required configuration omitted\n", stderr); | |
return EXIT_FAILURE; | |
} | |
// transmit arp request | |
if(!txarp(fd, txarp_cfg)) { | |
die_main("couldn't transmit [R]ARP request"); | |
} | |
if(close(fd) == -1) die_main("error closing raw socket"); | |
return EXIT_SUCCESS; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment