ethermess/arp-request.c

162 lines
3.8 KiB
C

#define _GNU_SOURCE
#include <arpa/inet.h>
#include <assert.h>
#include <err.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>
void drop_privileges(void) {
uid_t uid = getuid();
gid_t gid = getgid();
if (setresgid(gid, gid, gid) == -1) {
err(1, "setresgid");
}
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
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);
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;
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
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
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));
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
if (write(packet_socket, frame, sizeof(frame)) != sizeof(frame)) {
err(1, "write");
}
// Close the socket (tho I'm not 100% sure it's needed)
if (close(packet_socket) == -1) {
err(1, "close");
}
return 0;
}