libpcap使用

Python Scapy工具-sniff函数 Scapy 是适用于 Python 的一个快速、高层次的屏幕抓取和 web 抓取框架,用于抓取 web站点并从页面中提取结构化的数据。 Scapy可以轻松地处理扫描(Scanning)、路由跟踪(Tracerouting)、探测(Probing)、单元测试(Unit Tests)、攻击(Attacks)和发现网络(Network Discorvery)之类的传统任务。 Scapy的安装如下: 通过cmd命令:pip install scapy 进行安装 sniff函数及其参数如下: sniff(count= 阅读详情
AI摘要

本文详细介绍如何使用libpcap库构建网络嗅探器,包括环境配置、安装、基本使用方法及数据包过滤等核心功能的实现与应用实例解析。

libpcap使用



libpcap是一个网络数据包捕获函数库,功能非常强大,Linux下著名的tcpdump就是以它为基础的。今天我们利用它来完成一个我们自己的网络嗅探器(sniffer)




首先先介绍一下本次实验的环境:
Ubuntu 11.04,IP:192.168.1.1,广播地址:192.168.1.255,子网掩码:255.255.255.0
可以使用下面的命令设置:
sudo ifconfig eth0 192.168.1.1 broadcast 192.168.1.255 netmask 255.255.255.0





1.安装
在http://www.tcpdump.org/下载libpcap(tcpdump的源码也可以从这个网站下载)
解压
./configure
make
sudo make install





2.使用
安装好libpcap后,我们要使用它啦,先写一个简单的程序,并介绍如何使用libpcap库编译它:
Makefile:
all: test.c
	gcc -g -Wall -o test test.c -lpcap

clean:
	rm -rf *.o test

其后的程序的Makefile均类似,故不再重复


test1.c
#include <pcap.h>
#include <stdio.h>

int main()
{
  char errBuf[PCAP_ERRBUF_SIZE], * device;
  
  device = pcap_lookupdev(errBuf);
  
  if(device)
  {
    printf("success: device: %s\n", device);
  }
  else
  {
    printf("error: %s\n", errBuf);
  }
  
  return 0;
}





可以成功编译,不过运行的时候却提示找不到libpcap.so.1,因为libpcap.so.1默认安装到了/usr/local/lib下,我们做一个符号链接到/usr/lib/下即可:






运行test的时候输出"no suitable device found",原因是我们没有以root权限运行,root权限运行后就正常了:







下面开始正式讲解如何使用libpcap:
首先要使用libpcap,我们必须包含pcap.h头文件,可以在/usr/local/include/pcap/pcap.h找到,其中包含了每个类型定义的详细说明。




1.获取网络接口
首先我们需要获取监听的网络接口:
我们可以手动指定或让libpcap自动选择,先介绍如何让libpcap自动选择:
char * pcap_lookupdev(char * errbuf)
上面这个函数返回第一个合适的网络接口的字符串指针,如果出错,则errbuf存放出错信息字符串,errbuf至少应该是PCAP_ERRBUF_SIZE个字节长度的。注意,很多libpcap函数都有这个参数。
pcap_lookupdev()一般可以在跨平台的,且各个平台上的网络接口名称都不相同的情况下使用。
如果我们手动指定要监听的网络接口,则这一步跳过,我们在第二步中将要监听的网络接口字符串硬编码在pcap_open_live里。




2.释放网络接口
在操作为网络接口后,我们应该要释放它:
void pcap_close(pcap_t * p)
该函数用于关闭pcap_open_live()获取的pcap_t的网络接口对象并释放相关资源。




3.打开网络接口
获取网络接口后,我们需要打开它:
pcap_t * pcap_open_live(const char * device, int snaplen, int promisc, int to_ms, char * errbuf)
上面这个函数会返回指定接口的pcap_t类型指针,后面的所有操作都要使用这个指针。
第一个参数是第一步获取的网络接口字符串,可以直接使用硬编码。
第二个参数是对于每个数据包,从开头要抓多少个字节,我们可以设置这个值来只抓每个数据包的头部,而不关心具体的内容。典型的以太网帧长度是1518字节,但其他的某些协议的数据包会更长一点,但任何一个协议的一个数据包长度都必然小于65535个字节。
第三个参数指定是否打开混杂模式(Promiscuous Mode),0表示非混杂模式,任何其他值表示混合模式。如果要打开混杂模式,那么网卡必须也要打开混杂模式,可以使用如下的命令打开eth0混杂模式:
ifconfig eth0 promisc
第四个参数指定需要等待的毫秒数,超过这个数值后,第3步获取数据包的这几个函数就会立即返回。0表示一直等待直到有数据包到来。
第五个参数是存放出错信息的数组。




4.获取数据包
打开网络接口后就已经开始监听了,那如何知道收到了数据包呢?有下面3种方法:
a)
u_char * pcap_next(pcap_t * p, struct pcap_pkthdr * h)
如果返回值为NULL,表示没有抓到包
第一个参数是第2步返回的pcap_t类型的指针
第二个参数是保存收到的第一个数据包的pcap_pkthdr类型的指针
pcap_pkthdr类型的定义如下:
struct pcap_pkthdr
{
  struct timeval ts;    /* time stamp */
  bpf_u_int32 caplen;   /* length of portion present */
  bpf_u_int32 len;      /* length this packet (off wire) */
};

注意这个函数只要收到一个数据包后就会立即返回


b)
int pcap_loop(pcap_t * p, int cnt, pcap_handler callback, u_char * user)
第一个参数是第2步返回的pcap_t类型的指针
第二个参数是需要抓的数据包的个数,一旦抓到了cnt个数据包,pcap_loop立即返回。负数的cnt表示pcap_loop永远循环抓包,直到出现错误。
第三个参数是一个回调函数指针,它必须是如下的形式:
void callback(u_char * userarg, const struct pcap_pkthdr * pkthdr, const u_char * packet)
第一个参数是pcap_loop的最后一个参数,当收到足够数量的包后pcap_loop会调用callback回调函数,同时将pcap_loop()的user参数传递给它
第二个参数是收到的数据包的pcap_pkthdr类型的指针
第三个参数是收到的数据包数据




c)
int pcap_dispatch(pcap_t * p, int cnt, pcap_handler callback, u_char * user)

这个函数和pcap_loop()非常类似,只是在超过to_ms毫秒后就会返回(to_ms是pcap_open_live()的第4个参数)


例子:

test2:

#include <pcap.h>
#include <time.h>
#include <stdlib.h>
#include <stdio.h>

int main()
{
  char errBuf[PCAP_ERRBUF_SIZE], * devStr;
  
  /* get a device */
  devStr = pcap_lookupdev(errBuf);
  
  if(devStr)
  {
    printf("success: device: %s\n", devStr);
  }
  else
  {
    printf("error: %s\n", errBuf);
    exit(1);
  }
  
  /* open a device, wait until a packet arrives */
  pcap_t * device = pcap_open_live(devStr, 65535, 1, 0, errBuf);
  
  if(!device)
  {
    printf("error: pcap_open_live(): %s\n", errBuf);
    exit(1);
  }

  /* wait a packet to arrive */
  struct pcap_pkthdr packet;
  const u_char * pktStr = pcap_next(device, &packet);

  if(!pktStr)
  {
    printf("did not capture a packet!\n");
    exit(1);
  }
  
  printf("Packet length: %d\n", packet.len);
  printf("Number of bytes: %d\n", packet.caplen);
  printf("Recieved time: %s\n", ctime((const time_t *)&packet.ts.tv_sec)); 
  
  pcap_close(device);
  
  return 0;
}






打开两个终端,先ping 192.168.1.10,由于我们的ip是192.168.1.1,因此我们可以收到广播的数据包,另一个终端运行test,就会抓到这个包。







下面的这个程序会把收到的数据包内容全部打印出来,运行方式和上一个程序一样:
test3:
#include <pcap.h>
#include <time.h>
#include <stdlib.h>
#include <stdio.h>

void getPacket(u_char * arg, const struct pcap_pkthdr * pkthdr, const u_char * packet)
{
  int * id = (int *)arg;
  
  printf("id: %d\n", ++(*id));
  printf("Packet length: %d\n", pkthdr->len);
  printf("Number of bytes: %d\n", pkthdr->caplen);
  printf("Recieved time: %s", ctime((const time_t *)&pkthdr->ts.tv_sec)); 
  
  int i;
  for(i=0; i<pkthdr->len; ++i)
  {
    printf(" %02x", packet[i]);
    if( (i + 1) % 16 == 0 )
    {
      printf("\n");
    }
  }
  
  printf("\n\n");
}

int main()
{
  char errBuf[PCAP_ERRBUF_SIZE], * devStr;
  
  /* get a device */
  devStr = pcap_lookupdev(errBuf);
  
  if(devStr)
  {
    printf("success: device: %s\n", devStr);
  }
  else
  {
    printf("error: %s\n", errBuf);
    exit(1);
  }
  
  /* open a device, wait until a packet arrives */
  pcap_t * device = pcap_open_live(devStr, 65535, 1, 0, errBuf);
  
  if(!device)
  {
    printf("error: pcap_open_live(): %s\n", errBuf);
    exit(1);
  }
  
  /* wait loop forever */
  int id = 0;
  pcap_loop(device, -1, getPacket, (u_char*)&id);
  
  pcap_close(device);

  return 0;
}






从上图可以看出,如果我们没有按Ctrl+c,test会一直抓到包,因为我们将pcap_loop()设置为永远循环




由于ping属于icmp协议,并且发出icmp协议数据包之前必须先通过arp协议获取目的主机的mac地址,因此我们抓到的包是arp协议的,而arp协议的数据包长度正好是42字节(14字节的以太网帧头+28字节的arp数据)。具体内容请参考相关网络协议说明。






5.分析数据包
我们既然已经抓到数据包了,那么我们要开始分析了,这部分留给读者自己完成,具体内容可以参考相关的网络协议说明。在本文的最后,我会示范性的写一个分析arp协议的sniffer,仅供参考。要特别注意一点,网络上的数据是网络字节顺序的,因此分析前需要转换为主机字节顺序(ntohs()函数)。




6.过滤数据包
我们抓到的数据包往往很多,如何过滤掉我们不感兴趣的数据包呢?
几乎所有的操作系统(BSD, AIX, Mac OS, Linux等)都会在内核中提供过滤数据包的方法,主要都是基于BSD Packet Filter(BPF)结构的。libpcap利用BPF来过滤数据包。
过滤数据包需要完成3件事:
a) 构造一个过滤表达式
b) 编译这个表达式
c) 应用这个过滤器



a)

BPF使用一种类似于汇编语言的语法书写过滤表达式,不过libpcap和tcpdump都把它封装成更高级且更容易的语法了,具体可以man tcpdump,以下是一些例子:
src host 192.168.1.177
只接收源ip地址是192.168.1.177的数据包


dst port 80
只接收tcp/udp的目的端口是80的数据包


not tcp
只接收不使用tcp协议的数据包


tcp[13] == 0x02 and (dst port 22 or dst port 23)
只接收SYN标志位置位且目标端口是22或23的数据包(tcp首部开始的第13个字节)


icmp[icmptype] == icmp-echoreply or icmp[icmptype] == icmp-echo
只接收icmp的ping请求和ping响应的数据包


ehter dst 00:e0:09:c1:0e:82
只接收以太网mac地址是00:e0:09:c1:0e:82的数据包


ip[8] == 5
只接收ip的ttl=5的数据包(ip首部开始的第8个字节)


b)
构造完过滤表达式后,我们需要编译它,使用如下函数:
int pcap_compile(pcap_t * p, struct bpf_program * fp, char * str, int optimize, bpf_u_int32 netmask)
fp:这是一个传出参数,存放编译后的bpf
str:过滤表达式
optimize:是否需要优化过滤表达式
metmask:简单设置为0即可


c)
最后我们需要应用这个过滤表达式:
int pcap_setfilter(pcap_t * p,  struct bpf_program * fp)
第二个参数fp就是前一步pcap_compile()的第二个参数




应用完过滤表达式之后我们便可以使用pcap_loop()或pcap_next()等抓包函数来抓包了。


下面的程序演示了如何过滤数据包,我们只接收目的端口是80的数据包:
test4.c
#include <pcap.h>
#include <time.h>
#include <stdlib.h>
#include <stdio.h>

void getPacket(u_char * arg, const struct pcap_pkthdr * pkthdr, const u_char * packet)
{
  int * id = (int *)arg;
  
  printf("id: %d\n", ++(*id));
  printf("Packet length: %d\n", pkthdr->len);
  printf("Number of bytes: %d\n", pkthdr->caplen);
  printf("Recieved time: %s", ctime((const time_t *)&pkthdr->ts.tv_sec)); 
  
  int i;
  for(i=0; i<pkthdr->len; ++i)
  {
    printf(" %02x", packet[i]);
    if( (i + 1) % 16 == 0 )
    {
      printf("\n");
    }
  }
  
  printf("\n\n");
}

int main()
{
  char errBuf[PCAP_ERRBUF_SIZE], * devStr;
  
  /* get a device */
  devStr = pcap_lookupdev(errBuf);
  
  if(devStr)
  {
    printf("success: device: %s\n", devStr);
  }
  else
  {
    printf("error: %s\n", errBuf);
    exit(1);
  }
  
  /* open a device, wait until a packet arrives */
  pcap_t * device = pcap_open_live(devStr, 65535, 1, 0, errBuf);
  
  if(!device)
  {
    printf("error: pcap_open_live(): %s\n", errBuf);
    exit(1);
  }
  
  /* construct a filter */
  struct bpf_program filter;
  pcap_compile(device, &filter, "dst port 80", 1, 0);
  pcap_setfilter(device, &filter);
  
  /* wait loop forever */
  int id = 0;
  pcap_loop(device, -1, getPacket, (u_char*)&id);
  
  pcap_close(device);

  return 0;
}




在下面的这一个例子中,客户机通过tcp的9732端口连接服务器,发送字符'A',之后服务器将'A'+1即'B'返回给客户机,具体实现可以参考:http://blog.youkuaiyun.com/htttw/article/details/7519964


服务器的ip是192.168.56.101,客户机的ip是192.168.56.1
服务器:



Makefile:

all: tcp_client.c tcp_server.c
	gcc -g -Wall -o tcp_client tcp_client.c
	gcc -g -Wall -o tcp_server tcp_server.c

clean:
	rm -rf *.o tcp_client tcp_server


tcp_server:

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

#define PORT 9832
#define SERVER_IP "192.168.56.101"

int main()
{
  /* create a socket */
  int server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
  
  struct sockaddr_in server_addr;
  server_addr.sin_family = AF_INET;
  server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);
  server_addr.sin_port = htons(PORT);
  
  /* bind with the local file */
  bind(server_sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
  
  /* listen */
  listen(server_sockfd, 5);
  
  char ch;
  int client_sockfd;
  struct sockaddr_in client_addr;
  socklen_t len = sizeof(client_addr);
  while(1)
  {
    printf("server waiting:\n");
    
    /* accept a connection */
    client_sockfd = accept(server_sockfd, (struct sockaddr *)&client_addr, &len);
    
    /* exchange data */
    read(client_sockfd, &ch, 1);
    printf("get char from client: %c\n", ch);
    ++ch;
    write(client_sockfd, &ch, 1);
    
    /* close the socket */
    close(client_sockfd);
  }
  
  return 0;
}


tcp_client:

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

#define PORT 9832
#define SERVER_IP "192.168.56.101"

int main()
{
  /* create a socket */
  int sockfd = socket(AF_INET, SOCK_STREAM, 0);
  
  struct sockaddr_in address;
  address.sin_family = AF_INET;
  address.sin_addr.s_addr = inet_addr(SERVER_IP);
  address.sin_port = htons(PORT);
  
  /* connect to the server */
  int result = connect(sockfd, (struct sockaddr *)&address, sizeof(address));
  if(result == -1)
  {
    perror("connect failed: ");
    exit(1);
  }
  
  /* exchange data */
  char ch = 'A';
  write(sockfd, &ch, 1);
  read(sockfd, &ch, 1);
  printf("get char from server: %c\n", ch);
  
  /* close the socket */
  close(sockfd);
  
  return 0;
}



运行方法如下,首先在服务器上运行tcp_server,然后运行我们的监听器,然后在客户机上运行tcp_client,注意,我们可以先清空arp缓存,这样就可以看到整个通信过程(包括一开始的arp广播)
在客户机上运行下列命令来清空记录服务器的arp缓存:
sudo arp -d 192.168.56.101
arp -a后发现已经删除了记录服务器的arp缓存




抓包的结果如下所示,由于包太多了,无法全部截图,因此我把所有内容保存在下面的文本中了:



全部的包如下:

hutao@hutao-VirtualBox:~/test3$ sudo ./test
success: device: eth0
id: 1
Packet length: 60
Number of bytes: 60
Recieved time: Sat Apr 28 19:57:50 2012
 ff ff ff ff ff ff 0a 00 27 00 00 00 08 06 00 01
 08 00 06 04 00 01 0a 00 27 00 00 00 c0 a8 38 01
 00 00 00 00 00 00 c0 a8 38 65 00 00 00 00 00 00
 00 00 00 00 00 00 00 00 00 00 00 00

id: 2
Packet length: 42
Number of bytes: 42
Recieved time: Sat Apr 28 19:57:50 2012
 0a 00 27 00 00 00 08 00 27 9c ff b1 08 06 00 01
 08 00 06 04 00 02 08 00 27 9c ff b1 c0 a8 38 65
 0a 00 27 00 00 00 c0 a8 38 01

id: 3
Packet length: 74
Number of bytes: 74
Recieved time: Sat Apr 28 19:57:50 2012
 08 00 27 9c ff b1 0a 00 27 00 00 00 08 00 45 00
 00 3c d4 af 40 00 40 06 74 55 c0 a8 38 01 c0 a8
 38 65 8e 20 26 68 79 e1 63 8c 00 00 00 00 a0 02
 39 08 d4 13 00 00 02 04 05 b4 04 02 08 0a 00 14
 b7 23 00 00 00 00 01 03 03 06

id: 4
Packet length: 74
Number of bytes: 74
Recieved time: Sat Apr 28 19:57:50 2012
 0a 00 27 00 00 00 08 00 27 9c ff b1 08 00 45 00
 00 3c 00 00 40 00 40 06 49 05 c0 a8 38 65 c0 a8
 38 01 26 68 8e 20 b6 c4 e6 e5 79 e1 63 8d a0 12
 38 90 f1 e5 00 00 02 04 05 b4 04 02 08 0a 00 57
 a1 2c 00 14 b7 23 01 03 03 05

id: 5
Packet length: 66
Number of bytes: 66
Recieved time: Sat Apr 28 19:57:50 2012
 08 00 27 9c ff b1 0a 00 27 00 00 00 08 00 45 00
 00 34 d4 b0 40 00 40 06 74 5c c0 a8 38 01 c0 a8
 38 65 8e 20 26 68 79 e1 63 8d b6 c4 e6 e6 80 10
 00 e5 fb c1 00 00 01 01 08 0a 00 14 b7 24 00 57
 a1 2c

id: 6
Packet length: 67
Number of bytes: 67
Recieved time: Sat Apr 28 19:57:50 2012
 08 00 27 9c ff b1 0a 00 27 00 00 00 08 00 45 00
 00 35 d4 b1 40 00 40 06 74 5a c0 a8 38 01 c0 a8
 38 65 8e 20 26 68 79 e1 63 8d b6 c4 e6 e6 80 18
 00 e5 ba b7 00 00 01 01 08 0a 00 14 b7 25 00 57
 a1 2c 41

id: 7
Packet length: 66
Number of bytes: 66
Recieved time: Sat Apr 28 19:57:50 2012
 0a 00 27 00 00 00 08 00 27 9c ff b1 08 00 45 00
 00 34 47 cb 40 00 40 06 01 42 c0 a8 38 65 c0 a8
 38 01 26 68 8e 20 b6 c4 e6 e6 79 e1 63 8e 80 10
 01 c5 f1 dd 00 00 01 01 08 0a 00 57 a1 2e 00 14
 b7 25

id: 8
Packet length: 67
Number of bytes: 67
Recieved time: Sat Apr 28 19:57:50 2012
 0a 00 27 00 00 00 08 00 27 9c ff b1 08 00 45 00
 00 35 47 cc 40 00 40 06 01 40 c0 a8 38 65 c0 a8
 38 01 26 68 8e 20 b6 c4 e6 e6 79 e1 63 8e 80 18
 01 c5 f1 de 00 00 01 01 08 0a 00 57 a1 2e 00 14
 b7 25 42

id: 9
Packet length: 66
Number of bytes: 66
Recieved time: Sat Apr 28 19:57:50 2012
 0a 00 27 00 00 00 08 00 27 9c ff b1 08 00 45 00
 00 34 47 cd 40 00 40 06 01 40 c0 a8 38 65 c0 a8
 38 01 26 68 8e 20 b6 c4 e6 e7 79 e1 63 8e 80 11
 01 c5 f1 dd 00 00 01 01 08 0a 00 57 a1 2e 00 14
 b7 25

id: 10
Packet length: 66
Number of bytes: 66
Recieved time: Sat Apr 28 19:57:50 2012
 08 00 27 9c ff b1 0a 00 27 00 00 00 08 00 45 00
 00 34 d4 b2 40 00 40 06 74 5a c0 a8 38 01 c0 a8
 38 65 8e 20 26 68 79 e1 63 8e b6 c4 e6 e7 80 10
 00 e5 fb bc 00 00 01 01 08 0a 00 14 b7 25 00 57
 a1 2e

id: 11
Packet length: 66
Number of bytes: 66
Recieved time: Sat Apr 28 19:57:50 2012
 08 00 27 9c ff b1 0a 00 27 00 00 00 08 00 45 00
 00 34 d4 b3 40 00 40 06 74 59 c0 a8 38 01 c0 a8
 38 65 8e 20 26 68 79 e1 63 8e b6 c4 e6 e7 80 11
 00 e5 fb bb 00 00 01 01 08 0a 00 14 b7 25 00 57
 a1 2e

id: 12
Packet length: 66
Number of bytes: 66
Recieved time: Sat Apr 28 19:57:50 2012
 0a 00 27 00 00 00 08 00 27 9c ff b1 08 00 45 00
 00 34 47 ce 40 00 40 06 01 3f c0 a8 38 65 c0 a8
 38 01 26 68 8e 20 b6 c4 e6 e8 79 e1 63 8f 80 10
 01 c5 f1 dd 00 00 01 01 08 0a 00 57 a1 2e 00 14
 b7 25

id: 13
Packet length: 66
Number of bytes: 66
Recieved time: Sat Apr 28 19:57:50 2012
 08 00 27 9c ff b1 0a 00 27 00 00 00 08 00 45 00
 00 34 d4 b4 40 00 40 06 74 58 c0 a8 38 01 c0 a8
 38 65 8e 20 26 68 79 e1 63 8f b6 c4 e6 e8 80 10
 00 e5 fb b9 00 00 01 01 08 0a 00 14 b7 26 00 57
 a1 2e



仔细研究即可发现服务器与客户机是如何通过tcp通信的。









下面的这个程序可以获取eth0的ip和子网掩码等信息:

test5:

#include <stdio.h>
#include <stdlib.h>
#include <pcap.h>
#include <errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main()
{
  /* ask pcap to find a valid device for use to sniff on */
  char * dev;   /* name of the device */ 
  char errbuf[PCAP_ERRBUF_SIZE];
  dev = pcap_lookupdev(errbuf);

  /* error checking */
  if(!dev)
  {
    printf("pcap_lookupdev() error: %s\n", errbuf);
    exit(1);
  }

  /* print out device name */
  printf("dev name: %s\n", dev);

  /* ask pcap for the network address and mask of the device */
  bpf_u_int32 netp;   /* ip */
  bpf_u_int32 maskp;  /* subnet mask */
  int ret;            /* return code */
  ret = pcap_lookupnet(dev, &netp, &maskp, errbuf);

  if(ret == -1)
  {
    printf("pcap_lookupnet() error: %s\n", errbuf);
    exit(1);
  }

  /* get the network address in a human readable form */
  char * net;   /* dot notation of the network address */
  char * mask;  /* dot notation of the network mask */
  struct in_addr addr;

  addr.s_addr = netp;
  net = inet_ntoa(addr);

  if(!net)
  {
    perror("inet_ntoa() ip error: ");
    exit(1);
  }

  printf("ip: %s\n", net);

  /* do the same as above for the device's mask */
  addr.s_addr = maskp;
  mask = inet_ntoa(addr);
  
  if(!mask)
  {
    perror("inet_ntoa() sub mask error: ");
    exit(1);
  }
  
  printf("sub mask: %s\n", mask);

  return 0;
}


结果如图:





int pcap_lookupnet(const char * device, bpf_u_int32 * netp, bpf_u_int32 * maskp, char * errbuf)
可以获取指定设备的ip地址,子网掩码等信息

netp:传出参数,指定网络接口的ip地址

maskp:传出参数,指定网络接口的子网掩码

pcap_lookupnet()失败返回-1
我们使用inet_ntoa()将其转换为可读的点分十进制形式的字符串






本文的绝大部分来源于libpcap的官方文档:libpcapHakin9LuisMartinGarcia.pdf,可以在官网下载,文档只有9页,不过很详细,还包括了数据链路层,网络层,传输层,应用层等的分析。很好!


更多参考可以man pcap


最后为了方便大家,本文的所有代码和上述的pdf文档都一并上传上来了:

http://download.youkuaiyun.com/detail/htttw/4264686



完成!



【计网 从头自己构建协议】一、libpcap 介绍 & 手撕以太网帧 理论的学习总是枯燥的,想要加深对理论的理解,最好的方法就是自己实践一遍。想要亲手实现各种协议,就必须能够接触底层 API。可惜的是,底层的 API 要么是在驱动里,要么是在系统里,都不对外开放,一般只能接触到运输层的 TCP/UDP。我们必须借助第三方库才能实现对底层操控。libpcap就是这样一个库,它帮我们实现了底层驱动,并将控制权向上开放,提供了发送和监听数据包的功能。著名的网络分析工具 Wireshark 就是基于这个库实现的。 阅读详情

相关推荐

libpcap详解

libpcap(packet capture library),即数据包捕获函数库,是Unix/Linux平台下的网络数据包捕获函数库。它是一个独立于系统的用户层的包捕获API接口,为底层网络监测提供了一个可移植的框架。 一、工作原理 libpcap主要由两部分组成:网络分接头(network tap)和数据过滤器(packet filter)。网络分接头从网络设备驱动程序中收集数据进行拷贝,过滤器决定是否接收该数据包。libpcap利用BSD packet filter(BPF)算法对网卡接收到的链路层数

kking_edc的博客 8814

python代码scapy库 通过 conf 无法获取信息

问题,python代码scapy库 无法通过 conf 获取网卡等信息,也捕获不到数据包。原因是Windows平台需要安装Winpcap才能进行嗅探,安装wireshark亦可

kaihang_cai的博客 8200

libpcap 使用(1)

   计算机网络自顶向下 一书中,介绍了ethereal。 该软件开源,用于抓包分析。版本最新的更名为wireshark,是目前最好的网络抓包分析工具。ethereal主要使用了gtk+ 和 libpcap来进行设计。gtk +提供用户界面,而libpcap是unix/linux平台下的网络数据包捕获函数包,大多数网络监控软件都以它为基础。Libpcap可以在绝大多数类unix平台下工作,提供了网

岳麓听雨专栏 5590

【Scapy】获取流量包【原创】

使用Scapy来嗅探流量包,发送数据包等

一直加班的程序猿 6761

libpcap使用入门实例

libpcap是

tt11212的专栏 907

libpcap

//pcaptest.htypedef struct{//定义以太网的头部数据类型 u_char dmac[6]; u_char smac[6]; u_char etype[2]; }ETHHEADER; typedef struct{//IP 层数据包报文头部的数据类型 u_char version:4; u_char header_len:4; u_char

yuan08shandong的博客 1552

libpcap原理及使用

2.9 libpcap 本文最初整理在我的github上SDN-Learning-notes libpcap(Packet Capture Library)即数据包捕获函数库,是Unix/Linux平台下的网络数据包捕获函数库。它是一个独立于系统的用户层包捕获的API接口,为底层网络监测提供了一个可移植的框架。著名的软件TCPDUMP就是在libpcap的的基础上开发而成的libpcap可以实现

虾米的博客 3万+

java libpcap_libpcap详解

libpcap(Packet Capture Library),即数据包捕获函数库,是Unix/Linux平台下的网络数据包捕获函数库。它是一个独立于系统的用户层包捕获的API接口,为底层网络监测提供了一个可移植的框架。一、libpcap工作原理libpcap主要由两部份组成:网络分接头(Network Tap)和数据过滤器(Packet Filter)。网络分接头从网络设备驱动程序中收集数据拷贝...

weixin_33516557的博客 921

libpcap的安装

最近参加了一个实训项目,是把一个WireShark抓到的pcapng文件用libpcap库还原成原始的TS流,需求分析弄了一天,安装环境用了一天,接下来再开始写代码,让我们一起完成这个项目吧。 首先是安装CentOS环境(其实只要是Linux就可以),这里给出资源网站:https://www.centos.org/download/ 可以选择DVD download或者最小化安装。 这里注意,如果...

NCU_CirclePlan的博客 2143

Qwen3-VL、Qwen3-Next与Qwen3.5多模态架构深度解剖

多模态大模型的核心不在参数规模,而在于视觉与文本如何协同建模。本文从基础概念切入,解析视觉语言模型中视觉编码器、跨模态注意力、特征对齐与token压缩等关键原理;阐述ViT、CLIP-ViT及动态压缩模块(HDTC)在语义表征、空间感知与计算效率上的技术权衡;强调结构设计如何决定模型在医学影像、工业质检、遥感分析等高要求场景中的真实能力边界;最终聚焦通义千问最新三套VL架构——Qwen3-VL、Qwen3-Next与Qwen3.5——揭示其背后迥异的融合哲学与工程取舍,为选型、微调与推理优化提供底层依据。

weixin_33778544的博客 818

告别Keil!用VSCode+PlatformIO玩转STC单片机(附自动下载配置)

本文详细介绍了如何从Keil迁移到VSCode+PlatformIO进行STC单片机开发,包括环境搭建、自动下载配置等实用技巧。通过对比传统IDE与现代工具链的差异,展示了VSCode+PlatformIO在代码编辑体验、跨平台支持和开发效率上的优势,帮助开发者实现更流畅的STC单片机开发流程。

weixin_27252001的博客 459

python调用libpcap_python-libpcap

python-libpcapCython libpcap, read/write/merge/captureFeaturesRead pcap fileWrite pcap fileMerge pcap fileMulti-file quick mergeGet first ifaceGet iface listSend raw packetCapture dataInstallationTo i...

weixin_33139275的博客 1249

HEVC/VVC中R-Lambda码率控制算法原理

文章目录1. 比特分配1.1 GOP级比特分配1.2 帧级比特分配1.3 LCU级比特分配2. 码率控制2.1 λ的计算和更新2.2 参数QP的确定 VVC中码率控制算法沿用了HEVC中经典的R-λ码率控制模型,R-λ由中科大的李斌博士提出(详情参考JCTVC-K0103)。R-λ模型的基础为视频编码中 R(比特 bpp)和 D(失真 MSE)之间的双曲线模型,即 D(R)=CR−KD(R) = ...

Aidoneus_y 6138

Libpcap使用总结

<br /><br />                                      <br /><br />Libpcap使用总结<br /><br />libpcap是一个与实现无关的访问操作系统所提供的分组捕获机制的分组捕获函数库,用于访问数据链路层。这个库为不同的平台提供了一致的编程接口,在安装了 libpcap 的平台上,以 libpcap 为接口写的程序、应用,能够自由的跨平台使用。操作系统所提供的分组捕获机制主要有三种:BPF(Berkeley Packet Filter),DL

qiaoxx094的专栏 884

3dsMax高效导入OSGB倾斜模型的3个关键步骤与插件应用

本文详细解析了在3dsMax中高效导入OSGB倾斜模型的三个核心步骤:建立索引、处理贴图路径以及选择合适的插件。针对导入过程中常见的模型崩溃、贴图丢失和操作卡顿问题,提供了从数据结构理解到插件选型的实战解决方案,帮助用户实现大规模实景模型在3dsMax中的流畅导入与编辑。

weixin_29238147的博客 471

嵌入式铁头山羊stm32:一文总结

本文是一篇STM32嵌入式开发的综合知识总结,涵盖了STM32基础概念、GPIO、通信协议、外设驱动等核心内容。文章首先介绍了STM32的基本概念和型号含义,然后详细讲解了GPIO的8种工作模式及实验案例。接着重点阐述了USART、I2C、SPI三种通信协议的原理和实现方法,包括硬件连接、编程接口和实验应用。此外还介绍了OLED显示原理、中断系统、定时器、ADC转换等关键技术,每个模块都配有对应的实验案例和代码实现参考。文章内容全面系统,既包含理论知识,又提供实践指导,适合作为STM32开发的快速参考手册。

古泽汉书的博客 2789

libpcap库的安装与使用

1 安装 sudo apt-get install libpcap-dev 2 使用 需要包含的头文件: #include <pcap.h 编译的时候需要加: -lpcap 利用libpcap函数库开发应用程序的基本步骤: 打开网络设备 设置过滤规则(可选) 捕获数据 关闭网络设备 常用函数: pcap_lookupdev( )(可选) 查看设备名,返回网卡名 pcap_t *pcap_open_live(const char *device,int snaplen,int promisc,

qq_45164331的博客 5262

C#上位机国产化适配全方案:兼容统信UOS+鲲鹏/昇腾+国产PLC,可直接交付国企项目

去年下半年,我帮天津滨海新区一家给一汽丰田做配套的汽车零部件国企,完成了3条产线、12套C#上位机的全栈国产化适配。这个项目的背景很典型,也很戳中国企客户的命门:一开始客户找了两家本地的集成商,都没搞定:第一家只会用.NET Framework,根本不知道怎么在统信UOS上跑C#;第二家勉强用.NET Core 3.1跑起来了,但和汇川国产PLC的通信丢包率高达1.2%,产线根本不敢用。我们接手后,用了2个月完成全栈适配:从Windows Server 2019迁移到统信UOS 1070d服务器版,从.NE

这里只有干货:从 Modbus 通信到 MES 对接,从 YOLO 训练到工控机部署,带你搞定工业软件开发全流程。 498

libpcap使用简解

libpcap的简单实用案例以及工作原理浅析

勿忘初心,方得始终 6433

moveit——基于octomap的碰撞检测

机械臂对棚顶进行碰撞检测。

如果想成为中心,那么就到中心去吧。 1356

vue3(微信公众号H5网页):调用wx.config报错config:fail,invalid signature,在vue3 h5项目开发微信内置浏览器JS-SDK/JS-API提示签名失效/错误

微信公众号网页,vue3 H5网页,uniapp h5,wx.config 报错:config:fail,invalid signature?无效签名,微信JSSDK分享接口中wx.config 出现invalid signature问题的解决办法,微信公众号签名失效,wx.config中出现invalid signature的解决方法,H5页面调起微信的拍照与分享功能,导致的config:invalid signature这个错误的解决办法,微信网页调用分享功能出现config:fail,invalid

✅ 精选推荐文章 5053

[笔记] libpcap编译及使用

官网以上就是今天要讲的内容,本文仅仅简单介绍了libpcap安装和使用。libpcap库使用关于博主码云:https://gitee.com/shiver个人博客:www.shiver.fun。

二次元怪兽的博客 1959

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

Libpcap一 libpcap简介与编译

a

yuanbinquan的专栏 2344

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

libpcap抓包程序

安装libpcap: libpcap 安装和测试 https://blog.youkuaiyun.com/wanzzg/article/details/83972024 在ubuntu下安装libpcap库 https://blog.youkuaiyun.com/cfjtaishan/article/details/7096085 Ubuntu16.04 install libpcap安装和测试 h...

囡囡的博客 659

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

Centos下安装libpcap

libpcap是一个网络数据包捕获函数库,功能非常强大,Linux下著名的tcpdump就是以它为基础的。我们可以利用它来完成自己的sniffer。 1.安装 下载最新版本的libpcap,下载地址:https://www.winpcap.org/install/ 解压缩libpcap-1.5.3.tar.gz ./configure make sudo make install 2.测试 写一个...

bubbleyang的博客 2247
上一篇: Linux使用蓝牙
下一篇: ffmpeg的编译与使用
htttw
博客等级 码龄18年 378粉丝 73原创
评论 85
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值