#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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; }