Skip to content

Commit 08d6ee6

Browse files
njha-jsAnna Schumaker
authored andcommitted
sunrpc: implement rfc2203 rpcsec_gss seqnum cache
This implements a sequence number cache of the last three (right now hardcoded) sent sequence numbers for a given XID, as suggested by the RFC. From RFC2203 5.3.3.1: "Note that the sequence number algorithm requires that the client increment the sequence number even if it is retrying a request with the same RPC transaction identifier. It is not infrequent for clients to get into a situation where they send two or more attempts and a slow server sends the reply for the first attempt. With RPCSEC_GSS, each request and reply will have a unique sequence number. If the client wishes to improve turn around time on the RPC call, it can cache the RPCSEC_GSS sequence number of each request it sends. Then when it receives a response with a matching RPC transaction identifier, it can compute the checksum of each sequence number in the cache to try to match the checksum in the reply's verifier." Signed-off-by: Nikhil Jha <[email protected]> Acked-by: Chuck Lever <[email protected]> Signed-off-by: Anna Schumaker <[email protected]>
1 parent a5806cd commit 08d6ee6

File tree

5 files changed

+57
-28
lines changed

5 files changed

+57
-28
lines changed

include/linux/sunrpc/xprt.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
#define RPC_MAXCWND(xprt) ((xprt)->max_reqs << RPC_CWNDSHIFT)
3131
#define RPCXPRT_CONGESTED(xprt) ((xprt)->cong >= (xprt)->cwnd)
3232

33+
#define RPC_GSS_SEQNO_ARRAY_SIZE 3U
34+
3335
enum rpc_display_format_t {
3436
RPC_DISPLAY_ADDR = 0,
3537
RPC_DISPLAY_PORT,
@@ -66,7 +68,8 @@ struct rpc_rqst {
6668
struct rpc_cred * rq_cred; /* Bound cred */
6769
__be32 rq_xid; /* request XID */
6870
int rq_cong; /* has incremented xprt->cong */
69-
u32 rq_seqno; /* gss seq no. used on req. */
71+
u32 rq_seqnos[RPC_GSS_SEQNO_ARRAY_SIZE]; /* past gss req seq nos. */
72+
unsigned int rq_seqno_count; /* number of entries in rq_seqnos */
7073
int rq_enc_pages_num;
7174
struct page **rq_enc_pages; /* scratch pages for use by
7275
gss privacy code */
@@ -119,6 +122,18 @@ struct rpc_rqst {
119122
#define rq_svec rq_snd_buf.head
120123
#define rq_slen rq_snd_buf.len
121124

125+
static inline int xprt_rqst_add_seqno(struct rpc_rqst *req, u32 seqno)
126+
{
127+
if (likely(req->rq_seqno_count < RPC_GSS_SEQNO_ARRAY_SIZE))
128+
req->rq_seqno_count++;
129+
130+
/* Shift array to make room for the newest element at the beginning */
131+
memmove(&req->rq_seqnos[1], &req->rq_seqnos[0],
132+
(RPC_GSS_SEQNO_ARRAY_SIZE - 1) * sizeof(req->rq_seqnos[0]));
133+
req->rq_seqnos[0] = seqno;
134+
return 0;
135+
}
136+
122137
/* RPC transport layer security policies */
123138
enum xprtsec_policies {
124139
RPC_XPRTSEC_NONE = 0,

include/trace/events/rpcgss.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,7 @@ TRACE_EVENT(rpcgss_seqno,
409409
__entry->task_id = task->tk_pid;
410410
__entry->client_id = task->tk_client->cl_clid;
411411
__entry->xid = be32_to_cpu(rqst->rq_xid);
412-
__entry->seqno = rqst->rq_seqno;
412+
__entry->seqno = *rqst->rq_seqnos;
413413
),
414414

415415
TP_printk(SUNRPC_TRACE_TASK_SPECIFIER " xid=0x%08x seqno=%u",
@@ -440,7 +440,7 @@ TRACE_EVENT(rpcgss_need_reencode,
440440
__entry->client_id = task->tk_client->cl_clid;
441441
__entry->xid = be32_to_cpu(task->tk_rqstp->rq_xid);
442442
__entry->seq_xmit = seq_xmit;
443-
__entry->seqno = task->tk_rqstp->rq_seqno;
443+
__entry->seqno = *task->tk_rqstp->rq_seqnos;
444444
__entry->ret = ret;
445445
),
446446

include/trace/events/sunrpc.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1100,7 +1100,7 @@ TRACE_EVENT(xprt_transmit,
11001100
__entry->client_id = rqst->rq_task->tk_client ?
11011101
rqst->rq_task->tk_client->cl_clid : -1;
11021102
__entry->xid = be32_to_cpu(rqst->rq_xid);
1103-
__entry->seqno = rqst->rq_seqno;
1103+
__entry->seqno = *rqst->rq_seqnos;
11041104
__entry->status = status;
11051105
),
11061106

net/sunrpc/auth_gss/auth_gss.c

Lines changed: 36 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1545,6 +1545,7 @@ static int gss_marshal(struct rpc_task *task, struct xdr_stream *xdr)
15451545
struct kvec iov;
15461546
struct xdr_buf verf_buf;
15471547
int status;
1548+
u32 seqno;
15481549

15491550
/* Credential */
15501551

@@ -1556,15 +1557,16 @@ static int gss_marshal(struct rpc_task *task, struct xdr_stream *xdr)
15561557
cred_len = p++;
15571558

15581559
spin_lock(&ctx->gc_seq_lock);
1559-
req->rq_seqno = (ctx->gc_seq < MAXSEQ) ? ctx->gc_seq++ : MAXSEQ;
1560+
seqno = (ctx->gc_seq < MAXSEQ) ? ctx->gc_seq++ : MAXSEQ;
1561+
xprt_rqst_add_seqno(req, seqno);
15601562
spin_unlock(&ctx->gc_seq_lock);
1561-
if (req->rq_seqno == MAXSEQ)
1563+
if (*req->rq_seqnos == MAXSEQ)
15621564
goto expired;
15631565
trace_rpcgss_seqno(task);
15641566

15651567
*p++ = cpu_to_be32(RPC_GSS_VERSION);
15661568
*p++ = cpu_to_be32(ctx->gc_proc);
1567-
*p++ = cpu_to_be32(req->rq_seqno);
1569+
*p++ = cpu_to_be32(*req->rq_seqnos);
15681570
*p++ = cpu_to_be32(gss_cred->gc_service);
15691571
p = xdr_encode_netobj(p, &ctx->gc_wire_ctx);
15701572
*cred_len = cpu_to_be32((p - (cred_len + 1)) << 2);
@@ -1678,17 +1680,31 @@ gss_refresh_null(struct rpc_task *task)
16781680
return 0;
16791681
}
16801682

1683+
static u32
1684+
gss_validate_seqno_mic(struct gss_cl_ctx *ctx, u32 seqno, __be32 *seq, __be32 *p, u32 len)
1685+
{
1686+
struct kvec iov;
1687+
struct xdr_buf verf_buf;
1688+
struct xdr_netobj mic;
1689+
1690+
*seq = cpu_to_be32(seqno);
1691+
iov.iov_base = seq;
1692+
iov.iov_len = 4;
1693+
xdr_buf_from_iov(&iov, &verf_buf);
1694+
mic.data = (u8 *)p;
1695+
mic.len = len;
1696+
return gss_verify_mic(ctx->gc_gss_ctx, &verf_buf, &mic);
1697+
}
1698+
16811699
static int
16821700
gss_validate(struct rpc_task *task, struct xdr_stream *xdr)
16831701
{
16841702
struct rpc_cred *cred = task->tk_rqstp->rq_cred;
16851703
struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred);
16861704
__be32 *p, *seq = NULL;
1687-
struct kvec iov;
1688-
struct xdr_buf verf_buf;
1689-
struct xdr_netobj mic;
16901705
u32 len, maj_stat;
16911706
int status;
1707+
int i = 1; /* don't recheck the first item */
16921708

16931709
p = xdr_inline_decode(xdr, 2 * sizeof(*p));
16941710
if (!p)
@@ -1705,13 +1721,10 @@ gss_validate(struct rpc_task *task, struct xdr_stream *xdr)
17051721
seq = kmalloc(4, GFP_KERNEL);
17061722
if (!seq)
17071723
goto validate_failed;
1708-
*seq = cpu_to_be32(task->tk_rqstp->rq_seqno);
1709-
iov.iov_base = seq;
1710-
iov.iov_len = 4;
1711-
xdr_buf_from_iov(&iov, &verf_buf);
1712-
mic.data = (u8 *)p;
1713-
mic.len = len;
1714-
maj_stat = gss_verify_mic(ctx->gc_gss_ctx, &verf_buf, &mic);
1724+
maj_stat = gss_validate_seqno_mic(ctx, task->tk_rqstp->rq_seqnos[0], seq, p, len);
1725+
/* RFC 2203 5.3.3.1 - compute the checksum of each sequence number in the cache */
1726+
while (unlikely(maj_stat == GSS_S_BAD_SIG && i < task->tk_rqstp->rq_seqno_count))
1727+
maj_stat = gss_validate_seqno_mic(ctx, task->tk_rqstp->rq_seqnos[i], seq, p, len);
17151728
if (maj_stat == GSS_S_CONTEXT_EXPIRED)
17161729
clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
17171730
if (maj_stat)
@@ -1750,7 +1763,7 @@ gss_wrap_req_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx,
17501763
if (!p)
17511764
goto wrap_failed;
17521765
integ_len = p++;
1753-
*p = cpu_to_be32(rqstp->rq_seqno);
1766+
*p = cpu_to_be32(*rqstp->rq_seqnos);
17541767

17551768
if (rpcauth_wrap_req_encode(task, xdr))
17561769
goto wrap_failed;
@@ -1847,7 +1860,7 @@ gss_wrap_req_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx,
18471860
if (!p)
18481861
goto wrap_failed;
18491862
opaque_len = p++;
1850-
*p = cpu_to_be32(rqstp->rq_seqno);
1863+
*p = cpu_to_be32(*rqstp->rq_seqnos);
18511864

18521865
if (rpcauth_wrap_req_encode(task, xdr))
18531866
goto wrap_failed;
@@ -2001,7 +2014,7 @@ gss_unwrap_resp_integ(struct rpc_task *task, struct rpc_cred *cred,
20012014
offset = rcv_buf->len - xdr_stream_remaining(xdr);
20022015
if (xdr_stream_decode_u32(xdr, &seqno))
20032016
goto unwrap_failed;
2004-
if (seqno != rqstp->rq_seqno)
2017+
if (seqno != *rqstp->rq_seqnos)
20052018
goto bad_seqno;
20062019
if (xdr_buf_subsegment(rcv_buf, &gss_data, offset, len))
20072020
goto unwrap_failed;
@@ -2045,7 +2058,7 @@ gss_unwrap_resp_integ(struct rpc_task *task, struct rpc_cred *cred,
20452058
trace_rpcgss_unwrap_failed(task);
20462059
goto out;
20472060
bad_seqno:
2048-
trace_rpcgss_bad_seqno(task, rqstp->rq_seqno, seqno);
2061+
trace_rpcgss_bad_seqno(task, *rqstp->rq_seqnos, seqno);
20492062
goto out;
20502063
bad_mic:
20512064
trace_rpcgss_verify_mic(task, maj_stat);
@@ -2077,7 +2090,7 @@ gss_unwrap_resp_priv(struct rpc_task *task, struct rpc_cred *cred,
20772090
if (maj_stat != GSS_S_COMPLETE)
20782091
goto bad_unwrap;
20792092
/* gss_unwrap decrypted the sequence number */
2080-
if (be32_to_cpup(p++) != rqstp->rq_seqno)
2093+
if (be32_to_cpup(p++) != *rqstp->rq_seqnos)
20812094
goto bad_seqno;
20822095

20832096
/* gss_unwrap redacts the opaque blob from the head iovec.
@@ -2093,7 +2106,7 @@ gss_unwrap_resp_priv(struct rpc_task *task, struct rpc_cred *cred,
20932106
trace_rpcgss_unwrap_failed(task);
20942107
return -EIO;
20952108
bad_seqno:
2096-
trace_rpcgss_bad_seqno(task, rqstp->rq_seqno, be32_to_cpup(--p));
2109+
trace_rpcgss_bad_seqno(task, *rqstp->rq_seqnos, be32_to_cpup(--p));
20972110
return -EIO;
20982111
bad_unwrap:
20992112
trace_rpcgss_unwrap(task, maj_stat);
@@ -2118,14 +2131,14 @@ gss_xmit_need_reencode(struct rpc_task *task)
21182131
if (!ctx)
21192132
goto out;
21202133

2121-
if (gss_seq_is_newer(req->rq_seqno, READ_ONCE(ctx->gc_seq)))
2134+
if (gss_seq_is_newer(*req->rq_seqnos, READ_ONCE(ctx->gc_seq)))
21222135
goto out_ctx;
21232136

21242137
seq_xmit = READ_ONCE(ctx->gc_seq_xmit);
2125-
while (gss_seq_is_newer(req->rq_seqno, seq_xmit)) {
2138+
while (gss_seq_is_newer(*req->rq_seqnos, seq_xmit)) {
21262139
u32 tmp = seq_xmit;
21272140

2128-
seq_xmit = cmpxchg(&ctx->gc_seq_xmit, tmp, req->rq_seqno);
2141+
seq_xmit = cmpxchg(&ctx->gc_seq_xmit, tmp, *req->rq_seqnos);
21292142
if (seq_xmit == tmp) {
21302143
ret = false;
21312144
goto out_ctx;
@@ -2134,7 +2147,7 @@ gss_xmit_need_reencode(struct rpc_task *task)
21342147

21352148
win = ctx->gc_win;
21362149
if (win > 0)
2137-
ret = !gss_seq_is_newer(req->rq_seqno, seq_xmit - win);
2150+
ret = !gss_seq_is_newer(*req->rq_seqnos, seq_xmit - win);
21382151

21392152
out_ctx:
21402153
gss_put_ctx(ctx);

net/sunrpc/xprt.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1365,7 +1365,7 @@ xprt_request_enqueue_transmit(struct rpc_task *task)
13651365
INIT_LIST_HEAD(&req->rq_xmit2);
13661366
goto out;
13671367
}
1368-
} else if (!req->rq_seqno) {
1368+
} else if (req->rq_seqno_count == 0) {
13691369
list_for_each_entry(pos, &xprt->xmit_queue, rq_xmit) {
13701370
if (pos->rq_task->tk_owner != task->tk_owner)
13711371
continue;
@@ -1898,6 +1898,7 @@ xprt_request_init(struct rpc_task *task)
18981898
req->rq_snd_buf.bvec = NULL;
18991899
req->rq_rcv_buf.bvec = NULL;
19001900
req->rq_release_snd_buf = NULL;
1901+
req->rq_seqno_count = 0;
19011902
xprt_init_majortimeo(task, req, task->tk_client->cl_timeout);
19021903

19031904
trace_xprt_reserve(req);

0 commit comments

Comments
 (0)