Capture Packets Using C
In this article, we delve into a C code example that demonstrates the fundamentals of network packet capturing, parsing, and flow analysis. The code leverages the powerful libpcap library to capture network packets in real-time, extracting essential information such as IP addresses, port numbers, and protocol types. By organizing these packets into network flows, the code offers insights into the traffic patterns within a network. This exploration showcases the foundation of network monitoring and serves as an educational piece for those interested in understanding the inner workings of packet-level analysis and the intricacies of network communication.
#include <stdio.h>
#include <pcap.h>
#include <netinet/ip.h>
#include <netinet/ether.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <arpa/inet.h>
Here you're including necessary header files for standard I/O operations (stdio.h), capturing network packets (pcap.h), handling IP headers (netinet/ip.h), Ethernet headers (netinet/ether.h), TCP headers (netinet/tcp.h), UDP headers (netinet/udp.h), and IP address conversion (arpa/inet.h).
#define MAX_FLOWS 1000
#define MAX_PACKETS 1000
You're defining constants for the maximum number of flows and packets to be processed.
typedef struct {
??struct in_addr src_ip;
??struct in_addr dst_ip;
??uint16_t src_port;
??uint16_t dst_port;
??int packet_count;
??char protocol;
} FlowInfo;
You're defining a structure FlowInfo to store information about a network flow, including source and destination IP addresses, port numbers, packet count, and the protocol used.
FlowInfo flows[MAX_FLOWS];
int flow_count = 0;
int packet_count = 0;
char protocol = 'O';
int tcp_flow = 0;
int udp_flow = 0;
pcap_dumper_t *pcap_dumper;
You're declaring variables to store information about flows, packet counts, protocols, and network interfaces. pcap_dumper_t is a type used for writing packets to a PCAP file.
int find_flow_index(const struct in_addr src_ip, const struct in_addr dst_ip, const uint16_t src_port, const uint16_t dst_port) {
??for (int i= 0; i < flow_count; i++) {
??if ((flows[i].src_ip.s_addr == src_ip.s_addr && flows[i].dst_ip.s_addr == dst_ip.s_addr && flows[i].src_port == src_port && flows[i].dst_port == dst_port) ||?
????(flows[i].src_ip.s_addr == dst_ip.s_addr && flows[i].dst_ip.s_addr == src_ip.s_addr && flows[i].src_port == dst_port && flows[i].dst_port == src_port))
????{
??????return i;
????}
??}
??return -1;
}
You're defining a function find_flow_index that searches for a specific network flow based on source and destination IP addresses, and source and destination ports.
void packet_handler(unsigned char *user_data, const struct pcap_pkthdr *pkthdr, const unsigned char *packet) {
??printf("Packet captured!\n");
??printf("Timestamp: %ld.%06ld\n", (long)pkthdr>ts.tv_sec, (long)pkthdr->ts.tv_usec);
??printf("Packet length: %d\n", pkthdr->len);
??const struct ethhdr *eth_header?= (struct ethhdr *)packet;
??const struct ip *ip_header = (struct ip *)(packet + sizeof(struct ethhdr));
??char src_ip[INET_ADDRSTRLEN];
??char dst_ip[INET_ADDRSTRLEN];
??inet_ntop(AF_INET, &(ip_header->ip_src), src_ip, INET_ADDRSTRLEN);
??inet_ntop(AF_INET, &(ip_header->ip_dst), dst_ip, INET_ADDRSTRLEN);
??printf("Source IP: %s\n", src_ip);
??printf("Destination Ip: %s\n", dst_ip);
??if (ip_header->ip_p == IPPROTO_TCP) {
????const struct tcphdr *tcp_header = (struct tcphdr *)(packet + sizeof(struct ethhdr) + ip_header->ip_hl * 4);
????printf("Source Port: %d\n", ntohs(tcp_header >th_sport));
????printf("Destination Port: %d\n", ntohs(tcp_header->th_dport));
????printf("TCP Flags: 0x%02X\n", tcp_header-> th_flags);
????printf("Protocol: TCP\n");
????int flow_index = find_flow_index(ip_header->ip_src, ip_header->ip_dst, ntohs(tcp_header->th_sport), ntohs(tcp_header->th_dport));
????if (flow_index == -1) {
??????if (flow_count <MAX_FLOWS) {
????????flows[flow_count].src_ip = ip_header->ip_src;
????????flows[flow_count].dst_ip = ip_header->ip_dst;
????????flows[flow_count].src_port = ntohs(tcp_header->th_sport);
????????flows[flow_count].dst_port = ntohs(tcp_header->th_dport);
????????flows[flow_count].packet_count = 1;
????????flows[flow_count].protocol = 'T';
????????flow_count++;
??????}
??????else {
????????printf("Max flow count reached. Cannot add a new flow.\n");
??????}
????}
????else {
??????flows[flow_index].packet_count++;
????}
领英推荐
??}?
??else if (ip_header->ip_p == IPPROTO_UDP) {
????const struct udphdr *udp_header = (struct udphdr *)(packet + sizeof(struct ethhdr) + ip_header->ip_hl *4);
????printf("Source Port: %d\n", ntohs(udp_header->uh_sport));
????printf("Destination Port: %d\n", ntohs(udp_header->uh_dport));
????printf("Protocol: UDP\n");
????int flow_index = find_flow_index(ip_header->ip_src, ip_header->ip_dst, ntohs(udp_header->uh_sport), ntohs(udp_header->uh_dport));
????if (flow_index == -1) {
??????if (flow_count < MAX_FLOWS) {
????????flows[flow_count].src_ip = ip_header->ip_src;
????????flows[flow_count].dst_ip = ip_header->ip_dst;
????????flows[flow_count].src_port = ntohs(udp_header->uh_sport);
????????flows[flow_count].dst_port = ntohs(udp_header->uh_dport);
????????flows[flow_count].packet_count = 1;
????????flows[flow_count].protocol = 'U';
????????flow_count++;
??????}
??????else {
????????printf("Max flow count reached. Cannot add a new flow.\n");
??????}
????}
????else {
??????flows[flow_index].packet_count++;
????}
??}
??packet_count++;
??printf("\n");
??pcap_dump((u_char *)pcap_dumper, pkthdr, packet);
??if (packet_count >= MAX_PACKETS) {
????pcap_breakloop((pcap_t *)user_data);
??}
}
You're defining a function packet_handler which processes each captured packet, extracts relevant information (such as IP addresses, ports, and protocols), updates flow information, and writes packets to a PCAP file.
The main() function is the core of your program:
int main() {
??pcap_t *handle;
??char errbuf[PCAP_ERRBUF_SIZE];
??handle = pcap_open_live("wlp2s0", BUFSIZ, 1, 1000, errbuf); // Change "wlp2s0" to your desired network interface
??if (handle == NULL) {
????printf("Error opening device: %s\n", errbuf);
????return 1;
??}
??pcap_dumper = pcap_dump_open(handle, "captured_packets.pcap"); // Create the pcap dumper
??if (pcap_dumper == NULL) {
????printf("Error opening pcap file for writing.\n");
????return 1;
??}
??pcap_loop(handle, 0, packet_handler, (unsigned char *)handle);
??pcap_dump_close(pcap_dumper); // Close the pcap dumper
??pcap_close(handle);
??for (int k = 0; k< flow_count; k++) {
????if (flows[k].protocol == 'T') {
??????tcp_flow +=1;
????}
????else {
??????udp_flow +=1;
????}
??}
??// Print flow information
??for (int i = 0; i < flow_count; i++) {
????printf("Flow %d:\n", i + 1);
????printf("Source IP: %s\n", inet_ntoa(flows[i].src_ip));
????printf("Destination IP: %s\n", inet_ntoa(flows[i].dst_ip));
????printf("Port Numbers: %d, %d\n", flows[i].src_port, flows[i].dst_port);
????printf("Packet Count: %d\n", flows[i].packet_count);
????printf("Protocol: %c\n", flows[i].protocol);
????printf("\n");
??}
??printf("Total UDP Flowss: %d\n", udp_flow);
??printf("Total TCP Flows: %d\n", tcp_flow);
??return 0;
}
Here, you're initializing necessary variables, opening a network interface for packet capturing, creating a PCAP dumper, looping through captured packets, handling each packet using the packet_handler function, and then closing the network interface and the PCAP dumper.
After the packet capture loop, you're counting and categorizing the flow types (TCP and UDP) and printing the captured flow information.
Credit: Sultan Muhammad - Don't Forget to mention