2014-05-17 10:47:18

by Kinglong Mee

[permalink] [raw]
Subject: [PATCH] NFS4.0: Add IPv6 support

Signed-off-by: Kinglong Mee <[email protected]>
---
nfs4.0/lib/rpc/rpc.py | 15 ++++++++-----
nfs4.0/nfs4lib.py | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++
nfs4.0/testserver.py | 26 +++++-----------------
3 files changed, 77 insertions(+), 25 deletions(-)

diff --git a/nfs4.0/lib/rpc/rpc.py b/nfs4.0/lib/rpc/rpc.py
index 60f70bd..8b39df4 100644
--- a/nfs4.0/lib/rpc/rpc.py
+++ b/nfs4.0/lib/rpc/rpc.py
@@ -188,6 +188,9 @@ class RPCClient(object):
self.debug = 0
t = threading.currentThread()
self.lock = threading.Lock()
+ self.af = socket.AF_INET;
+ if host.find(':') != -1:
+ self.af = socket.AF_INET6;
self.remotehost = host
self.remoteport = port
self.timeout = timeout
@@ -207,6 +210,7 @@ class RPCClient(object):
self._init_security(self.sec_list) # Note this can make calls
self.security = sec_list[0]

+
def _init_security(self, list):
# Each element of list must have functions:
# initialize, secure_data, make_cred, make_verf
@@ -235,8 +239,7 @@ class RPCClient(object):
if t in self._socket:
out = self._socket[t]
else:
- out = self._socket[t] = socket.socket(socket.AF_INET,
- socket.SOCK_STREAM)
+ out = self._socket[t] = socket.socket(self.af, socket.SOCK_STREAM)
if self.uselowport:
self.bindsocket(out)
out.connect((self.remotehost, self.remoteport))
@@ -301,8 +304,7 @@ class RPCClient(object):
t = threading.currentThread()
self.lock.acquire()
self._socket[t].close()
- out = self._socket[t] = socket.socket(socket.AF_INET,
- socket.SOCK_STREAM)
+ out = self._socket[t] = socket.socket(self.af, socket.SOCK_STREAM)
# out.bind
out.connect((self.remotehost, self.remoteport))
out.settimeout(self.timeout)
@@ -454,7 +456,10 @@ class RPCClient(object):

class Server(object):
def __init__(self, host='', port=51423, name="SERVER"):
- self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ try:
+ self.s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
+ except:
+ self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.s.bind((host, port))
self.port = self.s.getsockname()[1]
diff --git a/nfs4.0/nfs4lib.py b/nfs4.0/nfs4lib.py
index 5fc7bf3..994e0e1 100644
--- a/nfs4.0/nfs4lib.py
+++ b/nfs4.0/nfs4lib.py
@@ -39,6 +39,7 @@ import time
import struct
import socket
import sys
+import re

class NFSException(rpc.RPCError):
pass
@@ -1013,4 +1014,64 @@ def bitmap2list(bitmap):
bitmap >>= 1
return out

+def path_components(path, use_dots=True):
+ """Convert a string '/a/b/c' into an array ['a', 'b', 'c']"""
+ out = []
+ for c in path.split('/'):
+ if c == '':
+ pass
+ elif use_dots and c == '.':
+ pass
+ elif use_dots and c == '..':
+ del out[-1]
+
+def parse_nfs_url(url):
+ """Parse [nfs://]host:port/path, format taken from rfc 2224
+ multipath addr:port pair are as such:
+
+ $ip1:$port1,$ip2:$port2..
+
+ Returns triple server, port, path.
+ """
+ p = re.compile(r"""
+ (?:nfs://)? # Ignore an optionally prepended 'nfs://'
+ (?P<servers>[^/]+)
+ (?P<path>/.*)? # set path=everything else, must start with /
+ $
+ """, re.VERBOSE)
+
+ m = p.match(url)
+ if m:
+ servers = m.group('servers')
+ server_list = []
+
+ for server in servers.split(','):
+ server = server.strip()
+
+ idx = server.rfind(':')
+ bracket_idx = server.rfind(']')
+
+ # the first : is before ipv6 addr ] -> no port specified
+ if bracket_idx > idx:
+ idx = -1
+
+ if idx >= 0:
+ host = server[:idx]
+ port = server[idx+1:]
+ else:
+ host = server
+ port = None
+
+ # remove brackets around IPv6 addrs, if they exist
+ if host.startswith('[') and host.endswith(']'):
+ host = host[1:-1]
+
+ port = (2049 if not port else int(port))
+ server_list.append((host, port))

+ path = m.group('path')
+ path = (path_components(path) if path else [])
+
+ return tuple(server_list), path
+ else:
+ raise ValueError("Error parsing NFS URL: %s" % url)
diff --git a/nfs4.0/testserver.py b/nfs4.0/testserver.py
index 606e2f0..41be74a 100755
--- a/nfs4.0/testserver.py
+++ b/nfs4.0/testserver.py
@@ -35,7 +35,6 @@ if __name__ == "__main__":
if os.path.isfile(os.path.join(sys.path[0], 'lib', 'testmod.py')):
sys.path.insert(1, os.path.join(sys.path[0], 'lib'))

-import re
import nfs4lib
import testmod
from optparse import OptionParser, OptionGroup, IndentedHelpFormatter
@@ -57,23 +56,6 @@ if not hasattr(os, "getgid"):
else:
GID = os.getgid()

-
-def parse_url(url):
- """Parse [nfs://]host:port/path"""
- p = re.compile(r"""
- (?:nfs://)? # Ignore an optionally prepended 'nfs://'
- (?P<host>[^:]+) # set host=everything up to next :
- :?
- (?P<port>[^/]*) # set port=everything up to next /
- (?P<path>/.*$|$) # set path=everything else
- """, re.VERBOSE)
-
- m = p.match(url)
- if m:
- return m.group('host'), m.group('port'), m.group('path')
- else:
- return None, None, None
-
def unixpath2comps(str, pathcomps=None):
if pathcomps is None or str[0] == '/':
pathcomps = []
@@ -284,9 +266,13 @@ def main():
if not args:
p.error("Need a server")
url = args.pop(0)
- opt.server, opt.port, opt.path = parse_url(url)
- if not opt.server:
+ server_list, opt.path = nfs4lib.parse_nfs_url(url)
+
+ if not server_list:
p.error("%s not a valid server name" % url)
+
+ opt.server, opt.port = server_list[0]
+
if not opt.port:
opt.port = 2049
else:
--
1.9.0