]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
nfsd: Fill NFSv4.1 server implementation fields in OP_EXCHANGE_ID response
authorPali Rohár <pali@kernel.org>
Sat, 5 Oct 2024 18:33:49 +0000 (20:33 +0200)
committerChuck Lever <chuck.lever@oracle.com>
Tue, 19 Nov 2024 01:22:58 +0000 (20:22 -0500)
NFSv4.1 OP_EXCHANGE_ID response from server may contain server
implementation details (domain, name and build time) in optional
nfs_impl_id4 field. Currently nfsd does not fill this field.

Send these information in NFSv4.1 OP_EXCHANGE_ID response. Fill them with
the same values as what is Linux NFSv4.1 client doing. Domain is hardcoded
to "kernel.org", name is composed in the same way as "uname -srvm" output
and build time is hardcoded to zeros.

NFSv4.1 client and server implementation fields are useful for statistic
purposes or for identifying type of clients and servers.

Signed-off-by: Pali Rohár <pali@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
fs/nfsd/nfs4proc.c
fs/nfsd/nfs4state.c
fs/nfsd/nfs4xdr.c
fs/nfsd/xdr4.h

index 93089f7064f0b4ae07523cc910c49aef2a0b05d5..49b4cbfe914dfb9d635b443ea447366ce8e16376 100644 (file)
@@ -3453,6 +3453,7 @@ static const struct nfsd4_operation nfsd4_ops[] = {
        /* NFSv4.1 operations */
        [OP_EXCHANGE_ID] = {
                .op_func = nfsd4_exchange_id,
+               .op_release = nfsd4_exchange_id_release,
                .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP
                                | OP_MODIFIES_SOMETHING,
                .op_name = "OP_EXCHANGE_ID",
index e0862173cd9eac15f5238a3a4e6ab89aa823982a..2f229d007b586e13e090553acb4102e48b133afe 100644 (file)
@@ -3524,6 +3524,12 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                __func__, rqstp, exid, exid->clname.len, exid->clname.data,
                addr_str, exid->flags, exid->spa_how);
 
+       exid->server_impl_name = kasprintf(GFP_KERNEL, "%s %s %s %s",
+                                          utsname()->sysname, utsname()->release,
+                                          utsname()->version, utsname()->machine);
+       if (!exid->server_impl_name)
+               return nfserr_jukebox;
+
        if (exid->flags & ~EXCHGID4_FLAG_MASK_A)
                return nfserr_inval;
 
@@ -3661,6 +3667,23 @@ out_copy:
        exid->seqid = conf->cl_cs_slot.sl_seqid + 1;
        nfsd4_set_ex_flags(conf, exid);
 
+       exid->nii_domain.len = sizeof("kernel.org") - 1;
+       exid->nii_domain.data = "kernel.org";
+
+       /*
+        * Note that RFC 8881 places no length limit on
+        * nii_name, but this implementation permits no
+        * more than NFS4_OPAQUE_LIMIT bytes.
+        */
+       exid->nii_name.len = strlen(exid->server_impl_name);
+       if (exid->nii_name.len > NFS4_OPAQUE_LIMIT)
+               exid->nii_name.len = NFS4_OPAQUE_LIMIT;
+       exid->nii_name.data = exid->server_impl_name;
+
+       /* just send zeros - the date is in nii_name */
+       exid->nii_time.tv_sec = 0;
+       exid->nii_time.tv_nsec = 0;
+
        dprintk("nfsd4_exchange_id seqid %d flags %x\n",
                conf->cl_cs_slot.sl_seqid, conf->cl_exchange_flags);
        status = nfs_ok;
@@ -3677,6 +3700,14 @@ out_nolock:
        return status;
 }
 
+void
+nfsd4_exchange_id_release(union nfsd4_op_u *u)
+{
+       struct nfsd4_exchange_id *exid = &u->exchange_id;
+
+       kfree(exid->server_impl_name);
+}
+
 static __be32 check_slot_seqid(u32 seqid, u32 slot_seqid, bool slot_inuse)
 {
        /* The slot is in use, and no response has been sent. */
index abbfd2b58c82b43977a32e7187e8dad3c8ae3279..37f8301cfb8a628051f322a3137b6a895e0d11f9 100644 (file)
@@ -4825,6 +4825,25 @@ nfsd4_encode_server_owner4(struct xdr_stream *xdr, struct svc_rqst *rqstp)
        return nfsd4_encode_opaque(xdr, nn->nfsd_name, strlen(nn->nfsd_name));
 }
 
+static __be32
+nfsd4_encode_nfs_impl_id4(struct xdr_stream *xdr, struct nfsd4_exchange_id *exid)
+{
+       __be32 status;
+
+       /* nii_domain */
+       status = nfsd4_encode_opaque(xdr, exid->nii_domain.data,
+                                    exid->nii_domain.len);
+       if (status != nfs_ok)
+               return status;
+       /* nii_name */
+       status = nfsd4_encode_opaque(xdr, exid->nii_name.data,
+                                    exid->nii_name.len);
+       if (status != nfs_ok)
+               return status;
+       /* nii_time */
+       return nfsd4_encode_nfstime4(xdr, &exid->nii_time);
+}
+
 static __be32
 nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, __be32 nfserr,
                         union nfsd4_op_u *u)
@@ -4859,8 +4878,11 @@ nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, __be32 nfserr,
        if (nfserr != nfs_ok)
                return nfserr;
        /* eir_server_impl_id<1> */
-       if (xdr_stream_encode_u32(xdr, 0) != XDR_UNIT)
+       if (xdr_stream_encode_u32(xdr, 1) != XDR_UNIT)
                return nfserr_resource;
+       nfserr = nfsd4_encode_nfs_impl_id4(xdr, exid);
+       if (nfserr != nfs_ok)
+               return nfserr;
 
        return nfs_ok;
 }
index 2a21a7662e030cbd0f54060edd2b91e4d8c86d7e..d5bb9a3c2da426d7305b19d98d722c7a670d7291 100644 (file)
@@ -567,6 +567,7 @@ struct nfsd4_exchange_id {
        struct xdr_netobj nii_domain;
        struct xdr_netobj nii_name;
        struct timespec64 nii_time;
+       char            *server_impl_name;
 };
 
 struct nfsd4_sequence {
@@ -930,6 +931,7 @@ extern __be32 nfsd4_setclientid(struct svc_rqst *rqstp,
                struct nfsd4_compound_state *, union nfsd4_op_u *u);
 extern __be32 nfsd4_setclientid_confirm(struct svc_rqst *rqstp,
                struct nfsd4_compound_state *, union nfsd4_op_u *u);
+void nfsd4_exchange_id_release(union nfsd4_op_u *u);
 extern __be32 nfsd4_exchange_id(struct svc_rqst *rqstp,
                struct nfsd4_compound_state *, union nfsd4_op_u *u);
 extern __be32 nfsd4_backchannel_ctl(struct svc_rqst *,