Skip to content

Instantly share code, notes, and snippets.

@nuew
Last active November 14, 2019 02:40
Show Gist options
  • Save nuew/f50160a14f97532369ada78fbf5b9e3d to your computer and use it in GitHub Desktop.
Save nuew/f50160a14f97532369ada78fbf5b9e3d to your computer and use it in GitHub Desktop.
arp/rarp debugging utility
#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