您的位置:首页 > 运维架构 > Linux

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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: