自己动手学TCP/IP--tftp协议

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.com/?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的源码。

标签:NetWork, TCP/IP

评论已关闭