Linux开发数据包嗅探器:如何通过结构体与原始套接字获取MAC地址?
Hey there! Let's walk through how to correctly extract and print source/destination MAC addresses in your Linux packet sniffer—your initial approach using struct ether_header is totally on the right track, so let's flesh it out properly.
The Core Idea: Using struct ether_header
The <net/ethernet.h> header defines struct ether_header, which maps exactly to the 14-byte Ethernet frame header. It has three key fields:
ether_dhost[6]: 6-byte destination MAC addressether_shost[6]: 6-byte source MAC addressether_type: 2-byte field indicating the upper-layer protocol (e.g., IP, ARP)
Full Working Code Example
Here's a complete, error-handled version of your sniffer that properly prints MAC addresses in standard format:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <net/ethernet.h> #include <arpa/inet.h> int main() { int sock_fd; unsigned char packet_buf[1024]; struct sockaddr_ll sock_addr; socklen_t addr_len = sizeof(sock_addr); // Create raw socket to capture all Ethernet frames sock_fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); if (sock_fd == -1) { perror("Failed to create raw socket"); exit(EXIT_FAILURE); } printf("Sniffing packets (press Ctrl+C to stop)...\n\n"); // Continuously capture and process packets while (1) { ssize_t bytes_read = recvfrom( sock_fd, packet_buf, sizeof(packet_buf), 0, (struct sockaddr *)&sock_addr, &addr_len ); if (bytes_read == -1) { perror("Failed to receive packet"); close(sock_fd); exit(EXIT_FAILURE); } // Cast buffer to Ethernet header structure struct ether_header *eth_header = (struct ether_header *)packet_buf; // Print Destination MAC (use %02x to ensure 2-digit hex with leading zeros) printf("Destination MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", eth_header->ether_dhost[0], eth_header->ether_dhost[1], eth_header->ether_dhost[2], eth_header->ether_dhost[3], eth_header->ether_dhost[4], eth_header->ether_dhost[5]); // Print Source MAC printf("Source MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", eth_header->ether_shost[0], eth_header->ether_shost[1], eth_header->ether_shost[2], eth_header->ether_shost[3], eth_header->ether_shost[4], eth_header->ether_shost[5]); // Optional: Print upper-layer protocol type (convert from network to host byte order) printf("EtherType: 0x%04x\n\n", ntohs(eth_header->ether_type)); } close(sock_fd); return 0; }
Key Details to Note
%02xvs%x: Using%02xensures each byte is printed as two hexadecimal digits (e.g.,0ainstead ofa), which matches the standard MAC address format (AA:BB:CC:DD:EE:FF).- Raw Socket Permissions: You must run this program as root (use
sudo ./your_sniffer), since creating raw sockets requires theCAP_NET_RAWprivilege. - EtherType Conversion: The
ether_typefield is stored in network byte order (big-endian), so we usentohs()to convert it to host byte order before printing. - Filtering Packets: If you don't want to capture all frames, replace
ETH_P_ALLwith a specific protocol type (e.g.,htons(ETH_P_IP)for IP packets only).
Compilation & Execution
Compile with gcc:
gcc sniffer.c -o sniffer
Run with root privileges:
sudo ./sniffer
内容的提问来源于stack exchange,提问作者Krea
相关产品推荐
相关产品推荐

