Windows: test for and react to SMB Extended Session Timeout support
authorJeffrey Altman <jaltman@secure-endpoints.com>
Wed, 29 Jul 2009 18:31:45 +0000 (14:31 -0400)
committerJeffrey Altman <jaltman@openafs.org>
Wed, 29 Jul 2009 20:59:53 +0000 (13:59 -0700)
SMB Extended Session Timeout Support is available only on
Windows systems with specific versions of the mrxsmb.sys driver.
Add a test for those driver versions.  If a supporting version
is present use the extended session timeout value instead of the
standard timeout value for the redirector timeout.  Adjust the
rx hard, conn, and idle timeouts accordingly.

The SMB module will define the ExtendedSessTimeout registry
value if it does not exist.  We rely on the fact that this is
done after the rx timeout values are calculated.  The mrxsmb
driver only reads the value at boot.

LICENSE MIT

Reviewed-on: http://gerrit.openafs.org/248
Tested-by: Jeffrey Altman <jaltman@openafs.org>
Reviewed-by: Jeffrey Altman <jaltman@openafs.org>
Reviewed-by: Asanka Herath <asanka@secure-endpoints.com>
Tested-by: Asanka Herath <asanka@secure-endpoints.com>
Reviewed-by: Derrick Brashear <shadow@dementia.org>

src/WINNT/afsd/cm_conn.c
src/WINNT/afsd/cm_conn.h
src/WINNT/afsd/cm_utils.c
src/WINNT/afsd/cm_utils.h
src/WINNT/afsd/smb.c

index 39b8a20..c333dad 100644 (file)
@@ -30,6 +30,7 @@ unsigned short IdleDeadtimeout = CM_CONN_IDLEDEADTIME;
 
 #define LANMAN_WKS_PARAM_KEY "SYSTEM\\CurrentControlSet\\Services\\lanmanworkstation\\parameters"
 #define LANMAN_WKS_SESSION_TIMEOUT "SessTimeout"
+#define LANMAN_WKS_EXT_SESSION_TIMEOUT "ExtendedSessTimeout"
 
 afs_uint32 cryptall = 0;
 afs_uint32 cm_anonvldb = 0;
@@ -63,11 +64,36 @@ void cm_InitConn(void)
                            0, KEY_QUERY_VALUE, &parmKey);
        if (code == ERROR_SUCCESS)
         {
-           dummyLen = sizeof(DWORD);
-           code = RegQueryValueEx(parmKey, LANMAN_WKS_SESSION_TIMEOUT, NULL, NULL, 
-                                  (BYTE *) &dwValue, &dummyLen);
-           if (code == ERROR_SUCCESS)
-                RDRtimeout = dwValue;
+            BOOL extTimeouts = msftSMBRedirectorSupportsExtendedTimeouts();
+
+            if (extTimeouts) {
+                /* 
+                 * The default value is 1000 seconds.  However, this default
+                 * will not apply to "\\AFS" unless "AFS" is listed in 
+                 * ServersWithExtendedSessTimeout which we will add when we
+                 * create the ExtendedSessTimeout value in smb_Init()
+                 */
+                dummyLen = sizeof(DWORD);
+                code = RegQueryValueEx(parmKey, 
+                                       LANMAN_WKS_EXT_SESSION_TIMEOUT,
+                                        NULL, NULL,
+                                        (BYTE *) &dwValue, &dummyLen);
+                if (code == ERROR_SUCCESS) {
+                    RDRtimeout = dwValue;
+                    afsi_log("lanmanworkstation : ExtSessTimeout %u", RDRtimeout);
+                }
+            } 
+            if (!extTimeouts || code != ERROR_SUCCESS) {
+                dummyLen = sizeof(DWORD);
+                code = RegQueryValueEx(parmKey, 
+                                       LANMAN_WKS_SESSION_TIMEOUT,
+                                       NULL, NULL,
+                                       (BYTE *) &dwValue, &dummyLen);
+                if (code == ERROR_SUCCESS) {
+                    RDRtimeout = dwValue;
+                    afsi_log("lanmanworkstation : SessTimeout %u", RDRtimeout);
+                }
+            }
            RegCloseKey(parmKey);
         }
 
@@ -98,17 +124,27 @@ void cm_InitConn(void)
             RegCloseKey(parmKey);
        }
 
-       afsi_log("lanmanworkstation : SessTimeout %u", RDRtimeout);
+        /* 
+         * If these values were not set via cpp macro or obtained 
+         * from the registry, we use a value that is derived from
+         * the smb redirector session timeout (RDRtimeout).
+         *
+         * The UNIX cache manager uses 120 seconds for the hard dead
+         * timeout and 50 seconds for the connection and idle timeouts.
+         *
+         * We base our values on those while making sure we leave 
+         * enough time for overhead.  
+         */
        if (ConnDeadtimeout == 0) {
-           ConnDeadtimeout = (unsigned short) (RDRtimeout / 2);
+           ConnDeadtimeout = (unsigned short) ((RDRtimeout / 2) < 50 ? (RDRtimeout / 2) : 50);
             afsi_log("ConnDeadTimeout is %d", ConnDeadtimeout);
         }
        if (HardDeadtimeout == 0) {
-           HardDeadtimeout = (unsigned short) RDRtimeout;
+           HardDeadtimeout = (unsigned short) (RDRtimeout > 125 ? 120 : (RDRtimeout - 5));
             afsi_log("HardDeadTimeout is %d", HardDeadtimeout);
         }
        if (IdleDeadtimeout == 0) {
-           IdleDeadtimeout = (unsigned short) RDRtimeout;
+           IdleDeadtimeout = (unsigned short) ConnDeadtimeout;
             afsi_log("IdleDeadTimeout is %d", IdleDeadtimeout);
         }
        osi_EndOnce(&once);
index d5f41e2..5e0b673 100644 (file)
 #define __CM_CONN_H_ENV__ 1
 
 #define        CM_CONN_DEFAULTRDRTIMEOUT       45
+#ifndef CM_CONN_CONNDEADTIME
 #define CM_CONN_CONNDEADTIME            0
+#endif
+#ifndef CM_CONN_HARDDEADTIME
 #define CM_CONN_HARDDEADTIME             0
+#endif
+#ifndef CM_CONN_IDLEDEADTIME
 #define CM_CONN_IDLEDEADTIME             0
+#endif
 
 extern unsigned short ConnDeadtimeout;
 extern unsigned short HardDeadtimeout;
index 90c12d3..3011967 100644 (file)
@@ -786,3 +786,180 @@ cm_LoadAfsdHookLib(void)
 
     return hLib;
 }
+
+/*
+ * Obtain the file info structure for the specified file.
+ * If a full path is not specified, the search order is the
+ * same as that used by LoadLibrary().
+ */
+BOOL
+cm_GetOSFileVersion (char *filename, LARGE_INTEGER *liVer)
+{
+    DWORD dwHandle;
+    DWORD dwSize;
+    char* pInfo = NULL;
+    BOOL  rc;
+    UINT uLen;
+    void *pbuf;
+    VS_FIXEDFILEINFO vsf;
+
+    dwSize = GetFileVersionInfoSizeA(filename,&dwHandle);
+    if (dwSize == 0) {
+        rc = FALSE;
+        goto done;
+    }
+    pInfo = (char*)malloc(dwSize);
+    if (!pInfo) {
+        rc = FALSE;
+        goto done;
+    }
+    rc = GetFileVersionInfoA(filename, dwHandle, dwSize, pInfo);
+    if (!rc)
+        goto done;
+    rc = VerQueryValueA(pInfo,"\\",&pbuf, &uLen);
+    if (!rc)
+        goto done;
+    memcpy(&vsf, pbuf, sizeof(VS_FIXEDFILEINFO));
+
+    liVer->LowPart = vsf.dwFileVersionLS;
+    liVer->HighPart = vsf.dwFileVersionMS;
+    rc = TRUE;
+
+  done:
+    if (pInfo)
+        free(pInfo);
+    return rc;
+}
+
+typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
+typedef BOOL (WINAPI *LPFN_DISABLEWOW64FSREDIRECTION) (PVOID *);
+typedef BOOL (WINAPI *LPFN_REVERTWOW64FSREDIRECTION) (PVOID);
+
+BOOL msftSMBRedirectorSupportsExtendedTimeouts(void) 
+{
+    static BOOL fChecked = FALSE;
+    static BOOL fSupportsExtendedTimeouts = FALSE;
+
+    if (!fChecked)
+    {
+        BOOL isWow64 = FALSE;
+        OSVERSIONINFOEX Version;
+        HANDLE h1 = NULL;
+        LPFN_ISWOW64PROCESS fnIsWow64Process = NULL;
+        LPFN_DISABLEWOW64FSREDIRECTION fnDisableWow64FsRedirection = NULL;
+        LPFN_REVERTWOW64FSREDIRECTION fnRevertWow64FsRedirection = NULL;
+        PVOID Wow64RedirectionState;
+        LARGE_INTEGER fvFile, fvHotFixMin;
+
+        h1 = GetModuleHandle("kernel32.dll"); /* no refcount increase */
+        /* 
+         * If we don't find the fnIsWow64Process function then we
+         * are not running in a Wow64 environment
+         */
+        fnIsWow64Process =
+            (LPFN_ISWOW64PROCESS)GetProcAddress(h1, "IsWow64Process");
+
+        memset (&Version, 0x00, sizeof(Version));
+        Version.dwOSVersionInfoSize = sizeof(Version);
+        GetVersionEx((OSVERSIONINFO *) &Version);
+
+        /* 
+         * Support is available as hot fixes / service packs on:
+         *   XP SP2
+         *   XP SP3
+         *   2003 and XP64 SP2
+         *   Vista and 2008 SP2
+         *   Win7 and 2008 R2
+         */
+        if (Version.dwPlatformId == VER_PLATFORM_WIN32_NT &&
+            Version.dwMajorVersion >= 5) {
+
+            /* 32-bit XP */
+            if (Version.dwMajorVersion == 5 &&
+                Version.dwMinorVersion == 1) {
+                
+                fvHotFixMin.HighPart = (5 << 16) | 1;
+
+                switch (Version.wServicePackMajor) {
+                case 3:
+                    fvHotFixMin.LowPart = (2600 << 16) | 5815;
+                    break;
+                case 2:
+                    fvHotFixMin.LowPart = (2600 << 16) | 3572;
+                    break;
+                default:
+                    fSupportsExtendedTimeouts = (Version.wServicePackMajor > 3);
+                    goto checked;
+                }
+            }
+
+            /* 64-bit XP and Server 2003 */
+            else if (Version.dwMajorVersion == 5 &&
+                     Version.dwMinorVersion == 2) {
+                
+                fvHotFixMin.HighPart = (5 << 16) | 2;
+
+                switch (Version.wServicePackMajor) {
+                case 2:
+                    fvHotFixMin.LowPart = (3790 << 16) | 4479;
+                    break;
+                case 1:
+                    fvHotFixMin.LowPart = (3790 << 16) | 3310;
+                    break;
+                default:
+                    fSupportsExtendedTimeouts = (Version.wServicePackMajor > 2);
+                    goto checked;
+                }
+            }
+
+            /* Vista and Server 2008 */
+            else if (Version.dwMajorVersion == 6 &&
+                     Version.dwMinorVersion == 0) {
+                
+                fvHotFixMin.HighPart = (6 << 16) | 0;
+
+                switch (Version.wServicePackMajor) {
+                case 2:
+                    fvHotFixMin.LowPart = (6002 << 16) | 18005;
+                    break;
+                default:
+                    fSupportsExtendedTimeouts = (Version.wServicePackMajor > 2);
+                    goto checked;
+                }
+            }
+
+            /* Windows 7 and Server 2008 R2 and beyond */
+            else if (Version.dwMajorVersion > 6 ||
+                     Version.dwMajorVersion == 6 &&
+                     Version.dwMinorVersion >= 1) {
+                fSupportsExtendedTimeouts = TRUE;
+                goto checked;
+            }
+
+            /* If wow64, disable wow64 redirection and preserve the existing state */
+            if (fnIsWow64Process && 
+                 fnIsWow64Process(GetCurrentProcess(), &isWow64) &&
+                 isWow64) {
+                fnDisableWow64FsRedirection =
+                    (LPFN_DISABLEWOW64FSREDIRECTION)GetProcAddress(h1, "Wow64DisableWow64FsRedirection");
+                fnRevertWow64FsRedirection =
+                    (LPFN_REVERTWOW64FSREDIRECTION)GetProcAddress(h1, "Wow64RevertWow64FsRedirection");
+                fnDisableWow64FsRedirection(&Wow64RedirectionState);
+            }
+            
+            if (cm_GetOSFileVersion("drivers\\mrxsmb.sys", &fvFile) ||
+                (fvFile.QuadPart >= fvHotFixMin.QuadPart))
+                fSupportsExtendedTimeouts = TRUE;
+
+            /* If wow64, restore the previous redirection state */
+            if (fnIsWow64Process && isWow64) {
+                fnRevertWow64FsRedirection(Wow64RedirectionState);
+            }            
+        }
+      checked:
+        fChecked = TRUE;
+    }
+
+    return fSupportsExtendedTimeouts;
+}
+
index f1708cc..653ff96 100644 (file)
@@ -85,4 +85,8 @@ extern BOOL cm_TargetPerceivedAsDirectory(const fschar_t *target);
 
 extern HANDLE cm_LoadAfsdHookLib(void);
 
+extern BOOL cm_GetOSFileVersion(char *filename, LARGE_INTEGER *liVer);
+
+extern BOOL msftSMBRedirectorSupportsExtendedTimeouts(void);
+
 #endif /*  __CM_UTILS_H_ENV__ */
index 4cc33cb..1611b2a 100644 (file)
@@ -9692,7 +9692,7 @@ configureExtendedSMBSessionTimeouts(void)
         {
             dwType = REG_DWORD;
            dwSize = sizeof(dwValue);
-            dwValue = 600;      /* 10 minutes */
+            dwValue = 300;      /* 5 minutes */
             RegSetValueEx( hkLanMan, "ExtendedSessTimeout", 0, dwType, (const BYTE *)&dwValue, dwSize);
         }
         RegCloseKey(hkLanMan);
@@ -9997,7 +9997,10 @@ void smb_StartListeners(int locked)
     configureBackConnectionHostNames();
 
     /* Configure Extended SMB Session Timeouts */
-    configureExtendedSMBSessionTimeouts();
+    if (msftSMBRedirectorSupportsExtendedTimeouts()) {
+        afsi_log("Microsoft SMB Redirector supports Extended Timeouts");
+        configureExtendedSMBSessionTimeouts();
+    }
 
     smb_ListenerState = SMB_LISTENER_STARTED;
     cm_VolStatus_Network_Started(cm_NetbiosName