上篇文章中我们讲到了Linux中clientid的协商过程,这个过程是由函数nfs4_init_clientid()实现的。这个函数包含两个步骤:(1)发起SETCLIENTID请求,由函数nfs4_proc_setclientid()实现;(2)发起SETCLIENTID_CONFIRM请求,由函数nfs4_proc_setclientid_confirm()实现。这篇文章中我们详细详解SETCLIENTID请求。
1.客户端代码
首先介绍两个数据结构:nfs4_setclientid和nfs4_setclientid_res。
nfs4_setclientid是SETCLIENTID请求报文使用的数据结构。
struct nfs4_setclientid {
const nfs4_verifier * sc_verifier; // 客户端重启信息
// 客户端信息字符串实际长度
unsigned int sc_name_len;
// 客户端信息(客户端地址/服务器地址 传输层协议)
char sc_name[NFS4_SETCLIENTID_NAMELEN + 1];
// CALLBACK服务编号
u32 sc_prog;
// CALLBACK服务中传输层协议名称的长度
unsigned int sc_netid_len;
// CALLBACK服务中传输层协议名称
char sc_netid[RPCBIND_MAXNETIDLEN + 1];
// CALLBACK服务中RPC服务器地址的长度
unsigned int sc_uaddr_len;
// CALLBACK服务中RPC服务器地址(RPC服务器地址.端口号)
char sc_uaddr[RPCBIND_MAXUADDRLEN + 1];
// 客户端编号
u32 sc_cb_ident;
};
nfs4_setclientid_res是存储SETCLIENTID应答报文数据的数据结构。
struct nfs4_setclientid_res {
u64 clientid;
nfs4_verifier confirm;
};
客户端发起SETCLIENTID请求的函数是nfs4_proc_setclientid(),这个函数的流程很简单,就是设法设置SETCLIENTID请求报文中各个字段的值,然后发起RPC调用。函数流程如下:
参数clp:一个NFS客户端的数据结构
参数program:这是CALLBACK服务的编号,固定为0x40000000
参数port:CALLBACK服务中RPC服务器端(NFS客户端)使用的端口号
参数cred:这是一个用户信息,发起SETCLIENTID请求的用户信息
参数res:这是输出参数,用于保存SETCLIENTID请求应答报文中的信息.
int nfs4_proc_setclientid(struct nfs_client *clp, u32 program,
unsigned short port, struct rpc_cred *cred,
struct nfs4_setclientid_res *res)
{
nfs4_verifier sc_verifier; // 8字节
struct nfs4_setclientid setclientid = {
.sc_verifier = &sc_verifier, // 这是客户端的开机时间
.sc_prog = program, // CALLBACK服务编号
.sc_cb_ident = clp->cl_cb_ident, // 这是NFSv4客户端的一个编号,创建NFSv4客户端时就设置了这个编号.
};
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SETCLIENTID], // SETCLIENTID请求编号
.rpc_argp = &setclientid, // SETCLIENTID请求的输入参数
.rpc_resp = res, // SETCLIENTID请求的返回值
.rpc_cred = cred, // 用户信息
};
int status;
/* nfs_client_id4 */ // 初始化sc_verifier,初始化为客户端的开机时间(确切说是nfs模块的加载时间)
nfs4_init_boot_verifier(clp, &sc_verifier);
rcu_read_lock();
// 客户端信息
setclientid.sc_name_len = scnprintf(setclientid.sc_name,
sizeof(setclientid.sc_name), "%s/%s %s",
clp->cl_ipaddr, // 这是NFS客户端的地址
rpc_peeraddr2str(clp->cl_rpcclient,
RPC_DISPLAY_ADDR), // NFS服务器的地址
rpc_peeraddr2str(clp->cl_rpcclient,
RPC_DISPLAY_PROTO)); // NFS服务器传输层协议
/* cb_client4 */
// CALLBACK使用的传输层协议
setclientid.sc_netid_len = scnprintf(setclientid.sc_netid,
sizeof(setclientid.sc_netid),
rpc_peeraddr2str(clp->cl_rpcclient,
RPC_DISPLAY_NETID)); // 这是NFSv4使用的传输层协议 tcp
rcu_read_unlock();
// CALLBACK服务地址和端口号
setclientid.sc_u