-
-
Save fbion/1db55d74e5a2cad65f4fe85c2b90d2aa to your computer and use it in GitHub Desktop.
Capture packets from bpf devices on macOS.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdlib.h> | |
#include <stdio.h> | |
#include <fcntl.h> | |
#include <sys/uio.h> | |
#include <unistd.h> | |
#include <string.h> | |
#include <sys/errno.h> | |
#include <sys/types.h> | |
#include <sys/ioctl.h> | |
#include <net/bpf.h> | |
#include <netinet/in.h> | |
#include <net/if.h> | |
#include "bpf.h" | |
void print_bpf_options(BpfOption option) | |
{ | |
fprintf(stderr, "BpfOption:\n"); | |
fprintf(stderr, " BPF Device: %s\n", option.deviceName); | |
fprintf(stderr, " Network Interface: %s\n", option.interfaceName); | |
fprintf(stderr, " Buffer Length: %d\n", option.bufferLength); | |
} | |
void print_bpf_sniffer_params(BpfSniffer sniffer) | |
{ | |
fprintf(stderr, "BpfSniffer:\n"); | |
fprintf(stderr, " Opened BPF Device: %s\n", sniffer.deviceName); | |
fprintf(stderr, " Buffer Length: %d\n", sniffer.bufferLength); | |
} | |
int pick_bpf_device(BpfSniffer *sniffer) | |
{ | |
char dev[11] = {0}; | |
for (int i = 0; i < 99; ++i) { | |
sprintf(dev, "/dev/bpf%i", i); | |
sniffer->fd = open(dev, O_RDWR); | |
if (sniffer->fd != -1) { | |
strcpy(sniffer->deviceName, dev); | |
return 0; | |
} | |
} | |
return -1; | |
} | |
int new_bpf_sniffer(BpfOption option, BpfSniffer *sniffer) | |
{ | |
if (strlen(option.deviceName) == 0) { | |
if (pick_bpf_device(sniffer) == -1) | |
return -1; | |
} else { | |
sniffer->fd = open(option.deviceName, O_RDWR); | |
if (sniffer->fd != -1) | |
return -1; | |
} | |
if (option.bufferLength == 0) { | |
/* Get Buffer Length */ | |
if (ioctl(sniffer->fd, BIOCGBLEN, &sniffer->bufferLength) == -1) { | |
perror("ioctl BIOCGBLEN"); | |
return -1; | |
} | |
} else { | |
/* Set Buffer Length */ | |
/* The buffer must be set before the file is attached to an interface with BIOCSETIF. */ | |
if (ioctl(sniffer->fd, BIOCSBLEN, &option.bufferLength) == -1) { | |
perror("ioctl BIOCSBLEN"); | |
return -1; | |
} | |
sniffer->bufferLength = option.bufferLength; | |
} | |
struct ifreq interface; | |
strcpy(interface.ifr_name, option.interfaceName); | |
if(ioctl(sniffer->fd, BIOCSETIF, &interface) > 0) { | |
perror("ioctl BIOCSETIF"); | |
return -1; | |
} | |
unsigned int enable = 1; | |
if (ioctl(sniffer->fd, BIOCIMMEDIATE, &enable) == -1) { | |
perror("ioctl BIOCIMMEDIATE"); | |
return -1; | |
} | |
if (ioctl(sniffer->fd, BIOCPROMISC, NULL) == -1) { | |
perror("ioctl BIOCPROMISC"); | |
return -1; | |
} | |
sniffer->readBytesConsumed = 0; | |
sniffer->lastReadLength = 0; | |
sniffer->buffer = malloc(sizeof(char) * sniffer->bufferLength); | |
return 0; | |
} | |
int read_bpf_packet_data(BpfSniffer *sniffer, CapturedInfo *info) | |
{ | |
struct bpf_hdr *bpfPacket; | |
if (sniffer->readBytesConsumed + sizeof(sniffer->buffer) >= sniffer->lastReadLength) { | |
sniffer->readBytesConsumed = 0; | |
memset(sniffer->buffer, 0, sniffer->bufferLength); | |
ssize_t lastReadLength = read(sniffer->fd, sniffer->buffer, sniffer->bufferLength); | |
if (lastReadLength == -1) { | |
sniffer->lastReadLength = 0; | |
perror("read bpf packet:"); | |
return -1; | |
} | |
sniffer->lastReadLength = (unsigned int) lastReadLength; | |
} | |
bpfPacket = (struct bpf_hdr*)((long)sniffer->buffer + (long)sniffer->readBytesConsumed); | |
info->data = sniffer->buffer + (long)sniffer->readBytesConsumed + bpfPacket->bh_hdrlen; | |
sniffer->readBytesConsumed += BPF_WORDALIGN(bpfPacket->bh_hdrlen + bpfPacket->bh_caplen); | |
return bpfPacket->bh_datalen; | |
} | |
int close_bpf_sniffer(BpfSniffer *sniffer) | |
{ | |
free(sniffer->buffer); | |
if (close(sniffer->fd) == -1) | |
return -1; | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef _PCAP_BPF | |
#define _PCAP_BPF | |
typedef struct { | |
char deviceName[11]; | |
char interfaceName[16]; | |
unsigned int bufferLength; | |
} BpfOption; | |
typedef struct { | |
int fd; | |
char deviceName[11]; | |
unsigned int bufferLength; | |
unsigned int lastReadLength; | |
unsigned int readBytesConsumed; | |
char *buffer; | |
} BpfSniffer; | |
typedef struct { | |
char *data; | |
} CapturedInfo; | |
void print_bpf_options(BpfOption option); | |
void print_bpf_sniffer_params(BpfSniffer sniffer); | |
int new_bpf_sniffer(BpfOption option, BpfSniffer *sniffer); | |
int read_bpf_packet_data(BpfSniffer *sniffer, CapturedInfo *info); | |
int close_bpf_sniffer(BpfSniffer *sniffer); | |
#endif /* _PCAP_BPF */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdlib.h> | |
#include <stdio.h> | |
#include <fcntl.h> | |
#include <sys/uio.h> | |
#include <unistd.h> | |
#include <string.h> | |
#include <sys/errno.h> | |
#include <sys/types.h> | |
#include <sys/ioctl.h> | |
#include <net/bpf.h> | |
#include <netinet/tcp.h> | |
#include <netinet/ip.h> | |
#include <netinet/if_ether.h> | |
#include <netinet/in.h> | |
#include <net/if.h> | |
#include <arpa/inet.h> | |
#include "bpf.h" | |
int main() | |
{ | |
BpfOption option; | |
strcpy(option.interfaceName, "en0"); | |
option.bufferLength = 32767; | |
print_bpf_options(option); | |
BpfSniffer sniffer; | |
if (new_bpf_sniffer(option, &sniffer) == -1) | |
return 1; | |
print_bpf_sniffer_params(sniffer); | |
CapturedInfo info; | |
int dataLength; | |
while((dataLength = read_bpf_packet_data(&sniffer, &info)) != -1) { | |
printf("--------------------------------------------------\n"); | |
printf("Payload length: %d\n", dataLength); | |
struct ether_header* eh = (struct ether_header*)info.data; | |
printf(" Ethernet Frame\n"); | |
printf(" src mac address: %x:%x:%x:%x:%x:%x\n", | |
eh->ether_shost[0], | |
eh->ether_shost[1], | |
eh->ether_shost[2], | |
eh->ether_shost[3], | |
eh->ether_shost[4], | |
eh->ether_shost[5]); | |
printf(" dst mac address: %x:%x:%x:%x:%x:%x\n", | |
eh->ether_dhost[0], | |
eh->ether_dhost[1], | |
eh->ether_dhost[2], | |
eh->ether_dhost[3], | |
eh->ether_dhost[4], | |
eh->ether_dhost[5]); | |
if (ntohs(eh->ether_type) == ETHERTYPE_IP) { | |
printf(" type: IPv4, %x\n", eh->ether_type); | |
struct ip* ip = (struct ip*)((long)eh + sizeof(struct ether_header)); | |
printf(" IP Frame\n"); | |
printf(" headerLength: %d\n", ip->ip_hl * 4); | |
printf(" version: %d\n", ip->ip_v); | |
printf(" protocol: %d\n", ip->ip_p); | |
printf(" ttl: %d\n", ip->ip_ttl); | |
printf(" dst ip: %s\n", inet_ntoa(ip->ip_dst)); | |
printf(" src ip: %s\n", inet_ntoa(ip->ip_src)); | |
if (ip->ip_p == IPPROTO_TCP) { | |
struct tcphdr* tcp = (struct tcphdr*)((long)ip + (ip->ip_hl * 4)); | |
printf(" TCP Packet\n"); | |
printf(" dst port: %d\n", tcp->th_dport); | |
printf(" src port: %d\n", tcp->th_sport); | |
} | |
} else { | |
printf(" type: Other, %x\n", eh->ether_type); | |
} | |
} | |
close_bpf_sniffer(&sniffer); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment