linux实现抓包 (使用原始套接字数据连路层协议)
2013-11-18 15:06
651 查看
网络作业2 IP分组流量分析程序
参考:http://www.cnblogs.com/rollenholt/articles/2585432.html
/article/1753467.html
http://www.cnblogs.com/rollenholt/articles/2585432.html
http://baike.baidu.com/link?url=PipBWz6NFljSSDX_q-AeKVO1shLJijhn4xU4iaOPAN9aaDkaQZvL3n10XCnur0rURR3-4Q6TA6HDw-N8DYR-Xq
http://wenku.baidu.com/link?url=gomqzjumyFcoIPceA9jZXdkazLIIbGR_xxF5cu11EZVqvTRVnqiH3sl30kUgoW5nZjZEB1TdTXFZm6UZRPfIce5W7fk1m6TD1N2pTii8Ycq
开发一个IP分组流量分析程序,实现以下功能:捕获并分析通过本地网卡的IP分组,输入捕获IP分组的时间限制,输出每个IP分组头部的主要字段(包括版本、总长度、协议、源地址与目的地址等),协议字段需要区分出具体类型(例如TCP、UDP、ICMP、IGMP、IPv6等)。
实现方案:
1.原始套接字抓包
2.linux信号机制计时。
1.原始套接字抓包
常用的套接字有流式套接字(SOCK_STREAM,对应于TCP)和数据报套接字(SOCK_DGRAM,对应于UDP),这两种套接字接收到的仅仅是应用层的数据,用户一般见不到MAC层、IP层和传输层(TCP/UDP)的帧头。而这里需要用到的原始套接字,是可以看到原始的网络报文的。如下图所示MAC帧地址,获得的包为从目的MAC地址到CRC之前(不包括CRC)
以如下方式使用原始套接字
(sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)) < 0 )//使用原始套接字,ETH_P_ALL可以获取所有经过本机的数据包,当然前提是网卡为混杂模式。协议参数可选择的很多,这里列几个常用的。
上面还提到,网卡要设置成混杂模式。根据百度百科:混杂模式(Promiscuous
Mode)是指一台机器能够接收所有经过它的数据流,而不论其目的地址是否是他。是相对于通常模式(又称“非混杂模式”)而言的。
通过以下设置:
strncpy(ethreq.ifr_name, "wlan0", IFNAMSIZ);//指定需要经过的网卡。 if( ioctl(sock, SIOCGIFFLAGS, ðreq) == -1 )//获得该网卡的接口信息。 { perror("ioctl:"); close(sock); exit(1); } ethreq.ifr_flags |= IFF_PROMISC;//设置成混杂模式。 if( ioctl(sock, SIOCSIFFLAGS, ðreq) == -1)//将网卡的接口信息写回。 { perror("ioctl:"); close(sock); exit(1); }
先指定要配置的网卡名称,在通过ifreq.ifr_flags 设置混杂模式。这里涉及到如下一个数据结构,
struct ifreq { # define IFHWADDRLEN 6 # define IFNAMSIZ IF_NAMESIZE union { char ifrn_name[IFNAMSIZ]; /* Interface name, e.g. "en0". */ } ifr_ifrn; union { struct sockaddr ifru_addr; struct sockaddr ifru_dstaddr; struct sockaddr ifru_broadaddr; struct sockaddr ifru_netmask; struct sockaddr ifru_hwaddr; short int ifru_flags; int ifru_ivalue; int ifru_mtu; struct ifmap ifru_map; char ifru_slave[IFNAMSIZ]; /* Just fits the size */ char ifru_newname[IFNAMSIZ]; __caddr_t ifru_data; } ifr_ifru; }; # define ifr_name ifr_ifrn.ifrn_name /* interface name*/ 在这里就是网卡eth0或eth1 # define ifr_hwaddr ifr_ifru.ifru_hwaddr /* MAC address */ mac地址 # define ifr_addr ifr_ifru.ifru_addr /* address */ source地址 # define ifr_dstaddr ifr_ifru.ifru_dstaddr /* other end of p-p lnk */ 目的ip地址 # define ifr_broadaddr ifr_ifru.ifru_broadaddr /* broadcast address */ 广播地址 # define ifr_netmask ifr_ifru.ifru_netmask /* interface net mask */ 子网掩码 # define ifr_flags ifr_ifru.ifru_flags /* flags */ 模式标志 设置混杂模式 # define ifr_metric ifr_ifru.ifru_ivalue /* metric */ # define ifr_mtu ifr_ifru.ifru_mtu /* mtu */ # define ifr_map ifr_ifru.ifru_map /* device map */ # define ifr_slave ifr_ifru.ifru_slave /* slave device */ # define ifr_data ifr_ifru.ifru_data /* for use by interface */ # define ifr_ifindex ifr_ifru.ifru_ivalue /* interface index */ # define ifr_bandwidth ifr_ifru.ifru_ivalue /* link bandwidth */ # define ifr_qlen ifr_ifru.ifru_ivalue /* queue length */ # define ifr_newname ifr_ifru.ifru_newname /* New name */ # define _IOT_ifreq _IOT(_IOTS(char),IFNAMSIZ,_IOTS(char),16,0,0) # define _IOT_ifreq_short _IOT(_IOTS(char),IFNAMSIZ,_IOTS(short),1,0,0) # define _IOT_ifreq_int _IOT(_IOTS(char),IFNAMSIZ,_IOTS(int),1,0,0)
最后使用recvfrom接收信息。
recvfrom(sock, buffer, MAXLINE, 0, NULL, NULL);//接收通过网卡的信息。
2.linux信号机制定时。
linux信号机制就不多说了,这里利用信号机制和系统调用alarm弄了一个定时器。
完整代码:
#include "mysock.h"
#include "analysis.h"
unsigned int sec = 3;
void alarm_h()
{
sec = 0;
// printf("*************alarm*****************\n");
}
int main()
{
int sock;
unsigned char buffer[MAXLINE];//接收缓冲区,以太网环境中ip数据包长度规定为46-1500,加上mac层的14(mac帧最后的4字节crc不包括在内),为64-1518,不过貌似再长了可以分片自己重组。
int n = 0;
struct ifreq ethreq;
if( (sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) < 0 )//使用原始套接字,ETH_P_ALL可以获取所有经过本机的数据包,当然前提是网卡为混杂模式。
{
perror("socket:");
exit(1);
}
strncpy(ethreq.ifr_name, "wlan0", IFNAMSIZ);//指定需要经过的网卡。 if( ioctl(sock, SIOCGIFFLAGS, ðreq) == -1 )//获得该网卡的接口信息。 { perror("ioctl:"); close(sock); exit(1); } ethreq.ifr_flags |= IFF_PROMISC;//设置成混杂模式。 if( ioctl(sock, SIOCSIFFLAGS, ðreq) == -1)//将网卡的接口信息写回。 { perror("ioctl:"); close(sock); exit(1); }
struct sigaction newact, oldact;//使用SIGALRM信号计时。
newact.sa_handler = alarm_h;//信号处理函数
sigemptyset(&newact.sa_mask);//清空屏蔽字
newact.sa_flags = 0;
while(1)
{
sigaction(SIGALRM, &newact, &oldact);//改变该信号的默认行为,旧的存在oldact中,使用之后别忘了还回来。
printf("input seconds:\t");
scanf("%d", &sec);//请用户输入信息。
alarm(sec);//开始计时。sec若为0关闭计时。
while(sec > 0)//计时时间到的时候,SIFALRM信号的处理函数会将sec置零。
{
printf("-----\n");
n = recvfrom(sock, buffer, MAXLINE, 0, NULL, NULL);//接收通过网卡的信息。
if(n < 0)
{
perror("recvform:");
continue;
}
printf("recv %d bytes!\n", n);
int i;
for(i = 0 ; i < n ; i++)
{
printf("%02x\t", buffer[i]);//打印信息
if( (i+1)%12 == 0)
printf("\n");
}
printf("\n");
analysis_mac(buffer);//解析数据包。这里只能解析IP数据报。
}
alarm(0);//停止计时器。
sigaction(SIGALRM, &oldact, NULL);//改成默认行为。
}
return 0;
}
相关文章推荐
- Linux网络编程之原始套接字-ping协议实现
- Linux下使用原始套接字实现ping 功能
- Linux网络编程之原始套接字-ping协议实现
- Linux下使用原始套接字实现ping命令
- unix/linux下线程私有数据实现原理及使用方法
- 使用Netty实现通用二进制协议的高效数据传输
- Linux下使用RAW SOCKET原始套接字构造UDP原始数据帧广播到局域网,在局域网的另一台计算机上显示UDP发送的信息
- 使用Chrome快速实现数据的抓取(二)——协议
- linux网络编程:使用多进程实现socket同时收发数据
- linux 网络编程:使用两线程实现socket同时收发数据
- Linux下使用KCP协议传输数据的UDP编程优点以及使用细节
- unix/linux下线程私有数据实现原理及使用方法
- Linux网络编程:使用select函数实现socket 收发数据
- UNIX环境编程--------原始套接字学习笔记-----Linux原始套接字实现分析
- Linux原始套接字之ARP协议实现
- Android下使用TCPDUMP实现数据抓包教程
- Linux下实现MySQL数据备份和恢复的命令使用全攻略
- Linux原始套接字实现分析
- Windows下利用原始套接字实现的一个抓包程序Demo
- [转] unix/linux下线程私有数据实现原理及使用方法