Add arp-request.c
This commit is contained in:
parent
e29fd159d3
commit
7486c135a8
3 changed files with 197 additions and 4 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,3 +1,4 @@
|
|||
*.swp
|
||||
*.o
|
||||
ethertype-dump
|
||||
arp-request
|
||||
|
|
13
Makefile
13
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
|
||||
|
|
187
arp-request.c
Normal file
187
arp-request.c
Normal 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;
|
||||
}
|
Loading…
Reference in a new issue