NFS协议RPC通信数据包解码发现头4个字节不知道是什么?

原创 吴就业 113 0 2024-06-24

本文为博主原创文章,未经博主允许不得转载。

本文链接:https://wujiuye.com/article/639149f557c541ce95302f13cf5b1647

作者:吴就业
链接:https://wujiuye.com/article/639149f557c541ce95302f13cf5b1647
来源:吴就业的网络日记
本文为博主原创文章,未经博主允许不得转载。

上一篇nfs协议的rpc通信协议,我们了解到nfs的rpc通信协议,无论是rpc请求还是rpc响应,其协议头都是以4个字节的消息id开始。

rpc请求:

rpc响应:

但当我们抓包并写代码解码的过程中发现,我们解码每个TCP数据包:先获取TCP数据包的Payload,再拿Payload解码为nfs的rpc协议数据包,无论是rpc请求还是rpc响应,都是要先跳过前四个字节,才是rpc协议的消息id,这样解码才正确。

解码代码如下:

func (l *listener) NextAppProtocolData(src, dst *Endpoint, data []byte) {
	// 头4个字节到底是什么?
	// fmt.Println(binary.BigEndian.Uint32(data[0:]))
	xid := binary.BigEndian.Uint32(data[4:])
	msgType := binary.BigEndian.Uint32(data[8:])
	if msgType == 0 {
		fmt.Println("request", xid, msgType)
		rpcReqBody := data[12:]
		nfsVer := binary.BigEndian.Uint32(rpcReqBody[8:])
		op := binary.BigEndian.Uint32(rpcReqBody[12:])
		fmt.Println(binary.BigEndian.Uint32(rpcReqBody[0:]),
			binary.BigEndian.Uint32(rpcReqBody[4:]),
			nfsVer, op)
		if nfsVer != 3 || op != 7 {
			return
		}
		fmt.Println(fmt.Sprintf("%s -> %s", src.String(), dst.String()))
		fmt.Println(xid, msgType)
	} else {
		rpcStatus := binary.BigEndian.Uint32(data[12:])
		fmt.Println("response:", xid, msgType, rpcStatus)
	}
}

那么跳过的前四个字节到底是什么呢?

我们用Wireshark分析数据包发现Wireshark能解码nfs协议,并且我们发现Wireshark能正常解码头4个字节。

截屏2024-06-24 14.39.57

这个Fragment header占四个字节,共32bit,前一个bit是一个bool值。我们通过右键->Copy->Field name拿到这两个字段的名称分别为rpc.lastfrag和rpc.fraglen。

截屏2024-06-24 14.41.36

从名字来看,这两个字段都跟rpc协议有关。

我们找到了Wireshark的一篇文档:Display Filter Reference: Remote Procedure Call,有这两个字段的描述,然后通过查找资料,大概理解这两个字段的意思:

RPC的消息在传输之前可能会分成多个片段(fragments)传输,在数据包很大,超过TCP协议Payload所允许的大小的情况下就会分段。rpc.lastfrag 标志一个数据包是否是最后一个片段,如果不是则需要等待接收完才能解码,而 rpc.fraglen 则提供了每个片段的具体长度,有助于正确地重新组装原始的RPC消息。

我们自己实现解码器也应该正确处理多fragments传输的情况,所以头4个字节的rpc.lastfrag和rpc.fraglen至关重要。至此,我们就理解了头4个字节是什么了。

那么,go代码如何实现解码rpc.lastfrag和rpc.fraglen?

代码如下:

// 头4个字节到底是什么?
	lastfrag := data[0]>>7&1 == 1
	fraglen := uint32(data[3]) | uint32(data[2])<<8 | uint32(data[1])<<16 | uint32(data[0]&0b0111_1111)<<24
	fmt.Println("rpc.lastfrag=", lastfrag, "rpc.fraglen", fraglen)
#网络

声明:公众号、CSDN、掘金的曾用名:“Java艺术”,因此您可能看到一些早期的文章的图片有“Java艺术”的水印。

文章推荐

解决http event stream一直卡到最后一个消息才一次性返回

我在用http event stream实现类似ChatGPT的打字机效果时遇到了这个问题:浏览器打开debug模式,看http event stream请求,EventStream直到最后一个消息发完才一直显示完所有消息。并不是请求失败,也不是没有响应,只是看起来像是卡住了,一直等到响应的数据全部接收完才一次性的回调onmessage方法。

nfs协议的rpc通信协议

通过开源项目go-nfs-client理解nfsv3的rpc通信协议,从而知道怎么解析抓取的数据包,获取需要的信息。

nfs协议,写文件流程分析

理解nfsv3协议的打开一个文件写和创建一个文件写的流程,以及相关操作的协议的理解、请求和响应的结构体的理解。

tcpdump抓包分析实战-学习网络问题排查必备技能-抓包分析,附多个案例讲解

了解网络协议、学会利用tcpdump抓包,学会利用Wireshark分析数据包,将能帮助我们解决一些仅从客户端日记分析或仅从服务端日记分析无法解决的疑难杂症。本篇结合笔者经历的一些实战案例,带大家掌握网络问题排查必备技能:tcpdump抓包分析。

tcpdump抓包分析实战-客户端接收到网关响应的body是空的

只因请求头deviceId多了一个‘\n’导致,服务端接收到的body是空的。

带宽问题排查实战-记一次线上文件下载慢问题排查

上传的文件是仅办公网络可访问,办法室网络有带宽限制,一个页面加载上百张图片,很容易达到带宽限制,所以出现下载很慢。