/* capture_packet.c - 截获所有以太网帧数据并进行具体分析 */
/* 常用函数的头文件 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <signal.h>
/* 与网络相关的头文件 */
#include <netinet/ip_icmp.h>
#include <net/if_arp.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <netdb.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <signal.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/if_ether.h>
#include <net/ethernet.h>
#include <linux/igmp.h>
#include <netinet/tcp.h>
/* 全局变量结构的结构体原型 - 包含要记录的任何全局信息 */
struct
global_info {
unsigned
int
bytes;
/* 网卡接收的总字节数 */
unsigned
int
packet_num;
/* 网卡接受的帧的总数量 */
unsigned
int
packet_arp;
/* 接收到的arp包的数量 */
unsigned
int
packet_rarp;
/* 接收到的rarp包的数量 */
unsigned
int
packet_ip;
/* 接收到的ip包的数量 */
unsigned
int
packet_icmp;
/* 接收到的icmp包的数量 */
unsigned
int
packet_igmp;
/* 接收到的igmp包的数量 */
unsigned
int
packet_tcp;
/* 接收到的tcp包的数量 */
unsigned
int
packet_udp;
/* 接收到的udp包的数量 */
int
print_flag_frame;
/* 是否打印帧头信息标志, 1表示打印, 0表示不打印 */
int
print_flag_arp;
/* 是否打印arp头信息标志 */
int
print_flag_ip;
/* 是否打印ip头信息标志 */
int
print_flag_rarp;
/* 是否打印rarp头信息标志 */
int
print_flag_tcp;
/* 是否打印tcp头信息标志 */
int
print_flag_udp;
/* 是否打印udp头信息标志 */
int
print_flag_icmp;
/* 是否打印icmp头信息标志 */
int
print_flag_igmp;
/* 是否打印igmp头信息标志 */
};
/* 定义一个全局变量,用于存储全局信息 */
struct
global_info global;
struct
ip_pair {
unsigned
int
source_ip;
unsigned
int
dest_ip;
unsigned
int
count;
};
/* 定义一个用于存储ip对的结构体数组 */
struct
ip_pair ip_pair[10000];
/* 一个用于初始化全局信息的函数 */
void
init_global(
struct
global_info * var );
/* 一个用于打印全局信息的函数 */
void
print_global(
struct
global_info var );
/* 打印一个错误,并退出 */
void
error_and_exit(
char
* msg,
int
exit_code );
/* 设置网卡成混杂模式 */
int
set_card_promisc(
char
* interface_name,
int
sock );
/* 把mac地址转换一个字符串 */
void
mac_to_str(
char
* buf,
char
* mac_buf );
/* 用于打印帮助信息 */
void
help(
void
);
/* 截获网卡帧数据,并进行数据分用*/
void
do_frame(
int
sockfd );
/* 处理ip层数据 */
void
do_ip(
char
* data );
/* 打印ip头信息 */
void
print_ip(
struct
iphdr * );
/* 处理arp层数据 */
void
do_arp(
char
* data );
/* 打印arp头信息 */
void
print_arp(
struct
arphdr * );
/* 处理rarp数据 */
void
do_rarp(
char
* data );
/* 处理tcp层数据 */
void
do_tcp(
char
* data );
/* 打印tcp层头信息 */
void
print_tcp(
struct
tcphdr * );
/* 处理udp层数据 */
void
do_udp(
char
* data );
/* 打印udp层头信息 */
void
print_udp(
struct
udphdr * );
/* 处理icmp层数据 */
void
do_icmp(
char
* data );
/* 打印icmp头信息 */
void
print_icmp(
struct
icmphdr * );
/* 处理igmp层数据 */
void
do_igmp(
char
* data );
/* 打印igmp头信息 */
void
print_igmp(
struct
igmphdr * );
/* 初始化一个全局结构体 */
void
init_global(
struct
global_info * var )
{
var->bytes = 0;
var->packet_num = 0;
var->packet_arp = 0;
var->packet_rarp = 0;
var->packet_ip = 0;
var->packet_icmp = 0;
var->packet_igmp = 0;
var->packet_tcp = 0;
var->packet_udp = 0;
var->print_flag_frame = 0;
var->print_flag_arp = 0;
var->print_flag_ip = 0;
var->print_flag_rarp = 0;
var->print_flag_tcp = 0;
var->print_flag_udp = 0;
var->print_flag_icmp = 0;
var->print_flag_igmp = 0;
}
/* 一个用于打印全局信息的函数 */
void
print_global(
struct
global_info var )
{
printf
(
"\n\n********** 全局信息 *****************\n\n"
);
printf
(
"总共接收字节数: %d kbytes.\n"
, var.bytes / 1024 );
printf
(
"总共接受包数量: %d\n\n"
, var.packet_num );
if
( var.packet_arp )
printf
(
"接收 arp 包数量: %d\n"
, var.packet_arp );
if
( var.packet_rarp)
printf
(
"接收 rarp 包数量: %d\n"
, var.packet_rarp );
if
( var.packet_ip )
printf
(
"接收 ip 包数量: %d\n"
, var.packet_ip );
if
( var.packet_icmp)
printf
(
"接收 icmp 包数量: %d\n"
, var.packet_icmp );
if
( var.packet_igmp)
printf
(
"接收 igmp 包数量: %d\n"
, var.packet_igmp );
if
( var.packet_tcp )
printf
(
"接收 tcp 包数量: %d\n"
, var.packet_tcp );
if
( var.packet_udp )
printf
(
"接收 udp 包数量: %d\n"
, var.packet_udp );
printf
(
"\n"
);
}
/* 用于处理当下按ctrl-c时的处理函数 */
void
sig_int(
int
sig )
{
print_global( global );
int
i;
/*
for( i=0; i<global.packet_ip; i++ ){
printf("%15s ==>> ", inet_ntoa( *(struct in_addr *)( &ip_pair[i].source_ip ) ) );
printf("%15s \n", inet_ntoa( *(struct in_addr *)( &ip_pair[i].dest_ip ) ));
}
*/
exit
( 0 );
}
/* 打印错误信息,并退出 */
void
error_and_exit(
char
* msg,
int
exit_code )
{
herror( msg );
exit
( exit_code );
}
/* 设置网卡模式成混帐模式,这样的话可以截获以太网帧数据 */
int
set_card_promisc(
char
* interface_name,
int
sock )
{
/* 用于套接口ioctl的接口请求结构体 */
struct
ifreq ifr;
/* 复制网卡名称进入请求结构体的名称元素 */
strncpy
(ifr.ifr_name, interface_name ,
strlen
( interface_name )+1);
/* 通过ioctl获得相应信息 */
if
((ioctl(sock, SIOCGIFFLAGS, &ifr) == -1)) {
error_and_exit(
"ioctl"
, 2);
}
/* 设置网卡模式标志为混杂模式 */
ifr.ifr_flags |= IFF_PROMISC;
/* 通过ioctl把参数传递给网卡 */
if
(ioctl(sock, SIOCSIFFLAGS, &ifr) == -1 )
error_and_exit(
"ioctl"
, 3);
}
/* 把mac地址转换成字符串 */
void
mac_to_str(
char
* buf,
char
* mac_buf )
{
sprintf
( mac_buf,
"%02x:%02x:%02x:%02x:%02x:%02x"
,(unsigned
char
) *buf, (unsigned
char
)(*(buf+1)),
(unsigned
char
)(*(buf+2)), (unsigned
char
)(*(buf+3)),
(unsigned
char
)(*(buf+4)), (unsigned
char
)(*(buf+5)));
mac_buf[17] = 0;
}
void
help(
void
)
{
printf
(
"Usage: capture [-h] [协议名称 ...].\n"
);
printf
(
"默认情况: 打印所有包信息.\n"
);
}
void
print_udp(
struct
udphdr * pudp )
{
printf
(
"==================== udp 头信息 ======================\n"
);
printf
(
"16位源端口号 : %d\n"
, ntohs( pudp->source ) );
printf
(
"16位目的端口号: %d\n"
, ntohs( pudp->dest ) );
printf
(
"16位udp长度: %d\n"
, ntohs( pudp->len ) );
printf
(
"16位udp校验和: %d\n"
, ntohs( pudp->check ) );
if
( ntohs( pudp->len ) !=
sizeof
(
struct
udphdr ) && ntohs( pudp->len ) < 20 ){
char
* data = (
char
* )pudp +
sizeof
(
struct
udphdr );
printf
(
"UDP数据: %s\n"
, data );
}
}
void
do_udp(
char
* data )
{
global.packet_udp ++;
struct
udphdr * pudp = (
struct
udphdr * )data;
if
( global.print_flag_udp )
print_udp( pudp );
}
void
print_tcp(
struct
tcphdr * ptcp )
{
printf
(
"==================== tcp 头信息 =====================\n"
);
printf
(
"源端口号 : %d\n"
, ntohs( ptcp->source ) );
printf
(
"目的端口号: %d\n"
, ntohs( ptcp->dest ) );
printf
(
"32位序列号 : %u\n"
, ntohl( ptcp->seq ) );
printf
(
"32位确认序号: %u\n"
, ntohl( ptcp->ack_seq ) );
printf
(
"首部长度: %d\n"
, ptcp->doff * 4 );
printf
(
"6个标志位: \n"
);
printf
(
" 紧急指针 urg : %d\n"
, ptcp->urg );
printf
(
" 确认序号位 ack : %d\n"
, ptcp->ack );
printf
(
" 接受方尽快将报文交给应用层 psh : %d\n"
, ptcp->psh );
printf
(
" 重建连接 rst : %d\n"
, ptcp->rst );
printf
(
" 用来发起连接的同步序号 syn : %d\n"
, ptcp->syn );
printf
(
" 发送端完成任务 fin : %d\n"
, ptcp->fin );
printf
(
"16位窗口大小: %d\n"
, ntohs( ptcp->window ) );
printf
(
"16位校验和: %d\n"
, ntohs( ptcp->check ) );
printf
(
"16位紧急指针: %d\n"
, ntohs( ptcp->urg_ptr ) );
if
( ptcp->doff * 4 == 20 ){
printf
(
"选项数据: 没有\n"
);
}
else
{
printf
(
"选项数据: %d 字节\n"
, ptcp->doff * 4 - 20 );
}
char
* data = (
char
* )ptcp;
data += ptcp->doff * 4;
printf
(
"数据长度: %d 字节\n"
,
strlen
(data) );
if
(
strlen
(data) < 10 )
printf
(
"数据: %s\n"
, data );
}
void
do_tcp(
char
* data )
{
global.packet_tcp ++;
struct
tcphdr * ptcp;
ptcp = (
struct
tcphdr * )data;
if
( global.print_flag_tcp )
print_tcp( ptcp );
}
void
print_igmp(
struct
igmphdr * pigmp )
{
printf
(
"==================== igmp 包信息 ==========================\n"
);
printf
(
"igmp 版本: %d\n"
, pigmp->type & 15 );
printf
(
"igmp 类型: %d\n"
, pigmp->type >> 4 );
printf
(
"igmp 码: %d\n"
, pigmp->code );
printf
(
"igmp 校验和: %d\n"
, ntohs( pigmp->csum ) );
printf
(
"igmp 组地址: %d\n"
, ntohl( pigmp->group ) );
}
void
do_igmp(
char
* data )
{
global.packet_igmp ++;
struct
igmphdr * pigmp = (
struct
igmphdr * ) data;
if
( global.print_flag_igmp )
print_igmp( pigmp );
}
void
print_icmp(
struct
icmphdr * picmp )
{
printf
(
"==================== icmp 包信息 ===========================\n"
);
printf
(
"消息类型: %d "
, picmp->type );
switch
( picmp->type ){
case
ICMP_ECHOREPLY:
printf
(
"Ping的回显应答\n"
);
break
;
case
ICMP_DEST_UNREACH:
printf
(
"目的不可达\n"
);
break
;
case
ICMP_SOURCE_QUENCH:
printf
(
"源端被关闭\n"
);
break
;
case
ICMP_REDIRECT:
printf
(
"重定相\n"
);
break
;
case
ICMP_ECHO:
printf
(
"ping的回显请求\n"
);
break
;
case
ICMP_TIME_EXCEEDED:
printf
(
"超时\n"
);
break
;
case
ICMP_PARAMETERPROB:
printf
(
"参数问题\n"
);
break
;
case
ICMP_TIMESTAMP:
printf
(
"时间戳请求\n"
);
break
;
case
ICMP_TIMESTAMPREPLY:
printf
(
"时间戳应答\n"
);
break
;
case
ICMP_INFO_REQUEST:
printf
(
"信息请求\n"
);
break
;
case
ICMP_INFO_REPLY:
printf
(
"信息应答\n"
);
break
;
case
ICMP_ADDRESS:
printf
(
"地址掩码请求\n"
);
break
;
case
ICMP_ADDRESSREPLY:
printf
(
"地址掩码应答\n"
);
break
;
default
:
printf
(
"未知消息类型\n"
);
break
;
}
printf
(
"消息类型的子选项: %d "
, picmp->code );
switch
( picmp->type ){
case
ICMP_ECHOREPLY:
printf
(
"Ping的回显应答\n"
);
break
;
case
ICMP_DEST_UNREACH:
switch
( picmp->type ){
case
ICMP_NET_UNREACH:
printf
(
"网络不可到达\n"
);
break
;
case
ICMP_HOST_UNREACH:
printf
(
"主机不可到达\n"
);
break
;
case
ICMP_PROT_UNREACH:
printf
(
"协议不可到达\n"
);
break
;
case
ICMP_PORT_UNREACH:
printf
(
"端口不可到达\n"
);
break
;
case
ICMP_FRAG_NEEDED:
printf
(
"需要进行分片,但是又设置不分片位\n"
);
break
;
case
ICMP_SR_FAILED:
printf
(
"源站选路失败\n"
);
break
;
case
ICMP_NET_UNKNOWN:
printf
(
"目的网络不认识\n"
);
break
;
case
ICMP_HOST_UNKNOWN:
printf
(
"目的主机不认识\n"
);
break
;
case
ICMP_HOST_ISOLATED:
printf
(
"源主机北隔离\n"
);
break
;
case
ICMP_NET_ANO:
printf
(
"目的网络被强制禁止\n"
);
break
;
case
ICMP_HOST_ANO:
printf
(
"目的主机被强制禁止\n"
);
break
;
case
ICMP_NET_UNR_TOS:
printf
(
"由于服务类型TOS,网络不可到达\n"
);
break
;
case
ICMP_HOST_UNR_TOS:
printf
(
"由于服务类型TOS,主机不可到达\n"
);
break
;
case
ICMP_PKT_FILTERED:
printf
(
"由于过滤,通信被强制禁止\n"
);
break
;
case
ICMP_PREC_VIOLATION:
printf
(
"主机越权\n"
);
break
;
case
ICMP_PREC_CUTOFF:
printf
(
"优先权中止生效\n"
);
break
;
default
:
printf
(
"未知代码\n"
);
break
;
}
break
;
case
ICMP_SOURCE_QUENCH:
printf
(
"源端被关闭\n"
);
break
;
case
ICMP_REDIRECT:
switch
( picmp->type ){
case
ICMP_REDIR_NET:
printf
(
"对网络重定向\n"
);
break
;
case
ICMP_REDIR_HOST:
printf
(
"对主机重定向\n"
);
break
;
case
ICMP_REDIR_NETTOS:
printf
(
"对服务类型和网络重定向\n"
);
break
;
case
ICMP_REDIR_HOSTTOS:
printf
(
"对服务类型和主机重定向\n"
);
break
;
defalut:
printf
(
"未知代码\n"
);
break
;
}
break
;
case
ICMP_ECHO:
printf
(
"ping的回显请求\n"
);
break
;
case
ICMP_TIME_EXCEEDED:
switch
( picmp->type ){
case
ICMP_EXC_TTL:
printf
(
"在传输期间生存时间为0\n"
);
break
;
case
ICMP_EXC_FRAGTIME:
printf
(
"在数据组装期间生存时间为0\n"
);
break
;
default
:
printf
(
"未知代码\n"
);
break
;
}
break
;
case
ICMP_PARAMETERPROB:
switch
( picmp->type ){
case
0:
printf
(
"IP首部错误(包括各种差错)\n"
);
break
;
case
1:
printf
(
"缺少必须的选项\n"
);
break
;
default
:
printf
(
"原因未知\n"
);
break
;
}
break
;
case
ICMP_TIMESTAMP:
printf
(
"时间戳请求\n"
);
break
;
case
ICMP_TIMESTAMPREPLY:
printf
(
"时间戳应答\n"
);
break
;
case
ICMP_INFO_REQUEST:
printf
(
"信息请求\n"
);
break
;
case
ICMP_INFO_REPLY:
printf
(
"信息应答\n"
);
break
;
case
ICMP_ADDRESS:
printf
(
"地址掩码请求\n"
);
break
;
case
ICMP_ADDRESSREPLY:
printf
(
"地址掩码应答\n"
);
break
;
default
:
printf
(
"未知消息类型\n"
);
break
;
}
printf
(
"校验和: %d\n"
, ntohs(picmp->checksum) );
}
void
do_icmp(
char
* data )
{
global.packet_icmp ++;
struct
icmphdr * picmp = (
struct
icmphdr * ) data;
if
( global.print_flag_icmp )
print_icmp( picmp );
}
void
print_ip(
struct
iphdr * iph )
{
printf
(
"=============== ip 头信息 ===============\n"
);
printf
(
"IP 首部长度:%d\n"
, iph->ihl * 4 );
printf
(
"IP 版本 :%d\n"
, iph->version );
printf
(
"服务类型(tos): %d\n"
, iph->tos );
printf
(
"总长度字节: %d\n"
, ntohs(iph->tot_len) );
printf
(
"16位标识: %d\n"
, ntohs(iph->id) );
printf
(
"frag off: %d\n"
, ntohs(iph->frag_off) );
printf
(
"8位生存事件: %d\n"
, iph->ttl );
printf
(
"8位协议: %d\n"
, iph->protocol );
printf
(
"16位首部校验和: %d\n"
, ntohs(iph->check) );
printf
(
"32位源IP地址 : %s\n"
, inet_ntoa( *(
struct
in_addr *)(&iph->saddr)) );
printf
(
"32位目的IP地址: %s\n"
, inet_ntoa( *(
struct
in_addr *)(&iph->daddr)) );
}
void
ip_count(
struct
iphdr * iph )
{
ip_pair[ global.packet_ip - 1 ].source_ip = iph->saddr;
ip_pair[ global.packet_ip - 1 ].dest_ip = iph->daddr;
}
void
do_ip(
char
* data )
{
global.packet_ip ++;
struct
iphdr *pip;
pip = (
struct
iphdr * ) data;
/* pip = point to ip layer */
if
( global.print_flag_ip )
print_ip( pip );
ip_count( pip );
char
* pdata = data + pip->ihl * 4;
switch
( pip->protocol ){
case
IPPROTO_ICMP:
do_icmp( pdata );
break
;
case
IPPROTO_IGMP:
do_igmp( pdata );
break
;
case
IPPROTO_TCP:
do_tcp( pdata );
break
;
case
IPPROTO_UDP:
do_udp( pdata );
break
;
default
:
printf
(
"IP: 未知其上层协议.\n"
);
break
;
}
}
void
print_arp(
struct
arphdr * parp )
{
printf
(
"硬件类型: %d "
, ntohs(parp->ar_hrd) );
switch
( ntohs( parp->ar_hrd ) ){
case
ARPHRD_ETHER:
printf
(
"Ethernet 10/100Mbps.\n"
);
break
;
case
ARPHRD_EETHER:
printf
(
"Experimental Ethernet.\n"
);
break
;
case
ARPHRD_AX25:
printf
(
"AX.25 Level 2.\n"
);
break
;
case
ARPHRD_PRONET:
printf
(
"PROnet token ring.\n"
);
break
;
case
ARPHRD_IEEE802:
printf
(
"IEEE 802.2 Ethernet/TR/TB.\n"
);
break
;
case
ARPHRD_APPLETLK:
printf
(
"APPLEtalk.\n"
);
break
;
case
ARPHRD_ATM:
printf
(
"ATM.\n"
);
break
;
case
ARPHRD_IEEE1394:
printf
(
"IEEE 1394 IPv4 .\n"
);
break
;
default
:
printf
(
"Unknow.\n"
);
break
;
}
printf
(
"映射的协议地址类型: %d "
, ntohs(parp->ar_pro) );
switch
( ntohs(parp->ar_pro) ){
case
ETHERTYPE_IP:
printf
(
"IP.\n"
);
break
;
default
:
printf
(
"error.\n"
);
break
;
}
printf
(
"硬件地址长度: %d\n"
, parp->ar_hln );
printf
(
"协议地址长度: %d\n"
, parp->ar_pln );
printf
(
"操作码: %d "
, ntohs(parp->ar_op) );
switch
( ntohs(parp->ar_op) ){
case
ARPOP_REQUEST:
printf
(
"ARP 请求.\n"
);
break
;
case
ARPOP_REPLY:
printf
(
"ARP 应答.\n"
);
break
;
case
ARPOP_RREQUEST:
printf
(
"RARP 请求.\n"
);
break
;
case
ARPOP_RREPLY:
printf
(
"RARP 应答.\n"
);
break
;
case
ARPOP_InREQUEST:
printf
(
"InARP 请求.\n"
);
break
;
case
ARPOP_InREPLY:
printf
(
"InARP 应答.\n"
);
break
;
case
ARPOP_NAK:
printf
(
"(ATM)ARP NAK.\n"
);
break
;
default
:
printf
(
"arp 操作码错误.\n"
);
break
;
}
char
* addr = (
char
*)(parp + 1);
char
buf[18];
mac_to_str( addr, buf );
printf
(
"发送端以太网地址: %s\n"
, buf );
printf
(
"发送端IP地址: %s\n"
, inet_ntoa( *(
struct
in_addr *)(addr+6) ));
mac_to_str( addr+10, buf );
printf
(
"目的以太网地址: %s\n"
, buf );
printf
(
"目的IP地址: %s\n"
, inet_ntoa( *(
struct
in_addr *)(addr+16) ));
}
void
do_arp(
char
* data )
{
global.packet_arp ++;
struct
arphdr * parp;
parp = (
struct
arphdr * ) data;
if
( global.print_flag_arp ) {
printf
(
"============= arp 头信息 ==============\n"
);
print_arp( parp );
}
}
void
do_rarp(
char
* data )
{
global.packet_rarp ++;
struct
arphdr * parp;
parp = (
struct
arphdr * ) data;
if
( global.print_flag_rarp ){
printf
(
"============= rarp 头信息 =============\n"
);
print_arp( parp );
}
}
/* 打印以太网帧的包头信息 */
void
print_frame(
struct
ether_header * peth )
{
/* 定义一个数组,用于存储把mac地址转换成字符串后的字符串 */
char
buf[ 18 ];
printf
(
"\n================================== 第 %d 个包 =======================================\n\n"
, global.packet_num );
printf
(
"==== 以太网帧信息 =====\n"
);
char
* shost = peth->ether_shost;
mac_to_str( shost, buf );
printf
(
"源以太网地址: %s\n"
, buf );
char
* dhost = peth->ether_dhost;
mac_to_str( dhost, buf );
printf
(
"目的以太网地址:%s\n"
, buf );
}
/* 用于从网卡接受一帧数据,同时根据以太网协议字段传递数据给相应的上层协议处理 */
void
do_frame(
int
sock )
{
/* 用于存储一帧数据 */
char
frame_buf[ 2000 ];
/* 清空帧数据缓冲区 */
bzero( frame_buf,
sizeof
(frame_buf) );
int
len =
sizeof
( frame_buf );
/* 用于存储接受字节数 */
int
recv_num;
/* 用于存储发送方的地址信息 */
struct
sockaddr_in addr;
/* 从网卡接收一帧数据 */
recv_num = recvfrom( sock, (
char
*)frame_buf,
sizeof
( frame_buf ), 0, (
struct
sockaddr * )&addr, &len );
/* 所接收的包的总数自加1 */
global.packet_num ++;
/* 从网卡接收的字节总数 */
global.bytes += recv_num;
/* 打印接收的包是第几个包 */
//printf("此帧数据长度: %d\n", recv_num );
/* 定义一个用于指向以太网帧的指针 (这里我们只考虑最常见的以太网帧的情况) */
struct
ether_header * peth;
/* 让以太网头指针指向从网卡接受到的帧的数据的开头 */
peth = (
struct
ether_header *)frame_buf;
/* 传递以太网帧首地址给打印以太网帧信息的打印函数 */
if
( global.print_flag_frame )
print_frame( peth );
/* 定义一个数据指针,用于指向以太网帧的数据部分 */
char
* pdata;
/* 让 pdata 指向以太网帧的数据部分 */
pdata = frame_buf +
sizeof
(
struct
ether_header );
/* 根据以太网帧的协议字段进行数据分用 - 也就是进行数据拆封,根据协议字段调用相应层的处理函数 */
switch
( ntohs( peth->ether_type ) ){
case
ETHERTYPE_PUP:
break
;
case
ETHERTYPE_IP:
do_ip( pdata );
break
;
case
ETHERTYPE_ARP:
do_arp( pdata );
break
;
case
ETHERTYPE_REVARP:
do_rarp( pdata );
break
;
default
:
printf
(
"Unkonw ethernet type %d %x.\n"
, ntohs(peth->ether_type), ntohs(peth->ether_type) );
break
;
}
}
/* 主函数, 处理命令行输入, 设置好全局变量, 并调用接受和处理帧的函数 */
int
main(
int
argc,
char
** argv )
{
/* 用于存储套接口文件描述符 */
int
sockfd;
/* 初始化全局变量 */
init_global( &global );
if
( argc == 1 ) {
/* 表示打印所有包头信息 */
global.print_flag_frame = 1;
global.print_flag_arp = 1;
global.print_flag_ip = 1;
global.print_flag_rarp = 1;
global.print_flag_tcp = 1;
global.print_flag_udp = 1;
global.print_flag_icmp = 1;
global.print_flag_igmp = 1;
}
else
{
/* 帮助 或者 通过指定协议名称只打印某层些协议 */
if
( !strcasecmp( argv[1],
"-h"
) ){
help();
exit
( 0 );
}
else
{
int
i;
for
( i=1; i < argc; i++ ){
if
( !strcasecmp( argv[i],
"frame"
) )
global.print_flag_frame = 1;
else
if
( !strcasecmp( argv[i],
"arp"
) )
global.print_flag_arp = 1;
else
if
( !strcasecmp( argv[i],
"rarp"
) )
global.print_flag_rarp = 1;
else
if
( !strcasecmp( argv[i],
"ip"
) )
global.print_flag_ip = 1;
else
if
( !strcasecmp( argv[i],
"tcp"
) )
global.print_flag_tcp = 1;
else
if
( !strcasecmp( argv[i],
"udp"
) )
global.print_flag_udp = 1;
else
if
( !strcasecmp( argv[i],
"icmp"
) )
global.print_flag_icmp = 1;
else
if
( !strcasecmp( argv[i],
"igmp"
) )
global.print_flag_igmp = 1;
}
}
}
/* 通过协议族AF_PACKET类信SOCK_RAW, 类型SOCK_RAW创建一个用于可以接受网卡帧数据的套接口,同时返回套就口文件描述符 */
if
( (sockfd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)) ) == -1 )
error_and_exit(
"socket"
, 1 );
/* 如果发生错误,返回错误值, 并退出 */
/* 设定网卡eth0成混杂模式 */
set_card_promisc(
"eth0"
, sockfd );
/* 设定信号处理函数, 下面是设置当我们按下ctrl-c时所调用的处理函数 */
signal
( SIGINT, sig_int );
/* 无限循环接收以太网卡数据帧, 并进行数据分用,直到你按下ctrl-c */
while
( 1 ){
do_frame( sockfd );
}
return
0;
}