Chinaunix首页 | 论坛 | 博客
  • 博客访问: 728106
  • 博文数量: 130
  • 博客积分: 10
  • 博客等级: 民兵
  • 技术积分: 2198
  • 用 户 组: 普通用户
  • 注册时间: 2011-11-29 12:48
个人简介

每一个“丑得人神共愤”的泡妞高 手都有一颗坚忍的心,这证明了人类 在绝境中毫不妥协的求生精神,反正丑都丑了,索性放开手脚大干一场,这就叫“无产阶级失去的是锁链,得到的是全世界”

文章分类

全部博文(130)

文章存档

2013年(130)

我的朋友

分类: LINUX

2013-07-11 10:36:37

原文:

如果你的工作涉及网络管理或是安全,或者你仅仅是对你的本地网络传输了什么好奇,从网络上抓取几个数据包会是一个有用的经验。通过一点C代码和网络的基本知识,你甚至可以抓取目的主机不是你的机器的数据。本文,我们会涉及Ethnet,甚至是最广泛使用的LAN技术。同样,我们会假设其源和目的主机属于同一个LAN,其原因稍后解释。

首先,我们会简单回忆一下一个普通的Ethnet网卡如何工作。如果你以了解这部分,那么可以跳过。用户的ip package被封装成 Ethnet frame(当package通过一个Ethnet segment时叫这个名字)。
Ethnet package仅仅是跟大的底层package,它包含源的IP以及一些必须带到目的主机的必要信息(见图一)。特殊的,目的地址会通过一个叫ARP的机制被映射成6 bytes目的Ethnet地址(通常叫做MAC地址)。
所以,frame包含需要通过连接他们的网线从源主机发送到目的主机的package。大致是这样的,frame将会经过hug/switch这类设备,但是因为我们假设只在LAN内,所以不涉及路由/网关。

Figure 1. IP Packets as Ethernet Frames
在Ethnet层,没有routing处理。换句话说,源的frame不会直接送到目的主机;而是frame将会copy到所有连接的网线,这时网卡会看到它(见图二)。
每个网卡会读frame的前6个bytes(及目的主机的MAC地址),但是只有自己的MAC地址和package中的MAC地址一致的网卡才会接受frame。
这时,frame将会被网络驱动解析,并且以前的IP package将会被恢复,并且通过网络协议上传到应用层。

Figure 2. Sending Ethernet Frames over the LAN
更准确的说,网络驱动会检查Ethnet frame头部的Protocol Type字段(见Figure 1),并且基于那个值,把package转发到适当的接受函数。大多数时候,接收函数将会把IP头去掉,并且把剩下部分上传给TCP/UDP接受函数。这些协议,相应的,会把它们送到socket处理函数。socket处理函数会最终把package数据送到应用程序。在这段时间,packet会失去所有与网络相关的信息,例如源地址(IP和MAC)以及端口号,ip option,TCP参数等。更多地,如果目的主机没有一个包含正确参数的打开的socket,那么packet将会被丢弃,并且不会上传到应用层。

相应的,我们当我们嗅探网络的package时,将会有两个问题。一个是Ethnet寻址-我们不能读一个目的地址不是我们主机的Ethnet package;另一个与协议栈处理有关-为了使package不被丢弃,我们对于每一个port都应该有一个监听socket。而且packet的部分信息将会在协议栈处理的时候被丢弃。

第一个问题不是根本,因为我们不关心其他主机的packet,并且趋于嗅探心目的地址是我们主机的package第二个问题必须解决。我们稍后将会一个一个地看到这些问题。

The PF_PACKET Protocol

当你用标准的socket调用打开一个socket sock = socket(domain, type, protocol),你必须指明你打算使用哪个domain(或者family)。
常用的family对于在LAN中的主机是PF_UNIX,对于基于IPv4的通信,则是PF_INET。并且,你必须指明你的socket类型,以及一个基于你使用的family的可能值。
常用的值,对于PF_INET,包含SOCK_STREAM(对应于TCP连接),SOCK_DGRAM(对应于UDP)。socke类型的作用是packet在被传到应用层之前,内核应该如何处理。
最后,你声明的协议将会处理经过socket的packet(跟多细节请参考socket的man(3))。

在2.0以后的版本中,引进了一个新的protocol family,叫PF_PACKET。它允许application直接处理网卡驱动接收/发送的packet,这样可以避免网络协议栈的处理。任何经过socket的packet将会直接送到Ethnet interface,任何interface接收的packet将会直接送到application。

PF_PACKET family支持两种socket类型,SOCK_DGRAM和SOCK_RAW。前者把增加/删除Ethnet层的header的任务交给kernel。后者把这个control交给应用层。socket()函数的protocol字段必须符合定义在/usr/include/linux/if_ether.h的Ethnet标识,这些标识标识一个可以处理Ethnet的协议。除非处理一个非常特殊的协议,你可以使用ETH_P_IP,通常他包含所有适合IP的协议(例如TCP,UDP,ICMP,raw IP等)。

因为他们有非常严格的安全含义(例如你可以迫使一个frame带有欺骗性的MAC地址),PF_PACKET只允许root使用。

PF_PACKET family轻易的解决了使用协议栈的问题。通过Listing 1,我们打开一个属于PF_PACKET family的socket,声明一个SOCK_RAW类型的sock,并且使用IP-related的协议类型。然后我们开始从socket读数据,经过一些检查,我们输出从Ethnet层和IP header中提取的一些信息。通过以图一显示的偏移交叉检查,你将会发现对于应用层来获得network层的数据是多么简单。

  1. #include <stdio.h>
  2. #include <errno.h>
  3. #include <unistd.h>
  4. #include <sys/socket.h>
  5. #include <sys/types.h>
  6. #include <linux/in.h>
  7. #include <linux/if_ether.h>

  8. int main(int argc, char **argv) {
  9.   int sock, n;
  10.   char buffer[2048];
  11.   unsigned char *iphead, *ethhead;

  12.   if ( (sock=socket(PF_PACKET, SOCK_RAW,
  13.                     htons(ETH_P_IP)))<0) {
  14.     perror("socket");
  15.     exit(1);
  16.   }

  17.   while (1) {
  18.     printf("----------\n");
  19.     n = recvfrom(sock,buffer,2048,0,NULL,NULL);
  20.     printf("%d bytes read\n",n);

  21.     /* Check to see if the packet contains at least
  22.      * complete Ethernet (14), IP (20) and TCP/UDP
  23.      * (8) headers.
  24.      */
  25.     if (n<42) {
  26.       perror("recvfrom():");
  27.       printf("Incomplete packet (errno is %d)\n",
  28.              errno);
  29.       close(sock);
  30.       exit(0);
  31.     }

  32.     ethhead = buffer;
  33.     printf("Source MAC address: "
  34.            "%02x:%02x:%02x:%02x:%02x:%02x\n",
  35.            ethhead[0],ethhead[1],ethhead[2],
  36.            ethhead[3],ethhead[4],ethhead[5]);
  37.     printf("Destination MAC address: "
  38.            "%02x:%02x:%02x:%02x:%02x:%02x\n",
  39.            ethhead[6],ethhead[7],ethhead[8],
  40.            ethhead[9],ethhead[10],ethhead[11]);

  41.     iphead = buffer+14; /* Skip Ethernet header */
  42.     if (*iphead==0x45) { /* Double check for IPv4
  43.                           * and no options present */
  44.       printf("Source host %d.%d.%d.%d\n",
  45.              iphead[12],iphead[13],
  46.              iphead[14],iphead[15]);
  47.       printf("Dest host %d.%d.%d.%d\n",
  48.              iphead[16],iphead[17],
  49.              iphead[18],iphead[19]);
  50.       printf("Source,Dest ports %d,%d\n",
  51.              (iphead[20]<<8)+iphead[21],
  52.              (iphead[22]<<8)+iphead[23]);
  53.       printf("Layer-4 protocol %d\n",iphead[9]);
  54.     }
  55.   }

  56. }

Listing 1. Protocol Stack-Handling Sniffed Packets

假如你的机器连接到Ethnet LAN,当从另一台机器发送packet到你的机器时,你可以测试我们小程序(你可以ping/telnet你的主机)。你可以看到所有发送到你的主机的packet,但是你不能看到转发到其他的host的packet。

Promiscuous vs. Nonpromiscuous Mode

PF_PACKET family运行application接受net card层的数据,但是仍然不允许读不是到该主机的主机。如同我们之前看到的,这是由于network card会丢弃不含该host MAC地址的packet,这叫做(非混乱模式),通常指的是,每一个network card处理与自己相关的packet。但是有三种例外:目的MAC地址是广播地址(FF:FF:FF:FF:FF:FF)的frame将会被接收,目的MAC地址是多播地址的将会被启动接受多播的网卡接收,一个被置成混乱模式(promiscuous mode)的网卡将会接收所有经过它的网卡。

最后一种情况是最有意思的情况。我们可以通过在一个打开的socket调用ioctl来实现。因为这是一个潜在的安全威胁的操作,它只允许root用户操作。加入“sock”是一个已经打开的socket,下列的操作将会实现:

  1. strncpy(ethreq.ifr_name,"eth0",IFNAMSIZ);
  2. ioctl(sock, SIOCGIFFLAGS, &ethreq);
  3. ethreq.ifr_flags |= IFF_PROMISC;
  4. ioctl(sock, SIOCSIFFLAGS, &ethreq);

(ethrep是一个在/usr/include/net/if.h中定义的ifreq structure)。

第一个ioctl读取当前Ethnet card的设置;然后将设置“位或”IFF_PROMISC,IFF_PROMISC打开混乱模式(promiscuous),然后通过第二个ioctl写回网卡。

通过Listing 2看一个更复杂的例子。在一个连接到LAN的机器上编译,运行它,将会看到所有网线上的packet,甚至不是到你的主机的packet。这时因为你的网卡现在是工作在混乱模式(promiscuous)下。你可以通过ifconfig命令的第三行输出确认。

如果你的LAN不是使用hub而是使用Ethnet switchs,你将会只会看到switch分发到你的主机的packet。这是有switch的工作方式决定的,并且你只能做有限的工作(除了MAC地址欺骗,这个不在本文范围)。对于hubs/switch的更多信息,参考Resource section

  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <errno.h>
  4. #include <unistd.h>
  5. #include <sys/socket.h>
  6. #include <sys/types.h>
  7. #include <linux/in.h>
  8. #include <linux/if_ether.h>
  9. #include <net/if.h>
  10. #include <sys/ioctl.h>

  11. int main(int argc, char **argv) {
  12.   int sock, n;
  13.   char buffer[2048];
  14.   unsigned char *iphead, *ethhead;
  15.   struct ifreq ethreq;
  16.   
  17.   if ( (sock=socket(PF_PACKET, SOCK_RAW,
  18.                     htons(ETH_P_IP)))<0) {
  19.     perror("socket");
  20.     exit(1);
  21.   }

  22.   /* Set the network card in promiscuos mode */
  23.   strncpy(ethreq.ifr_name,"eth0",IFNAMSIZ);
  24.   if (ioctl(sock,SIOCGIFFLAGS,&ethreq)==-1) {
  25.     perror("ioctl");
  26.     close(sock);
  27.     exit(1);
  28.   }
  29.   ethreq.ifr_flags|=IFF_PROMISC;
  30.   if (ioctl(sock,SIOCSIFFLAGS,&ethreq)==-1) {
  31.     perror("ioctl");
  32.     close(sock);
  33.     exit(1);
  34.   }
  35.   
  36.   while (1) {
  37.     printf("----------\n");
  38.     n = recvfrom(sock,buffer,2048,0,NULL,NULL);
  39.     printf("%d bytes read\n",n);

  40.     /* Check to see if the packet contains at least
  41.      * complete Ethernet (14), IP (20) and TCP/UDP
  42.      * (8) headers.
  43.      */
  44.     if (n<42) {
  45.       perror("recvfrom():");
  46.       printf("Incomplete packet (errno is %d)\n",
  47.              errno);
  48.       close(sock);
  49.       exit(0);
  50.     }

  51.     ethhead = buffer;
  52.     printf("Source MAC address: "
  53.            "%02x:%02x:%02x:%02x:%02x:%02x\n",
  54.            ethhead[0],ethhead[1],ethhead[2],
  55.            ethhead[3],ethhead[4],ethhead[5]);
  56.     printf("Destination MAC address: "
  57.            "%02x:%02x:%02x:%02x:%02x:%02x\n",
  58.            ethhead[6],ethhead[7],ethhead[8],
  59.            ethhead[9],ethhead[10],ethhead[11]);

  60.     iphead = buffer+14; /* Skip Ethernet header */
  61.     if (*iphead==0x45) { /* Double check for IPv4
  62.                           * and no options present */
  63.       printf("Source host %d.%d.%d.%d\n",
  64.              iphead[12],iphead[13],
  65.              iphead[14],iphead[15]);
  66.       printf("Dest host %d.%d.%d.%d\n",
  67.              iphead[16],iphead[17],
  68.              iphead[18],iphead[19]);
  69.       printf("Source,Dest ports %d,%d\n",
  70.              (iphead[20]<<8)+iphead[21],
  71.              (iphead[22]<<8)+iphead[23]);
  72.       printf("Layer-4 protocol %d\n",iphead[9]);
  73.     }
  74.   }
  75.   
  76. }

Listing 2. Needs caption

The Linux Packet Filter

我们的所欲的嗅探的问题都貌似解决了,但是仍有一个重要的问题:如果你尝试实验2中的example,并且你的LAN面临许多traffic(几个发送许多NETBIOS packet的主机就够浪费一些带宽),你将会发现我们的嗅探器输出太多的数据了。随着网络traffic的增加,由于pc不能足够迅速的处理packet,sniffer将会开始丢失packet。

解决这个问题的方法是过滤你接收到的packet,只输出你感兴趣的信息。一个想法就是在sniffer的代码中插入"if statement";这将会过滤掉输入,但是在从性能方面考虑不是非常有效。kernel仍需要向上传送网络中的所有的packet,这样会浪费时间,并且sniffer依旧会检查每个packet来决定是否输出相关数据。

另一个解决方案是在packet处理的过程中尽早的插入filter(他从network驱动层开始并且终止在应用层,Figure 3)。Linux kernel允许我们在PF_PACKET处理协议中插入一个叫过LPF的filter,它在网卡接处理接收数据中断不久后就开始执行。filter决定哪些数据应该发送到application,以及那些数据应该抛弃。

Figure 3. Packet-Processing Chain
为了尽可能的灵活,以及不限制与程序员的一组预定义的条件,packet-filter引擎被作为一个运行在user定义的状态机来实现的。这个程序用一种叫做BPF(Berkeley packet filter)的伪机器指令来实现,基于Steve McCanne和Van Jacobson的一篇论文(参考Resource).BPF看似像有一组寄存器和一些load/store指令计算算术和条件跳转的一种汇编语言。

每一个packet上都运行filter的代码,BPF处理的memory空间是packet数据中的bytes。filter的处理结果是一个表明该packet中有多少bytes(如果有)该传达application层的整数。这有一个好处,更多时候,你只关心一个packet的开头几个bytes,并且你可以省下copy其他bytes的处理时间。

(Not) Programming the Filter

即使BPF语言很简单易学,我们大多数更喜欢用可读的filter表达式。所以我们不用BPF语言的指令(这些可以通过上面提到的论文中到找),我们将会讨论如何从一个逻辑表达式中获得一个可用的机器指令。

首先,你得先从LBL安装一个tcpdump程序。但是如果你在读本文,你很可能早就知道并使用tcpdump。tcpdump的第一个版本由提交BPF提议的一些人实现。事实上,tcpdump通过一个叫libpcap的lib使用BPF抓包/过滤packet。该lib是独立于操作系统的。当在Linux中使用,就会使用Linux的packet filter实现BPF函数。

libpcap提供的最有用的函数当中的一个就是pcap_compile(),它接收一个逻辑表达式作为输入,输出BPF filter code。tcpdump使用这个函数把澀输入的命令行表达式转换成BPF filter。对我们来说,有趣的是tcpdump很少使用-d(输出filter的代码)。

例如,“tcpdump host 192.168.9.10”会开始嗅探和抓取源/目的IP地址是192.168.9.10的packet。“tcpdump -d host 192.168.9.10”将会输入组织filter的BPF代码。

  1. echer:~# tcpdump -d host 192.168.9.10
  2. (000) ldh [12]
  3. (001) jeq #0x800 jt 2 jf 6
  4. (002) ld [26]
  5. (003) jeq #0xc0a8090a jt 12 jf 4
  6. (004) ld [30]
  7. (005) jeq #0xc0a8090a jt 12 jf 13
  8. (006) jeq #0x806 jt 8 jf 7
  9. (007) jeq #0x8035 jt 8 jf 13
  10. (008) ld [28]
  11. (009) jeq #0xc0a8090a jt 12 jf 10
  12. (010) ld [38]
  13. (011) jeq #0xc0a8090a jt 12 jf 13
  14. (012) ret #68
  15. (013) ret #0

Listing 3. Tcpdump -d Results

让我们简要的看看代码:lines 0-1 and 6-7表示通过比较protocol IDs(/usr/include/linux/if_ether.h)与frame中第12个偏移,被抓取的packet实际是TP/ARP/RARP协议。如果比较失败,packet将会丢弃(line 13)。

Lines 2-5和8-11比较源/目的IP地址与192.168.9.10。注意不同的协议中的偏移是不同的:如果是IP,偏移是28/38。如果IP匹配,那么packet将会被filter接收,并且前68bytes将会被传到application(line 12)。

filter code不都是优化后的,因为它是有一个为通用的BPF生成的,并且不为当前运行filter程序的架构优化。LPF的特殊情况,被PF_PACKET处理程序运行的filter,也许已经检查Ethnet protocol了。这取决于你在socket()函数中你使用的protocol类型:如果不是“ETH_P_ALL”(指所有Ethnet frame都应该被抓取),那么只有声明Ethent protocol的packet会到达filter。例如:一个ETH_P_ALL的socket,我们可以重写一个如下的更快更紧凑的filter:

  1. (000) ld [26]
  2. (001) jeq #0xc0a8090a jt 4 jf 2
  3. (002) ld [30]
  4. (003) jeq #0xc0a8090a jt 4 jf 5
  5. (004) ret #68
  6. (005) ret #0

安装filter

安装filter是一个简单的操作:只需要创建一个包含filter的sock_filter structure,并且将它关联到一个打开的socket上。

filter structure可以通过tcpdump -dd而获得。输出的filter将会如同C中你可以copy&paste的数组,如List4.然后你可以用过setsockopt函数将它关联到一个socket上。

  1. escher:~# tcpdump -dd host 192.168.9.01
  2. { 0x28, 0, 0, 0x0000000c },
  3. { 0x15, 0, 4, 0x00000800 },
  4. { 0x20, 0, 0, 0x0000001a },
  5. { 0x15, 8, 0, 0xc0a80901 },
  6. { 0x20, 0, 0, 0x0000001e },
  7. { 0x15, 6, 7, 0xc0a80901 },
  8. { 0x15, 1, 0, 0x00000806 },
  9. { 0x15, 0, 5, 0x00008035 },
  10. { 0x20, 0, 0, 0x0000001c },
  11. { 0x15, 2, 0, 0xc0a80901 },
  12. { 0x20, 0, 0, 0x00000026 },
  13. { 0x15, 0, 1, 0xc0a80901 },
  14. { 0x6, 0, 0, 0x00000044 },
  15. { 0x6, 0, 0, 0x00000000 },

Listing 4. tcpdump with --dd Switch

我们将会通过一个复杂的例子Listing 5来结束本文。他与前两个例子及其相似,除了增加LSFcode并且调用setsockopt。filter被配置成只选择UDP packet,源/目的IP地址为192.168.9.10并且UDP端口等于5000。

为了测试这个例子,你需要一个简易生成随机UDPpacket的办法(例如“sendip”或“apsend” )。同时,你也许想使其适用于在你的LAN中匹配的IP address。为了实现,把filter code中的0xc0a8090a改成你希望的IP地址的十六进制数形式。

最后值得关注的是当你退出程序时网卡的状态。因为我们没有重置Ethnet flags,网卡将会维持在混乱模式(promiscuous)。为了解决这个问题,你只需要在函数退出前,为Control-C(SIGINT)信号安装一个将Ethnet flag重启为它先前状态(在“位或”之前保存的值)的处理函数。

  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <errno.h>
  4. #include <unistd.h>
  5. #include <sys/socket.h>
  6. #include <sys/types.h>
  7. #include <linux/in.h>
  8. #include <linux/if_ether.h>
  9. #include <net/if.h>
  10. #include <linux/filter.h>
  11. #include <sys/ioctl.h>

  12. int main(int argc, char **argv) {
  13.   int sock, n;
  14.   char buffer[2048];
  15.   unsigned char *iphead, *ethhead;
  16.   struct ifreq ethreq;

  17.   /*
  18.     udp and host 192.168.9.10 and src port 5000
  19.     (000) ldh [12]
  20.     (001) jeq #0x800 jt 2 jf 14
  21.     (002) ldb [23]
  22.     (003) jeq #0x11 jt 4 jf 14
  23.     (004) ld [26]
  24.     (005) jeq #0xc0a8090a jt 8 jf 6
  25.     (006) ld [30]
  26.     (007) jeq #0xc0a8090a jt 8 jf 14
  27.     (008) ldh [20]
  28.     (009) jset #0x1fff jt 14 jf 10
  29.     (010) ldxb 4*([14]&0xf)
  30.     (011) ldh [x + 14]
  31.     (012) jeq #0x1388 jt 13 jf 14
  32.     (013) ret #68
  33.     (014) ret #0
  34.   */
  35.   struct sock_filter BPF_code[]= {
  36.     { 0x28, 0, 0, 0x0000000c },
  37.     { 0x15, 0, 12, 0x00000800 },
  38.     { 0x30, 0, 0, 0x00000017 },
  39.     { 0x15, 0, 10, 0x00000011 },
  40.     { 0x20, 0, 0, 0x0000001a },
  41.     { 0x15, 2, 0, 0xc0a8090a },
  42.     { 0x20, 0, 0, 0x0000001e },
  43.     { 0x15, 0, 6, 0xc0a8090a },
  44.     { 0x28, 0, 0, 0x00000014 },
  45.     { 0x45, 4, 0, 0x00001fff },
  46.     { 0xb1, 0, 0, 0x0000000e },
  47.     { 0x48, 0, 0, 0x0000000e },
  48.     { 0x15, 0, 1, 0x00001388 },
  49.     { 0x6, 0, 0, 0x00000044 },
  50.     { 0x6, 0, 0, 0x00000000 }
  51.   };
  52.   struct sock_fprog Filter;
  53.     
  54.   Filter.len = 15;
  55.   Filter.filter = BPF_code;
  56.   
  57.   if ( (sock=socket(PF_PACKET, SOCK_RAW,
  58.                     htons(ETH_P_IP)))<0) {
  59.     perror("socket");
  60.     exit(1);
  61.   }

  62.   /* Set the network card in promiscuos mode */
  63.   strncpy(ethreq.ifr_name,"eth0",IFNAMSIZ);
  64.   if (ioctl(sock,SIOCGIFFLAGS,&ethreq)==-1) {
  65.     perror("ioctl");
  66.     close(sock);
  67.     exit(1);
  68.   }
  69.   ethreq.ifr_flags|=IFF_PROMISC;
  70.   if (ioctl(sock,SIOCSIFFLAGS,&ethreq)==-1) {
  71.     perror("ioctl");
  72.     close(sock);
  73.     exit(1);
  74.   }
  75.  
  76.   /* Attach the filter to the socket */
  77.   if(setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER,
  78.                 &Filter, sizeof(Filter))<0){
  79.     perror("setsockopt");
  80.     close(sock);
  81.     exit(1);
  82.   }
  83.  
  84.   while (1) {
  85.     printf("----------\n");
  86.     n = recvfrom(sock,buffer,2048,0,NULL,NULL);
  87.     printf("%d bytes read\n",n);

  88.     /* Check to see if the packet contains at least
  89.      * complete Ethernet (14), IP (20) and TCP/UDP
  90.      * (8) headers.
  91.      */
  92.     if (n<42) {
  93.       perror("recvfrom():");
  94.       printf("Incomplete packet (errno is %d)\n",
  95.              errno);
  96.       close(sock);
  97.       exit(0);
  98.     }

  99.     ethhead = buffer;
  100.     printf("Source MAC address: "
  101.            "%02x:%02x:%02x:%02x:%02x:%02x\n",
  102.            ethhead[0],ethhead[1],ethhead[2],
  103.            ethhead[3],ethhead[4],ethhead[5]);
  104.     printf("Destination MAC address: "
  105.            "%02x:%02x:%02x:%02x:%02x:%02x\n",
  106.            ethhead[6],ethhead[7],ethhead[8],
  107.            ethhead[9],ethhead[10],ethhead[11]);

  108.     iphead = buffer+14; /* Skip Ethernet header */
  109.     if (*iphead==0x45) { /* Double check for IPv4
  110.                           * and no options present */
  111.       printf("Source host %d.%d.%d.%d\n",
  112.              iphead[12],iphead[13],
  113.              iphead[14],iphead[15]);
  114.       printf("Dest host %d.%d.%d.%d\n",
  115.              iphead[16],iphead[17],
  116.              iphead[18],iphead[19]);
  117.       printf("Source,Dest ports %d,%d\n",
  118.              (iphead[20]<<8)+iphead[21],
  119.              (iphead[22]<<8)+iphead[23]);
  120.       printf("Layer-4 protocol %d\n",iphead[9]);
  121.     }
  122.   }
  123.   
  124. }
Listing 5. Needs caption

Conclusions

在LAN中sniffer packet是一个有测试网络问题/收集量度的有用工具。有时候,例如tcpdump/ethereal这类常用工具,不会完全满足你的需求,这时重写sniffer将会起到很大哦帮助。由于LPF,你可以有效方便的完成。

Resources

For further details on Ethernet networks, please refer to , which contains some articles on networking basics and Ethernet networking.

The BPF language is described in the following paper by Steven McCanne and Van Jacobson: “The BSD Packet Filter: a New Architecture for User-level Packet Capture”, available at .
The tcpdump program is available at .

阅读(1049) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~