diff options
-rw-r--r-- | include/linux/sunrpc/auth.h | 15 | ||||
-rw-r--r-- | include/linux/sunrpc/xdr.h | 6 | ||||
-rw-r--r-- | include/trace/events/sunrpc.h | 29 | ||||
-rw-r--r-- | net/sunrpc/auth.c | 56 | ||||
-rw-r--r-- | net/sunrpc/auth_gss/auth_gss.c | 191 | ||||
-rw-r--r-- | net/sunrpc/auth_null.c | 23 | ||||
-rw-r--r-- | net/sunrpc/auth_unix.c | 61 | ||||
-rw-r--r-- | net/sunrpc/clnt.c | 66 |
8 files changed, 266 insertions, 181 deletions
diff --git a/include/linux/sunrpc/auth.h b/include/linux/sunrpc/auth.h index eed3cb16ccf1..96e237f8e60b 100644 --- a/include/linux/sunrpc/auth.h +++ b/include/linux/sunrpc/auth.h | |||
@@ -131,11 +131,12 @@ struct rpc_credops { | |||
131 | void (*crdestroy)(struct rpc_cred *); | 131 | void (*crdestroy)(struct rpc_cred *); |
132 | 132 | ||
133 | int (*crmatch)(struct auth_cred *, struct rpc_cred *, int); | 133 | int (*crmatch)(struct auth_cred *, struct rpc_cred *, int); |
134 | __be32 * (*crmarshal)(struct rpc_task *, __be32 *); | 134 | int (*crmarshal)(struct rpc_task *task, |
135 | struct xdr_stream *xdr); | ||
135 | int (*crrefresh)(struct rpc_task *); | 136 | int (*crrefresh)(struct rpc_task *); |
136 | __be32 * (*crvalidate)(struct rpc_task *, __be32 *); | 137 | __be32 * (*crvalidate)(struct rpc_task *, __be32 *); |
137 | int (*crwrap_req)(struct rpc_task *, kxdreproc_t, | 138 | int (*crwrap_req)(struct rpc_task *task, |
138 | void *, __be32 *, void *); | 139 | struct xdr_stream *xdr); |
139 | int (*crunwrap_resp)(struct rpc_task *, kxdrdproc_t, | 140 | int (*crunwrap_resp)(struct rpc_task *, kxdrdproc_t, |
140 | void *, __be32 *, void *); | 141 | void *, __be32 *, void *); |
141 | int (*crkey_timeout)(struct rpc_cred *); | 142 | int (*crkey_timeout)(struct rpc_cred *); |
@@ -165,9 +166,13 @@ struct rpc_cred * rpcauth_lookup_credcache(struct rpc_auth *, struct auth_cred * | |||
165 | void rpcauth_init_cred(struct rpc_cred *, const struct auth_cred *, struct rpc_auth *, const struct rpc_credops *); | 166 | void rpcauth_init_cred(struct rpc_cred *, const struct auth_cred *, struct rpc_auth *, const struct rpc_credops *); |
166 | struct rpc_cred * rpcauth_lookupcred(struct rpc_auth *, int); | 167 | struct rpc_cred * rpcauth_lookupcred(struct rpc_auth *, int); |
167 | void put_rpccred(struct rpc_cred *); | 168 | void put_rpccred(struct rpc_cred *); |
168 | __be32 * rpcauth_marshcred(struct rpc_task *, __be32 *); | 169 | int rpcauth_marshcred(struct rpc_task *task, |
170 | struct xdr_stream *xdr); | ||
169 | __be32 * rpcauth_checkverf(struct rpc_task *, __be32 *); | 171 | __be32 * rpcauth_checkverf(struct rpc_task *, __be32 *); |
170 | int rpcauth_wrap_req(struct rpc_task *task, kxdreproc_t encode, void *rqstp, __be32 *data, void *obj); | 172 | int rpcauth_wrap_req_encode(struct rpc_task *task, |
173 | struct xdr_stream *xdr); | ||
174 | int rpcauth_wrap_req(struct rpc_task *task, | ||
175 | struct xdr_stream *xdr); | ||
171 | int rpcauth_unwrap_resp(struct rpc_task *task, kxdrdproc_t decode, void *rqstp, __be32 *data, void *obj); | 176 | int rpcauth_unwrap_resp(struct rpc_task *task, kxdrdproc_t decode, void *rqstp, __be32 *data, void *obj); |
172 | bool rpcauth_xmit_need_reencode(struct rpc_task *task); | 177 | bool rpcauth_xmit_need_reencode(struct rpc_task *task); |
173 | int rpcauth_refreshcred(struct rpc_task *); | 178 | int rpcauth_refreshcred(struct rpc_task *); |
diff --git a/include/linux/sunrpc/xdr.h b/include/linux/sunrpc/xdr.h index 787939d13643..6df9ac1ca471 100644 --- a/include/linux/sunrpc/xdr.h +++ b/include/linux/sunrpc/xdr.h | |||
@@ -87,6 +87,12 @@ xdr_buf_init(struct xdr_buf *buf, void *start, size_t len) | |||
87 | #define xdr_one cpu_to_be32(1) | 87 | #define xdr_one cpu_to_be32(1) |
88 | #define xdr_two cpu_to_be32(2) | 88 | #define xdr_two cpu_to_be32(2) |
89 | 89 | ||
90 | #define rpc_auth_null cpu_to_be32(RPC_AUTH_NULL) | ||
91 | #define rpc_auth_unix cpu_to_be32(RPC_AUTH_UNIX) | ||
92 | #define rpc_auth_gss cpu_to_be32(RPC_AUTH_GSS) | ||
93 | |||
94 | #define rpc_call cpu_to_be32(RPC_CALL) | ||
95 | |||
90 | #define rpc_success cpu_to_be32(RPC_SUCCESS) | 96 | #define rpc_success cpu_to_be32(RPC_SUCCESS) |
91 | #define rpc_prog_unavail cpu_to_be32(RPC_PROG_UNAVAIL) | 97 | #define rpc_prog_unavail cpu_to_be32(RPC_PROG_UNAVAIL) |
92 | #define rpc_prog_mismatch cpu_to_be32(RPC_PROG_MISMATCH) | 98 | #define rpc_prog_mismatch cpu_to_be32(RPC_PROG_MISMATCH) |
diff --git a/include/trace/events/sunrpc.h b/include/trace/events/sunrpc.h index 627650800676..2b3f9d139e75 100644 --- a/include/trace/events/sunrpc.h +++ b/include/trace/events/sunrpc.h | |||
@@ -213,6 +213,35 @@ DECLARE_EVENT_CLASS(rpc_task_queued, | |||
213 | DEFINE_RPC_QUEUED_EVENT(sleep); | 213 | DEFINE_RPC_QUEUED_EVENT(sleep); |
214 | DEFINE_RPC_QUEUED_EVENT(wakeup); | 214 | DEFINE_RPC_QUEUED_EVENT(wakeup); |
215 | 215 | ||
216 | DECLARE_EVENT_CLASS(rpc_failure, | ||
217 | |||
218 | TP_PROTO(const struct rpc_task *task), | ||
219 | |||
220 | TP_ARGS(task), | ||
221 | |||
222 | TP_STRUCT__entry( | ||
223 | __field(unsigned int, task_id) | ||
224 | __field(unsigned int, client_id) | ||
225 | ), | ||
226 | |||
227 | TP_fast_assign( | ||
228 | __entry->task_id = task->tk_pid; | ||
229 | __entry->client_id = task->tk_client->cl_clid; | ||
230 | ), | ||
231 | |||
232 | TP_printk("task:%u@%u", | ||
233 | __entry->task_id, __entry->client_id) | ||
234 | ); | ||
235 | |||
236 | #define DEFINE_RPC_FAILURE(name) \ | ||
237 | DEFINE_EVENT(rpc_failure, rpc_bad_##name, \ | ||
238 | TP_PROTO( \ | ||
239 | const struct rpc_task *task \ | ||
240 | ), \ | ||
241 | TP_ARGS(task)) | ||
242 | |||
243 | DEFINE_RPC_FAILURE(callhdr); | ||
244 | |||
216 | TRACE_EVENT(rpc_stats_latency, | 245 | TRACE_EVENT(rpc_stats_latency, |
217 | 246 | ||
218 | TP_PROTO( | 247 | TP_PROTO( |
diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c index 275e84e817b7..add2135d9b01 100644 --- a/net/sunrpc/auth.c +++ b/net/sunrpc/auth.c | |||
@@ -756,12 +756,21 @@ destroy: | |||
756 | } | 756 | } |
757 | EXPORT_SYMBOL_GPL(put_rpccred); | 757 | EXPORT_SYMBOL_GPL(put_rpccred); |
758 | 758 | ||
759 | __be32 * | 759 | /** |
760 | rpcauth_marshcred(struct rpc_task *task, __be32 *p) | 760 | * rpcauth_marshcred - Append RPC credential to end of @xdr |
761 | * @task: controlling RPC task | ||
762 | * @xdr: xdr_stream containing initial portion of RPC Call header | ||
763 | * | ||
764 | * On success, an appropriate verifier is added to @xdr, @xdr is | ||
765 | * updated to point past the verifier, and zero is returned. | ||
766 | * Otherwise, @xdr is in an undefined state and a negative errno | ||
767 | * is returned. | ||
768 | */ | ||
769 | int rpcauth_marshcred(struct rpc_task *task, struct xdr_stream *xdr) | ||
761 | { | 770 | { |
762 | struct rpc_cred *cred = task->tk_rqstp->rq_cred; | 771 | const struct rpc_credops *ops = task->tk_rqstp->rq_cred->cr_ops; |
763 | 772 | ||
764 | return cred->cr_ops->crmarshal(task, p); | 773 | return ops->crmarshal(task, xdr); |
765 | } | 774 | } |
766 | 775 | ||
767 | __be32 * | 776 | __be32 * |
@@ -772,26 +781,37 @@ rpcauth_checkverf(struct rpc_task *task, __be32 *p) | |||
772 | return cred->cr_ops->crvalidate(task, p); | 781 | return cred->cr_ops->crvalidate(task, p); |
773 | } | 782 | } |
774 | 783 | ||
775 | static void rpcauth_wrap_req_encode(kxdreproc_t encode, struct rpc_rqst *rqstp, | 784 | /** |
776 | __be32 *data, void *obj) | 785 | * rpcauth_wrap_req_encode - XDR encode the RPC procedure |
786 | * @task: controlling RPC task | ||
787 | * @xdr: stream where on-the-wire bytes are to be marshalled | ||
788 | * | ||
789 | * On success, @xdr contains the encoded and wrapped message. | ||
790 | * Otherwise, @xdr is in an undefined state. | ||
791 | */ | ||
792 | int rpcauth_wrap_req_encode(struct rpc_task *task, struct xdr_stream *xdr) | ||
777 | { | 793 | { |
778 | struct xdr_stream xdr; | 794 | kxdreproc_t encode = task->tk_msg.rpc_proc->p_encode; |
779 | 795 | ||
780 | xdr_init_encode(&xdr, &rqstp->rq_snd_buf, data, rqstp); | 796 | encode(task->tk_rqstp, xdr, task->tk_msg.rpc_argp); |
781 | encode(rqstp, &xdr, obj); | 797 | return 0; |
782 | } | 798 | } |
799 | EXPORT_SYMBOL_GPL(rpcauth_wrap_req_encode); | ||
783 | 800 | ||
784 | int | 801 | /** |
785 | rpcauth_wrap_req(struct rpc_task *task, kxdreproc_t encode, void *rqstp, | 802 | * rpcauth_wrap_req - XDR encode and wrap the RPC procedure |
786 | __be32 *data, void *obj) | 803 | * @task: controlling RPC task |
804 | * @xdr: stream where on-the-wire bytes are to be marshalled | ||
805 | * | ||
806 | * On success, @xdr contains the encoded and wrapped message, | ||
807 | * and zero is returned. Otherwise, @xdr is in an undefined | ||
808 | * state and a negative errno is returned. | ||
809 | */ | ||
810 | int rpcauth_wrap_req(struct rpc_task *task, struct xdr_stream *xdr) | ||
787 | { | 811 | { |
788 | struct rpc_cred *cred = task->tk_rqstp->rq_cred; | 812 | const struct rpc_credops *ops = task->tk_rqstp->rq_cred->cr_ops; |
789 | 813 | ||
790 | if (cred->cr_ops->crwrap_req) | 814 | return ops->crwrap_req(task, xdr); |
791 | return cred->cr_ops->crwrap_req(task, encode, rqstp, data, obj); | ||
792 | /* By default, we encode the arguments normally. */ | ||
793 | rpcauth_wrap_req_encode(encode, rqstp, data, obj); | ||
794 | return 0; | ||
795 | } | 815 | } |
796 | 816 | ||
797 | static int | 817 | static int |
diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index 4b52e2b11c58..b333b1bdad45 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c | |||
@@ -1526,18 +1526,20 @@ out: | |||
1526 | } | 1526 | } |
1527 | 1527 | ||
1528 | /* | 1528 | /* |
1529 | * Marshal credentials. | 1529 | * Marshal credentials. |
1530 | * Maybe we should keep a cached credential for performance reasons. | 1530 | * |
1531 | */ | 1531 | * The expensive part is computing the verifier. We can't cache a |
1532 | static __be32 * | 1532 | * pre-computed version of the verifier because the seqno, which |
1533 | gss_marshal(struct rpc_task *task, __be32 *p) | 1533 | * is different every time, is included in the MIC. |
1534 | */ | ||
1535 | static int gss_marshal(struct rpc_task *task, struct xdr_stream *xdr) | ||
1534 | { | 1536 | { |
1535 | struct rpc_rqst *req = task->tk_rqstp; | 1537 | struct rpc_rqst *req = task->tk_rqstp; |
1536 | struct rpc_cred *cred = req->rq_cred; | 1538 | struct rpc_cred *cred = req->rq_cred; |
1537 | struct gss_cred *gss_cred = container_of(cred, struct gss_cred, | 1539 | struct gss_cred *gss_cred = container_of(cred, struct gss_cred, |
1538 | gc_base); | 1540 | gc_base); |
1539 | struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); | 1541 | struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); |
1540 | __be32 *cred_len; | 1542 | __be32 *p, *cred_len; |
1541 | u32 maj_stat = 0; | 1543 | u32 maj_stat = 0; |
1542 | struct xdr_netobj mic; | 1544 | struct xdr_netobj mic; |
1543 | struct kvec iov; | 1545 | struct kvec iov; |
@@ -1545,7 +1547,13 @@ gss_marshal(struct rpc_task *task, __be32 *p) | |||
1545 | 1547 | ||
1546 | dprintk("RPC: %5u %s\n", task->tk_pid, __func__); | 1548 | dprintk("RPC: %5u %s\n", task->tk_pid, __func__); |
1547 | 1549 | ||
1548 | *p++ = htonl(RPC_AUTH_GSS); | 1550 | /* Credential */ |
1551 | |||
1552 | p = xdr_reserve_space(xdr, 7 * sizeof(*p) + | ||
1553 | ctx->gc_wire_ctx.len); | ||
1554 | if (!p) | ||
1555 | goto out_put_ctx; | ||
1556 | *p++ = rpc_auth_gss; | ||
1549 | cred_len = p++; | 1557 | cred_len = p++; |
1550 | 1558 | ||
1551 | spin_lock(&ctx->gc_seq_lock); | 1559 | spin_lock(&ctx->gc_seq_lock); |
@@ -1554,12 +1562,14 @@ gss_marshal(struct rpc_task *task, __be32 *p) | |||
1554 | if (req->rq_seqno == MAXSEQ) | 1562 | if (req->rq_seqno == MAXSEQ) |
1555 | goto out_expired; | 1563 | goto out_expired; |
1556 | 1564 | ||
1557 | *p++ = htonl((u32) RPC_GSS_VERSION); | 1565 | *p++ = cpu_to_be32(RPC_GSS_VERSION); |
1558 | *p++ = htonl((u32) ctx->gc_proc); | 1566 | *p++ = cpu_to_be32(ctx->gc_proc); |
1559 | *p++ = htonl((u32) req->rq_seqno); | 1567 | *p++ = cpu_to_be32(req->rq_seqno); |
1560 | *p++ = htonl((u32) gss_cred->gc_service); | 1568 | *p++ = cpu_to_be32(gss_cred->gc_service); |
1561 | p = xdr_encode_netobj(p, &ctx->gc_wire_ctx); | 1569 | p = xdr_encode_netobj(p, &ctx->gc_wire_ctx); |
1562 | *cred_len = htonl((p - (cred_len + 1)) << 2); | 1570 | *cred_len = cpu_to_be32((p - (cred_len + 1)) << 2); |
1571 | |||
1572 | /* Verifier */ | ||
1563 | 1573 | ||
1564 | /* We compute the checksum for the verifier over the xdr-encoded bytes | 1574 | /* We compute the checksum for the verifier over the xdr-encoded bytes |
1565 | * starting with the xid and ending at the end of the credential: */ | 1575 | * starting with the xid and ending at the end of the credential: */ |
@@ -1567,27 +1577,27 @@ gss_marshal(struct rpc_task *task, __be32 *p) | |||
1567 | iov.iov_len = (u8 *)p - (u8 *)iov.iov_base; | 1577 | iov.iov_len = (u8 *)p - (u8 *)iov.iov_base; |
1568 | xdr_buf_from_iov(&iov, &verf_buf); | 1578 | xdr_buf_from_iov(&iov, &verf_buf); |
1569 | 1579 | ||
1570 | /* set verifier flavor*/ | 1580 | p = xdr_reserve_space(xdr, sizeof(*p)); |
1571 | *p++ = htonl(RPC_AUTH_GSS); | 1581 | if (!p) |
1572 | 1582 | goto out_put_ctx; | |
1583 | *p++ = rpc_auth_gss; | ||
1573 | mic.data = (u8 *)(p + 1); | 1584 | mic.data = (u8 *)(p + 1); |
1574 | maj_stat = gss_get_mic(ctx->gc_gss_ctx, &verf_buf, &mic); | 1585 | maj_stat = gss_get_mic(ctx->gc_gss_ctx, &verf_buf, &mic); |
1575 | if (maj_stat == GSS_S_CONTEXT_EXPIRED) { | 1586 | if (maj_stat == GSS_S_CONTEXT_EXPIRED) |
1576 | goto out_expired; | 1587 | goto out_expired; |
1577 | } else if (maj_stat != 0) { | 1588 | else if (maj_stat != 0) |
1578 | pr_warn("gss_marshal: gss_get_mic FAILED (%d)\n", maj_stat); | 1589 | goto out_put_ctx; |
1579 | task->tk_status = -EIO; | 1590 | if (xdr_stream_encode_opaque_inline(xdr, (void **)&p, mic.len) < 0) |
1580 | goto out_put_ctx; | 1591 | goto out_put_ctx; |
1581 | } | ||
1582 | p = xdr_encode_opaque(p, NULL, mic.len); | ||
1583 | gss_put_ctx(ctx); | 1592 | gss_put_ctx(ctx); |
1584 | return p; | 1593 | return 0; |
1585 | out_expired: | 1594 | out_expired: |
1595 | gss_put_ctx(ctx); | ||
1586 | clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); | 1596 | clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); |
1587 | task->tk_status = -EKEYEXPIRED; | 1597 | return -EKEYEXPIRED; |
1588 | out_put_ctx: | 1598 | out_put_ctx: |
1589 | gss_put_ctx(ctx); | 1599 | gss_put_ctx(ctx); |
1590 | return NULL; | 1600 | return -EMSGSIZE; |
1591 | } | 1601 | } |
1592 | 1602 | ||
1593 | static int gss_renew_cred(struct rpc_task *task) | 1603 | static int gss_renew_cred(struct rpc_task *task) |
@@ -1716,61 +1726,45 @@ out_bad: | |||
1716 | return ret; | 1726 | return ret; |
1717 | } | 1727 | } |
1718 | 1728 | ||
1719 | static void gss_wrap_req_encode(kxdreproc_t encode, struct rpc_rqst *rqstp, | 1729 | static int gss_wrap_req_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx, |
1720 | __be32 *p, void *obj) | 1730 | struct rpc_task *task, struct xdr_stream *xdr) |
1721 | { | ||
1722 | struct xdr_stream xdr; | ||
1723 | |||
1724 | xdr_init_encode(&xdr, &rqstp->rq_snd_buf, p, rqstp); | ||
1725 | encode(rqstp, &xdr, obj); | ||
1726 | } | ||
1727 | |||
1728 | static inline int | ||
1729 | gss_wrap_req_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx, | ||
1730 | kxdreproc_t encode, struct rpc_rqst *rqstp, | ||
1731 | __be32 *p, void *obj) | ||
1732 | { | 1731 | { |
1733 | struct xdr_buf *snd_buf = &rqstp->rq_snd_buf; | 1732 | struct rpc_rqst *rqstp = task->tk_rqstp; |
1734 | struct xdr_buf integ_buf; | 1733 | struct xdr_buf integ_buf, *snd_buf = &rqstp->rq_snd_buf; |
1735 | __be32 *integ_len = NULL; | ||
1736 | struct xdr_netobj mic; | 1734 | struct xdr_netobj mic; |
1737 | u32 offset; | 1735 | __be32 *p, *integ_len; |
1738 | __be32 *q; | 1736 | u32 offset, maj_stat; |
1739 | struct kvec *iov; | ||
1740 | u32 maj_stat = 0; | ||
1741 | int status = -EIO; | ||
1742 | 1737 | ||
1738 | p = xdr_reserve_space(xdr, 2 * sizeof(*p)); | ||
1739 | if (!p) | ||
1740 | goto wrap_failed; | ||
1743 | integ_len = p++; | 1741 | integ_len = p++; |
1744 | offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base; | 1742 | *p = cpu_to_be32(rqstp->rq_seqno); |
1745 | *p++ = htonl(rqstp->rq_seqno); | ||
1746 | 1743 | ||
1747 | gss_wrap_req_encode(encode, rqstp, p, obj); | 1744 | if (rpcauth_wrap_req_encode(task, xdr)) |
1745 | goto wrap_failed; | ||
1748 | 1746 | ||
1747 | offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base; | ||
1749 | if (xdr_buf_subsegment(snd_buf, &integ_buf, | 1748 | if (xdr_buf_subsegment(snd_buf, &integ_buf, |
1750 | offset, snd_buf->len - offset)) | 1749 | offset, snd_buf->len - offset)) |
1751 | return status; | 1750 | goto wrap_failed; |
1752 | *integ_len = htonl(integ_buf.len); | 1751 | *integ_len = cpu_to_be32(integ_buf.len); |
1753 | 1752 | ||
1754 | /* guess whether we're in the head or the tail: */ | 1753 | p = xdr_reserve_space(xdr, 0); |
1755 | if (snd_buf->page_len || snd_buf->tail[0].iov_len) | 1754 | if (!p) |
1756 | iov = snd_buf->tail; | 1755 | goto wrap_failed; |
1757 | else | ||
1758 | iov = snd_buf->head; | ||
1759 | p = iov->iov_base + iov->iov_len; | ||
1760 | mic.data = (u8 *)(p + 1); | 1756 | mic.data = (u8 *)(p + 1); |
1761 | |||
1762 | maj_stat = gss_get_mic(ctx->gc_gss_ctx, &integ_buf, &mic); | 1757 | maj_stat = gss_get_mic(ctx->gc_gss_ctx, &integ_buf, &mic); |
1763 | status = -EIO; /* XXX? */ | ||
1764 | if (maj_stat == GSS_S_CONTEXT_EXPIRED) | 1758 | if (maj_stat == GSS_S_CONTEXT_EXPIRED) |
1765 | clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); | 1759 | clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); |
1766 | else if (maj_stat) | 1760 | else if (maj_stat) |
1767 | return status; | 1761 | goto wrap_failed; |
1768 | q = xdr_encode_opaque(p, NULL, mic.len); | 1762 | /* Check that the trailing MIC fit in the buffer, after the fact */ |
1769 | 1763 | if (xdr_stream_encode_opaque_inline(xdr, (void **)&p, mic.len) < 0) | |
1770 | offset = (u8 *)q - (u8 *)p; | 1764 | goto wrap_failed; |
1771 | iov->iov_len += offset; | ||
1772 | snd_buf->len += offset; | ||
1773 | return 0; | 1765 | return 0; |
1766 | wrap_failed: | ||
1767 | return -EMSGSIZE; | ||
1774 | } | 1768 | } |
1775 | 1769 | ||
1776 | static void | 1770 | static void |
@@ -1821,61 +1815,63 @@ out: | |||
1821 | return -EAGAIN; | 1815 | return -EAGAIN; |
1822 | } | 1816 | } |
1823 | 1817 | ||
1824 | static inline int | 1818 | static int gss_wrap_req_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx, |
1825 | gss_wrap_req_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx, | 1819 | struct rpc_task *task, struct xdr_stream *xdr) |
1826 | kxdreproc_t encode, struct rpc_rqst *rqstp, | ||
1827 | __be32 *p, void *obj) | ||
1828 | { | 1820 | { |
1821 | struct rpc_rqst *rqstp = task->tk_rqstp; | ||
1829 | struct xdr_buf *snd_buf = &rqstp->rq_snd_buf; | 1822 | struct xdr_buf *snd_buf = &rqstp->rq_snd_buf; |
1830 | u32 offset; | 1823 | u32 pad, offset, maj_stat; |
1831 | u32 maj_stat; | ||
1832 | int status; | 1824 | int status; |
1833 | __be32 *opaque_len; | 1825 | __be32 *p, *opaque_len; |
1834 | struct page **inpages; | 1826 | struct page **inpages; |
1835 | int first; | 1827 | int first; |
1836 | int pad; | ||
1837 | struct kvec *iov; | 1828 | struct kvec *iov; |
1838 | char *tmp; | ||
1839 | 1829 | ||
1830 | status = -EIO; | ||
1831 | p = xdr_reserve_space(xdr, 2 * sizeof(*p)); | ||
1832 | if (!p) | ||
1833 | goto wrap_failed; | ||
1840 | opaque_len = p++; | 1834 | opaque_len = p++; |
1841 | offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base; | 1835 | *p = cpu_to_be32(rqstp->rq_seqno); |
1842 | *p++ = htonl(rqstp->rq_seqno); | ||
1843 | 1836 | ||
1844 | gss_wrap_req_encode(encode, rqstp, p, obj); | 1837 | if (rpcauth_wrap_req_encode(task, xdr)) |
1838 | goto wrap_failed; | ||
1845 | 1839 | ||
1846 | status = alloc_enc_pages(rqstp); | 1840 | status = alloc_enc_pages(rqstp); |
1847 | if (status) | 1841 | if (unlikely(status)) |
1848 | return status; | 1842 | goto wrap_failed; |
1849 | first = snd_buf->page_base >> PAGE_SHIFT; | 1843 | first = snd_buf->page_base >> PAGE_SHIFT; |
1850 | inpages = snd_buf->pages + first; | 1844 | inpages = snd_buf->pages + first; |
1851 | snd_buf->pages = rqstp->rq_enc_pages; | 1845 | snd_buf->pages = rqstp->rq_enc_pages; |
1852 | snd_buf->page_base -= first << PAGE_SHIFT; | 1846 | snd_buf->page_base -= first << PAGE_SHIFT; |
1853 | /* | 1847 | /* |
1854 | * Give the tail its own page, in case we need extra space in the | 1848 | * Move the tail into its own page, in case gss_wrap needs |
1855 | * head when wrapping: | 1849 | * more space in the head when wrapping. |
1856 | * | 1850 | * |
1857 | * call_allocate() allocates twice the slack space required | 1851 | * Still... Why can't gss_wrap just slide the tail down? |
1858 | * by the authentication flavor to rq_callsize. | ||
1859 | * For GSS, slack is GSS_CRED_SLACK. | ||
1860 | */ | 1852 | */ |
1861 | if (snd_buf->page_len || snd_buf->tail[0].iov_len) { | 1853 | if (snd_buf->page_len || snd_buf->tail[0].iov_len) { |
1854 | char *tmp; | ||
1855 | |||
1862 | tmp = page_address(rqstp->rq_enc_pages[rqstp->rq_enc_pages_num - 1]); | 1856 | tmp = page_address(rqstp->rq_enc_pages[rqstp->rq_enc_pages_num - 1]); |
1863 | memcpy(tmp, snd_buf->tail[0].iov_base, snd_buf->tail[0].iov_len); | 1857 | memcpy(tmp, snd_buf->tail[0].iov_base, snd_buf->tail[0].iov_len); |
1864 | snd_buf->tail[0].iov_base = tmp; | 1858 | snd_buf->tail[0].iov_base = tmp; |
1865 | } | 1859 | } |
1860 | status = -EIO; | ||
1861 | offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base; | ||
1866 | maj_stat = gss_wrap(ctx->gc_gss_ctx, offset, snd_buf, inpages); | 1862 | maj_stat = gss_wrap(ctx->gc_gss_ctx, offset, snd_buf, inpages); |
1867 | /* slack space should prevent this ever happening: */ | 1863 | /* slack space should prevent this ever happening: */ |
1868 | BUG_ON(snd_buf->len > snd_buf->buflen); | 1864 | if (unlikely(snd_buf->len > snd_buf->buflen)) |
1869 | status = -EIO; | 1865 | goto wrap_failed; |
1870 | /* We're assuming that when GSS_S_CONTEXT_EXPIRED, the encryption was | 1866 | /* We're assuming that when GSS_S_CONTEXT_EXPIRED, the encryption was |
1871 | * done anyway, so it's safe to put the request on the wire: */ | 1867 | * done anyway, so it's safe to put the request on the wire: */ |
1872 | if (maj_stat == GSS_S_CONTEXT_EXPIRED) | 1868 | if (maj_stat == GSS_S_CONTEXT_EXPIRED) |
1873 | clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); | 1869 | clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); |
1874 | else if (maj_stat) | 1870 | else if (maj_stat) |
1875 | return status; | 1871 | goto wrap_failed; |
1876 | 1872 | ||
1877 | *opaque_len = htonl(snd_buf->len - offset); | 1873 | *opaque_len = cpu_to_be32(snd_buf->len - offset); |
1878 | /* guess whether we're in the head or the tail: */ | 1874 | /* guess whether the pad goes into the head or the tail: */ |
1879 | if (snd_buf->page_len || snd_buf->tail[0].iov_len) | 1875 | if (snd_buf->page_len || snd_buf->tail[0].iov_len) |
1880 | iov = snd_buf->tail; | 1876 | iov = snd_buf->tail; |
1881 | else | 1877 | else |
@@ -1887,37 +1883,36 @@ gss_wrap_req_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx, | |||
1887 | snd_buf->len += pad; | 1883 | snd_buf->len += pad; |
1888 | 1884 | ||
1889 | return 0; | 1885 | return 0; |
1886 | wrap_failed: | ||
1887 | return status; | ||
1890 | } | 1888 | } |
1891 | 1889 | ||
1892 | static int | 1890 | static int gss_wrap_req(struct rpc_task *task, struct xdr_stream *xdr) |
1893 | gss_wrap_req(struct rpc_task *task, | ||
1894 | kxdreproc_t encode, void *rqstp, __be32 *p, void *obj) | ||
1895 | { | 1891 | { |
1896 | struct rpc_cred *cred = task->tk_rqstp->rq_cred; | 1892 | struct rpc_cred *cred = task->tk_rqstp->rq_cred; |
1897 | struct gss_cred *gss_cred = container_of(cred, struct gss_cred, | 1893 | struct gss_cred *gss_cred = container_of(cred, struct gss_cred, |
1898 | gc_base); | 1894 | gc_base); |
1899 | struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); | 1895 | struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); |
1900 | int status = -EIO; | 1896 | int status; |
1901 | 1897 | ||
1902 | dprintk("RPC: %5u %s\n", task->tk_pid, __func__); | 1898 | dprintk("RPC: %5u %s\n", task->tk_pid, __func__); |
1899 | status = -EIO; | ||
1903 | if (ctx->gc_proc != RPC_GSS_PROC_DATA) { | 1900 | if (ctx->gc_proc != RPC_GSS_PROC_DATA) { |
1904 | /* The spec seems a little ambiguous here, but I think that not | 1901 | /* The spec seems a little ambiguous here, but I think that not |
1905 | * wrapping context destruction requests makes the most sense. | 1902 | * wrapping context destruction requests makes the most sense. |
1906 | */ | 1903 | */ |
1907 | gss_wrap_req_encode(encode, rqstp, p, obj); | 1904 | status = rpcauth_wrap_req_encode(task, xdr); |
1908 | status = 0; | ||
1909 | goto out; | 1905 | goto out; |
1910 | } | 1906 | } |
1911 | switch (gss_cred->gc_service) { | 1907 | switch (gss_cred->gc_service) { |
1912 | case RPC_GSS_SVC_NONE: | 1908 | case RPC_GSS_SVC_NONE: |
1913 | gss_wrap_req_encode(encode, rqstp, p, obj); | 1909 | status = rpcauth_wrap_req_encode(task, xdr); |
1914 | status = 0; | ||
1915 | break; | 1910 | break; |
1916 | case RPC_GSS_SVC_INTEGRITY: | 1911 | case RPC_GSS_SVC_INTEGRITY: |
1917 | status = gss_wrap_req_integ(cred, ctx, encode, rqstp, p, obj); | 1912 | status = gss_wrap_req_integ(cred, ctx, task, xdr); |
1918 | break; | 1913 | break; |
1919 | case RPC_GSS_SVC_PRIVACY: | 1914 | case RPC_GSS_SVC_PRIVACY: |
1920 | status = gss_wrap_req_priv(cred, ctx, encode, rqstp, p, obj); | 1915 | status = gss_wrap_req_priv(cred, ctx, task, xdr); |
1921 | break; | 1916 | break; |
1922 | } | 1917 | } |
1923 | out: | 1918 | out: |
diff --git a/net/sunrpc/auth_null.c b/net/sunrpc/auth_null.c index d0ceac57c06e..797f8472c21b 100644 --- a/net/sunrpc/auth_null.c +++ b/net/sunrpc/auth_null.c | |||
@@ -59,15 +59,21 @@ nul_match(struct auth_cred *acred, struct rpc_cred *cred, int taskflags) | |||
59 | /* | 59 | /* |
60 | * Marshal credential. | 60 | * Marshal credential. |
61 | */ | 61 | */ |
62 | static __be32 * | 62 | static int |
63 | nul_marshal(struct rpc_task *task, __be32 *p) | 63 | nul_marshal(struct rpc_task *task, struct xdr_stream *xdr) |
64 | { | 64 | { |
65 | *p++ = htonl(RPC_AUTH_NULL); | 65 | __be32 *p; |
66 | *p++ = 0; | 66 | |
67 | *p++ = htonl(RPC_AUTH_NULL); | 67 | p = xdr_reserve_space(xdr, 4 * sizeof(*p)); |
68 | *p++ = 0; | 68 | if (!p) |
69 | 69 | return -EMSGSIZE; | |
70 | return p; | 70 | /* Credential */ |
71 | *p++ = rpc_auth_null; | ||
72 | *p++ = xdr_zero; | ||
73 | /* Verifier */ | ||
74 | *p++ = rpc_auth_null; | ||
75 | *p = xdr_zero; | ||
76 | return 0; | ||
71 | } | 77 | } |
72 | 78 | ||
73 | /* | 79 | /* |
@@ -125,6 +131,7 @@ const struct rpc_credops null_credops = { | |||
125 | .crdestroy = nul_destroy_cred, | 131 | .crdestroy = nul_destroy_cred, |
126 | .crmatch = nul_match, | 132 | .crmatch = nul_match, |
127 | .crmarshal = nul_marshal, | 133 | .crmarshal = nul_marshal, |
134 | .crwrap_req = rpcauth_wrap_req_encode, | ||
128 | .crrefresh = nul_refresh, | 135 | .crrefresh = nul_refresh, |
129 | .crvalidate = nul_validate, | 136 | .crvalidate = nul_validate, |
130 | }; | 137 | }; |
diff --git a/net/sunrpc/auth_unix.c b/net/sunrpc/auth_unix.c index fc8a59134640..1d5b7ed9c6f7 100644 --- a/net/sunrpc/auth_unix.c +++ b/net/sunrpc/auth_unix.c | |||
@@ -99,37 +99,55 @@ unx_match(struct auth_cred *acred, struct rpc_cred *cred, int flags) | |||
99 | * Marshal credentials. | 99 | * Marshal credentials. |
100 | * Maybe we should keep a cached credential for performance reasons. | 100 | * Maybe we should keep a cached credential for performance reasons. |
101 | */ | 101 | */ |
102 | static __be32 * | 102 | static int |
103 | unx_marshal(struct rpc_task *task, __be32 *p) | 103 | unx_marshal(struct rpc_task *task, struct xdr_stream *xdr) |
104 | { | 104 | { |
105 | struct rpc_clnt *clnt = task->tk_client; | 105 | struct rpc_clnt *clnt = task->tk_client; |
106 | struct rpc_cred *cred = task->tk_rqstp->rq_cred; | 106 | struct rpc_cred *cred = task->tk_rqstp->rq_cred; |
107 | __be32 *base, *hold; | 107 | __be32 *p, *cred_len, *gidarr_len; |
108 | int i; | 108 | int i; |
109 | struct group_info *gi = cred->cr_cred->group_info; | 109 | struct group_info *gi = cred->cr_cred->group_info; |
110 | 110 | ||
111 | *p++ = htonl(RPC_AUTH_UNIX); | 111 | /* Credential */ |
112 | base = p++; | 112 | |
113 | *p++ = htonl(jiffies/HZ); | 113 | p = xdr_reserve_space(xdr, 3 * sizeof(*p)); |
114 | 114 | if (!p) | |
115 | /* | 115 | goto marshal_failed; |
116 | * Copy the UTS nodename captured when the client was created. | 116 | *p++ = rpc_auth_unix; |
117 | */ | 117 | cred_len = p++; |
118 | p = xdr_encode_array(p, clnt->cl_nodename, clnt->cl_nodelen); | 118 | *p++ = xdr_zero; /* stamp */ |
119 | 119 | if (xdr_stream_encode_opaque(xdr, clnt->cl_nodename, | |
120 | *p++ = htonl((u32) from_kuid(&init_user_ns, cred->cr_cred->fsuid)); | 120 | clnt->cl_nodelen) < 0) |
121 | *p++ = htonl((u32) from_kgid(&init_user_ns, cred->cr_cred->fsgid)); | 121 | goto marshal_failed; |
122 | hold = p++; | 122 | p = xdr_reserve_space(xdr, 3 * sizeof(*p)); |
123 | if (!p) | ||
124 | goto marshal_failed; | ||
125 | *p++ = cpu_to_be32(from_kuid(&init_user_ns, cred->cr_cred->fsuid)); | ||
126 | *p++ = cpu_to_be32(from_kgid(&init_user_ns, cred->cr_cred->fsgid)); | ||
127 | |||
128 | gidarr_len = p++; | ||
123 | if (gi) | 129 | if (gi) |
124 | for (i = 0; i < UNX_NGROUPS && i < gi->ngroups; i++) | 130 | for (i = 0; i < UNX_NGROUPS && i < gi->ngroups; i++) |
125 | *p++ = htonl((u32) from_kgid(&init_user_ns, gi->gid[i])); | 131 | *p++ = cpu_to_be32(from_kgid(&init_user_ns, |
126 | *hold = htonl(p - hold - 1); /* gid array length */ | 132 | gi->gid[i])); |
127 | *base = htonl((p - base - 1) << 2); /* cred length */ | 133 | *gidarr_len = cpu_to_be32(p - gidarr_len - 1); |
134 | *cred_len = cpu_to_be32((p - cred_len - 1) << 2); | ||
135 | p = xdr_reserve_space(xdr, (p - gidarr_len - 1) << 2); | ||
136 | if (!p) | ||
137 | goto marshal_failed; | ||
138 | |||
139 | /* Verifier */ | ||
140 | |||
141 | p = xdr_reserve_space(xdr, 2 * sizeof(*p)); | ||
142 | if (!p) | ||
143 | goto marshal_failed; | ||
144 | *p++ = rpc_auth_null; | ||
145 | *p = xdr_zero; | ||
128 | 146 | ||
129 | *p++ = htonl(RPC_AUTH_NULL); | 147 | return 0; |
130 | *p++ = htonl(0); | ||
131 | 148 | ||
132 | return p; | 149 | marshal_failed: |
150 | return -EMSGSIZE; | ||
133 | } | 151 | } |
134 | 152 | ||
135 | /* | 153 | /* |
@@ -202,6 +220,7 @@ const struct rpc_credops unix_credops = { | |||
202 | .crdestroy = unx_destroy_cred, | 220 | .crdestroy = unx_destroy_cred, |
203 | .crmatch = unx_match, | 221 | .crmatch = unx_match, |
204 | .crmarshal = unx_marshal, | 222 | .crmarshal = unx_marshal, |
223 | .crwrap_req = rpcauth_wrap_req_encode, | ||
205 | .crrefresh = unx_refresh, | 224 | .crrefresh = unx_refresh, |
206 | .crvalidate = unx_validate, | 225 | .crvalidate = unx_validate, |
207 | }; | 226 | }; |
diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index c4203f6138ef..d6750b7f169a 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c | |||
@@ -77,7 +77,8 @@ static void call_timeout(struct rpc_task *task); | |||
77 | static void call_connect(struct rpc_task *task); | 77 | static void call_connect(struct rpc_task *task); |
78 | static void call_connect_status(struct rpc_task *task); | 78 | static void call_connect_status(struct rpc_task *task); |
79 | 79 | ||
80 | static __be32 *rpc_encode_header(struct rpc_task *task); | 80 | static int rpc_encode_header(struct rpc_task *task, |
81 | struct xdr_stream *xdr); | ||
81 | static __be32 *rpc_verify_header(struct rpc_task *task); | 82 | static __be32 *rpc_verify_header(struct rpc_task *task); |
82 | static int rpc_ping(struct rpc_clnt *clnt); | 83 | static int rpc_ping(struct rpc_clnt *clnt); |
83 | 84 | ||
@@ -1728,10 +1729,7 @@ static void | |||
1728 | rpc_xdr_encode(struct rpc_task *task) | 1729 | rpc_xdr_encode(struct rpc_task *task) |
1729 | { | 1730 | { |
1730 | struct rpc_rqst *req = task->tk_rqstp; | 1731 | struct rpc_rqst *req = task->tk_rqstp; |
1731 | kxdreproc_t encode; | 1732 | struct xdr_stream xdr; |
1732 | __be32 *p; | ||
1733 | |||
1734 | dprint_status(task); | ||
1735 | 1733 | ||
1736 | xdr_buf_init(&req->rq_snd_buf, | 1734 | xdr_buf_init(&req->rq_snd_buf, |
1737 | req->rq_buffer, | 1735 | req->rq_buffer, |
@@ -1740,18 +1738,13 @@ rpc_xdr_encode(struct rpc_task *task) | |||
1740 | req->rq_rbuffer, | 1738 | req->rq_rbuffer, |
1741 | req->rq_rcvsize); | 1739 | req->rq_rcvsize); |
1742 | 1740 | ||
1743 | p = rpc_encode_header(task); | 1741 | req->rq_snd_buf.head[0].iov_len = 0; |
1744 | if (p == NULL) | 1742 | xdr_init_encode(&xdr, &req->rq_snd_buf, |
1743 | req->rq_snd_buf.head[0].iov_base, req); | ||
1744 | if (rpc_encode_header(task, &xdr)) | ||
1745 | return; | 1745 | return; |
1746 | 1746 | ||
1747 | encode = task->tk_msg.rpc_proc->p_encode; | 1747 | task->tk_status = rpcauth_wrap_req(task, &xdr); |
1748 | if (encode == NULL) | ||
1749 | return; | ||
1750 | |||
1751 | task->tk_status = rpcauth_wrap_req(task, encode, req, p, | ||
1752 | task->tk_msg.rpc_argp); | ||
1753 | if (task->tk_status == 0) | ||
1754 | xprt_request_prepare(req); | ||
1755 | } | 1748 | } |
1756 | 1749 | ||
1757 | /* | 1750 | /* |
@@ -1762,6 +1755,7 @@ call_encode(struct rpc_task *task) | |||
1762 | { | 1755 | { |
1763 | if (!rpc_task_need_encode(task)) | 1756 | if (!rpc_task_need_encode(task)) |
1764 | goto out; | 1757 | goto out; |
1758 | dprint_status(task); | ||
1765 | /* Encode here so that rpcsec_gss can use correct sequence number. */ | 1759 | /* Encode here so that rpcsec_gss can use correct sequence number. */ |
1766 | rpc_xdr_encode(task); | 1760 | rpc_xdr_encode(task); |
1767 | /* Did the encode result in an error condition? */ | 1761 | /* Did the encode result in an error condition? */ |
@@ -1779,6 +1773,8 @@ call_encode(struct rpc_task *task) | |||
1779 | rpc_exit(task, task->tk_status); | 1773 | rpc_exit(task, task->tk_status); |
1780 | } | 1774 | } |
1781 | return; | 1775 | return; |
1776 | } else { | ||
1777 | xprt_request_prepare(task->tk_rqstp); | ||
1782 | } | 1778 | } |
1783 | 1779 | ||
1784 | /* Add task to reply queue before transmission to avoid races */ | 1780 | /* Add task to reply queue before transmission to avoid races */ |
@@ -2322,25 +2318,33 @@ out_retry: | |||
2322 | } | 2318 | } |
2323 | } | 2319 | } |
2324 | 2320 | ||
2325 | static __be32 * | 2321 | static int |
2326 | rpc_encode_header(struct rpc_task *task) | 2322 | rpc_encode_header(struct rpc_task *task, struct xdr_stream *xdr) |
2327 | { | 2323 | { |
2328 | struct rpc_clnt *clnt = task->tk_client; | 2324 | struct rpc_clnt *clnt = task->tk_client; |
2329 | struct rpc_rqst *req = task->tk_rqstp; | 2325 | struct rpc_rqst *req = task->tk_rqstp; |
2330 | __be32 *p = req->rq_svec[0].iov_base; | 2326 | __be32 *p; |
2331 | 2327 | int error; | |
2332 | /* FIXME: check buffer size? */ | 2328 | |
2333 | 2329 | error = -EMSGSIZE; | |
2334 | *p++ = req->rq_xid; /* XID */ | 2330 | p = xdr_reserve_space(xdr, RPC_CALLHDRSIZE << 2); |
2335 | *p++ = htonl(RPC_CALL); /* CALL */ | 2331 | if (!p) |
2336 | *p++ = htonl(RPC_VERSION); /* RPC version */ | 2332 | goto out_fail; |
2337 | *p++ = htonl(clnt->cl_prog); /* program number */ | 2333 | *p++ = req->rq_xid; |
2338 | *p++ = htonl(clnt->cl_vers); /* program version */ | 2334 | *p++ = rpc_call; |
2339 | *p++ = htonl(task->tk_msg.rpc_proc->p_proc); /* procedure */ | 2335 | *p++ = cpu_to_be32(RPC_VERSION); |
2340 | p = rpcauth_marshcred(task, p); | 2336 | *p++ = cpu_to_be32(clnt->cl_prog); |
2341 | if (p) | 2337 | *p++ = cpu_to_be32(clnt->cl_vers); |
2342 | req->rq_slen = xdr_adjust_iovec(&req->rq_svec[0], p); | 2338 | *p = cpu_to_be32(task->tk_msg.rpc_proc->p_proc); |
2343 | return p; | 2339 | |
2340 | error = rpcauth_marshcred(task, xdr); | ||
2341 | if (error < 0) | ||
2342 | goto out_fail; | ||
2343 | return 0; | ||
2344 | out_fail: | ||
2345 | trace_rpc_bad_callhdr(task); | ||
2346 | rpc_exit(task, error); | ||
2347 | return error; | ||
2344 | } | 2348 | } |
2345 | 2349 | ||
2346 | static __be32 * | 2350 | static __be32 * |