173 lines
3.9 KiB
C
173 lines
3.9 KiB
C
#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 <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>
|
|
|
|
uint32_t crc32_table[256];
|
|
|
|
void precompute_crc32_table(void) {
|
|
for (size_t index = 0; index < 256; index++) {
|
|
uint32_t value = index;
|
|
|
|
for (size_t i = 0; i < 8; i++) {
|
|
uint32_t low_bit = value & 1;
|
|
value = value >> 1;
|
|
if (low_bit) {
|
|
value ^= 0xEDB88320;
|
|
}
|
|
}
|
|
|
|
crc32_table[index] = value;
|
|
}
|
|
}
|
|
|
|
uint32_t crc32(unsigned char *buf, size_t len) {
|
|
uint32_t value = 0xffffffff;
|
|
|
|
for (size_t index = 0; index < len; index++) {
|
|
value = crc32_table[(value & 0xff) ^ buf[index]] ^ (value >> 8);
|
|
}
|
|
|
|
return value ^ 0xffffffff;
|
|
}
|
|
|
|
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';
|
|
}
|
|
|
|
int main(int argc, char **argv) {
|
|
if (argc != 2) {
|
|
fprintf(stderr, "Usage: %s interface\n", argv[0]);
|
|
exit(1);
|
|
}
|
|
|
|
// Set up CRC32
|
|
precompute_crc32_table();
|
|
|
|
// 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");
|
|
}
|
|
|
|
// Find the index of the network interface
|
|
struct ifreq ifr;
|
|
strncpy(ifr.ifr_name, argv[1], 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");
|
|
}
|
|
|
|
for(;;) {
|
|
unsigned char frame[1522]; // 1522 is the largest a 802.3 frame can be
|
|
|
|
if (fflush(stdout) == -1) {
|
|
err(1, "fflush");
|
|
}
|
|
|
|
errno = 0;
|
|
ssize_t frame_length = recv(packet_socket, frame, sizeof(frame), MSG_TRUNC);
|
|
if (frame_length == -1) {
|
|
err(1, "recv");
|
|
}
|
|
size_t frame_data_length = frame_length <= 1522 ? frame_length : 1522;
|
|
|
|
if (frame_length < 64) {
|
|
errno = 0;
|
|
if (printf("Runt frame (%zdB)\n", frame_length) == -1) {
|
|
err(1, "printf");
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// Compute the CRC32 of the frame
|
|
uint32_t crc32_residue = crc32(frame, frame_data_length);
|
|
|
|
if (crc32_residue != 0xc704dd7b) {
|
|
errno = 0;
|
|
if (printf("CRC failed (%04"PRIx32")\n", crc32_residue) == -1) {
|
|
err(1, "printf");
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// Extract the MACs
|
|
// 012345 012345
|
|
// dest source
|
|
char destination_mac[18], source_mac[18];
|
|
format_mac(&frame[0], destination_mac);
|
|
format_mac(&frame[6], source_mac);
|
|
|
|
// Extract EtherType / length field
|
|
// 012345 012345 01
|
|
// dest source ^^
|
|
// It is stored in the network byte order, that is, high byte 1st
|
|
uint16_t ethertype = (frame[12] << 8) | frame[13];
|
|
|
|
const char *ethertype_meaning = NULL;
|
|
if (ethertype <= 1500) {
|
|
ethertype_meaning = "packet length";
|
|
} else if (ethertype < 0x0600) {
|
|
ethertype_meaning = "undefined";
|
|
} else if (ethertype == 0x0800) {
|
|
ethertype_meaning = "IPv4";
|
|
} else if (ethertype == 0x0806) {
|
|
ethertype_meaning = "ARP";
|
|
} else if (ethertype == 0x86dd) {
|
|
ethertype_meaning = "IPv6";
|
|
}
|
|
|
|
errno = 0;
|
|
if(printf("%s -> %s ethertype: %04" PRIx16, source_mac, destination_mac, ethertype) == -1) {
|
|
err(1, "printf");
|
|
}
|
|
|
|
errno = 0;
|
|
if (ethertype_meaning == NULL) {
|
|
if (printf(", length: %zd\n", frame_length) == -1) {
|
|
err(1, "printf");
|
|
}
|
|
} else {
|
|
if (printf(" (%s), length: %zd\n", ethertype_meaning, frame_length) == -1) {
|
|
err(1, "printf");
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|