分享

基于libcpap对tcp协议以太网帧和ip的解析

 wusiqi111 2017-12-23
一、数据封装
      当我们应用程序用TCP传输数据的时候,数据被送入协议栈中,然后逐个通过每一层,知道最后到物理层数据转换成比特流,送入网络。而再这个过程中,每一层都会对要发送的数据加一些首部信息。整个过程如下图。



1、以太网帧格式
        以太网常用帧格式有两种,一种是Ethernet II,另一种是IEEE 802.3 格式。这两种格式区别是:Ethernet II中包含一个Type字 段,。而IEEE 802.3格式中,此位置是长度字段。 其中Type字段描述了,以太网首部 后面所跟数据包的类型,例如Type为0x8000时为IP协议包,Type为8060时,后面为ARP协议包。以太网中多数数据帧使用的是Ethernet II帧格式。
      a、 Ethernet II帧格式

    

     b、IEEE 802.3 帧格式

        
     前导码:Ethernet II是由8个8'b10101010构成,IEEE802.3由7个8'b10101010+1个字节SFD..
     目的地址:目的设备的MAC物理地址。
     源 地址 :发送设备的MAC物理地址。
     类型(Ethernet II)以太网首部 后面所跟数据包的类型,例如Type为0x8000时为IP协议包,Type为8060时,后面为ARP协议包。
     长度(IEEE802.3):当长度小于1500时,说明该帧为IEEE802.3帧格式,大于1500时,说明该帧为Ethernet II帧格式。
     数据:数据长度最小为46字节,不足46字节时,填充至46字节。因为最小帧长度是64字节,所以,46+6+6+2+4=64。(不算前导码)
     FCS: 就是CRC校验值

     以太网首部就是:

2、IP数据包格式
    IP数据包格式如下图。IP首部,如图也表示出来了。

    
1) 版本号 4位:表示IP协议的版本号,如目前广泛使用的是IPv4,还有其他版本的IP协议,如IPv6。通信两端协议必须一致。一般的值为0100(IPv4),0110(IPv6)。
2) 首部长度: 4 位,可表示的最大十进制数值是 15。请注意,这个字段所表示数的单位是32位字 ( 1 32位字长是字节),因此,当 IP 的首部长度为 1111  (即十进制的 15),首部长度就达到 60字节。当 IP 分组的首部长度不是4字节的整数倍时,必须利用最后的填充字段加以填充。因此数据部分永远在 4字节的整数倍开始,这样在实现 IP协议时较为方便。首部长度限制为 60字节的缺点是有时可能不够用。这样做的目的是希望用户尽量减少开销。最常用的首部长度就是 20 字节 (即首部长度为 0101),这时不使用任何选项。
  3) 服务: 占 8 位,用来获得更好的服务。这个字段在旧标准中叫做服务类型,但实际上一直没有被使用过。1998IETF把这个字段改名为区分服务 DS (DifferentiatedServices)。只有在使用区分服务时,这个字段才起作用。
 4) 总长度:总长度指首都及数据之和的长度,单位为字节。因为总长度字段为 16位,所以数据报的最大长度为 216-1=65 535字节。    IP层下面的每一种数据链路层都有自己的帧格式,其中包括帧格式中的数据字段的最大长度,即最大传送单元 MTU (Maximum Transfer Unit)。当一个数据报封装成链路层的帧时,此数据报的总长度 (即首部加上数据部分)一定不能超过下面的数据链路层的MTU值。
5) 标识符: 占 16位。IP软件在存储器中维持一个计数器,每产生一个数据报,计数器就加 1,并将此值赋给标识字段。但这个“标识”并不是序号,因为 IP是无连接的服务,数据报不存在按序接收的问题。当数据报由于长度超过网络的 MTU 而必须分片时,这个标识字段的值就被复制到所有的数据报的标识字段中。相同的标识字段的值使分片后的各数据报片最后能正确地重装成为原来的数据报。
6) 标志:占位,但目前只有2位有意义。   标志字段中的最低位记为 MF (More Fragment)MF=1即表示后面“还有分片”的数据报。MF=0表示这已是若干数据报片

中的最后一个。标志字段中间的一位记为 DF(Don't Fragment),意思是“不能分片”。只有当 DF=0时才允许分片。

7)片偏移:占 13位。较长的分组在分片后,某片在原分组中的相对位置。也就是说,相对用户数据字段的起点,该片从何处开始。片偏移以 8个字节为偏移单位。这就是说,每个分片的长度一定是 8字节 (64)的整数倍。\
8)生存时间:占 8位,生存时间字段常用的英文缩写是TTL (Time To Live),其表明数据报在网络中的寿命。由发出数据报的源点设置这个字段。其目的是防止无法交付的数据报无限制地在因特网中兜围子,因而白白消耗网络资源。最初的设计是以秒作为 TTL的单位。每经过一个路由器时,就把TTL减去数据报在路由器消耗掉的一段时间。若数据报在路由器消耗的时间小于 1 秒,就把TTL值减 1。当 TTL值为 0时,就丢弃这个数据报。
9) 协议:占 8 位,协议字段指出此数据报携带的数据是使用何种协议,以便使目的主机的IP层知道应将数据部分上交给哪个处理过程。
比较常用的协议号:
     1    ICMP  2    IGMP   6    TCP   17    UDP  88    IGRP   89    OSPF

10) 首部检验和:占 16位。这个字段只检验数据报的首部,但不包括数据部分。这是因为数据报每经过一个路由器,都要重新计算一下首都检验和 (一些字段,如生存时间、标志、片偏移等都可能发生变化)。不检验数据部分可减少计算的工作量。

11) IP源地址: 32位IP地址

12) IP目的地址: 32位IP地址


example:

#include <pcap.h>  
#include <time.h>  
#include <stdlib.h>  
#include <stdio.h>  
#include <arpa/inet.h>
struct MacHead{
char destionMac[20];
char sourceMac[20];
char type[10]; 
int num;
 };
struct IPHead{
unsigned char IPHeadLen:4;//网络字节序大端在前
unsigned char version:4;
unsigned char DS;
unsigned short int len;
unsigned short int TAG;
//unsigned char MF:3;
unsigned short int MFAndShift;
unsigned char TTL;
unsigned char protocol;
unsigned short int checkSum;
unsigned char sourceIP[4];
unsigned char destionIP[4];
};
void getPacket(u_char * arg, const struct pcap_pkthdr * pkthdr, const u_char * packet)  
{  
if(packet[12]!=0x08){
return;
}
#if 1
  int * id = (int *)arg;  
  struct MacHead *macHead=(struct MacHead*)malloc(sizeof(struct MacHead));
  
  memset(macHead,0,sizeof(struct MacHead));
  macHead->num = 10;
 
 struct IPHead *ipHead=(struct IPHead*)malloc(sizeof(struct IPHead));
  
  memset(ipHead,0,sizeof(struct IPHead));
  memcpy(ipHead,packet+14,sizeof(struct IPHead));
  if(ipHead->protocol==17){
 printf("udp++++++++++ \r\n");     
  }else if(ipHead->protocol==88){
 printf("IGRP++++++++++ \r\n");    
  }else{
 printf("other protocol++++++++++ \r\n");  
   return;
  }
 // memcpy(macHead,packet,sizeof(struct MacHead));
  sprintf(macHead->destionMac,"%02x:%02x:%02x:%02x:%02x:%02x",packet[0],packet[1],packet[2],packet[3],packet[4],packet[5]);
  sprintf(macHead->sourceMac,"%02x:%02x:%02x:%02x:%02x:%02x",packet[6],packet[7],packet[8],packet[9],packet[10],packet[11]);
  sprintf(macHead->type,"%02x%02x",packet[12],packet[13]);
  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));   
  printf("destionMac=%s\r\n",macHead->destionMac);  
  printf("sourceMac=%s\r\n",macHead->sourceMac);
  printf("type=%s\r\n",macHead->type);
  printf("num=%d\r\n",macHead->num);

    

  printf("version=%d\r\n",ipHead->version);
  printf("IPHeadLen=%d\r\n",ipHead->IPHeadLen);
  printf("DS=%d\r\n",ipHead->DS);
  printf("len=%04x\r\n",ntohs(ipHead->len));
  printf("TAG=%04x\r\n",ntohs(ipHead->TAG));
  printf("MF=%x\r\n",ntohs(ipHead->MFAndShift)>>13);
  printf("shitf=%x\r\n",ntohs(ipHead->MFAndShift)&0x1FFF);
  printf("TTL=%0x,TTL=%d\r\n",ipHead->TTL,ipHead->TTL);
  printf("protocol=%0x,protocol=%d\r\n",ipHead->protocol,ipHead->protocol);
  printf("checkSum=%04x\r\n",ntohs(ipHead->checkSum));
  printf("sourceIP=%d.%d.%d.%d sourceIP=%x.%x.%x.%x\r\n",ipHead->sourceIP[0],ipHead->sourceIP[1],ipHead->sourceIP[2],ipHead->sourceIP[3],ipHead->sourceIP[0],ipHead->sourceIP[1],ipHead->sourceIP[2],ipHead->sourceIP[3]);
  printf("destionIP=%d.%d.%d.%d destionIP=%x.%x.%x.%x\r\n",ipHead->destionIP[0],ipHead->destionIP[1],ipHead->destionIP[2],ipHead->destionIP[3],ipHead->destionIP[0],ipHead->destionIP[1],ipHead->destionIP[2],ipHead->destionIP[3]);
    
  free(macHead);
  free(ipHead);
  
#else
  int * id = (int *)arg;  
  struct MacHead macHead;
  memset(&macHead,0,sizeof(struct MacHead));
 // memcpy(macHead,packet,sizeof(struct MacHead));
  sprintf(macHead.destionMac,"%02x:%02x:%02x:%02x:%02x:%02x",packet[0],packet[1],packet[2],packet[3],packet[4],packet[5]);
  sprintf(macHead.sourceMac,"%02x:%02x:%02x:%02x:%02x:%02x",packet[6],packet[7],packet[8],packet[9],packet[10],packet[11]);
  sprintf(macHead.type,"%02x%02x",packet[12],packet[13]);
  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));   
  printf("destionMac=%s\r\n",macHead.destionMac);  
  printf("sourceMac=%s\r\n",macHead.sourceMac);
  printf("type=%s\r\n",macHead.type);
#endif
 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_loop(device, 1, getPacket, (u_char*)&id);  
    
  pcap_close(device);  
  
  return 0;  
}  

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约