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;

}

原文地址:https://www.cnblogs.com/zxouxuewei/p/4937023.html