diff options
Diffstat (limited to 'rpc/rpc-lib/src/rpcsvc.c')
| -rw-r--r-- | rpc/rpc-lib/src/rpcsvc.c | 951 |
1 files changed, 813 insertions, 138 deletions
diff --git a/rpc/rpc-lib/src/rpcsvc.c b/rpc/rpc-lib/src/rpcsvc.c index 159edba53..037c157f2 100644 --- a/rpc/rpc-lib/src/rpcsvc.c +++ b/rpc/rpc-lib/src/rpcsvc.c @@ -1,20 +1,11 @@ /* - Copyright (c) 2010 Gluster, Inc. <http://www.gluster.com> + Copyright (c) 2008-2012 Red Hat, Inc. <http://www.redhat.com> This file is part of GlusterFS. - GlusterFS is free software; you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation; either version 3 of the License, - or (at your option) any later version. - - GlusterFS is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see - <http://www.gnu.org/licenses/>. + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. */ #ifndef _CONFIG_H @@ -34,6 +25,10 @@ #include "iobuf.h" #include "globals.h" #include "xdr-common.h" +#include "xdr-generic.h" +#include "rpc-common-xdr.h" +#include "syncop.h" +#include "rpc-drc.h" #include <errno.h> #include <pthread.h> @@ -47,6 +42,7 @@ #include <stdio.h> #include "xdr-rpcclnt.h" +#include "glusterfs-acl.h" struct rpcsvc_program gluster_dump_prog; @@ -105,7 +101,63 @@ out: return; } +rpcsvc_vector_sizer +rpcsvc_get_program_vector_sizer (rpcsvc_t *svc, uint32_t prognum, + uint32_t progver, uint32_t procnum) +{ + rpcsvc_program_t *program = NULL; + char found = 0; + + if (!svc) + return NULL; + + pthread_mutex_lock (&svc->rpclock); + { + list_for_each_entry (program, &svc->programs, program) { + if ((program->prognum == prognum) + && (program->progver == progver)) { + found = 1; + break; + } + } + } + pthread_mutex_unlock (&svc->rpclock); + + if (found) + return program->actors[procnum].vector_sizer; + else + return NULL; +} + +int +rpcsvc_request_outstanding (rpcsvc_t *svc, rpc_transport_t *trans, int delta) +{ + int ret = 0; + int old_count = 0; + int new_count = 0; + int limit = 0; + + pthread_mutex_lock (&trans->lock); + { + limit = svc->outstanding_rpc_limit; + if (!limit) + goto unlock; + old_count = trans->outstanding_rpc_count; + trans->outstanding_rpc_count += delta; + new_count = trans->outstanding_rpc_count; + + if (old_count <= limit && new_count > limit) + ret = rpc_transport_throttle (trans, _gf_true); + + if (old_count > limit && new_count <= limit) + ret = rpc_transport_throttle (trans, _gf_false); + } +unlock: + pthread_mutex_unlock (&trans->lock); + + return ret; +} /* This needs to change to returning errors, since @@ -143,7 +195,11 @@ rpcsvc_program_actor (rpcsvc_request_t *req) if (!found) { if (err != PROG_MISMATCH) { - gf_log (GF_RPCSVC, GF_LOG_WARNING, + /* log in DEBUG when nfs clients try to see if + * ACL requests are accepted by nfs server + */ + gf_log (GF_RPCSVC, (req->prognum == ACL_PROGRAM) ? + GF_LOG_DEBUG : GF_LOG_WARNING, "RPC program not available (req %u %u)", req->prognum, req->progver); err = PROG_UNAVAIL; @@ -182,6 +238,8 @@ rpcsvc_program_actor (rpcsvc_request_t *req) goto err; } + req->synctask = program->synctask; + err = SUCCESS; gf_log (GF_RPCSVC, GF_LOG_TRACE, "Actor found: %s - %s", program->progname, actor->procname); @@ -194,9 +252,9 @@ err: /* this procedure can only pass 4 arguments to registered notifyfn. To send more - * arguements call wrapper->notify directly. + * arguments call wrapper->notify directly. */ -inline void +static inline void rpcsvc_program_notify (rpcsvc_listener_t *listener, rpcsvc_event_t event, void *data) { @@ -219,7 +277,7 @@ out: } -inline int +static inline int rpcsvc_accept (rpcsvc_t *svc, rpc_transport_t *listen_trans, rpc_transport_t *new_trans) { @@ -249,9 +307,21 @@ rpcsvc_request_destroy (rpcsvc_request_t *req) iobref_unref (req->iobref); } + if (req->hdr_iobuf) + iobuf_unref (req->hdr_iobuf); + + /* This marks the "end" of an RPC request. Reply is + completely written to the socket and is on the way + to the client. It is time to decrement the + outstanding request counter by 1. + */ + rpcsvc_request_outstanding (req->svc, req->trans, -1); + rpc_transport_unref (req->trans); - mem_put (req->svc->rxpool, req); + GF_FREE (req->auxgidlarge); + + mem_put (req); out: return; @@ -280,7 +350,9 @@ rpcsvc_request_init (rpcsvc_t *svc, rpc_transport_t *trans, req->msg[0] = progmsg; req->iobref = iobref_ref (msg->iobref); if (msg->vectored) { - for (i = 1; i < msg->count; i++) { + /* msg->vector[2] is defined in structure. prevent a + out of bound access */ + for (i = 1; i < min (msg->count, 2); i++) { req->msg[i] = msg->vector[i]; } } @@ -331,6 +403,12 @@ rpcsvc_request_create (rpcsvc_t *svc, rpc_transport_t *trans, goto err; } + /* We just received a new request from the wire. Account for + it in the outsanding request counter to make sure we don't + ingest too many concurrent requests from the same client. + */ + ret = rpcsvc_request_outstanding (svc, trans, +1); + msgbuf = msg->vector[0].iov_base; msglen = msg->vector[0].iov_len; @@ -390,6 +468,7 @@ rpcsvc_request_create (rpcsvc_t *svc, rpc_transport_t *trans, * since we are not handling authentication failures for now. */ req->rpc_status = MSG_ACCEPTED; + req->reply = NULL; ret = 0; err: if (ret == -1) { @@ -405,14 +484,39 @@ err: int +rpcsvc_check_and_reply_error (int ret, call_frame_t *frame, void *opaque) +{ + rpcsvc_request_t *req = NULL; + + req = opaque; + + if (ret) + gf_log ("rpcsvc", GF_LOG_ERROR, + "rpc actor failed to complete successfully"); + + if (ret == RPCSVC_ACTOR_ERROR) { + ret = rpcsvc_error_reply (req); + if (ret) + gf_log ("rpcsvc", GF_LOG_WARNING, + "failed to queue error reply"); + } + + return 0; +} + +int rpcsvc_handle_rpc_call (rpcsvc_t *svc, rpc_transport_t *trans, rpc_transport_pollin_t *msg) { - rpcsvc_actor_t *actor = NULL; - rpcsvc_request_t *req = NULL; - int ret = -1; - uint16_t port = 0; - gf_boolean_t is_unix = _gf_false; + rpcsvc_actor_t *actor = NULL; + rpcsvc_actor actor_fn = NULL; + rpcsvc_request_t *req = NULL; + int ret = -1; + uint16_t port = 0; + gf_boolean_t is_unix = _gf_false; + gf_boolean_t unprivileged = _gf_false; + drc_cached_op_t *reply = NULL; + rpcsvc_drc_globals_t *drc = NULL; if (!trans || !svc) return -1; @@ -442,16 +546,13 @@ rpcsvc_handle_rpc_call (rpcsvc_t *svc, rpc_transport_t *trans, gf_log ("rpcsvc", GF_LOG_TRACE, "Client port: %d", (int)port); - if ((port > 1024) && (0 == svc->allow_insecure)) { //Non-privileged user, fail request - gf_log ("glusterd", GF_LOG_ERROR, "Request received from non-" - "privileged port. Failing request"); - return -1; - } + if (port > 1024) + unprivileged = _gf_true; } req = rpcsvc_request_create (svc, trans, msg); if (!req) - goto err; + goto out; if (!rpcsvc_request_accepted (req)) goto err_reply; @@ -460,40 +561,85 @@ rpcsvc_handle_rpc_call (rpcsvc_t *svc, rpc_transport_t *trans, if (!actor) goto err_reply; - if (actor && (req->rpc_err == SUCCESS)) { + if (0 == svc->allow_insecure && unprivileged && !actor->unprivileged) { + /* Non-privileged user, fail request */ + gf_log ("glusterd", GF_LOG_ERROR, + "Request received from non-" + "privileged port. Failing request"); + rpcsvc_request_destroy (req); + return -1; + } + + /* DRC */ + if (rpcsvc_need_drc (req)) { + drc = req->svc->drc; + + LOCK (&drc->lock); + reply = rpcsvc_drc_lookup (req); + + /* retransmission of completed request, send cached reply */ + if (reply && reply->state == DRC_OP_CACHED) { + gf_log (GF_RPCSVC, GF_LOG_INFO, "duplicate request:" + " XID: 0x%x", req->xid); + ret = rpcsvc_send_cached_reply (req, reply); + drc->cache_hits++; + UNLOCK (&drc->lock); + goto out; + + } /* retransmitted request, original op in transit, drop it */ + else if (reply && reply->state == DRC_OP_IN_TRANSIT) { + gf_log (GF_RPCSVC, GF_LOG_INFO, "op in transit," + " discarding. XID: 0x%x", req->xid); + ret = 0; + drc->intransit_hits++; + rpcsvc_request_destroy (req); + UNLOCK (&drc->lock); + goto out; + + } /* fresh request, cache it as in-transit and proceed */ + else { + ret = rpcsvc_cache_request (req); + } + UNLOCK (&drc->lock); + } + + if (req->rpc_err == SUCCESS) { /* Before going to xlator code, set the THIS properly */ THIS = svc->mydata; - if (req->count == 2) { - if (actor->vector_actor) { - ret = actor->vector_actor (req, &req->msg[1], 1, - req->iobref); - } else { - rpcsvc_request_seterr (req, PROC_UNAVAIL); - /* LOG TODO: print more info about procnum, - prognum etc, also print transport info */ - gf_log (GF_RPCSVC, GF_LOG_ERROR, - "No vectored handler present"); - ret = RPCSVC_ACTOR_ERROR; - } - } else if (actor->actor) { - ret = actor->actor (req); + actor_fn = actor->actor; + + if (!actor_fn) { + rpcsvc_request_seterr (req, PROC_UNAVAIL); + /* LOG TODO: print more info about procnum, + prognum etc, also print transport info */ + gf_log (GF_RPCSVC, GF_LOG_ERROR, + "No vectored handler present"); + ret = RPCSVC_ACTOR_ERROR; + goto err_reply; } - } -err_reply: - if (ret == RPCSVC_ACTOR_ERROR) { - ret = rpcsvc_error_reply (req); + if (req->synctask) { + if (msg->hdr_iobuf) + req->hdr_iobuf = iobuf_ref (msg->hdr_iobuf); + + ret = synctask_new (THIS->ctx->env, + (synctask_fn_t) actor_fn, + rpcsvc_check_and_reply_error, NULL, + req); + } else { + ret = actor_fn (req); + } } - if (ret) - gf_log ("rpcsvc", GF_LOG_WARNING, "failed to queue error reply"); +err_reply: + ret = rpcsvc_check_and_reply_error (ret, NULL, req); /* No need to propagate error beyond this function since the reply * has now been queued. */ ret = 0; -err: +out: return ret; } @@ -505,46 +651,49 @@ rpcsvc_handle_disconnect (rpcsvc_t *svc, rpc_transport_t *trans) rpcsvc_notify_wrapper_t *wrappers = NULL, *wrapper; int32_t ret = -1, i = 0, wrapper_count = 0; rpcsvc_listener_t *listener = NULL; - + event = (trans->listener == NULL) ? RPCSVC_EVENT_LISTENER_DEAD : RPCSVC_EVENT_DISCONNECT; - + pthread_mutex_lock (&svc->rpclock); { + if (!svc->notify_count) + goto unlock; + wrappers = GF_CALLOC (svc->notify_count, sizeof (*wrapper), gf_common_mt_rpcsvc_wrapper_t); if (!wrappers) { goto unlock; } - + list_for_each_entry (wrapper, &svc->notify, list) { if (wrapper->notify) { wrappers[i++] = *wrapper; } } - + wrapper_count = i; } unlock: pthread_mutex_unlock (&svc->rpclock); - + if (wrappers) { for (i = 0; i < wrapper_count; i++) { wrappers[i].notify (svc, wrappers[i].data, event, trans); } - + GF_FREE (wrappers); } - + if (event == RPCSVC_EVENT_LISTENER_DEAD) { listener = rpcsvc_get_listener (svc, -1, trans->listener); rpcsvc_listener_destroy (listener); } - + return ret; } - + int rpcsvc_notify (rpc_transport_t *trans, void *mydata, @@ -651,7 +800,7 @@ err: return txrecord; } -inline int +static inline int rpcsvc_get_callid (rpcsvc_t *rpc) { return GF_UNIVERSAL_ANSWER; @@ -734,33 +883,36 @@ rpcsvc_callback_build_record (rpcsvc_t *rpc, int prognum, int progver, char *record = NULL; struct iovec recordhdr = {0, }; size_t pagesize = 0; + size_t xdr_size = 0; int ret = -1; if ((!rpc) || (!recbuf)) { goto out; } + /* Fill the rpc structure and XDR it into the buffer got above. */ + ret = rpcsvc_fill_callback (prognum, progver, procnum, payload, xid, + &request); + if (ret == -1) { + gf_log ("rpcsvc", GF_LOG_WARNING, "cannot build a rpc-request " + "xid (%"PRIu64")", xid); + goto out; + } + /* First, try to get a pointer into the buffer which the RPC * layer can use. */ - request_iob = iobuf_get (rpc->ctx->iobuf_pool); + xdr_size = xdr_sizeof ((xdrproc_t)xdr_callmsg, &request); + + request_iob = iobuf_get2 (rpc->ctx->iobuf_pool, (xdr_size + payload)); if (!request_iob) { goto out; } - pagesize = ((struct iobuf_pool *)rpc->ctx->iobuf_pool)->page_size; + pagesize = iobuf_pagesize (request_iob); record = iobuf_ptr (request_iob); /* Now we have it. */ - /* Fill the rpc structure and XDR it into the buffer got above. */ - ret = rpcsvc_fill_callback (prognum, progver, procnum, payload, xid, - &request); - if (ret == -1) { - gf_log ("rpcsvc", GF_LOG_WARNING, "cannot build a rpc-request " - "xid (%"PRIu64")", xid); - goto out; - } - recordhdr = rpcsvc_callback_build_header (record, pagesize, &request, payload); @@ -834,21 +986,22 @@ out: return ret; } -inline int -rpcsvc_transport_submit (rpc_transport_t *trans, struct iovec *hdrvec, - int hdrcount, struct iovec *proghdr, int proghdrcount, - struct iovec *progpayload, int progpayloadcount, - struct iobref *iobref, void *priv) +int +rpcsvc_transport_submit (rpc_transport_t *trans, struct iovec *rpchdr, + int rpchdrcount, struct iovec *proghdr, + int proghdrcount, struct iovec *progpayload, + int progpayloadcount, struct iobref *iobref, + void *priv) { int ret = -1; rpc_transport_reply_t reply = {{0, }}; - if ((!trans) || (!hdrvec) || (!hdrvec->iov_base)) { + if ((!trans) || (!rpchdr) || (!rpchdr->iov_base)) { goto out; } - reply.msg.rpchdr = hdrvec; - reply.msg.rpchdrcount = hdrcount; + reply.msg.rpchdr = rpchdr; + reply.msg.rpchdrcount = rpchdrcount; reply.msg.proghdr = proghdr; reply.msg.proghdrcount = proghdrcount; reply.msg.progpayload = progpayload; @@ -904,13 +1057,14 @@ out: */ struct iobuf * rpcsvc_record_build_record (rpcsvc_request_t *req, size_t payload, - struct iovec *recbuf) + size_t hdrlen, struct iovec *recbuf) { struct rpc_msg reply; struct iobuf *replyiob = NULL; char *record = NULL; struct iovec recordhdr = {0, }; size_t pagesize = 0; + size_t xdr_size = 0; rpcsvc_t *svc = NULL; int ret = -1; @@ -918,19 +1072,25 @@ rpcsvc_record_build_record (rpcsvc_request_t *req, size_t payload, return NULL; svc = req->svc; - replyiob = iobuf_get (svc->ctx->iobuf_pool); - pagesize = iobpool_pagesize ((struct iobuf_pool *)svc->ctx->iobuf_pool); - if (!replyiob) { - goto err_exit; - } - - record = iobuf_ptr (replyiob); /* Now we have it. */ /* Fill the rpc structure and XDR it into the buffer got above. */ ret = rpcsvc_fill_reply (req, &reply); if (ret) goto err_exit; + xdr_size = xdr_sizeof ((xdrproc_t)xdr_replymsg, &reply); + + /* Payload would include 'readv' size etc too, where as + that comes as another payload iobuf */ + replyiob = iobuf_get2 (svc->ctx->iobuf_pool, (xdr_size + hdrlen)); + if (!replyiob) { + goto err_exit; + } + + pagesize = iobuf_pagesize (replyiob); + + record = iobuf_ptr (replyiob); /* Now we have it. */ + recordhdr = rpcsvc_record_build_header (record, pagesize, reply, payload); if (!recordhdr.iov_base) { @@ -985,7 +1145,9 @@ rpcsvc_submit_generic (rpcsvc_request_t *req, struct iovec *proghdr, struct iovec recordhdr = {0, }; rpc_transport_t *trans = NULL; size_t msglen = 0; + size_t hdrlen = 0; char new_iobref = 0; + rpcsvc_drc_globals_t *drc = NULL; if ((!req) || (!req->trans)) return -1; @@ -1003,7 +1165,7 @@ rpcsvc_submit_generic (rpcsvc_request_t *req, struct iovec *proghdr, gf_log (GF_RPCSVC, GF_LOG_TRACE, "Tx message: %zu", msglen); /* Build the buffer containing the encoded RPC reply. */ - replyiob = rpcsvc_record_build_record (req, msglen, &recordhdr); + replyiob = rpcsvc_record_build_record (req, msglen, hdrlen, &recordhdr); if (!replyiob) { gf_log (GF_RPCSVC, GF_LOG_ERROR,"Reply record creation failed"); goto disconnect_exit; @@ -1020,20 +1182,31 @@ rpcsvc_submit_generic (rpcsvc_request_t *req, struct iovec *proghdr, iobref_add (iobref, replyiob); + /* cache the request in the duplicate request cache for appropriate ops */ + if (req->reply) { + drc = req->svc->drc; + + LOCK (&drc->lock); + ret = rpcsvc_cache_reply (req, iobref, &recordhdr, 1, + proghdr, hdrcount, + payload, payloadcount); + UNLOCK (&drc->lock); + } + ret = rpcsvc_transport_submit (trans, &recordhdr, 1, proghdr, hdrcount, payload, payloadcount, iobref, req->trans_private); if (ret == -1) { gf_log (GF_RPCSVC, GF_LOG_ERROR, "failed to submit message " - "(XID: 0x%ux, Program: %s, ProgVers: %d, Proc: %d) to " + "(XID: 0x%x, Program: %s, ProgVers: %d, Proc: %d) to " "rpc-transport (%s)", req->xid, req->prog ? req->prog->progname : "(not matched)", req->prog ? req->prog->progver : 0, req->procnum, trans->name); } else { gf_log (GF_RPCSVC, GF_LOG_TRACE, - "submitted reply for rpc-message (XID: 0x%ux, " + "submitted reply for rpc-message (XID: 0x%x, " "Program: %s, ProgVers: %d, Proc: %d) to rpc-transport " "(%s)", req->xid, req->prog ? req->prog->progname: "-", req->prog ? req->prog->progver : 0, @@ -1063,7 +1236,7 @@ rpcsvc_error_reply (rpcsvc_request_t *req) if (!req) return -1; - gf_log_callingfn ("", GF_LOG_WARNING, "sending a RPC error reply"); + gf_log_callingfn ("", GF_LOG_DEBUG, "sending a RPC error reply"); /* At this point the req should already have been filled with the * appropriate RPC error numbers. @@ -1076,12 +1249,13 @@ rpcsvc_error_reply (rpcsvc_request_t *req) inline int rpcsvc_program_register_portmap (rpcsvc_program_t *newprog, uint32_t port) { - int ret = 0; + int ret = -1; /* FAIL */ if (!newprog) { goto out; } + /* pmap_set() returns 0 for FAIL and 1 for SUCCESS */ if (!(pmap_set (newprog->prognum, newprog->progver, IPPROTO_TCP, port))) { gf_log (GF_RPCSVC, GF_LOG_ERROR, "Could not register with" @@ -1089,7 +1263,7 @@ rpcsvc_program_register_portmap (rpcsvc_program_t *newprog, uint32_t port) goto out; } - ret = 0; + ret = 0; /* SUCCESS */ out: return ret; } @@ -1098,7 +1272,7 @@ out: inline int rpcsvc_program_unregister_portmap (rpcsvc_program_t *prog) { - int ret = 0; + int ret = -1; if (!prog) goto out; @@ -1114,6 +1288,11 @@ out: return ret; } +int +rpcsvc_register_portmap_enabled (rpcsvc_t *svc) +{ + return svc->register_portmap; +} int32_t rpcsvc_get_listener_port (rpcsvc_listener_t *listener) @@ -1126,11 +1305,11 @@ rpcsvc_get_listener_port (rpcsvc_listener_t *listener) switch (listener->trans->myinfo.sockaddr.ss_family) { case AF_INET: - listener_port = ((struct sockaddr_in6 *)&listener->trans->myinfo.sockaddr)->sin6_port; + listener_port = ((struct sockaddr_in *)&listener->trans->myinfo.sockaddr)->sin_port; break; case AF_INET6: - listener_port = ((struct sockaddr_in *)&listener->trans->myinfo.sockaddr)->sin_port; + listener_port = ((struct sockaddr_in6 *)&listener->trans->myinfo.sockaddr)->sin6_port; break; default: @@ -1152,7 +1331,7 @@ rpcsvc_get_listener (rpcsvc_t *svc, uint16_t port, rpc_transport_t *trans) { rpcsvc_listener_t *listener = NULL; char found = 0; - uint32_t listener_port = 0; + uint32_t listener_port = 0; if (!svc) { goto out; @@ -1215,28 +1394,44 @@ rpcsvc_submit_message (rpcsvc_request_t *req, struct iovec *proghdr, int -rpcsvc_program_unregister (rpcsvc_t *svc, rpcsvc_program_t *prog) +rpcsvc_program_unregister (rpcsvc_t *svc, rpcsvc_program_t *program) { int ret = -1; - - if (!svc || !prog) { + rpcsvc_program_t *prog = NULL; + if (!svc || !program) { goto out; } - ret = rpcsvc_program_unregister_portmap (prog); + ret = rpcsvc_program_unregister_portmap (program); if (ret == -1) { gf_log (GF_RPCSVC, GF_LOG_ERROR, "portmap unregistration of" " program failed"); goto out; } + pthread_mutex_lock (&svc->rpclock); + { + list_for_each_entry (prog, &svc->programs, program) { + if ((prog->prognum == program->prognum) + && (prog->progver == program->progver)) { + break; + } + } + } + pthread_mutex_unlock (&svc->rpclock); + + if (prog == NULL) { + ret = -1; + goto out; + } + gf_log (GF_RPCSVC, GF_LOG_DEBUG, "Program unregistered: %s, Num: %d," " Ver: %d, Port: %d", prog->progname, prog->prognum, prog->progver, prog->progport); pthread_mutex_lock (&svc->rpclock); { - list_del (&prog->program); + list_del_init (&prog->program); } pthread_mutex_unlock (&svc->rpclock); @@ -1244,8 +1439,8 @@ rpcsvc_program_unregister (rpcsvc_t *svc, rpcsvc_program_t *prog) out: if (ret == -1) { gf_log (GF_RPCSVC, GF_LOG_ERROR, "Program unregistration failed" - ": %s, Num: %d, Ver: %d, Port: %d", prog->progname, - prog->prognum, prog->progver, prog->progport); + ": %s, Num: %d, Ver: %d, Port: %d", program->progname, + program->prognum, program->progver, program->progport); } return ret; @@ -1343,7 +1538,7 @@ rpcsvc_create_listener (rpcsvc_t *svc, dict_t *options, char *name) { rpc_transport_t *trans = NULL; rpcsvc_listener_t *listener = NULL; - int32_t ret = -1; + int32_t ret = -1; if (!svc || !options) { goto out; @@ -1434,6 +1629,7 @@ rpcsvc_create_listeners (rpcsvc_t *svc, dict_t *options, char *name) } GF_FREE (transport_name); + transport_name = NULL; count++; } @@ -1445,17 +1641,13 @@ rpcsvc_create_listeners (rpcsvc_t *svc, dict_t *options, char *name) transport_type = NULL; out: - if (str != NULL) { - GF_FREE (str); - } + GF_FREE (str); - if (transport_type != NULL) { - GF_FREE (transport_type); - } + GF_FREE (transport_type); - if (tmp != NULL) { - GF_FREE (tmp); - } + GF_FREE (tmp); + + GF_FREE (transport_name); return count; } @@ -1577,7 +1769,6 @@ out: return ret; } - static void free_prog_details (gf_dump_rsp *rsp) { @@ -1633,15 +1824,17 @@ rpcsvc_dump (rpcsvc_request_t *req) uint32_t dump_rsp_len = 0; if (!req) - goto fail; + goto sendrsp; ret = build_prog_details (req, &rsp); if (ret < 0) { op_errno = -ret; - goto fail; + goto sendrsp; } -fail: + op_errno = 0; + +sendrsp: rsp.op_errno = gf_errno_to_error (op_errno); rsp.op_ret = ret; @@ -1651,17 +1844,14 @@ fail: iov.iov_base = rsp_buf; iov.iov_len = dump_rsp_len; - ret = xdr_serialize_dump_rsp (iov, &rsp); + ret = xdr_serialize_generic (iov, &rsp, (xdrproc_t)xdr_gf_dump_rsp); if (ret < 0) { - if (req) - req->rpc_err = GARBAGE_ARGS; - op_errno = EINVAL; - goto fail; + ret = RPCSVC_ACTOR_ERROR; + } else { + rpcsvc_submit_generic (req, &iov, 1, NULL, 0, NULL); + ret = 0; } - ret = rpcsvc_submit_generic (req, &iov, 1, NULL, 0, - NULL); - free_prog_details (&rsp); return ret; @@ -1670,8 +1860,119 @@ fail: int rpcsvc_init_options (rpcsvc_t *svc, dict_t *options) { + char *optstr = NULL; + int ret = -1; + + if ((!svc) || (!options)) + return -1; + svc->memfactor = RPCSVC_DEFAULT_MEMFACTOR; - return 0; + + svc->register_portmap = _gf_true; + if (dict_get (options, "rpc.register-with-portmap")) { + ret = dict_get_str (options, "rpc.register-with-portmap", + &optstr); + if (ret < 0) { + gf_log (GF_RPCSVC, GF_LOG_ERROR, "Failed to parse " + "dict"); + goto out; + } + + ret = gf_string2boolean (optstr, &svc->register_portmap); + if (ret < 0) { + gf_log (GF_RPCSVC, GF_LOG_ERROR, "Failed to parse bool " + "string"); + goto out; + } + } + + if (!svc->register_portmap) + gf_log (GF_RPCSVC, GF_LOG_DEBUG, "Portmap registration " + "disabled"); + + ret = rpcsvc_set_outstanding_rpc_limit (svc, options); +out: + return ret; +} + +int +rpcsvc_reconfigure_options (rpcsvc_t *svc, dict_t *options) +{ + xlator_t *xlator = NULL; + xlator_list_t *volentry = NULL; + char *srchkey = NULL; + char *keyval = NULL; + int ret = -1; + + if ((!svc) || (!svc->options) || (!options)) + return (-1); + + /* Fetch the xlator from svc */ + xlator = (xlator_t *) svc->mydata; + if (!xlator) + return (-1); + + /* Reconfigure the volume specific rpc-auth.addr allow part */ + volentry = xlator->children; + while (volentry) { + ret = gf_asprintf (&srchkey, "rpc-auth.addr.%s.allow", + volentry->xlator->name); + if (ret == -1) { + gf_log (GF_RPCSVC, GF_LOG_ERROR, "asprintf failed"); + return (-1); + } + + /* If found the srchkey, delete old key/val pair + * and set the key with new value. + */ + if (!dict_get_str (options, srchkey, &keyval)) { + dict_del (svc->options, srchkey); + ret = dict_set_str (svc->options, srchkey, keyval); + if (ret < 0) { + gf_log (GF_RPCSVC, GF_LOG_ERROR, + "dict_set_str error"); + GF_FREE (srchkey); + return (-1); + } + } + + GF_FREE (srchkey); + volentry = volentry->next; + } + + /* Reconfigure the volume specific rpc-auth.addr reject part */ + volentry = xlator->children; + while (volentry) { + ret = gf_asprintf (&srchkey, "rpc-auth.addr.%s.reject", + volentry->xlator->name); + if (ret == -1) { + gf_log (GF_RPCSVC, GF_LOG_ERROR, "asprintf failed"); + return (-1); + } + + /* If found the srchkey, delete old key/val pair + * and set the key with new value. + */ + if (!dict_get_str (options, srchkey, &keyval)) { + dict_del (svc->options, srchkey); + ret = dict_set_str (svc->options, srchkey, keyval); + if (ret < 0) { + gf_log (GF_RPCSVC, GF_LOG_ERROR, + "dict_set_str error"); + GF_FREE (srchkey); + return (-1); + } + } + + GF_FREE (srchkey); + volentry = volentry->next; + } + + ret = rpcsvc_init_options (svc, options); + if (ret) + return (-1); + + return rpcsvc_auth_reconf (svc, options); } int @@ -1713,21 +2014,63 @@ rpcsvc_transport_unix_options_build (dict_t **options, char *filepath) *options = dict; out: if (ret) { - if (fpath) - GF_FREE (fpath); + GF_FREE (fpath); if (dict) dict_unref (dict); } return ret; } +/* + * Reconfigure() the rpc.outstanding-rpc-limit param. + */ +int +rpcsvc_set_outstanding_rpc_limit (rpcsvc_t *svc, dict_t *options) +{ + int ret = -1; /* FAILURE */ + int rpclim = 0; + static char *rpclimkey = "rpc.outstanding-rpc-limit"; + + if ((!svc) || (!options)) + return (-1); + + /* Reconfigure() the rpc.outstanding-rpc-limit param */ + ret = dict_get_int32 (options, rpclimkey, &rpclim); + if (ret < 0) { + /* Fall back to default for FAILURE */ + rpclim = RPCSVC_DEFAULT_OUTSTANDING_RPC_LIMIT; + } else { + /* SUCCESS: round off to multiple of 8. + * If the input value fails Boundary check, fall back to + * default i.e. RPCSVC_DEFAULT_OUTSTANDING_RPC_LIMIT. + * NB: value 0 is special, means its unset i.e. unlimited. + */ + rpclim = ((rpclim + 8 - 1) >> 3) * 8; + if (rpclim < RPCSVC_MIN_OUTSTANDING_RPC_LIMIT) { + rpclim = RPCSVC_DEFAULT_OUTSTANDING_RPC_LIMIT; + } else if (rpclim > RPCSVC_MAX_OUTSTANDING_RPC_LIMIT) { + rpclim = RPCSVC_MAX_OUTSTANDING_RPC_LIMIT; + } + } + + if (svc->outstanding_rpc_limit != rpclim) { + svc->outstanding_rpc_limit = rpclim; + gf_log (GF_RPCSVC, GF_LOG_INFO, + "Configured %s with value %d", + rpclimkey, rpclim); + } + + return (0); +} + /* The global RPC service initializer. */ rpcsvc_t * -rpcsvc_init (glusterfs_ctx_t *ctx, dict_t *options) +rpcsvc_init (xlator_t *xl, glusterfs_ctx_t *ctx, dict_t *options, + uint32_t poolcount) { rpcsvc_t *svc = NULL; - int ret = -1, poolcount = 0; + int ret = -1; if ((!ctx) || (!options)) return NULL; @@ -1748,7 +2091,8 @@ rpcsvc_init (glusterfs_ctx_t *ctx, dict_t *options) goto free_svc; } - poolcount = RPCSVC_POOLCOUNT_MULT * svc->memfactor; + if (!poolcount) + poolcount = RPCSVC_POOLCOUNT_MULT * svc->memfactor; gf_log (GF_RPCSVC, GF_LOG_TRACE, "rx pool: %d", poolcount); svc->rxpool = mem_pool_new (rpcsvc_request_t, poolcount); @@ -1768,6 +2112,7 @@ rpcsvc_init (glusterfs_ctx_t *ctx, dict_t *options) ret = -1; svc->options = options; svc->ctx = ctx; + svc->mydata = xl; gf_log (GF_RPCSVC, GF_LOG_DEBUG, "RPC service inited."); gluster_dump_prog.options = options; @@ -1778,6 +2123,7 @@ rpcsvc_init (glusterfs_ctx_t *ctx, dict_t *options) "failed to register DUMP program"); goto free_svc; } + ret = 0; free_svc: if (ret == -1) { @@ -1789,10 +2135,339 @@ free_svc: } +int +rpcsvc_transport_peer_check_search (dict_t *options, char *pattern, + char *ip, char *hostname) +{ + int ret = -1; + char *addrtok = NULL; + char *addrstr = NULL; + char *dup_addrstr = NULL; + char *svptr = NULL; + + if ((!options) || (!ip)) + return -1; + + ret = dict_get_str (options, pattern, &addrstr); + if (ret < 0) { + ret = -1; + goto err; + } + + if (!addrstr) { + ret = -1; + goto err; + } + + dup_addrstr = gf_strdup (addrstr); + addrtok = strtok_r (dup_addrstr, ",", &svptr); + while (addrtok) { + + /* CASEFOLD not present on Solaris */ +#ifdef FNM_CASEFOLD + ret = fnmatch (addrtok, ip, FNM_CASEFOLD); +#else + ret = fnmatch (addrtok, ip, 0); +#endif + if (ret == 0) + goto err; + + /* compare hostnames if applicable */ + if (hostname) { +#ifdef FNM_CASEFOLD + ret = fnmatch (addrtok, hostname, FNM_CASEFOLD); +#else + ret = fnmatch (addrtok, hostname, 0); +#endif + if (ret == 0) + goto err; + } + + addrtok = strtok_r (NULL, ",", &svptr); + } + + ret = -1; +err: + GF_FREE (dup_addrstr); + + return ret; +} + + +static int +rpcsvc_transport_peer_check_allow (dict_t *options, char *volname, + char *ip, char *hostname) +{ + int ret = RPCSVC_AUTH_DONTCARE; + char *srchstr = NULL; + + if ((!options) || (!ip) || (!volname)) + return ret; + + ret = gf_asprintf (&srchstr, "rpc-auth.addr.%s.allow", volname); + if (ret == -1) { + gf_log (GF_RPCSVC, GF_LOG_ERROR, "asprintf failed"); + ret = RPCSVC_AUTH_DONTCARE; + goto out; + } + + ret = rpcsvc_transport_peer_check_search (options, srchstr, + ip, hostname); + GF_FREE (srchstr); + + if (ret == 0) + ret = RPCSVC_AUTH_ACCEPT; + else + ret = RPCSVC_AUTH_REJECT; +out: + return ret; +} + +static int +rpcsvc_transport_peer_check_reject (dict_t *options, char *volname, + char *ip, char *hostname) +{ + int ret = RPCSVC_AUTH_DONTCARE; + char *srchstr = NULL; + + if ((!options) || (!ip) || (!volname)) + return ret; + + ret = gf_asprintf (&srchstr, "rpc-auth.addr.%s.reject", + volname); + if (ret == -1) { + gf_log (GF_RPCSVC, GF_LOG_ERROR, "asprintf failed"); + ret = RPCSVC_AUTH_REJECT; + goto out; + } + + ret = rpcsvc_transport_peer_check_search (options, srchstr, + ip, hostname); + GF_FREE (srchstr); + + if (ret == 0) + ret = RPCSVC_AUTH_REJECT; + else + ret = RPCSVC_AUTH_DONTCARE; +out: + return ret; +} + + +/* Combines rpc auth's allow and reject options. + * Order of checks is important. + * First, REJECT if either rejects. + * If neither rejects, ACCEPT if either accepts. + * If neither accepts, DONTCARE + */ +int +rpcsvc_combine_allow_reject_volume_check (int allow, int reject) +{ + if (allow == RPCSVC_AUTH_REJECT || + reject == RPCSVC_AUTH_REJECT) + return RPCSVC_AUTH_REJECT; + + if (allow == RPCSVC_AUTH_ACCEPT || + reject == RPCSVC_AUTH_ACCEPT) + return RPCSVC_AUTH_ACCEPT; + + return RPCSVC_AUTH_DONTCARE; +} + +int +rpcsvc_auth_check (rpcsvc_t *svc, char *volname, + rpc_transport_t *trans) +{ + int ret = RPCSVC_AUTH_REJECT; + int accept = RPCSVC_AUTH_REJECT; + int reject = RPCSVC_AUTH_REJECT; + char *hostname = NULL; + char *ip = NULL; + char client_ip[RPCSVC_PEER_STRLEN] = {0}; + char *allow_str = NULL; + char *reject_str = NULL; + char *srchstr = NULL; + dict_t *options = NULL; + + if (!svc || !volname || !trans) + return ret; + + /* Fetch the options from svc struct and validate */ + options = svc->options; + if (!options) + return ret; + + ret = rpcsvc_transport_peername (trans, client_ip, RPCSVC_PEER_STRLEN); + if (ret != 0) { + gf_log (GF_RPCSVC, GF_LOG_ERROR, "Failed to get remote addr: " + "%s", gai_strerror (ret)); + return RPCSVC_AUTH_REJECT; + } + + /* Accept if its the default case: Allow all, Reject none + * The default volfile always contains a 'allow *' rule + * for each volume. If allow rule is missing (which implies + * there is some bad volfile generating code doing this), we + * assume no one is allowed mounts, and thus, we reject mounts. + */ + ret = gf_asprintf (&srchstr, "rpc-auth.addr.%s.allow", volname); + if (ret == -1) { + gf_log (GF_RPCSVC, GF_LOG_ERROR, "asprintf failed"); + return RPCSVC_AUTH_REJECT; + } + + ret = dict_get_str (options, srchstr, &allow_str); + GF_FREE (srchstr); + if (ret < 0) + return RPCSVC_AUTH_REJECT; + + ret = gf_asprintf (&srchstr, "rpc-auth.addr.%s.reject", volname); + if (ret == -1) { + gf_log (GF_RPCSVC, GF_LOG_ERROR, "asprintf failed"); + return RPCSVC_AUTH_REJECT; + } + + ret = dict_get_str (options, srchstr, &reject_str); + GF_FREE (srchstr); + if (reject_str == NULL && !strcmp ("*", allow_str)) + return RPCSVC_AUTH_ACCEPT; + + /* Non-default rule, authenticate */ + if (!get_host_name (client_ip, &ip)) + ip = client_ip; + + /* addr-namelookup check */ + if (svc->addr_namelookup == _gf_true) { + ret = gf_get_hostname_from_ip (ip, &hostname); + if (ret) { + if (hostname) + GF_FREE (hostname); + /* failed to get hostname, but hostname auth + * is enabled, so authentication will not be + * 100% correct. reject mounts + */ + return RPCSVC_AUTH_REJECT; + } + } + + accept = rpcsvc_transport_peer_check_allow (options, volname, + ip, hostname); + + reject = rpcsvc_transport_peer_check_reject (options, volname, + ip, hostname); + + if (hostname) + GF_FREE (hostname); + return rpcsvc_combine_allow_reject_volume_check (accept, reject); +} + +int +rpcsvc_transport_privport_check (rpcsvc_t *svc, char *volname, + rpc_transport_t *trans) +{ + union gf_sock_union sock_union; + int ret = RPCSVC_AUTH_REJECT; + socklen_t sinsize = sizeof (&sock_union.sin); + char *srchstr = NULL; + char *valstr = NULL; + uint16_t port = 0; + gf_boolean_t insecure = _gf_false; + + memset (&sock_union, 0, sizeof (sock_union)); + + if ((!svc) || (!volname) || (!trans)) + return ret; + + ret = rpcsvc_transport_peeraddr (trans, NULL, 0, &sock_union.storage, + sinsize); + if (ret != 0) { + gf_log (GF_RPCSVC, GF_LOG_ERROR, "Failed to get peer addr: %s", + gai_strerror (ret)); + ret = RPCSVC_AUTH_REJECT; + goto err; + } + + port = ntohs (sock_union.sin.sin_port); + gf_log (GF_RPCSVC, GF_LOG_TRACE, "Client port: %d", (int)port); + /* If the port is already a privileged one, dont bother with checking + * options. + */ + if (port <= 1024) { + ret = RPCSVC_AUTH_ACCEPT; + goto err; + } + + /* Disabled by default */ + ret = gf_asprintf (&srchstr, "rpc-auth.ports.%s.insecure", volname); + if (ret == -1) { + gf_log (GF_RPCSVC, GF_LOG_ERROR, "asprintf failed"); + ret = RPCSVC_AUTH_REJECT; + goto err; + } + + ret = dict_get_str (svc->options, srchstr, &valstr); + if (ret) { + gf_log (GF_RPCSVC, GF_LOG_ERROR, "Failed to" + " read rpc-auth.ports.insecure value"); + goto err; + } + + ret = gf_string2boolean (valstr, &insecure); + if (ret) { + gf_log (GF_RPCSVC, GF_LOG_ERROR, "Failed to" + " convert rpc-auth.ports.insecure value"); + goto err; + } + + ret = insecure ? RPCSVC_AUTH_ACCEPT : RPCSVC_AUTH_REJECT; + + if (ret == RPCSVC_AUTH_ACCEPT) + gf_log (GF_RPCSVC, GF_LOG_DEBUG, "Unprivileged port allowed"); + else + gf_log (GF_RPCSVC, GF_LOG_DEBUG, "Unprivileged port not" + " allowed"); + +err: + if (srchstr) + GF_FREE (srchstr); + + return ret; +} + + +char * +rpcsvc_volume_allowed (dict_t *options, char *volname) +{ + char globalrule[] = "rpc-auth.addr.allow"; + char *srchstr = NULL; + char *addrstr = NULL; + int ret = -1; + + if ((!options) || (!volname)) + return NULL; + + ret = gf_asprintf (&srchstr, "rpc-auth.addr.%s.allow", volname); + if (ret == -1) { + gf_log (GF_RPCSVC, GF_LOG_ERROR, "asprintf failed"); + goto out; + } + + if (!dict_get (options, srchstr)) + ret = dict_get_str (options, globalrule, &addrstr); + else + ret = dict_get_str (options, srchstr, &addrstr); + +out: + GF_FREE (srchstr); + + return addrstr; +} + + rpcsvc_actor_t gluster_dump_actors[] = { - [GF_DUMP_NULL] = {"NULL", GF_DUMP_NULL, NULL, NULL, NULL }, - [GF_DUMP_DUMP] = {"DUMP", GF_DUMP_DUMP, rpcsvc_dump, NULL, NULL }, - [GF_DUMP_MAXVALUE] = {"MAXVALUE", GF_DUMP_MAXVALUE, NULL, NULL, NULL }, + [GF_DUMP_NULL] = {"NULL", GF_DUMP_NULL, NULL, NULL, 0, DRC_NA}, + [GF_DUMP_DUMP] = {"DUMP", GF_DUMP_DUMP, rpcsvc_dump, NULL, 0, DRC_NA}, + [GF_DUMP_MAXVALUE] = {"MAXVALUE", GF_DUMP_MAXVALUE, NULL, NULL, 0, DRC_NA}, }; |
