Skip to content

Commit

Permalink
cifs: use the expiry output of dns_query to schedule next resolution
Browse files Browse the repository at this point in the history
commit 506c1da upstream.

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>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
sprasad-microsoft authored and gregkh committed Jul 20, 2021
1 parent 094d3b8 commit 22c0185
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 10 deletions.
2 changes: 1 addition & 1 deletion fs/cifs/cifs_dfs_ref.c
Expand Up @@ -173,7 +173,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);
Expand Down
4 changes: 4 additions & 0 deletions fs/cifs/cifsglob.h
Expand Up @@ -84,6 +84,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

Expand Down Expand Up @@ -654,6 +657,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 */
Expand Down
55 changes: 52 additions & 3 deletions fs/cifs/connect.c
Expand Up @@ -92,6 +92,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;
Expand All @@ -105,13 +107,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);
Expand All @@ -120,7 +122,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
Expand Down Expand Up @@ -720,6 +760,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;
Expand Down Expand Up @@ -1300,6 +1341,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)
/*
Expand Down Expand Up @@ -1382,6 +1424,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,
Expand Down Expand Up @@ -1462,6 +1505,12 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx)
/* 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:
Expand Down
10 changes: 6 additions & 4 deletions fs/cifs/dns_resolve.c
Expand Up @@ -36,14 +36,15 @@
* 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.
*
* 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;
Expand Down Expand Up @@ -78,13 +79,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:
Expand Down
2 changes: 1 addition & 1 deletion fs/cifs/dns_resolve.h
Expand Up @@ -24,7 +24,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 */
2 changes: 1 addition & 1 deletion fs/cifs/misc.c
Expand Up @@ -1105,7 +1105,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;

Expand Down

0 comments on commit 22c0185

Please sign in to comment.