From 7486c135a84934f841c9cdd43adb2e7bf3c32081 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juhani=20Krekel=C3=A4?= Date: Fri, 5 Jul 2019 16:19:35 +0300 Subject: [PATCH] Add arp-request.c --- .gitignore | 1 + Makefile | 13 ++-- arp-request.c | 187 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 197 insertions(+), 4 deletions(-) create mode 100644 arp-request.c diff --git a/.gitignore b/.gitignore index 7a01446..fe9d2ff 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ *.swp *.o ethertype-dump +arp-request diff --git a/Makefile b/Makefile index 948d040..0d81d14 100644 --- a/Makefile +++ b/Makefile @@ -7,24 +7,29 @@ CFLAGS += -Os -g -Wall -Wextra -pedantic CPPFLAGS += LDFLAGS += +BINS := ethertype-dump arp-request + .SUFFIXES: .SUFFIXES: .c .o .PHONY: all install uninstall clean distclean -all: ethertype-dump +all: $(BINS) ethertype-dump: ethertype-dump.o $(CC) -o $@ $< $(LDFLAGS) +arp-request: arp-request.o + $(CC) -o $@ $< $(LDFLAGS) + .c.o: $(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $< -install: all - install ethertype-dump $(BINDIR) +install: $(BINS) + install $(BINS) $(BINDIR) uninstall: - rm $(BINDIR)/ethertype-dump + cd $(BINDIR) && rm $(BINS) clean: rm -f ethertype-dump *.o diff --git a/arp-request.c b/arp-request.c new file mode 100644 index 0000000..5932d5d --- /dev/null +++ b/arp-request.c @@ -0,0 +1,187 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +char hexify(int nybble) { + assert(0 <= nybble && nybble <= 16); + return "0123456789abcdef"[nybble]; +} + +void format_mac(const unsigned char binary_address[6], char formatted[18]) { + for (size_t i = 0; i < 6; i++) { + unsigned char byte = binary_address[i]; + formatted[3*i] = hexify(byte >> 4); + formatted[3*i + 1] = hexify(byte & 0xf); + formatted[3*i + 2] = ':'; + } + formatted[17] = '\0'; +} + +void drop_privileges(void) { + uid_t uid = getuid(); + gid_t gid = getgid(); + + errno = 0; + if (setresgid(gid, gid, gid) == -1) { + err(1, "setresgid"); + } + errno = 0; + if (setresuid(uid, uid, uid) == -1) { + err(1, "setresuid"); + } +} + +int main(int argc, char **argv) { + if (argc != 3) { + fprintf(stderr, "Usage: %s interface ip\n", argv[0]); + exit(1); + } + + const char *interface_name = argv[1]; + const char *target_ip = argv[2]; + + // Create a packet socket + errno = 0; + int packet_socket = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); + if (packet_socket == -1) { + err(1, "socket"); + } + + // Only creating the socket requires root privs + drop_privileges(); + + // Find the index of the network interface + struct ifreq ifr; + strncpy(ifr.ifr_name, interface_name, IFNAMSIZ); + errno = 0; + if (ioctl(packet_socket, SIOCGIFINDEX, &ifr) == -1) { + err(1, "ioctl"); + } + + // Bind to the network interface + struct sockaddr_ll sll; + sll.sll_family = AF_PACKET; + sll.sll_protocol = htons(ETH_P_ALL); + sll.sll_ifindex = ifr.ifr_ifindex; + errno = 0; + if (bind(packet_socket, (const struct sockaddr*)&sll, sizeof(sll)) == -1) { + err(1, "bind"); + } + + // Construct the Ethernet frame with ARP packet inside it + // + // NOTE: This uses network byte order. That is, most significant + // byte goes first, not last like with x86 + // + // -- Ethernet -- + // 0 Destination MAC (6B) == FF:FF:FF:FF:FF:FF (broadcast address) + // 6 Source MAC (6B) + // 12 EtherType (2B) == 0x0806 + // -- ARP -- + // 14 Hardware type (2B) == 0x0001 (Ethernet) + // 16 Protocol type (2B) == 0x0800 (IPv4 EtherType) + // 18 Hardware address length (1B) == 6 + // 19 Protocol address length (1B) == 4 + // 20 Opcode (2B) == 0x0001 (request) + // 22 Sender hardware address (6B for Ethernet) + // 28 Sender protocol address (4B for IPv4) + // 32 Target hardware address (6B for Ethernet) == FF:FF:FF:FF:FF:FF (the value is ignored) + // 38 Target protocol address (4B for IPv4) + // + // = 42B + unsigned char frame[42]; + memset(frame, 0, sizeof(frame)); + + // Destination MAC + memset(&frame[0], 0xff, 6); + + // Source MAC + errno = 0; + strncpy(ifr.ifr_name, interface_name, IFNAMSIZ); + if (ioctl(packet_socket, SIOCGIFHWADDR, &ifr) == -1) { + err(1, "ioctl"); + } + if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { + errx(1, "Not an Ethernet interface"); + } + memcpy(&frame[6], ifr.ifr_hwaddr.sa_data, 6); + + // EtherType + frame[12] = 0x08; + frame[13] = 0x06; + + // Hardware type + frame[14] = 0x00; + frame[15] = 0x01; + + // Protocol type + frame[16] = 0x08; + frame[17] = 0x00; + + // Hardware address length + frame[18] = 6; + + // Protocol address length + frame[19] = 4; + + // Opcode + frame[20] = 0x00; + frame[21] = 0x01; + + // Sender hardware address + // This is the same as source MAC in the Ethernet header + memcpy(&frame[22], &frame[6], 6); + + // Sender protocol address + errno = 0; + strncpy(ifr.ifr_name, interface_name, IFNAMSIZ); + if (ioctl(packet_socket, SIOCGIFADDR, &ifr) == -1) { + err(1, "ioctl"); + } + if (ifr.ifr_addr.sa_family != AF_INET) { + errx(1, "Somehow, we've gotten a non-IPv4 address from kernel"); + } + struct sockaddr_in sin; + memcpy(&sin, &ifr.ifr_addr, sizeof(sin)); + memcpy(&frame[28], &sin.sin_addr.s_addr, 4); + + // Target hardware address + memset(&frame[32], 0xff, 6); + + // Target protocol address + memset(&sin, 0, sizeof(sin)); + errno = 0; + if (inet_pton(AF_INET, target_ip, &sin.sin_addr) == -1) { + err(1, "inet_pton"); + } + memcpy(&frame[38], &sin.sin_addr.s_addr, 4); + + // Send the frame + errno = 0; + if (write(packet_socket, frame, sizeof(frame)) != sizeof(frame)) { + err(1, "write"); + } + + // Close the socket (tho I'm not 100% sure it's needed) + errno = 0; + if (close(packet_socket) == -1) { + err(1, "close"); + } + + return 0; +}