TFTP(Trivial File Transfer Protocol,简单 文件传输协议 )
1.tftp的服务端口号是69
2.tftp是基于udp协议的
3.tftp是明文传输的,是一种比较轻量型的协议,一般用于bootloader加载内核
TFTP工作流程
服务端开启tftp服务,tftp是一种stand_alone服务,不是常驻内存的,是在有需要的时候才去调用的。首先,客户端发送一个读(RRQ:2个字节)或者写(WRQ:2个字节)的请求,数据包的目标端口是69。对于读或者写的报文格式如下:
RRQ/WRQ(2个字节)+文件名(N字节)+0(1字节)+模式(N字节)+0(1字节)
目前模式字段主要有2种:netascii,这是8位的ASCII码形式;另一种是octet,这是8位源数据类型。对于netascii是把回车和换行(CR/LF)解释成两个字节的。可以查看 http://www.firefoxbug.net/?p=1041
tftp-server接收到数据包:如果是发现是读(RRQ),就重新随机分配一个端口,直接发送数据(DATA:2个字节)+块编号(2个字节),然后是0~512字节数据包。客户端接收到数据包,发给服务端(ACK:2个字节)+块编号(2个字节)。如果是普通的数据包,那么数据段的大小一定是512字节,如果是最后一个数据包,肯定是小于512字节的。tftp就是通过发现了一个数据段小于512字节的数据包来声明结束文件的传输了。那么一个要传输的文件刚还是512字节的整数倍怎么办呢?tftp会在最后传输一个数据段大小是0包。
tftp-server接收到数据包:如果发现是写(WRQ),服务端就发回(ACK:2个字节)+(块编号0:2个字节)的包,接着客户端就发送(DATA:2个字节)+(块编号1:2个字节)+数据段给服务端,服务端发回(ACK:2个字节)+(块编号1:2个字节)。。。依次发送。
错误信息是系统自定义的,格式主要是error(2个字节)+错误码(2个字节)+错误信息(N个字节)
下面是tftp数据包的格式图
下面是C语言解析tftp包的一小段代码:
struct tftphdr { short th_opcode; /* packet type */ union { unsigned short tu_block; /* block # */ short tu_code; /* error code */ char tu_stuff[1]; /* request packet stuff */ } __attribute__ ((__packed__)) th_u; char th_data[1]; /* data or error string */ } __attribute__ ((__packed__)); // 解析udp包,packet_buffer是用rawsocket抓出来的以太网包, void ParseUDPPacket(unsigned char *packet_buffer) { struct ethhdr *eth_header;//以太网头 struct iphdr *ip_header; //ip头 struct udphdr *udp_header; //tcp头 eth_header = (struct ethhdr*)packet_buffer; ip_header = (struct iphdr*)(packet_buffer + sizeof(struct ethhdr)); udp_header = (struct udphdr*)(packet_buffer + sizeof(struct ethhdr) + ip_header->ihl*4); unsigned char *data = NULL; data = (packet_buffer + sizeof(struct ethhdr) + ip_header->ihl*4 + 8);//8代表UDP包头 struct tftphdr *tp = (struct tftphdr *)data; // /usr/include/arpa/tftp.h tftp_print(data,ntohs(udp_header->len)-8); //ntohs(udp_header->len)-8表示udp数据包长度 } /* * Print trivial file transfer program requests */ void tftp_print(register const u_char *bp, u_int length) { register const struct tftphdr *tp; register const u_char *p; register int opcode,i; static char tstr[] = " [|tftp]"; char buffer[520] = {'\0'}; tp = (const struct tftphdr *)bp; // printf(" %d", length); // printf("length of tftp_data = %d\n",length); /* Print tftp request type */ opcode = EXTRACT_16BITS(&tp->th_opcode); printf(" %s",tok2str(op2str, "tftp-#%d", opcode)); /* Bail if bogus opcode */ switch (opcode) { case RRQ: break; case WRQ: break; case ACK: break; case DATA: break; case ERROR: break; default: /* We shouldn't get here */ printf("(unknown #%d)", opcode); break; } return; }
详细的可以查看tcpdump的源码。