zoukankan      html  css  js  c++  java
  • TFTP网络协议分析---15

    TFTP网络协议分析

    周学伟

    文档说明:所有函数都依托与两个出口,发送和接收。

    1:作为发送时,要完成基于TFTP协议下的文件传输,但前提是知道木的PC机的MAC地址,因为当发送TFTP请求包时必须提供目的主机的MAC地址。则提供串口srcureCRT控制台,首先进行ARP请求包的发送,收到来自客户端的ARP应答包时,提取出目的主机的MAC地址,然后在发送TFTP请求包,等到目的主机返回数据报文后,文件传输即可开始,此过程,可用wireshark抓包工具进行检测。

    2:作为接收时,可在DM9000网卡芯片提供的中断标号初进行等待,当网卡收到相应的数据包时,即可让函数进行处理。

    1定义帧头的结构体

    typedef unsigned int u32;

    typedef unsigned short u16;

    typedef unsigned char u8;

    typedef struct eth_hdr

    {

        u8 d_mac[6];

        u8 s_mac[6];

        u16 type;

    }ETH_HDR;

    ARP数据包的格式

    以太网的目的地址d_mac

    以太网的源地址s_mac

    帧类型

    type

    硬件类型

    hwtype;

    协议类型

    protocol

    硬件地址长度

    hwlen

    协议地址长度

    protolen

    Opcode

    1:请求包

    2:应答包

    发送端的以太网地址smac[6]

    发送端的IP地址 sipaddr[4]

    目的以太网地址 dmac[6];

    目的IP地址

    dipaddr[4]

     

    typedef struct arp_hdr

    {

        ETH_HDR ethhdr;

        u16 hwtype;

        u16 protocol;

        u8 hwlen;

        u8 protolen;

        u16 opcode;

        u8 smac[6];

        u8 sipaddr[4];

        u8 dmac[6];

        u8 dipaddr[4];

    }ARP_HDR;

    ARP_HDR arpbuf;

    IP协议的报文格式

    版本

    vhl

    报文长度

    vhl

    服务级别

    tos

    报文长度

    len

    标识

    ipid

    标志

     ipoffset

    生命时间

    ttl

    用户协议

    proto

    报文校验

    ipchksum

    IP地址

     srcipaddr[4]

    目的IP地址

    u8   destipaddr[4]

    数据

     

     

    typedef struct ip_hdr

    {

        ETH_HDR ethhdr;

        u8 vhl;

        u8 tos;

        u16 len;

        u16 ipid;

        u16 ipoffset;

        u8 ttl;

        u8 proto;

        u16 ipchksum;

        u8 srcipaddr[4];

        u8 destipaddr[4];

    }IP_HDR;

    UDP协议格式

     

    16   位源端口号

     

    16位目的端口号

     

    16UDP长度

     

    16UDP检验

     

    数据(如果有)

     

    typedef struct udp_hdr

    {

        IP_HDR iphdr;

        u16 sport;

        u16 dport;

        u16 len;

        u16 udpchksum;

    }UDP_HDR;

    TFTP报文格式

     

    IP首部

     

    UDP首部

     

    opcode

     

    文件名

     

     0(默认)

     

    传输模式

     

      0(默认)

     

     

    操作码

    opcode

    快编码

    blocknum

    数据

     data[0]

     

    typedef struct tftp_package

    {

        u16 opcode;

        u16 blocknum;

        u8 data[0];     

    }TFTP_PAK;

    #define PROTO_ARP 0x0806

    #define PROTO_IP 0x0800

    #define PROTO_UDP 0x11

    u8 buffer[1500];

    u8 host_mac_addr[6] = {0xff,0xff,0xff,0xff,0xff,0xff};

    u8 mac_addr[6] = {9,8,7,6,5,4};

    u8 ip_addr[4] = {192,168,1,30};

    u8 host_ip_addr[4] = {192,168,1,100};

    u16 packet_len;

    2进入ARP.C中处理

    #include "arp.h"

    #define HON(n) ((((u16)((n) & 0xff)) << 8) | (((n) & 0xff00) >> 8))//作为将小端数据变换为大端数据的宏

    /*1*****************以下是发送arp请求包****************/

    /*************************************************

    函数名称:ARP请求包发送函数

    调用入口:在主函数中调用的利用secureCRT控制台发送

    函数说明:

    **************************************************/

    void arp_request()

    {

         memcpy(arpbuf.ethhdr.d_mac,host_mac_addr,6);//以太网的目的地址

         memcpy(arpbuf.ethhdr.s_mac,mac_addr,6);//以太网的源地址

         arpbuf.ethhdr.type = HON(0x0806);//帧类型,固定为0x0806(采用网络字节序大端格式 

         arpbuf.hwtype = HON(1);//硬件类型(采用网络字节序大端格式)

         arpbuf.protocol = HON(0x0800);//协议类型,固定位0x0800(采用网络字节序大端格式)

         arpbuf.hwlen = 6;//硬件地址长度

         arpbuf.protolen = 4;//协议地址长度

           

         arpbuf.opcode = HON(1);//opcode判断是请求包还是应答包

         memcpy(arpbuf.smac,mac_addr,6);//源Mac地址

         memcpy(arpbuf.sipaddr,ip_addr,4);//源IP地址

         memcpy(arpbuf.dipaddr,host_ip_addr,4);//目的IP地址

         packet_len = 14+28;

          

         dm9000_tx(&arpbuf,packet_len);

    }

    /*************************************************

    函数名称:ARP数据包的解析处理函数

    调用入口:net_handle()_网络数据包处理分析函数

    函数说明:判断是请求包还是应答包

    **************************************************/

    u8 arp_process(u8 *buf, u32 len)

    {

        u32 i;

        ARP_HDR *arp_p = (ARP_HDR *)buf;//定义arp_p指针指向接收到的数据缓存区中

       

        if (packet_len<28)//判断是否小于28字节,小于就不做处理

            return 0;

       

        switch (HON(arp_p->opcode))

        {

               case 2://若是ARP响应包,则打印在串口

                   /***************arp响应包***************/

                memcpy(host_ip_addr,arp_p->sipaddr,4);//提取目的主机的IP地址,打印

                printf("host ip is : ");

                for(i=0;i<4;i++)

                   printf("%03d ",host_ip_addr[i]);

                printf(" ");

       

                memcpy(host_mac_addr,arp_p->smac,6);//提取目的PC机的MAC地址,打印

                printf("host mac is : ");

                for(i=0;i<6;i++)

                   printf("%02x ",host_mac_addr[i]);

                printf(" ");

                break;

               

            case 1: //若是arp请求包,则发送ARP响应包

                /***********发送arp响应包**************/

                memcpy(arpbuf.ethhdr.d_mac,arp_p->smac,6);//以太网的目的地址

                memcpy(arpbuf.ethhdr.s_mac,mac_addr,6);//以太网的源地址

                arpbuf.ethhdr.type = HON(0x0806);//帧类型,固定为0x0806(采用网络字节序大端格式)

          

                arpbuf.hwtype = HON(1);//硬件类型(采用网络字节序大端格式)

                arpbuf.protocol = HON(0x0800);//协议类型,固定位0x0800(采用网络字节序大端格式)

        

                arpbuf.hwlen = 6;//硬件地址长度

                arpbuf.protolen = 4;//协议地址长度

                arpbuf.opcode = HON(2);//opcode判断是请求包还是应答包

                memcpy(arpbuf.smac,mac_addr,6);//源Mac地址

                memcpy(arpbuf.sipaddr,ip_addr,4);//源IP地址

                memcpy(arpbuf.dmac,arp_p->smac,6);//目的MAC地址

                memcpy(arpbuf.dipaddr,arp_p->sipaddr,4);//目的IP地址

                packet_len = 14+28;//总体包的长度

        

                dm9000_tx(&arpbuf,packet_len);//调用dm9000发送函数,发送应答包     

                break;

         }

    }

    /*************************************************

    函数名称:UDP协议数据包处理函数

    调用入口:在ip_process()接收到的IP类型的数据包处理函数

    函数说明:UDP协议包下封装的是TFT协议包

    **************************************************/

    void udp_process(u8* buf, u32 len)

    {

         UDP_HDR *udphdr = (UDP_HDR *)buf;//定义udphdr 指针指向接收到的数据缓存区中

        

         tftp_process(buf,len,HON(udphdr->sport)); //调用tftp_process()TFTP处理函数  

    }

    /************************************************

    函数名称:接收到的IP类型的数据包处理函数

    调用接口:在net_handle网络数据包处理分析函数中

    函数说明:IP协议包下封装的是UDP协议包

    **************************************************/

    void ip_process(u8 *buf, u32 len)

    {

         IP_HDR *p = (IP_HDR *)buf;   //定义p指针指向接收到的数据缓存区中

        

         switch(p->proto)//判断协议类型是否为UDP的协议包

         {

             case PROTO_UDP://如果是则调用udp_processUDP处理函数

                 udp_process(buf,len);

                 break;

                

             default:

                 break;   

         }

    }

    /*********************************

    函数名称:网络数据包处理分析函数

    调用接口:调用者在DM9000的int_issue()函数处

    ********************************/

    void net_handle(u8 *buf, u32 len)

    {

         ETH_HDR *p = (ETH_HDR *)buf; //定义p指针指向接收到的数据缓存区中

        

         switch (HON(p->type))//判断接收到的数据包的类型

         {

              case PROTO_ARP://PROTO_ARP = 0x0806

                  arp_process(buf,len);//如果是ARP包则调用arp_process处理函数

                  break;

             

              case PROTO_IP://PROTO_IP = 0x0800

                    ip_process(buf,len);//如果是IP包则调用ip_process处理函数

                      break;

                

                default:

                      break;

         }

    }

    3调用TFTP.C处理

    #include "string.h"

    #include "arp.h"

    u8 sendbuf[1024];//发送缓存区

    u8* tftp_down_addr = 0x31000000;//数据保存的起始地址

    u16 serverport = 0;//源端口号

    u16 curblock = 1;//数据的块号

    #define HON(n) ((((u16)((n) & 0xff)) << 8) | (((n) & 0xff00) >> 8))//2个字节的数据更改为网络字节序,大端数据

    /*************************************************

    函数名称:计算校验码长度的函数

    调用入口:tftp_send_request()

    函数说明:无

    **************************************************/

    u16 checksum(u8 *ptr, int len)

    {

        u32 sum = 0;

        u16 *p = (u16 *)ptr;

        while (len > 1)

        {

            sum += *p++;

            len -= 2;

        }

        if(len == 1)

            sum += *(u8 *)p;

       

        while(sum>>16)

            sum = (sum&0xffff) + (sum>>16);

       

        return (u16)((~sum)&0xffff);

    }

    /*************************************************

    函数名称:发送tftp请求数据包的函数

    调用入口:在主函数中调用的利用secureCRT控制台发送

    函数说明:

    **************************************************/

    void tftp_send_request(const char *filename)

    {

        u8 *ptftp = &sendbuf[200];//TFTP的内存起始地址指向sendbuf缓存区

        u32 tftp_len = 0;//TFTP请求报文的长度

        UDP_HDR *udphdr;//定义UDP头的内存指向udphdr

        u8 *iphdr;//定义IP头的内存指向iphdr

       

        ptftp[0] = 0x00;

        ptftp[1] = 0x01;//写入操作码,网络字节序大端数据

        tftp_len += 2 ;//TFTP数据报文的长度变量

       

        sprintf(&ptftp[tftp_len],"%s",filename);//利用sprintf函数写入请求的文件名

        tftp_len += strlen(filename);//计算写入的文件名的长度

        ptftp[tftp_len] = '';//添加文件名的结束符

        tftp_len += 1;//长度加1

       

        sprintf(&ptftp[tftp_len],"%s","octet");//利用sprintf函数写入传输数据的模式

        tftp_len += strlen("octect");//计算写入的传输数据的模式的长度

        ptftp[tftp_len] = '';//添加文件名的结束符

        tftp_len += 1;//长度加1

         

        udphdr = ptftp-sizeof(UDP_HDR);//利用sizeof函数计算UDP头的长度

        iphdr =  ptftp-sizeof(UDP_HDR)+ sizeof(ETH_HDR);//利用sizeof函数计算IP头的长度

       

        /***************以下是UDP帧头信息****************/

        udphdr->sport = HON(48915);//UDP头的16位源端口号

        udphdr->dport = HON(69);//UDP头的16位目的端口号

        udphdr->len = HON(tftp_len+sizeof(UDP_HDR)-sizeof(IP_HDR));//UDP头的16位长度

        udphdr->udpchksum = 0x00;//UDP头的16位校验码

       

        /***************以下IP帧头信息*******************/

        udphdr->iphdr.vhl = 0x45;//版本和报文长度

        udphdr->iphdr.tos = 0x00;//服务级别

        udphdr->iphdr.len = HON(tftp_len+sizeof(UDP_HDR)-sizeof(ETH_HDR));//报文长度(采用网络字节序大端格式)

        udphdr->iphdr.ipid = HON(0x00);//标识(采用网络字节序)

        udphdr->iphdr.ipoffset = HON(0x4000);//标志(采用网络字节序大端格式)

        udphdr->iphdr.ttl = 0xff;//报文的生存时间

        udphdr->iphdr.proto = 17;//用户协议类型

        memcpy(udphdr->iphdr.srcipaddr,ip_addr,4);//源iP地址

        memcpy(udphdr->iphdr.destipaddr,host_ip_addr,4);//目的IP地址

        udphdr->iphdr.ipchksum = 0;//首先将报文校验设置为0

        udphdr->iphdr.ipchksum = checksum(iphdr,20);//利用checksum函数计算校验码的长度

       

        memcpy(udphdr->iphdr.ethhdr.s_mac,mac_addr,6);//源MAC地址

        memcpy(udphdr->iphdr.ethhdr.d_mac,host_mac_addr,6);//目的MAC地址

        udphdr->iphdr.ethhdr.type = HON(PROTO_IP);//IP的协议类型(采用网络字节序,大端格式)

       

        dm9000_tx((u32 *)udphdr,sizeof(UDP_HDR)+tftp_len);//利用dm9000_tx发送TFTP请求报文

    }

    /*************************************************

    函数名称:接收tftp请求数据包确认的函数

    调用入口:tftp_process()TFTP协议数据包处理函数

    函数说明:处理完成后利用dm9000_tx()函数发送响应报文

    **************************************************/

    void tftp_send_ack(u16 blocknum)

    {

        u8 *ptftp = &sendbuf[200];//ptftp指针指向sendbuf缓存区中

        u32 tftp_len = 0;//TFTP请求报文的长度

        UDP_HDR *udphdr;//定义UDP头的内存指向udphdr

        u8 *iphdr;//定义IP头的内存指向iphdr

       

        ptftp[0] = 0x00;//写入操作码,网络字节序大端数据

        ptftp[1] = 0x04;

        tftp_len += 2 ;//TFTP数据报文的长度变量

       

        ptftp[2] = (blocknum&0xff00)>>8;//提取客户端发送的数据报文中的块号

        ptftp[3] = (blocknum&0xff);//因为采用的是网路字节序大端格式,则要变换为小端个数

        tftp_len += 2 ;//TFTP数据报文的长度变量加2个季节

       

        udphdr = ptftp-sizeof(UDP_HDR);//利用sizeof函数计算UDP头的长度

        iphdr =  ptftp-sizeof(UDP_HDR)+ sizeof(ETH_HDR);//利用sizeof函数计算IP头的长度

       

        /****************以下是UDP帧头信息***************/

        udphdr->sport = HON(48915);;//UDP头的16位源端口号(采用网络字节序大端格式)

        udphdr->dport = HON(serverport);//UDP头的16位目的端口号(采用网络字节序大端格式)

        udphdr->len = HON(tftp_len+sizeof(UDP_HDR)-sizeof(IP_HDR));//UDP头的16位长度

        udphdr->udpchksum = 0x00;//UDP头的16位校验码

       

        /******************以下是IP帧头信息******************/

        udphdr->iphdr.vhl = 0x45;//版本和报文长度

        udphdr->iphdr.tos = 0x00;//服务级别

        udphdr->iphdr.len = HON(tftp_len+sizeof(UDP_HDR)-sizeof(ETH_HDR));//报文长度(采用网络字节序大端格式)

        udphdr->iphdr.ipid = HON(0x00);//标识(采用网络字节序)

        udphdr->iphdr.ipoffset = HON(0x4000);//标志(采用网络字节序大端格式)

        udphdr->iphdr.ttl = 0xff;//报文的生存时间

        udphdr->iphdr.proto = 17;//用户协议类型

        memcpy(udphdr->iphdr.srcipaddr,ip_addr,4);//源iP地址

        memcpy(udphdr->iphdr.destipaddr,host_ip_addr,4);//目的IP地址

        udphdr->iphdr.ipchksum = 0;//首先将报文校验设置为0

        udphdr->iphdr.ipchksum = checksum(iphdr,20);//利用checksum函数计算校验码的长度

       

        memcpy(udphdr->iphdr.ethhdr.s_mac,mac_addr,6);//源MAC地址

        memcpy(udphdr->iphdr.ethhdr.d_mac,host_mac_addr,6);//目的MAC地址

        udphdr->iphdr.ethhdr.type = HON(PROTO_IP);//IP的协议类型(采用网络字节序,大端格式)   

        dm9000_tx((u32 *)udphdr,sizeof(UDP_HDR)+tftp_len);//利用dm9000_tx发送TFTP请求报文

    }

    /***************************************************

    函数名称:TFTP协议数据包处理函数

    调用接口:在arp.C中的udp_process()UDP协议数据包处理函数

    函数说明:len表示整个接收到的数据包长度,包括:

    ----------------------------------------------

    | IP帧头 || UDP帧头 || TFTP帧头 || tftp数据 ||

    ----------------------------------------------

    ****************************************************/

    void tftp_process(u8 *buf, u32 len, u16 port)

    {

         u32 i;

         u32 tftp_len;//TFTP协议包长度

         serverport = port;//源端口号

          

         TFTP_PAK *ptftp = buf + sizeof(UDP_HDR);//定义ptftp 指针指向接收到的数据缓存区中

         tftp_len = len - sizeof(UDP_HDR);//TFTP协议包长度

         if(HON(ptftp->opcode) == 3)//当OPCODE=3时,表示该网络包是TFTP的数据报文

         {

               if (HON(ptftp->blocknum) == curblock)//按照块编号判断是否是最后一组数据报文

               {

                  for (i = 0;i<(tftp_len-4);i++)//复制数据到指定的内存中去

                  {

                      *(tftp_down_addr) = *(ptftp->data+i);

                  } 

                  tftp_send_ack(HON(ptftp->blocknum));//客户端发送接收数据的应答包

                  curblock++;  //更新标志块的编号

                  if ((tftp_len-4)<512)

                      curblock = 1;//判断是否为最后一个数据包,若是则更新内存块的编号

                      tftp_down_addr = 0x31000000; //更新内存地址

             }

        }

    }

    4 部分MAIN函数

     switch (num)

            {

                case 1:

                tftp_send_request("start.o");

                Break;   

                case 2:

                arp_request();

                break;

                default:

                printf("Error: wrong selection! ");

                break;    

            }

    5 dm9000中的处理函数

    /************************

       接收数据函数

    在外部中断处理函数中调用

    ***********************/

    void int_issue()

    {

        packet_len = dm9000_rx(&buffer[0]);  //计算接收到的数据包长度

         

        net_handle(&buffer[0],packet_len);//调用此函数进行数据包的opcode类型分析

       

        SRCPND = (1<<4);//清楚中断标志位

        INTPND = (1<<4);

        EINTPEND |= 1<<7;

    }

  • 相关阅读:
    抬起头,看到满天星星
    别再嫌弃你妈妈话多唠叨啦,她可能正在做声音健脑操呢
    此生不能不認識的一個人
    长尾夹除了夹东西还能做什么?
    Markdown使用经验总结
    注册quora失败
    电脑插入耳机后声音仍然外放
    centOS下安装tree命令
    解决ubuntu “无法获得锁"
    虚拟机上Ubuntu无法上网问题
  • 原文地址:https://www.cnblogs.com/zxouxuewei/p/4937023.html
Copyright © 2011-2022 走看看