cifs: use the expiry output of dns_query to schedule next resolution
authorShyam Prasad N <sprasad@microsoft.com>
Tue, 18 May 2021 15:05:50 +0000 (15:05 +0000)
committerSteve French <stfrench@microsoft.com>
Wed, 14 Jul 2021 15:06:03 +0000 (10:06 -0500)
We recently fixed DNS resolution of the server hostname during reconnect.
However, server IP address may change, even when the old one continues
to server (although sub-optimally).

We should schedule the next DNS resolution based on the TTL of
the DNS record used for the last resolution. This way, we resolve the
server hostname again when a DNS record expires.

Signed-off-by: Shyam Prasad N <sprasad@microsoft.com>
Reviewed-by: Paulo Alcantara (SUSE) <pc@cjr.nz>
Cc: <stable@vger.kernel.org> # v5.11+
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/cifs/cifs_dfs_ref.c
fs/cifs/cifsglob.h
fs/cifs/connect.c
fs/cifs/dns_resolve.c
fs/cifs/dns_resolve.h
fs/cifs/misc.c

index 57f9131..fa131da 100644 (file)
@@ -176,7 +176,7 @@ char *cifs_compose_mount_options(const char *sb_mountdata,
                }
        }
 
-       rc = dns_resolve_server_name_to_ip(name, &srvIP);
+       rc = dns_resolve_server_name_to_ip(name, &srvIP, NULL);
        if (rc < 0) {
                cifs_dbg(FYI, "%s: Failed to resolve server part of %s to IP: %d\n",
                         __func__, name, rc);
index 3c2e117..ade7080 100644 (file)
@@ -75,6 +75,9 @@
 #define SMB_ECHO_INTERVAL_MAX 600
 #define SMB_ECHO_INTERVAL_DEFAULT 60
 
+/* dns resolution interval in seconds */
+#define SMB_DNS_RESOLVE_INTERVAL_DEFAULT 600
+
 /* maximum number of PDUs in one compound */
 #define MAX_COMPOUND 5
 
@@ -646,6 +649,7 @@ struct TCP_Server_Info {
        /* point to the SMBD connection if RDMA is used instead of socket */
        struct smbd_connection *smbd_conn;
        struct delayed_work     echo; /* echo ping workqueue job */
+       struct delayed_work     resolve; /* dns resolution workqueue job */
        char    *smallbuf;      /* pointer to current "small" buffer */
        char    *bigbuf;        /* pointer to current "big" buffer */
        /* Total size of this PDU. Only valid from cifs_demultiplex_thread */
index 01dc451..f1a7e63 100644 (file)
@@ -78,6 +78,8 @@ static int reconn_set_ipaddr_from_hostname(struct TCP_Server_Info *server)
        int rc;
        int len;
        char *unc, *ipaddr = NULL;
+       time64_t expiry, now;
+       unsigned long ttl = SMB_DNS_RESOLVE_INTERVAL_DEFAULT;
 
        if (!server->hostname)
                return -EINVAL;
@@ -91,13 +93,13 @@ static int reconn_set_ipaddr_from_hostname(struct TCP_Server_Info *server)
        }
        scnprintf(unc, len, "\\\\%s", server->hostname);
 
-       rc = dns_resolve_server_name_to_ip(unc, &ipaddr);
+       rc = dns_resolve_server_name_to_ip(unc, &ipaddr, &expiry);
        kfree(unc);
 
        if (rc < 0) {
                cifs_dbg(FYI, "%s: failed to resolve server part of %s to IP: %d\n",
                         __func__, server->hostname, rc);
-               return rc;
+               goto requeue_resolve;
        }
 
        spin_lock(&cifs_tcp_ses_lock);
@@ -106,7 +108,45 @@ static int reconn_set_ipaddr_from_hostname(struct TCP_Server_Info *server)
        spin_unlock(&cifs_tcp_ses_lock);
        kfree(ipaddr);
 
-       return !rc ? -1 : 0;
+       /* rc == 1 means success here */
+       if (rc) {
+               now = ktime_get_real_seconds();
+               if (expiry && expiry > now)
+                       /*
+                        * To make sure we don't use the cached entry, retry 1s
+                        * after expiry.
+                        */
+                       ttl = (expiry - now + 1);
+       }
+       rc = !rc ? -1 : 0;
+
+requeue_resolve:
+       cifs_dbg(FYI, "%s: next dns resolution scheduled for %lu seconds in the future\n",
+                __func__, ttl);
+       mod_delayed_work(cifsiod_wq, &server->resolve, (ttl * HZ));
+
+       return rc;
+}
+
+
+static void cifs_resolve_server(struct work_struct *work)
+{
+       int rc;
+       struct TCP_Server_Info *server = container_of(work,
+                                       struct TCP_Server_Info, resolve.work);
+
+       mutex_lock(&server->srv_mutex);
+
+       /*
+        * Resolve the hostname again to make sure that IP address is up-to-date.
+        */
+       rc = reconn_set_ipaddr_from_hostname(server);
+       if (rc) {
+               cifs_dbg(FYI, "%s: failed to resolve hostname: %d\n",
+                               __func__, rc);
+       }
+
+       mutex_unlock(&server->srv_mutex);
 }
 
 #ifdef CONFIG_CIFS_DFS_UPCALL
@@ -680,6 +720,7 @@ static void clean_demultiplex_info(struct TCP_Server_Info *server)
        spin_unlock(&cifs_tcp_ses_lock);
 
        cancel_delayed_work_sync(&server->echo);
+       cancel_delayed_work_sync(&server->resolve);
 
        spin_lock(&GlobalMid_Lock);
        server->tcpStatus = CifsExiting;
@@ -1260,6 +1301,7 @@ cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect)
        spin_unlock(&cifs_tcp_ses_lock);
 
        cancel_delayed_work_sync(&server->echo);
+       cancel_delayed_work_sync(&server->resolve);
 
        if (from_reconnect)
                /*
@@ -1342,6 +1384,7 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx)
        INIT_LIST_HEAD(&tcp_ses->tcp_ses_list);
        INIT_LIST_HEAD(&tcp_ses->smb_ses_list);
        INIT_DELAYED_WORK(&tcp_ses->echo, cifs_echo_request);
+       INIT_DELAYED_WORK(&tcp_ses->resolve, cifs_resolve_server);
        INIT_DELAYED_WORK(&tcp_ses->reconnect, smb2_reconnect_server);
        mutex_init(&tcp_ses->reconnect_mutex);
        memcpy(&tcp_ses->srcaddr, &ctx->srcaddr,
@@ -1427,6 +1470,12 @@ smbd_connected:
        /* queue echo request delayed work */
        queue_delayed_work(cifsiod_wq, &tcp_ses->echo, tcp_ses->echo_interval);
 
+       /* queue dns resolution delayed work */
+       cifs_dbg(FYI, "%s: next dns resolution scheduled for %d seconds in the future\n",
+                __func__, SMB_DNS_RESOLVE_INTERVAL_DEFAULT);
+
+       queue_delayed_work(cifsiod_wq, &tcp_ses->resolve, (SMB_DNS_RESOLVE_INTERVAL_DEFAULT * HZ));
+
        return tcp_ses;
 
 out_err_crypto_release:
index d15b82d..8c616aa 100644 (file)
@@ -24,6 +24,7 @@
  * dns_resolve_server_name_to_ip - Resolve UNC server name to ip address.
  * @unc: UNC path specifying the server (with '/' as delimiter)
  * @ip_addr: Where to return the IP address.
+ * @expiry: Where to return the expiry time for the dns record.
  *
  * The IP address will be returned in string form, and the caller is
  * responsible for freeing it.
@@ -31,7 +32,7 @@
  * Returns length of result on success, -ve on error.
  */
 int
-dns_resolve_server_name_to_ip(const char *unc, char **ip_addr)
+dns_resolve_server_name_to_ip(const char *unc, char **ip_addr, time64_t *expiry)
 {
        struct sockaddr_storage ss;
        const char *hostname, *sep;
@@ -66,13 +67,14 @@ dns_resolve_server_name_to_ip(const char *unc, char **ip_addr)
 
        /* Perform the upcall */
        rc = dns_query(current->nsproxy->net_ns, NULL, hostname, len,
-                      NULL, ip_addr, NULL, false);
+                      NULL, ip_addr, expiry, false);
        if (rc < 0)
                cifs_dbg(FYI, "%s: unable to resolve: %*.*s\n",
                         __func__, len, len, hostname);
        else
-               cifs_dbg(FYI, "%s: resolved: %*.*s to %s\n",
-                        __func__, len, len, hostname, *ip_addr);
+               cifs_dbg(FYI, "%s: resolved: %*.*s to %s expiry %llu\n",
+                        __func__, len, len, hostname, *ip_addr,
+                        expiry ? (*expiry) : 0);
        return rc;
 
 name_is_IP_address:
index 5be060b..9fa2807 100644 (file)
@@ -12,7 +12,7 @@
 #define _DNS_RESOLVE_H
 
 #ifdef __KERNEL__
-extern int dns_resolve_server_name_to_ip(const char *unc, char **ip_addr);
+extern int dns_resolve_server_name_to_ip(const char *unc, char **ip_addr, time64_t *expiry);
 #endif /* KERNEL */
 
 #endif /* _DNS_RESOLVE_H */
index 184138b..844abeb 100644 (file)
@@ -1187,7 +1187,7 @@ int match_target_ip(struct TCP_Server_Info *server,
 
        cifs_dbg(FYI, "%s: target name: %s\n", __func__, target + 2);
 
-       rc = dns_resolve_server_name_to_ip(target, &tip);
+       rc = dns_resolve_server_name_to_ip(target, &tip, NULL);
        if (rc < 0)
                goto out;