基于C++实现linux环境网卡抓包和流量统计分析
Packet定义
#include <stdint.h>
#include <netinet/in.h>
enum Direction {
UNKNOWN = -1,
INCOMING = 0, // Recv packet
OUTGOING = 1, // Send packet
};
struct Packet {
uint32_t src_ip;
uint32_t des_ip;
uint16_t src_port;
uint16_t des_port;
uint32_t size;
uint8_t protocol;
Direction direction;
};
获取网卡的MAC地址
#include <fstream>
constexpr uint8_t MAC_IP_STR_LEN = 17;
uint8_t hex_to_decimal(char h) {
if (h >= '0' && h <= '9') {
return h - '0';
}
if (h >= 'a' && h <= 'f') {
return h - 'a' + 10;
}
return -1;
}
uint64_t mac_to_int64(const char* mac_ip) {
uint64_t res = 0;
for (uint8_t i = 0; i < MAC_IP_STR_LEN; ++i) {
if (mac_ip[i] == ':') {
continue;
}
res <<= 4;
uint8_t d = hex_to_decimal(mac_ip[i]);
if (d == -1) {
return -1;
}
res += d;
}
return res;
}
uint64_t get_mac_address(const std::string& device) {
std::string file_path = "/sys/class/net/" + device + "/address";
std::fstream fs(file_path, std::ios::in);
std::string res;
char buffer[128];
fs.getline(buffer, 128);
return mac_to_int64(buffer);
}
数据包处理和格式转换
#include <sys/types.h>
#include <functional>
#define ETHERTYPE_PUP 0x0200
#define ETHERTYPE_IP 0x0800
#define ETHERTYPE_ARP 0x0806
#define ETHERTYPE_REVARP 0x8035
#define ETHER_ADDR_LEN 6
#ifndef ETHERTYPE_8021Q
#define ETHERTYPE_8021Q 0x8100
#endif
#ifndef ETHERTYPE_IPV6
#define ETHERTYPE_IPV6 0x86dd
#endif
typedef std::function<bool(const uint8_t*, Packet*, uint32_t, uint64_t)> HandlePacket;
struct ether_header {
u_int8_t ether_dhost[ETHER_ADDR_LEN];
u_int8_t ether_shost[ETHER_ADDR_LEN];
u_int16_t ether_type;
} __attribute__((packed));
struct vlan_8021q_header {
u_int16_t priority_cfi_vid;
u_int16_t ether_type;
};
uint64_t mac_to_int64_ntohs(const uint8_t* mac_ip) {
uint64_t res = 0;
for (int8_t i = 0; i < 6; ++i) {
res <<= 8;
res += mac_ip[i];
}
return res;
}
bool handle_ip_packet(const uint8_t* ip_packet, Packet* packet_info, uint32_t ip_addr, uint64_t mac_addr) {
// Parse IP header
const uint8_t* ip_header = ip_packet;
int ip_header_version = ((*ip_packet) & 0xF0) >> 4;
// Only record IPv4 packet
if (4 != ip_header_version) {
return false;
}
int ip_header_length = ((*ip_packet) & 0x0F) << 2;
// Get protocol type
uint8_t protocol = *(ip_header + 9);
if (protocol != IPPROTO_TCP && protocol != IPPROTO_UDP) {
return false;
}
packet_info->protocol = protocol;
// Get IP address
memcpy(&packet_info->src_ip, ip_header + 12, 4);
memcpy(&packet_info->des_ip, ip_header + 16, 4);
// Parse transport layer header
const uint8_t* transport_layer_header = ip_header + ip_header_length;
uint16_t src_port, des_port;
memcpy(&src_port, transport_layer_header, 2);
memcpy(&des_port, transport_layer_header + 2, 2);
packet_info->src_port = ntohs(src_port);
packet_info->des_port = ntohs(des_port);
// Get the direction of packet
if (packet_info->direction == Direction::UNKNOWN) {
if (ip_addr == packet_info->src_ip) {
// Packet leaving Network Interface Card
packet_info->direction = Direction::OUTGOING;
} else if (ip_addr == packet_info->des_ip) {
// Packet entering Network Interface Card
packet_info->direction = Direction::INCOMING;
}
}
return true;
}
bool handle_eth_packet(const uint8_t* eth_packet, Packet* packet_info, uint32_t ip_addr, uint64_t mac_addr) {
const ether_header* eptr;
int ether_type;
const uint8_t* payload;
eptr = reinterpret_cast<const ether_header*>(eth_packet);
ether_type = ntohs(eptr->ether_type);
payload = eth_packet + sizeof(ether_header);
if (ether_type == ETHERTYPE_8021Q) {
const vlan_8021q_header* vptr;
vptr = reinterpret_cast<const vlan_8021q_header*>(payload);
ether_type = ntohs(vptr->ether_type);
payload += sizeof(vlan_8021q_header);
}
// Get the direction of packet
if (ether_type == ETHERTYPE_IP || ether_type == ETHERTYPE_IPV6) {
uint64_t src_mac_addr = mac_to_int64_ntohs(eptr->ether_shost);
uint64_t des_mac_addr = mac_to_int64_ntohs(eptr->ether_dhost);
packet_info->direction = Direction::UNKNOWN;
if (mac_addr == src_mac_addr) {
// Packet leaving Network Interface Card
packet_info->direction = Direction::OUTGOING;
} else if (mac_addr == des_mac_addr) {
// Packet entering Network Interface Card
packet_info->direction = Direction::INCOMING;
}
}
return handle_ip_packet(payload, packet_info, ip_addr, mac_addr);
}
监控指定网卡
void Monitor() {
char error_buffer[PCAP_ERRBUF_SIZE];
std::string device = "eth0";
uint64_t mac_addr_ = get_mac_address(device);
uint32_t ip_addr_;
pcap_if_t* nic_list;
if (pcap_findalldevs(&nic_list, error_buffer)) {
return;
}
for (pcap_if_t* nic_cur = nic_list; nic_cur; nic_cur = nic_cur->next) {
if (nic_cur->name != device) {
continue;
}
for (pcap_addr_t* addr = nic_cur->addresses; addr; addr = addr->next) {
sockaddr* realaddr;
if (addr->addr) {
realaddr = addr->addr;
} else if (addr->dstaddr) {
realaddr = addr->dstaddr;
} else {
continue;
}
if (realaddr->sa_family == AF_INET) {
sockaddr_in* sin = reinterpret_cast<sockaddr_in*>(realaddr);
if (sin->sin_addr.s_addr) {
ip_addr_ = sin->sin_addr.s_addr;
}
}
}
}
pcap_t* device_handle_ = pcap_open_live(device.c_str(), // 网卡设备名
BUFSIZ, // 抓取个数 最大不超过65535
0, // 混合模式
1000, // 超时时间
error_buffer);
HandlePacket handle_packet_;
int datalink = pcap_datalink(device_handle_);
if (datalink == DLT_EN10MB) {
handle_packet_ = handle_eth_packet;
} else if (datalink == DLT_RAW) {
handle_packet_ = handle_ip_packet;
} else {
return;
}
for (;;) {
pcap_pkthdr pkthdr;
const u_char* cur_packet = pcap_next(device_handle_, &pkthdr);
if (nullptr == cur_packet) {
continue;
}
Packet packet;
handle_packet_(cur_packet, &packet, ip_addr_, mac_addr_);
// Analysis(packet)
}
}
其他功能:获取指定IP的域名
std::string uint32_to_ip(uint32_t ip_addr) {
char ipv4_str[64] = {'\0'};
inet_ntop(AF_INET, &ip_addr, ipv4_str, 64);
return std::string(ipv4_str);
}
std::string get_domain_name(uint32_t ip) {
sockaddr_in addr_dst;
char hostname[128] = {0};
char servername[128] = {0};
memset(&addr_dst, 0, sizeof(addr_dst));
addr_dst.sin_family = AF_INET;
addr_dst.sin_addr.s_addr = ip;
bool ret = getnameinfo(
(struct sockaddr*)&addr_dst, sizeof(addr_dst), hostname, sizeof(hostname), servername, sizeof(servername), 0);
std::string res;
if (!ret) {
res = hostname;
}
if (res.size() < 7) {
res = uint32_to_ip(ip);
}
return res;
}
其他功能:获取网卡的带宽上限
int32_t get_device_speed(const std::string& device) {
try {
std::string file_path = "/sys/class/net/" + device + "/speed";
std::fstream fs(file_path, std::ios::in);
std::string res;
char buffer[128];
fs.getline(buffer, 128);
return std::stoi(buffer);
} catch (...) {
}
return -1;
}
浙公网安备 33010602011771号