基于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;
}
posted on 2025-08-23 22:19  umichan  阅读(91)  评论(0)    收藏  举报