2410 TFTP源码 学习笔记 - 湛蓝思微的blog | 嵌入式在线博客

来源:百度文库 编辑:神马文学网 时间:2024/04/29 21:50:18
.sk_buff的结构和操作:

struct sk_buff {

unsigned char pad[2];
unsigned char buf[ETH_FRAME_LEN];//buffer,这里是帧存储的位置
unsigned int truesize;    /* Buffer size       */

unsigned char *data;    /* Data head pointer     */这个指针总是指向当前层协议头在buf中的位置或者当前层协议数据部分在buf中的位置。
unsigned int len;    /* Length of actual data    */指示从*data位置到帧尾的length
};

buf[ETH_FRAME_LEN] 就是一帧实体,也是一帧协议栈的栈的实体。*data 是栈的指针,len则相当栈的底部,但是它是变化的,意义是*data到*data+len部分是当前协议层的内容(接收),或者这部分是已经填好的上层协议内容(发送)。 对应的操作有skb_push,skb_pull。

skb_push 用于从上层协议向下封装数据包,相当于压栈。char *skb_pull(struct sk_buff *skb, unsigned int ln) 就是要向栈中写入len字节前,先把栈指针*data-=ln, 而栈长len+=ln,返回当前*data指针,数据或者协议头(长度一定是ln)就可以往*data处填充了。很明显这是个向下生长的满栈 (FD) 。

skb_pull 用于从帧开始向上逐次解析协议。相当于弹栈的过程, char *skb_pull(struct sk_buff *skb, unsigned int ln)就是从栈中弹出ln个字节,*data+=ln, 栈长len-=ln,返回当前*data。弹出的ln字节就是下层协议已经处理过的协议头,返回值指向本层协议头,用它就可以开始解析本层协议了。

2、接收包的流程

1) 申请一个缓冲区sk_buf *skb, 以skb为参数调用以太网层的接收数据帧函数eth_rcv(skb)。eth_rcv(skb)调用board_eth_rcv(skb->data, &skb->len); board_eth_rcv调用CS8900 查询函数CS8900DBG_IsReceivedPacket()检查当前是否收到数据帧,如果收到调用RcvPkt((BYTE *)data, 1532);接收帧。 这样一帧数据就缓存到skb->buff中了。此时栈指针*data=buff,len=帧长。

2)处理以太网帧头,弹栈skb_pull(skb, ETH_HLEN)。以太网的帧头protocol解析上层协议是IP还是ARP,分别调用ip_rcv_packet(skb);(见4)或者arp_rcv_packet(skb);(见3)

3)如果是ARP报文,arp_rcv_packet(skb)处理ARP协议。判断ARP头的目标IP是否为本地IP,不是丢弃。是则判断ARP操作码是否为ARP请求,是发送ARP reply。缓存对方MAC IP 至ARP cache.当前帧的处理结束。

4)如果IP报文 ,ip_rcv_packet(skb); 处理IP层协议头,检查目的IP是否是本地IP,不是直接丢弃,是则根据IP头的上层协议是UDP还是ICMP,分别调用udp_rcv_packet(skb);或者icmp_rcv_packet(skb);

5)如果是ICMP数据包,icmp_rcv_packet(skb); 处理ICMP报文,如果ICMP头的类型是8,即请求回显,则发送一个回显ICMP报文。当前帧的处理结束。

6)如果是UDP数据包,udp_rcv_packet(skb)处理UDP协议头,检查目的端口是否为TFTP端口。由于程序只有TFTP作为UDP的上层协议。 如果是则弹栈后调用tftp_rcv_packet(skb);

7)如果是ftfp包,tftp_rcv_packet(skb);处理TFTP协议,限于功能,只处理WRQ和DATA两种请求,如果是WRQ请求,保存源IP和端口,发送一个ftfp应答block=0,开始block++计数并进入下载状态。

如果是DATA包,判断源IP和端口与上面保存的是否一致,当前tftp包的block号与block计数是否相等。相等则拷贝数据, 发送应答,block++,判断数据长是否小于512,是则表明block接收结束。当前tftp包的block号<>< font="">

3、发送包过程

1)sk_buff结构的另外两个操作,skb_put操作和skb_reserve(预留)操作:申请一个缓冲示进行任何操作的时候,*data=buff, len=0. 每层协议都有一个相对于下层协议头的偏移,这个偏移是一定的。skb_reserve(skb,len)操作是把skb->data+=len。每层协议都实现对应的reserve操作,它调用下层reserve,再把自己协议头的长度len添加到预留空间skb_reserve(skb,len)。

skb_put(skb,len)操作则是实现内容填充之后,skb->len+=len.

这两个操作和skb_push在发送数据包中起重要作用。如发送TFTP包,申请缓冲区,调用udp_skb_reserve(skb);把UDP头到以太网帧头的所有协议头的位置预留出来。再向*data处添加TFTP头和数据。

2)TFTP包发送过程

      发送TFTP应答,tftp_send_ack :申请缓冲,预留所有下层协议(UDP,IP,ETH)协议头空间,填写TFTP头和数据。调用udp_send(skb, client_ip, TFTP, client_port); 参数告诉下层相对的协议头怎么填。如TFTP,client_port 是给UDP协议层的,指定了本地端口和目标端口;client_ip则是给IP层的,给定目标IP。

      udp_send(skb, client_ip, TFTP, client_port); UDP层压栈后填写自己UDP协议头。调用ip_send(skb, ip, UDP); 指定目标IP和上层协议是UDP协议。

      IP层ip_send(skb, ip, UDP); 填写协议头,在这里注意校验和的计算。在ARP缓存中查找目标IP对应的MAC地址,查找成功然后调用下层eth_send(skb, dest_eth_addr, ETH_P_IP);查找失败发送ARP查询请求。

      ETH层 填充以太网帧头,调用底层驱动函数:board_eth_send(skb->data, skb->len);由CS8900芯片完成帧发送。