summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/admin-guide/en-US/markdown/admin_managing_volumes.md2
-rwxr-xr-xtests/bugs/bug-887145.t6
-rwxr-xr-xtests/bugs/bug-904300.t61
-rwxr-xr-xtests/bugs/bug-921072.t47
-rw-r--r--tests/nfs.rc21
-rw-r--r--xlators/nfs/server/src/mount3.c339
-rw-r--r--xlators/nfs/server/src/mount3.h12
-rw-r--r--xlators/nfs/server/src/nfs-mem-types.h1
-rw-r--r--xlators/nfs/server/src/nfs.c14
9 files changed, 462 insertions, 41 deletions
diff --git a/doc/admin-guide/en-US/markdown/admin_managing_volumes.md b/doc/admin-guide/en-US/markdown/admin_managing_volumes.md
index dd8ed4710..6c06e27a0 100644
--- a/doc/admin-guide/en-US/markdown/admin_managing_volumes.md
+++ b/doc/admin-guide/en-US/markdown/admin_managing_volumes.md
@@ -112,7 +112,7 @@ To tune volume options
nfs.trusted-sync All writes and COMMIT requests are treated as async. This implies that no write requests are guaranteed to be on server disks when the write reply is received at the NFS client. Trusted sync includes trusted-write behavior. off On | Off
- nfs.export-dir By default, all sub-volumes of NFS are exported as individual exports. Now, this option allows you to export only the specified subdirectory or subdirectories in the volume. This option can also be used in conjunction with nfs3.export-volumes option to restrict exports only to the subdirectories specified through this option. You must provide an absolute path. Enabled for all sub directories. Enable | Disable
+ nfs.export-dir This option can be used to export specified comma separated subdirectories in the volume. The path must be an absolute path. Along with path allowed list of IPs/hostname can be associated with each subdirectory. If provided connection will allowed only from these IPs. Format: \<dir\>[(hostspec[|hostspec|...])][,...]. Where hostspec can be an IP address, hostname or an IP range in CIDR notation. **Note**: Care must be taken while configuring this option as invalid entries and/or unreachable DNS servers can introduce unwanted delay in all the mount calls. No sub directory exported. Absolute path with allowed list of IP/hostname.
nfs.export-volumes Enable/Disable exporting entire volumes, instead if used in conjunction with nfs3.export-dir, can allow setting up only subdirectories as exports. on On | Off
diff --git a/tests/bugs/bug-887145.t b/tests/bugs/bug-887145.t
index 4d642f8c5..e2013e50b 100755
--- a/tests/bugs/bug-887145.t
+++ b/tests/bugs/bug-887145.t
@@ -1,14 +1,10 @@
#!/bin/bash
. $(dirname $0)/../include.rc
+. $(dirname $0)/../nfs.rc
cleanup;
-function is_nfs_export_available {
-exp=$(showmount -e | grep $V0 | wc -l)
-echo "$exp"
-}
-
TEST glusterd
TEST pidof glusterd
TEST $CLI volume create $V0 replica 2 $H0:$B0/${V0}{1,2};
diff --git a/tests/bugs/bug-904300.t b/tests/bugs/bug-904300.t
new file mode 100755
index 000000000..4276ee229
--- /dev/null
+++ b/tests/bugs/bug-904300.t
@@ -0,0 +1,61 @@
+#!/bin/bash
+
+. $(dirname $0)/../include.rc
+. $(dirname $0)/../nfs.rc
+
+cleanup;
+
+# 1-8
+TEST glusterd
+TEST pidof glusterd
+
+TEST $CLI volume create $V0 $H0:$B0/$V0;
+TEST $CLI volume start $V0
+EXPECT_WITHIN 20 1 is_nfs_export_available
+
+TEST mount -t nfs -o vers=3,nolock,soft,intr $H0:/$V0 $N0
+TEST mkdir $N0/dir1
+TEST umount $N0
+
+#
+# Case 1: Allow "dir1" to be mounted only from 127.0.0.1
+# 9-12
+TEST $CLI volume set $V0 export-dir \""/dir1(127.0.0.1)"\"
+EXPECT_WITHIN 20 2 is_nfs_export_available
+
+TEST mount -t nfs -o vers=3,nolock,soft,intr localhost:/$V0/dir1 $N0
+TEST umount $N0
+
+#
+# Case 2: Allow "dir1" to be mounted only from 8.8.8.8. This is
+# a negative test case therefore the mount should fail.
+# 13-16
+TEST $CLI volume set $V0 export-dir \""/dir1(8.8.8.8)"\"
+EXPECT_WITHIN 20 2 is_nfs_export_available
+
+TEST ! mount -t nfs -o vers=3,nolock,soft,intr $H0:/$V0/dir1 $N0
+TEST ! umount $N0
+
+
+# Case 3: Variation of test case1. Here we are checking with hostname
+# instead of ip address.
+# 17-20
+TEST $CLI volume set $V0 export-dir \""/dir1($H0)"\"
+EXPECT_WITHIN 20 2 is_nfs_export_available
+
+TEST mount -t nfs -o vers=3,nolock,soft,intr $H0:/$V0/dir1 $N0
+TEST umount $N0
+
+# Case 4: Variation of test case1. Here we are checking with IP range
+# 21-24
+TEST $CLI volume set $V0 export-dir \""/dir1(127.0.0.0/24)"\"
+EXPECT_WITHIN 20 2 is_nfs_export_available
+
+TEST mount -t nfs -o vers=3,nolock,soft,intr localhost:/$V0/dir1 $N0
+TEST umount $N0
+
+## Finish up
+TEST $CLI volume stop $V0;
+TEST $CLI volume delete $V0;
+
+cleanup;
diff --git a/tests/bugs/bug-921072.t b/tests/bugs/bug-921072.t
index c7fff75f1..e101d5b46 100755
--- a/tests/bugs/bug-921072.t
+++ b/tests/bugs/bug-921072.t
@@ -1,60 +1,43 @@
#!/bin/bash
. $(dirname $0)/../include.rc
+. $(dirname $0)/../nfs.rc
cleanup;
-# sleeps till the portmap registration of nfs services is done
-# NOTE: may take some time for nfs to export all volumes, hence,
-# showmount -e succeeding does NOT mean all volumes are available
-# for mount. In this case, its a single single-brick volume,
-# so no problem.
-sleep_till_nfs_awakens ()
-{
- while true
- do
- showmount -e 0 > /dev/null 2>&1;
- if [ $? -eq 0 ]; then
- return;
- else
- sleep 1;
- fi
- done
-}
-
#1
TEST glusterd
TEST pidof glusterd
TEST $CLI volume create $V0 $H0:$B0/$V0
TEST $CLI volume start $V0
-sleep_till_nfs_awakens
+EXPECT_WITHIN 20 1 is_nfs_export_available
TEST mount -t nfs -o vers=3,nolock,soft,intr $H0:/$V0 $N0
TEST umount $N0
# based on ip addresses (1-4)
# case 1: allow only localhost ip
TEST $CLI volume set $V0 nfs.rpc-auth-allow 127.0.0.1
-sleep_till_nfs_awakens
+EXPECT_WITHIN 20 1 is_nfs_export_available
TEST mount -t nfs -o vers=3,nolock,soft,intr localhost:/$V0 $N0
TEST umount $N0
# case 2: allow only non-localhost ip
TEST $CLI volume set $V0 nfs.rpc-auth-allow 192.168.1.1
-sleep_till_nfs_awakens
+EXPECT_WITHIN 20 1 is_nfs_export_available
#11
TEST ! mount -t nfs -o vers=3,nolock,soft,intr localhost:/$V0 $N0
TEST $CLI volume reset --mode=script $V0
# case 3: reject only localhost ip
TEST $CLI volume set $V0 nfs.rpc-auth-reject 127.0.0.1
-sleep_till_nfs_awakens
+EXPECT_WITHIN 20 1 is_nfs_export_available
TEST ! mount -t nfs -o vers=3,nolock,soft,intr localhost:/$V0 $N0
# case 4: reject only non-localhost ip
TEST $CLI volume set $V0 nfs.rpc-auth-reject 192.168.1.1
-sleep_till_nfs_awakens
+EXPECT_WITHIN 20 1 is_nfs_export_available
TEST mount -t nfs -o vers=3,nolock,soft,intr localhost:/$V0 $N0
TEST umount $N0
@@ -65,21 +48,21 @@ TEST umount $N0
# CASES WITH NFS.ADDR-NAMELOOKUP ON (5-12)
TEST $CLI volume reset --mode=script $V0
TEST $CLI volume set $V0 nfs.addr-namelookup on
-sleep_till_nfs_awakens
+EXPECT_WITHIN 20 1 is_nfs_export_available
#20
TEST mount -t nfs -o vers=3,nolock,soft,intr localhost:/$V0 $N0
TEST umount $N0
# case 5: allow only localhost
TEST $CLI volume set $V0 nfs.rpc-auth-allow localhost
-sleep_till_nfs_awakens
+EXPECT_WITHIN 20 1 is_nfs_export_available
TEST mount -t nfs -o vers=3,nolock,soft,intr localhost:/$V0 $N0
TEST umount $N0
# case 6: allow only somehost
TEST $CLI volume set $V0 nfs.rpc-auth-allow somehost
-sleep_till_nfs_awakens
+EXPECT_WITHIN 20 1 is_nfs_export_available
TEST ! mount -t nfs -o vers=3,nolock,soft,intr localhost:/$V0 $N0
@@ -87,13 +70,13 @@ TEST ! mount -t nfs -o vers=3,nolock,soft,intr localhost:/$V0 $N0
TEST $CLI volume reset --mode=script $V0
TEST $CLI volume set $V0 nfs.addr-namelookup on
TEST $CLI volume set $V0 nfs.rpc-auth-reject localhost
-sleep_till_nfs_awakens
+EXPECT_WITHIN 20 1 is_nfs_export_available
#30
TEST ! mount -t nfs -o vers=3,nolock,soft,intr localhost:/$V0 $N0
# case 8: reject only somehost
TEST $CLI volume set $V0 nfs.rpc-auth-reject somehost
-sleep_till_nfs_awakens
+EXPECT_WITHIN 20 1 is_nfs_export_available
TEST mount -t nfs -o vers=3,nolock,soft,intr localhost:/$V0 $N0
TEST umount $N0
@@ -103,14 +86,14 @@ TEST umount $N0
TEST $CLI volume reset --mode=script $V0
TEST $CLI volume set $V0 nfs.addr-namelookup on
TEST $CLI volume set $V0 nfs.rpc-auth-allow 127.0.0.1
-sleep_till_nfs_awakens
+EXPECT_WITHIN 20 1 is_nfs_export_available
TEST mount -t nfs -o vers=3,nolock,soft,intr localhost:/$V0 $N0
TEST umount $N0
# case 10: allow a non-localhost ip
TEST $CLI volume set $V0 nfs.rpc-auth-allow 192.168.1.1
-sleep_till_nfs_awakens
+EXPECT_WITHIN 20 1 is_nfs_export_available
#40
TEST ! mount -t nfs -o vers=3,nolock,soft,intr localhost:/$V0 $N0
@@ -118,13 +101,13 @@ TEST ! mount -t nfs -o vers=3,nolock,soft,intr localhost:/$V0 $N0
TEST $CLI volume reset --mode=script $V0
TEST $CLI volume set $V0 nfs.addr-namelookup on
TEST $CLI volume set $V0 nfs.rpc-auth-reject 127.0.0.1
-sleep_till_nfs_awakens
+EXPECT_WITHIN 20 1 is_nfs_export_available
TEST ! mount -t nfs -o vers=3,nolock,soft,intr localhost:/$V0 $N0
# case 12: reject only non-localhost ip
TEST $CLI volume set $V0 nfs.rpc-auth-reject 192.168.1.1
-sleep_till_nfs_awakens
+EXPECT_WITHIN 20 1 is_nfs_export_available
TEST mount -t nfs -o vers=3,nolock,soft,intr localhost:/$V0 $N0
TEST umount $N0
diff --git a/tests/nfs.rc b/tests/nfs.rc
new file mode 100644
index 000000000..f3abee842
--- /dev/null
+++ b/tests/nfs.rc
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+
+# Due to portmap registration NFS takes some time to
+# export all volumes. Therefore tests should start only
+# after exports are visible by showmount command. This
+# routine will check if showmount shows the exports or not
+#
+function is_nfs_export_available ()
+{
+ vol=$1
+
+ if [ "$vol" == "" ]; then
+ vol=$V0
+ fi
+
+ exp=$(showmount -e 2> /dev/null | grep $vol | wc -l)
+ echo "$exp"
+}
+
+
diff --git a/xlators/nfs/server/src/mount3.c b/xlators/nfs/server/src/mount3.c
index 983cbff02..adf018c94 100644
--- a/xlators/nfs/server/src/mount3.c
+++ b/xlators/nfs/server/src/mount3.c
@@ -45,6 +45,39 @@
#include <sys/socket.h>
#include <sys/uio.h>
+
+#define IPv4_ADDR_SIZE 32
+
+/* Macro to typecast the parameter to struct sockaddr_in
+ */
+#define SA(addr) ((struct sockaddr_in*)(addr))
+
+/* Macro will mask the ip address with netmask.
+ */
+#define MASKED_IP(ipv4addr, netmask) \
+ (ntohl(SA(ipv4addr)->sin_addr.s_addr) & (netmask))
+
+/* Macro will compare two IP address after applying the mask
+ */
+#define COMPARE_IPv4_ADDRS(ip1, ip2, netmask) \
+ ((MASKED_IP(ip1, netmask)) == (MASKED_IP(ip2, netmask)))
+
+/* This macro will assist in freeing up entire link list
+ * of host_auth_spec structure.
+ */
+#define FREE_HOSTSPEC(exp) do { \
+ struct host_auth_spec *host= exp->hostspec; \
+ while (NULL != host){ \
+ struct host_auth_spec* temp = host; \
+ host = host->next; \
+ if (NULL != temp->host_addr) { \
+ GF_FREE (temp->host_addr); \
+ } \
+ GF_FREE (temp); \
+ } \
+ exp->hostspec = NULL; \
+ } while (0)
+
typedef ssize_t (*mnt3_serializer) (struct iovec outmsg, void *args);
extern void *
@@ -645,6 +678,132 @@ err:
}
+/**
+ * This function will verify if the client is allowed to mount
+ * the directory or not. Client's IP address will be compared with
+ * allowed IP list or range present in mnt3_export structure.
+ *
+ * @param req - RPC request. This structure contains client's IP address.
+ * @param export - mnt3_export structure. Contains allowed IP list/range.
+ *
+ * @return 0 - on Success and -EACCES on failure.
+ */
+int
+mnt3_verify_auth (rpcsvc_request_t *req, struct mnt3_export *export)
+{
+ int retvalue = -EACCES;
+ int ret = 0;
+ int shiftbits = 0;
+ uint32_t ipv4netmask = 0;
+ uint32_t routingprefix = 0;
+ struct host_auth_spec *host = NULL;
+ struct sockaddr_in *client_addr = NULL;
+ struct sockaddr_in *allowed_addr = NULL;
+ struct addrinfo *allowed_addrinfo = NULL;
+
+ /* Sanity check */
+ if ((NULL == req) ||
+ (NULL == req->trans) ||
+ (NULL == export) ||
+ (NULL == export->hostspec)) {
+ gf_log (GF_MNT, GF_LOG_ERROR, "Invalid argument");
+ return retvalue;
+ }
+
+ host = export->hostspec;
+
+
+ /* Client's IP address. */
+ client_addr = (struct sockaddr_in *)(&(req->trans->peerinfo.sockaddr));
+
+ /* Try to see if the client IP matches the allowed IP list.*/
+ while (NULL != host){
+ GF_ASSERT (host->host_addr);
+
+ if (NULL != allowed_addrinfo) {
+ freeaddrinfo (allowed_addrinfo);
+ allowed_addrinfo = NULL;
+ }
+
+ /* Get the addrinfo for the allowed host (host_addr). */
+ ret = getaddrinfo (host->host_addr,
+ NULL,
+ NULL,
+ &allowed_addrinfo);
+ if (0 != ret){
+ gf_log (GF_MNT, GF_LOG_ERROR, "getaddrinfo: %s\n",
+ gai_strerror (ret));
+ host = host->next;
+
+ /* Failed to get IP addrinfo. Continue to check other
+ * allowed IPs in the list.
+ */
+ continue;
+ }
+
+ allowed_addr = (struct sockaddr_in *)(allowed_addrinfo->ai_addr);
+
+ if (NULL == allowed_addr) {
+ gf_log (GF_MNT, GF_LOG_ERROR, "Invalid structure");
+ break;
+ }
+
+ if (AF_INET == allowed_addr->sin_family){
+ if (IPv4_ADDR_SIZE < host->routeprefix) {
+ gf_log (GF_MNT, GF_LOG_ERROR, "invalid IP "
+ "configured for export-dir AUTH");
+ host = host->next;
+ continue;
+ }
+
+ /* -1 means no route prefix is provided. In this case
+ * the IP should be an exact match. Which is same as
+ * providing a route prefix of IPv4_ADDR_SIZE.
+ */
+ if (-1 == host->routeprefix) {
+ routingprefix = IPv4_ADDR_SIZE;
+ } else {
+ routingprefix = host->routeprefix;
+ }
+
+ /* Create a mask from the routing prefix. User provided
+ * CIDR address is split into IP address (host_addr) and
+ * routing prefix (routeprefix). This CIDR address may
+ * denote a single, distinct interface address or the
+ * beginning address of an entire network.
+ *
+ * e.g. the IPv4 block 192.168.100.0/24 represents the
+ * 256 IPv4 addresses from 192.168.100.0 to
+ * 192.168.100.255.
+ * Therefore to check if an IP matches 192.168.100.0/24
+ * we should mask the IP with FFFFFF00 and compare it
+ * with host address part of CIDR.
+ */
+ shiftbits = IPv4_ADDR_SIZE - routingprefix;
+ ipv4netmask = 0xFFFFFFFFUL << shiftbits;
+
+ /* Mask both the IPs and then check if they match
+ * or not. */
+ if (COMPARE_IPv4_ADDRS (allowed_addr,
+ client_addr,
+ ipv4netmask)){
+ retvalue = 0;
+ break;
+ }
+ }
+
+ /* Client IP didn't match the allowed IP.
+ * Check with the next allowed IP.*/
+ host = host->next;
+ }
+
+ if (NULL != allowed_addrinfo) {
+ freeaddrinfo (allowed_addrinfo);
+ }
+
+ return retvalue;
+}
+
int
mnt3_resolve_subdir (rpcsvc_request_t *req, struct mount3_state *ms,
struct mnt3_export *exp, char *subdir)
@@ -656,6 +815,16 @@ mnt3_resolve_subdir (rpcsvc_request_t *req, struct mount3_state *ms,
if ((!req) || (!ms) || (!exp) || (!subdir))
return ret;
+ /* Need to check AUTH */
+ if (NULL != exp->hostspec) {
+ ret = mnt3_verify_auth (req, exp);
+ if (0 != ret) {
+ gf_log (GF_MNT,GF_LOG_ERROR,
+ "AUTH verification failed");
+ return ret;
+ }
+ }
+
mres = GF_CALLOC (1, sizeof (mnt3_resolve_t), gf_nfs_mt_mnt3_resolve);
if (!mres) {
gf_log (GF_MNT, GF_LOG_ERROR, "Memory allocation failed");
@@ -1489,7 +1658,151 @@ mount3udp_delete_mountlist (char *hostname, dirpath *expname)
return 0;
}
+/**
+ * This function will parse the hostip (IP addres, IP range, or hostname)
+ * and fill the host_auth_spec structure.
+ *
+ * @param hostspec - struct host_auth_spec
+ * @param hostip - IP address, IP range (CIDR format) or hostname
+ *
+ * @return 0 - on success and -1 on failure
+ */
+int
+mnt3_export_fill_hostspec (struct host_auth_spec* hostspec, const char* hostip)
+{
+ char *ipdupstr = NULL;
+ char *savptr = NULL;
+ char *ip = NULL;
+ char *token = NULL;
+ int ret = -1;
+
+ /* Create copy of the string so that the source won't change
+ */
+ ipdupstr = gf_strdup (hostip);
+ if (NULL == ipdupstr) {
+ gf_log (GF_MNT, GF_LOG_ERROR, "Memory allocation failed");
+ goto err;
+ }
+
+ ip = strtok_r (ipdupstr, "/", &savptr);
+ hostspec->host_addr = gf_strdup (ip);
+ if (NULL == hostspec->host_addr) {
+ gf_log (GF_MNT, GF_LOG_ERROR, "Memory allocation failed");
+ goto err;
+ }
+
+ /* Check if the IP is in <IP address> / <Range> format.
+ * If yes, then strip the range and store it separately.
+ */
+ token = strtok_r (NULL, "/", &savptr);
+
+ if (NULL == token) {
+ hostspec->routeprefix = -1;
+ } else {
+ hostspec->routeprefix = atoi (token);
+ }
+
+ // success
+ ret = 0;
+err:
+ if (NULL != ipdupstr) {
+ GF_FREE (ipdupstr);
+ }
+ return ret;
+}
+
+
+/**
+ * This function will parse the AUTH parameter passed along with
+ * "export-dir" option. If AUTH parameter is present then it will be
+ * stripped from exportpath and stored in mnt3_export (exp) structure.
+ *
+ * @param exp - mnt3_export structure. Holds information needed for mount.
+ * @param exportpath - Value of "export-dir" key. Holds both export path
+ * and AUTH parameter for the path.
+ * exportpath format: <abspath>[(hostdesc[|hostspec|...])]
+ *
+ * @return This function will return 0 on success and -1 on failure.
+ */
+int
+mnt3_export_parse_auth_param (struct mnt3_export* exp, char* exportpath)
+{
+ char *token = NULL;
+ char *savPtr = NULL;
+ char *hostip = NULL;
+ struct host_auth_spec *host = NULL;
+ int ret = 0;
+
+ /* Using exportpath directly in strtok_r because we want
+ * to strip off AUTH parameter from exportpath. */
+ token = strtok_r (exportpath, "(", &savPtr);
+
+ /* Get the next token, which will be the AUTH parameter. */
+ token = strtok_r (NULL, ")", &savPtr);
+
+ if (NULL == token) {
+ /* If AUTH is not present then we should return success. */
+ return 0;
+ }
+
+ /* Free any previously allocated hostspec structure. */
+ if (NULL != exp->hostspec) {
+ GF_FREE (exp->hostspec);
+ exp->hostspec = NULL;
+ }
+
+ exp->hostspec = GF_CALLOC (1,
+ sizeof (*(exp->hostspec)),
+ gf_nfs_mt_auth_spec);
+ if (NULL == exp->hostspec){
+ gf_log (GF_MNT, GF_LOG_ERROR, "Memory allocation failed");
+ return -1;
+ }
+
+ /* AUTH parameter can have multiple entries. For each entry
+ * a host_auth_spec structure is created. */
+ host = exp->hostspec;
+
+ hostip = strtok_r (token, "|", &savPtr);
+
+ /* Parse all AUTH parameters separated by '|' */
+ while (NULL != hostip){
+ ret = mnt3_export_fill_hostspec (host, hostip);
+ if (0 != ret) {
+ gf_log(GF_MNT, GF_LOG_WARNING,
+ "Failed to parse hostspec: %s", hostip);
+ goto err;
+ }
+ hostip = strtok_r (NULL, "|", &savPtr);
+ if (NULL == hostip) {
+ break;
+ }
+
+ host->next = GF_CALLOC (1, sizeof (*(host)),
+ gf_nfs_mt_auth_spec);
+ if (NULL == host->next){
+ gf_log (GF_MNT,GF_LOG_ERROR,
+ "Memory allocation failed");
+ goto err;
+ }
+ host = host->next;
+ }
+
+ /* In case of success return from here */
+ return 0;
+err:
+ /* In case of failure free up hostspec structure. */
+ FREE_HOSTSPEC (exp);
+
+ return -1;
+}
+
+/**
+ * exportpath will also have AUTH options (ip address, subnet address or
+ * hostname) mentioned.
+ * exportpath format: <abspath>[(hostdesc[|hostspec|...])]
+ */
struct mnt3_export *
mnt3_init_export_ent (struct mount3_state *ms, xlator_t *xl, char *exportpath,
uuid_t volumeid)
@@ -1507,6 +1820,20 @@ mnt3_init_export_ent (struct mount3_state *ms, xlator_t *xl, char *exportpath,
return NULL;
}
+ if (NULL != exportpath) {
+ /* If exportpath is not NULL then we should check if AUTH
+ * parameter is present or not. If AUTH parameter is present
+ * then it will be stripped and stored in mnt3_export (exp)
+ * structure.
+ */
+ if (0 != mnt3_export_parse_auth_param (exp, exportpath)){
+ gf_log (GF_MNT, GF_LOG_ERROR,
+ "Failed to parse auth param");
+ goto err;
+ }
+ }
+
+
INIT_LIST_HEAD (&exp->explist);
if (exportpath)
alloclen = strlen (xl->name) + 2 + strlen (exportpath);
@@ -1516,8 +1843,6 @@ mnt3_init_export_ent (struct mount3_state *ms, xlator_t *xl, char *exportpath,
exp->expname = GF_CALLOC (alloclen, sizeof (char), gf_nfs_mt_char);
if (!exp->expname) {
gf_log (GF_MNT, GF_LOG_ERROR, "Memory allocation failed");
- GF_FREE (exp);
- exp = NULL;
goto err;
}
@@ -1543,7 +1868,17 @@ mnt3_init_export_ent (struct mount3_state *ms, xlator_t *xl, char *exportpath,
*/
uuid_copy (exp->volumeid, volumeid);
exp->vol = xl;
+
+ /* On success we should return from here*/
+ return exp;
err:
+ /* On failure free exp and it's members.*/
+ if (NULL != exp) {
+ FREE_HOSTSPEC (exp);
+ GF_FREE (exp);
+ exp = NULL;
+ }
+
return exp;
}
diff --git a/xlators/nfs/server/src/mount3.h b/xlators/nfs/server/src/mount3.h
index c0eae3644..b9721fc03 100644
--- a/xlators/nfs/server/src/mount3.h
+++ b/xlators/nfs/server/src/mount3.h
@@ -68,6 +68,13 @@ struct mountentry {
#define MNT3_EXPTYPE_VOLUME 1
#define MNT3_EXPTYPE_DIR 2
+/* Structure to hold export-dir AUTH parameter */
+struct host_auth_spec {
+ char *host_addr; /* Allowed IP or host name */
+ int routeprefix; /* Routing prefix */
+ struct host_auth_spec *next; /* Pointer to next AUTH struct */
+};
+
struct mnt3_export {
struct list_head explist;
@@ -75,6 +82,11 @@ struct mnt3_export {
* is exported or the subdirectory in the volume.
*/
char *expname;
+ /*
+ * IP address, hostname or subnets who are allowed to connect to expname
+ * subvolume or subdirectory
+ */
+ struct host_auth_spec* hostspec;
xlator_t *vol;
int exptype;
diff --git a/xlators/nfs/server/src/nfs-mem-types.h b/xlators/nfs/server/src/nfs-mem-types.h
index 005598c1f..072512a52 100644
--- a/xlators/nfs/server/src/nfs-mem-types.h
+++ b/xlators/nfs/server/src/nfs-mem-types.h
@@ -54,6 +54,7 @@ enum gf_nfs_mem_types_ {
gf_nfs_mt_nlm4_share,
gf_nfs_mt_aux_gids,
gf_nfs_mt_inode_ctx,
+ gf_nfs_mt_auth_spec,
gf_nfs_mt_end
};
#endif
diff --git a/xlators/nfs/server/src/nfs.c b/xlators/nfs/server/src/nfs.c
index c3a76c63e..49512438e 100644
--- a/xlators/nfs/server/src/nfs.c
+++ b/xlators/nfs/server/src/nfs.c
@@ -1124,7 +1124,19 @@ struct volume_options options[] = {
"be exported separately. This option can also be used "
"in conjunction with nfs3.export-volumes option to "
"restrict exports only to the subdirectories specified"
- " through this option. Must be an absolute path."
+ " through this option. Must be an absolute path. Along"
+ " with path allowed list of IPs/hostname can be "
+ "associated with each subdirectory. If provided "
+ "connection will allowed only from these IPs. By "
+ "default connections from all IPs are allowed. "
+ "Format: <dir>[(hostspec[|hostspec|...])][,...]. Where"
+ " hostspec can be an IP address, hostname or an IP "
+ "range in CIDR notation. "
+ "e.g. /foo(192.168.1.0/24|host1|10.1.1.8),/host2."
+ " NOTE: Care must be taken while configuring this "
+ "option as invalid entries and/or unreachable DNS "
+ "servers can introduce unwanted delay in all the mount"
+ " calls."
},
{ .key = {"nfs3.export-dirs"},
.type = GF_OPTION_TYPE_BOOL,