Add arp-request.c

This commit is contained in:
Juhani Krekelä 2019-07-05 16:19:35 +03:00
parent e29fd159d3
commit 7486c135a8
3 changed files with 197 additions and 4 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
*.swp
*.o
ethertype-dump
arp-request

View File

@ -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

187
arp-request.c Normal file
View File

@ -0,0 +1,187 @@
#define _GNU_SOURCE
#include <arpa/inet.h>
#include <assert.h>
#include <err.h>
#include <errno.h>
#include <inttypes.h>
#include <linux/if_packet.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
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;
}