ethermess/ethertype-dump.c

150 lines
3.6 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 <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();
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 != 2) {
fprintf(stderr, "Usage: %s interface\n", argv[0]);
exit(1);
}
// 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, argv[1], 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");
}
for(;;) {
unsigned char frame[1518]; // 1518 is the largest a 802.3 frame can be without FCS (which we apparently don't get)
if (fflush(stdout) == -1) {
err(1, "fflush");
}
ssize_t frame_length = recv(packet_socket, frame, sizeof(frame), MSG_TRUNC);
if (frame_length == -1) {
err(1, "recv");
}
size_t frame_data_length = (size_t)frame_length < sizeof(frame) ? (size_t)frame_length : sizeof(frame);
frame_length += 4; // Include the FCS in the true length
if (frame_data_length < 14) {
if (printf("Frame too short to contain header (%zdB)\n", frame_length) == -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";
}
if(printf("%s -> %s ethertype: %04" PRIx16, source_mac, destination_mac, ethertype) == -1) {
err(1, "printf");
}
if (ethertype_meaning == NULL) {
if (printf(", length: %zd", frame_length) == -1) {
err(1, "printf");
}
} else {
if (printf(" (%s), length: %zd", ethertype_meaning, frame_length) == -1) {
err(1, "printf");
}
}
if ((size_t)frame_length > sizeof(frame) + 4) { // +4 for the FCS
if (printf(" (overlong)\n") == -1) {
err(1, "printf");
}
} else {
if (printf("\n") == -1) {
err(1, "printf");
}
}
}
return 0;
}