From 9edaceca8165e2864267547311daf145bb520270 Mon Sep 17 00:00:00 2001 From: Rick Macklem Date: Sun, 11 Apr 2021 16:51:25 -0700 Subject: [PATCH] nfsd: cut the Linux NFSv4.1/4.2 some slack w.r.t. RFC5661 Recent testing of network partitioning a FreeBSD NFSv4.1 server from a Linux NFSv4.1 client identified problems with both the FreeBSD server and Linux client. Sometimes, after some Linux NFSv4.1/4.2 clients establish a new TCP connection, they will advance the sequence number for a session slot by 2 instead of 1. RFC5661 specifies that a server should reply NFS4ERR_SEQ_MISORDERED for this case. This might result in a system call error in the client and seems to disable future use of the slot by the client. Since advancing the sequence number by 2 seems harmless, allow this case if vfs.nfs.linuxseqsesshack is non-zero. Note that, if the order of RPCs is actually reversed, a subsequent RPC with a smaller sequence number value for the slot will be received. This will result in a NFS4ERR_SEQ_MISORDERED reply. This has not been observed during testing. Setting vfs.nfs.linuxseqsesshack to 0 will provide RFC5661 compliant behaviour. This fix affects the fairly rare case where a NFSv4 Linux client does a TCP reconnect and then apparently erroneously increments the sequence number for the session slot twice during the reconnect cycle. PR: 254816 MFC after: 2 weeks --- sys/fs/nfs/nfs_commonsubs.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/sys/fs/nfs/nfs_commonsubs.c b/sys/fs/nfs/nfs_commonsubs.c index 4afa4c2d9ab4..d7009b1e0ca4 100644 --- a/sys/fs/nfs/nfs_commonsubs.c +++ b/sys/fs/nfs/nfs_commonsubs.c @@ -98,6 +98,11 @@ int nfs_maxcopyrange = 10 * 1024 * 1024; SYSCTL_INT(_vfs_nfs, OID_AUTO, maxcopyrange, CTLFLAG_RW, &nfs_maxcopyrange, 0, "Max size of a Copy so RPC times reasonable"); +static int nfs_allowskip_sessionseq = 1; +SYSCTL_INT(_vfs_nfs, OID_AUTO, linuxseqsesshack, CTLFLAG_RW, + &nfs_allowskip_sessionseq, 0, "Allow client to skip ahead one seq# for" + " session slot"); + /* * This array of structures indicates, for V4: * retfh - which of 3 types of calling args are used @@ -4614,7 +4619,7 @@ nfsv4_getipaddr(struct nfsrv_descript *nd, struct sockaddr_in *sin, * Handle an NFSv4.1 Sequence request for the session. * If reply != NULL, use it to return the cached reply, as required. * The client gets a cached reply via this call for callbacks, however the - * server gets a cached reply via the nfsv4_seqsess_cachereply() call. + * server gets a cached reply via the nfsv4_seqsess_cacherep() call. */ int nfsv4_seqsession(uint32_t seqid, uint32_t slotid, uint32_t highslot, @@ -4648,12 +4653,24 @@ nfsv4_seqsession(uint32_t seqid, uint32_t slotid, uint32_t highslot, } else /* No reply cached, so just do it. */ slots[slotid].nfssl_inprog = 1; - } else if ((slots[slotid].nfssl_seq + 1) == seqid) { + } else if (slots[slotid].nfssl_seq + 1 == seqid || + (slots[slotid].nfssl_seq + 2 == seqid && + nfs_allowskip_sessionseq != 0)) { + /* + * Allowing the seqid to be ahead by 2 is technically + * a violation of RFC5661, but it seems harmless to do + * and avoids returning NFSERR_SEQMISORDERED to a + * slightly broken Linux NFSv4.1/4.2 client. + * If the RPCs are really out of order, one with a + * lower seqid will be subsequently received and that + * one will get a NFSERR_SEQMISORDERED reply. + * Can be disabled by setting vfs.nfs.linuxseqsesshack to 0. + */ if (slots[slotid].nfssl_reply != NULL) m_freem(slots[slotid].nfssl_reply); slots[slotid].nfssl_reply = NULL; slots[slotid].nfssl_inprog = 1; - slots[slotid].nfssl_seq++; + slots[slotid].nfssl_seq = seqid; } else error = NFSERR_SEQMISORDERED; return (error);