162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Witness Service client for CIFS 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2020 Samuel Cabrero <scabrero@suse.de> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/kref.h> 962306a36Sopenharmony_ci#include <net/genetlink.h> 1062306a36Sopenharmony_ci#include <uapi/linux/cifs/cifs_netlink.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include "cifs_swn.h" 1362306a36Sopenharmony_ci#include "cifsglob.h" 1462306a36Sopenharmony_ci#include "cifsproto.h" 1562306a36Sopenharmony_ci#include "fscache.h" 1662306a36Sopenharmony_ci#include "cifs_debug.h" 1762306a36Sopenharmony_ci#include "netlink.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistatic DEFINE_IDR(cifs_swnreg_idr); 2062306a36Sopenharmony_cistatic DEFINE_MUTEX(cifs_swnreg_idr_mutex); 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistruct cifs_swn_reg { 2362306a36Sopenharmony_ci int id; 2462306a36Sopenharmony_ci struct kref ref_count; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci const char *net_name; 2762306a36Sopenharmony_ci const char *share_name; 2862306a36Sopenharmony_ci bool net_name_notify; 2962306a36Sopenharmony_ci bool share_name_notify; 3062306a36Sopenharmony_ci bool ip_notify; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci struct cifs_tcon *tcon; 3362306a36Sopenharmony_ci}; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic int cifs_swn_auth_info_krb(struct cifs_tcon *tcon, struct sk_buff *skb) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci int ret; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_KRB_AUTH); 4062306a36Sopenharmony_ci if (ret < 0) 4162306a36Sopenharmony_ci return ret; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci return 0; 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic int cifs_swn_auth_info_ntlm(struct cifs_tcon *tcon, struct sk_buff *skb) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci int ret; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci if (tcon->ses->user_name != NULL) { 5162306a36Sopenharmony_ci ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_USER_NAME, tcon->ses->user_name); 5262306a36Sopenharmony_ci if (ret < 0) 5362306a36Sopenharmony_ci return ret; 5462306a36Sopenharmony_ci } 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci if (tcon->ses->password != NULL) { 5762306a36Sopenharmony_ci ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_PASSWORD, tcon->ses->password); 5862306a36Sopenharmony_ci if (ret < 0) 5962306a36Sopenharmony_ci return ret; 6062306a36Sopenharmony_ci } 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci if (tcon->ses->domainName != NULL) { 6362306a36Sopenharmony_ci ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_DOMAIN_NAME, tcon->ses->domainName); 6462306a36Sopenharmony_ci if (ret < 0) 6562306a36Sopenharmony_ci return ret; 6662306a36Sopenharmony_ci } 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci return 0; 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci/* 7262306a36Sopenharmony_ci * Sends a register message to the userspace daemon based on the registration. 7362306a36Sopenharmony_ci * The authentication information to connect to the witness service is bundled 7462306a36Sopenharmony_ci * into the message. 7562306a36Sopenharmony_ci */ 7662306a36Sopenharmony_cistatic int cifs_swn_send_register_message(struct cifs_swn_reg *swnreg) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci struct sk_buff *skb; 7962306a36Sopenharmony_ci struct genlmsghdr *hdr; 8062306a36Sopenharmony_ci enum securityEnum authtype; 8162306a36Sopenharmony_ci struct sockaddr_storage *addr; 8262306a36Sopenharmony_ci int ret; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 8562306a36Sopenharmony_ci if (skb == NULL) { 8662306a36Sopenharmony_ci ret = -ENOMEM; 8762306a36Sopenharmony_ci goto fail; 8862306a36Sopenharmony_ci } 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci hdr = genlmsg_put(skb, 0, 0, &cifs_genl_family, 0, CIFS_GENL_CMD_SWN_REGISTER); 9162306a36Sopenharmony_ci if (hdr == NULL) { 9262306a36Sopenharmony_ci ret = -ENOMEM; 9362306a36Sopenharmony_ci goto nlmsg_fail; 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci ret = nla_put_u32(skb, CIFS_GENL_ATTR_SWN_REGISTRATION_ID, swnreg->id); 9762306a36Sopenharmony_ci if (ret < 0) 9862306a36Sopenharmony_ci goto nlmsg_fail; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_NET_NAME, swnreg->net_name); 10162306a36Sopenharmony_ci if (ret < 0) 10262306a36Sopenharmony_ci goto nlmsg_fail; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_SHARE_NAME, swnreg->share_name); 10562306a36Sopenharmony_ci if (ret < 0) 10662306a36Sopenharmony_ci goto nlmsg_fail; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci /* 10962306a36Sopenharmony_ci * If there is an address stored use it instead of the server address, because we are 11062306a36Sopenharmony_ci * in the process of reconnecting to it after a share has been moved or we have been 11162306a36Sopenharmony_ci * told to switch to it (client move message). In these cases we unregister from the 11262306a36Sopenharmony_ci * server address and register to the new address when we receive the notification. 11362306a36Sopenharmony_ci */ 11462306a36Sopenharmony_ci if (swnreg->tcon->ses->server->use_swn_dstaddr) 11562306a36Sopenharmony_ci addr = &swnreg->tcon->ses->server->swn_dstaddr; 11662306a36Sopenharmony_ci else 11762306a36Sopenharmony_ci addr = &swnreg->tcon->ses->server->dstaddr; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci ret = nla_put(skb, CIFS_GENL_ATTR_SWN_IP, sizeof(struct sockaddr_storage), addr); 12062306a36Sopenharmony_ci if (ret < 0) 12162306a36Sopenharmony_ci goto nlmsg_fail; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (swnreg->net_name_notify) { 12462306a36Sopenharmony_ci ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_NET_NAME_NOTIFY); 12562306a36Sopenharmony_ci if (ret < 0) 12662306a36Sopenharmony_ci goto nlmsg_fail; 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci if (swnreg->share_name_notify) { 13062306a36Sopenharmony_ci ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_SHARE_NAME_NOTIFY); 13162306a36Sopenharmony_ci if (ret < 0) 13262306a36Sopenharmony_ci goto nlmsg_fail; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci if (swnreg->ip_notify) { 13662306a36Sopenharmony_ci ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_IP_NOTIFY); 13762306a36Sopenharmony_ci if (ret < 0) 13862306a36Sopenharmony_ci goto nlmsg_fail; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci authtype = cifs_select_sectype(swnreg->tcon->ses->server, swnreg->tcon->ses->sectype); 14262306a36Sopenharmony_ci switch (authtype) { 14362306a36Sopenharmony_ci case Kerberos: 14462306a36Sopenharmony_ci ret = cifs_swn_auth_info_krb(swnreg->tcon, skb); 14562306a36Sopenharmony_ci if (ret < 0) { 14662306a36Sopenharmony_ci cifs_dbg(VFS, "%s: Failed to get kerberos auth info: %d\n", __func__, ret); 14762306a36Sopenharmony_ci goto nlmsg_fail; 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci break; 15062306a36Sopenharmony_ci case NTLMv2: 15162306a36Sopenharmony_ci case RawNTLMSSP: 15262306a36Sopenharmony_ci ret = cifs_swn_auth_info_ntlm(swnreg->tcon, skb); 15362306a36Sopenharmony_ci if (ret < 0) { 15462306a36Sopenharmony_ci cifs_dbg(VFS, "%s: Failed to get NTLM auth info: %d\n", __func__, ret); 15562306a36Sopenharmony_ci goto nlmsg_fail; 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci break; 15862306a36Sopenharmony_ci default: 15962306a36Sopenharmony_ci cifs_dbg(VFS, "%s: secType %d not supported!\n", __func__, authtype); 16062306a36Sopenharmony_ci ret = -EINVAL; 16162306a36Sopenharmony_ci goto nlmsg_fail; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci genlmsg_end(skb, hdr); 16562306a36Sopenharmony_ci genlmsg_multicast(&cifs_genl_family, skb, 0, CIFS_GENL_MCGRP_SWN, GFP_ATOMIC); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci cifs_dbg(FYI, "%s: Message to register for network name %s with id %d sent\n", __func__, 16862306a36Sopenharmony_ci swnreg->net_name, swnreg->id); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci return 0; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cinlmsg_fail: 17362306a36Sopenharmony_ci genlmsg_cancel(skb, hdr); 17462306a36Sopenharmony_ci nlmsg_free(skb); 17562306a36Sopenharmony_cifail: 17662306a36Sopenharmony_ci return ret; 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci/* 18062306a36Sopenharmony_ci * Sends an uregister message to the userspace daemon based on the registration 18162306a36Sopenharmony_ci */ 18262306a36Sopenharmony_cistatic int cifs_swn_send_unregister_message(struct cifs_swn_reg *swnreg) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci struct sk_buff *skb; 18562306a36Sopenharmony_ci struct genlmsghdr *hdr; 18662306a36Sopenharmony_ci int ret; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 18962306a36Sopenharmony_ci if (skb == NULL) 19062306a36Sopenharmony_ci return -ENOMEM; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci hdr = genlmsg_put(skb, 0, 0, &cifs_genl_family, 0, CIFS_GENL_CMD_SWN_UNREGISTER); 19362306a36Sopenharmony_ci if (hdr == NULL) { 19462306a36Sopenharmony_ci ret = -ENOMEM; 19562306a36Sopenharmony_ci goto nlmsg_fail; 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci ret = nla_put_u32(skb, CIFS_GENL_ATTR_SWN_REGISTRATION_ID, swnreg->id); 19962306a36Sopenharmony_ci if (ret < 0) 20062306a36Sopenharmony_ci goto nlmsg_fail; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_NET_NAME, swnreg->net_name); 20362306a36Sopenharmony_ci if (ret < 0) 20462306a36Sopenharmony_ci goto nlmsg_fail; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_SHARE_NAME, swnreg->share_name); 20762306a36Sopenharmony_ci if (ret < 0) 20862306a36Sopenharmony_ci goto nlmsg_fail; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci ret = nla_put(skb, CIFS_GENL_ATTR_SWN_IP, sizeof(struct sockaddr_storage), 21162306a36Sopenharmony_ci &swnreg->tcon->ses->server->dstaddr); 21262306a36Sopenharmony_ci if (ret < 0) 21362306a36Sopenharmony_ci goto nlmsg_fail; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci if (swnreg->net_name_notify) { 21662306a36Sopenharmony_ci ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_NET_NAME_NOTIFY); 21762306a36Sopenharmony_ci if (ret < 0) 21862306a36Sopenharmony_ci goto nlmsg_fail; 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci if (swnreg->share_name_notify) { 22262306a36Sopenharmony_ci ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_SHARE_NAME_NOTIFY); 22362306a36Sopenharmony_ci if (ret < 0) 22462306a36Sopenharmony_ci goto nlmsg_fail; 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci if (swnreg->ip_notify) { 22862306a36Sopenharmony_ci ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_IP_NOTIFY); 22962306a36Sopenharmony_ci if (ret < 0) 23062306a36Sopenharmony_ci goto nlmsg_fail; 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci genlmsg_end(skb, hdr); 23462306a36Sopenharmony_ci genlmsg_multicast(&cifs_genl_family, skb, 0, CIFS_GENL_MCGRP_SWN, GFP_ATOMIC); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci cifs_dbg(FYI, "%s: Message to unregister for network name %s with id %d sent\n", __func__, 23762306a36Sopenharmony_ci swnreg->net_name, swnreg->id); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci return 0; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cinlmsg_fail: 24262306a36Sopenharmony_ci genlmsg_cancel(skb, hdr); 24362306a36Sopenharmony_ci nlmsg_free(skb); 24462306a36Sopenharmony_ci return ret; 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci/* 24862306a36Sopenharmony_ci * Try to find a matching registration for the tcon's server name and share name. 24962306a36Sopenharmony_ci * Calls to this function must be protected by cifs_swnreg_idr_mutex. 25062306a36Sopenharmony_ci * TODO Try to avoid memory allocations 25162306a36Sopenharmony_ci */ 25262306a36Sopenharmony_cistatic struct cifs_swn_reg *cifs_find_swn_reg(struct cifs_tcon *tcon) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci struct cifs_swn_reg *swnreg; 25562306a36Sopenharmony_ci int id; 25662306a36Sopenharmony_ci const char *share_name; 25762306a36Sopenharmony_ci const char *net_name; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci net_name = extract_hostname(tcon->tree_name); 26062306a36Sopenharmony_ci if (IS_ERR(net_name)) { 26162306a36Sopenharmony_ci int ret; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci ret = PTR_ERR(net_name); 26462306a36Sopenharmony_ci cifs_dbg(VFS, "%s: failed to extract host name from target '%s': %d\n", 26562306a36Sopenharmony_ci __func__, tcon->tree_name, ret); 26662306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci share_name = extract_sharename(tcon->tree_name); 27062306a36Sopenharmony_ci if (IS_ERR(share_name)) { 27162306a36Sopenharmony_ci int ret; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci ret = PTR_ERR(share_name); 27462306a36Sopenharmony_ci cifs_dbg(VFS, "%s: failed to extract share name from target '%s': %d\n", 27562306a36Sopenharmony_ci __func__, tcon->tree_name, ret); 27662306a36Sopenharmony_ci kfree(net_name); 27762306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci idr_for_each_entry(&cifs_swnreg_idr, swnreg, id) { 28162306a36Sopenharmony_ci if (strcasecmp(swnreg->net_name, net_name) != 0 28262306a36Sopenharmony_ci || strcasecmp(swnreg->share_name, share_name) != 0) { 28362306a36Sopenharmony_ci continue; 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci cifs_dbg(FYI, "Existing swn registration for %s:%s found\n", swnreg->net_name, 28762306a36Sopenharmony_ci swnreg->share_name); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci kfree(net_name); 29062306a36Sopenharmony_ci kfree(share_name); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci return swnreg; 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci kfree(net_name); 29662306a36Sopenharmony_ci kfree(share_name); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci return ERR_PTR(-EEXIST); 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci/* 30262306a36Sopenharmony_ci * Get a registration for the tcon's server and share name, allocating a new one if it does not 30362306a36Sopenharmony_ci * exists 30462306a36Sopenharmony_ci */ 30562306a36Sopenharmony_cistatic struct cifs_swn_reg *cifs_get_swn_reg(struct cifs_tcon *tcon) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci struct cifs_swn_reg *reg = NULL; 30862306a36Sopenharmony_ci int ret; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci mutex_lock(&cifs_swnreg_idr_mutex); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci /* Check if we are already registered for this network and share names */ 31362306a36Sopenharmony_ci reg = cifs_find_swn_reg(tcon); 31462306a36Sopenharmony_ci if (!IS_ERR(reg)) { 31562306a36Sopenharmony_ci kref_get(®->ref_count); 31662306a36Sopenharmony_ci mutex_unlock(&cifs_swnreg_idr_mutex); 31762306a36Sopenharmony_ci return reg; 31862306a36Sopenharmony_ci } else if (PTR_ERR(reg) != -EEXIST) { 31962306a36Sopenharmony_ci mutex_unlock(&cifs_swnreg_idr_mutex); 32062306a36Sopenharmony_ci return reg; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci reg = kmalloc(sizeof(struct cifs_swn_reg), GFP_ATOMIC); 32462306a36Sopenharmony_ci if (reg == NULL) { 32562306a36Sopenharmony_ci mutex_unlock(&cifs_swnreg_idr_mutex); 32662306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci kref_init(®->ref_count); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci reg->id = idr_alloc(&cifs_swnreg_idr, reg, 1, 0, GFP_ATOMIC); 33262306a36Sopenharmony_ci if (reg->id < 0) { 33362306a36Sopenharmony_ci cifs_dbg(FYI, "%s: failed to allocate registration id\n", __func__); 33462306a36Sopenharmony_ci ret = reg->id; 33562306a36Sopenharmony_ci goto fail; 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci reg->net_name = extract_hostname(tcon->tree_name); 33962306a36Sopenharmony_ci if (IS_ERR(reg->net_name)) { 34062306a36Sopenharmony_ci ret = PTR_ERR(reg->net_name); 34162306a36Sopenharmony_ci cifs_dbg(VFS, "%s: failed to extract host name from target: %d\n", __func__, ret); 34262306a36Sopenharmony_ci goto fail_idr; 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci reg->share_name = extract_sharename(tcon->tree_name); 34662306a36Sopenharmony_ci if (IS_ERR(reg->share_name)) { 34762306a36Sopenharmony_ci ret = PTR_ERR(reg->share_name); 34862306a36Sopenharmony_ci cifs_dbg(VFS, "%s: failed to extract share name from target: %d\n", __func__, ret); 34962306a36Sopenharmony_ci goto fail_net_name; 35062306a36Sopenharmony_ci } 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci reg->net_name_notify = true; 35362306a36Sopenharmony_ci reg->share_name_notify = true; 35462306a36Sopenharmony_ci reg->ip_notify = (tcon->capabilities & SMB2_SHARE_CAP_SCALEOUT); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci reg->tcon = tcon; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci mutex_unlock(&cifs_swnreg_idr_mutex); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci return reg; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_cifail_net_name: 36362306a36Sopenharmony_ci kfree(reg->net_name); 36462306a36Sopenharmony_cifail_idr: 36562306a36Sopenharmony_ci idr_remove(&cifs_swnreg_idr, reg->id); 36662306a36Sopenharmony_cifail: 36762306a36Sopenharmony_ci kfree(reg); 36862306a36Sopenharmony_ci mutex_unlock(&cifs_swnreg_idr_mutex); 36962306a36Sopenharmony_ci return ERR_PTR(ret); 37062306a36Sopenharmony_ci} 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_cistatic void cifs_swn_reg_release(struct kref *ref) 37362306a36Sopenharmony_ci{ 37462306a36Sopenharmony_ci struct cifs_swn_reg *swnreg = container_of(ref, struct cifs_swn_reg, ref_count); 37562306a36Sopenharmony_ci int ret; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci ret = cifs_swn_send_unregister_message(swnreg); 37862306a36Sopenharmony_ci if (ret < 0) 37962306a36Sopenharmony_ci cifs_dbg(VFS, "%s: Failed to send unregister message: %d\n", __func__, ret); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci idr_remove(&cifs_swnreg_idr, swnreg->id); 38262306a36Sopenharmony_ci kfree(swnreg->net_name); 38362306a36Sopenharmony_ci kfree(swnreg->share_name); 38462306a36Sopenharmony_ci kfree(swnreg); 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_cistatic void cifs_put_swn_reg(struct cifs_swn_reg *swnreg) 38862306a36Sopenharmony_ci{ 38962306a36Sopenharmony_ci mutex_lock(&cifs_swnreg_idr_mutex); 39062306a36Sopenharmony_ci kref_put(&swnreg->ref_count, cifs_swn_reg_release); 39162306a36Sopenharmony_ci mutex_unlock(&cifs_swnreg_idr_mutex); 39262306a36Sopenharmony_ci} 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_cistatic int cifs_swn_resource_state_changed(struct cifs_swn_reg *swnreg, const char *name, int state) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci switch (state) { 39762306a36Sopenharmony_ci case CIFS_SWN_RESOURCE_STATE_UNAVAILABLE: 39862306a36Sopenharmony_ci cifs_dbg(FYI, "%s: resource name '%s' become unavailable\n", __func__, name); 39962306a36Sopenharmony_ci cifs_signal_cifsd_for_reconnect(swnreg->tcon->ses->server, true); 40062306a36Sopenharmony_ci break; 40162306a36Sopenharmony_ci case CIFS_SWN_RESOURCE_STATE_AVAILABLE: 40262306a36Sopenharmony_ci cifs_dbg(FYI, "%s: resource name '%s' become available\n", __func__, name); 40362306a36Sopenharmony_ci cifs_signal_cifsd_for_reconnect(swnreg->tcon->ses->server, true); 40462306a36Sopenharmony_ci break; 40562306a36Sopenharmony_ci case CIFS_SWN_RESOURCE_STATE_UNKNOWN: 40662306a36Sopenharmony_ci cifs_dbg(FYI, "%s: resource name '%s' changed to unknown state\n", __func__, name); 40762306a36Sopenharmony_ci break; 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci return 0; 41062306a36Sopenharmony_ci} 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_cistatic bool cifs_sockaddr_equal(struct sockaddr_storage *addr1, struct sockaddr_storage *addr2) 41362306a36Sopenharmony_ci{ 41462306a36Sopenharmony_ci if (addr1->ss_family != addr2->ss_family) 41562306a36Sopenharmony_ci return false; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci if (addr1->ss_family == AF_INET) { 41862306a36Sopenharmony_ci return (memcmp(&((const struct sockaddr_in *)addr1)->sin_addr, 41962306a36Sopenharmony_ci &((const struct sockaddr_in *)addr2)->sin_addr, 42062306a36Sopenharmony_ci sizeof(struct in_addr)) == 0); 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci if (addr1->ss_family == AF_INET6) { 42462306a36Sopenharmony_ci return (memcmp(&((const struct sockaddr_in6 *)addr1)->sin6_addr, 42562306a36Sopenharmony_ci &((const struct sockaddr_in6 *)addr2)->sin6_addr, 42662306a36Sopenharmony_ci sizeof(struct in6_addr)) == 0); 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci return false; 43062306a36Sopenharmony_ci} 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_cistatic int cifs_swn_store_swn_addr(const struct sockaddr_storage *new, 43362306a36Sopenharmony_ci const struct sockaddr_storage *old, 43462306a36Sopenharmony_ci struct sockaddr_storage *dst) 43562306a36Sopenharmony_ci{ 43662306a36Sopenharmony_ci __be16 port = cpu_to_be16(CIFS_PORT); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci if (old->ss_family == AF_INET) { 43962306a36Sopenharmony_ci struct sockaddr_in *ipv4 = (struct sockaddr_in *)old; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci port = ipv4->sin_port; 44262306a36Sopenharmony_ci } else if (old->ss_family == AF_INET6) { 44362306a36Sopenharmony_ci struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)old; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci port = ipv6->sin6_port; 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci if (new->ss_family == AF_INET) { 44962306a36Sopenharmony_ci struct sockaddr_in *ipv4 = (struct sockaddr_in *)new; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci ipv4->sin_port = port; 45262306a36Sopenharmony_ci } else if (new->ss_family == AF_INET6) { 45362306a36Sopenharmony_ci struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)new; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci ipv6->sin6_port = port; 45662306a36Sopenharmony_ci } 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci *dst = *new; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci return 0; 46162306a36Sopenharmony_ci} 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_cistatic int cifs_swn_reconnect(struct cifs_tcon *tcon, struct sockaddr_storage *addr) 46462306a36Sopenharmony_ci{ 46562306a36Sopenharmony_ci int ret = 0; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci /* Store the reconnect address */ 46862306a36Sopenharmony_ci cifs_server_lock(tcon->ses->server); 46962306a36Sopenharmony_ci if (cifs_sockaddr_equal(&tcon->ses->server->dstaddr, addr)) 47062306a36Sopenharmony_ci goto unlock; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci ret = cifs_swn_store_swn_addr(addr, &tcon->ses->server->dstaddr, 47362306a36Sopenharmony_ci &tcon->ses->server->swn_dstaddr); 47462306a36Sopenharmony_ci if (ret < 0) { 47562306a36Sopenharmony_ci cifs_dbg(VFS, "%s: failed to store address: %d\n", __func__, ret); 47662306a36Sopenharmony_ci goto unlock; 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci tcon->ses->server->use_swn_dstaddr = true; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci /* 48162306a36Sopenharmony_ci * Unregister to stop receiving notifications for the old IP address. 48262306a36Sopenharmony_ci */ 48362306a36Sopenharmony_ci ret = cifs_swn_unregister(tcon); 48462306a36Sopenharmony_ci if (ret < 0) { 48562306a36Sopenharmony_ci cifs_dbg(VFS, "%s: Failed to unregister for witness notifications: %d\n", 48662306a36Sopenharmony_ci __func__, ret); 48762306a36Sopenharmony_ci goto unlock; 48862306a36Sopenharmony_ci } 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci /* 49162306a36Sopenharmony_ci * And register to receive notifications for the new IP address now that we have 49262306a36Sopenharmony_ci * stored the new address. 49362306a36Sopenharmony_ci */ 49462306a36Sopenharmony_ci ret = cifs_swn_register(tcon); 49562306a36Sopenharmony_ci if (ret < 0) { 49662306a36Sopenharmony_ci cifs_dbg(VFS, "%s: Failed to register for witness notifications: %d\n", 49762306a36Sopenharmony_ci __func__, ret); 49862306a36Sopenharmony_ci goto unlock; 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci cifs_signal_cifsd_for_reconnect(tcon->ses->server, false); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ciunlock: 50462306a36Sopenharmony_ci cifs_server_unlock(tcon->ses->server); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci return ret; 50762306a36Sopenharmony_ci} 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_cistatic int cifs_swn_client_move(struct cifs_swn_reg *swnreg, struct sockaddr_storage *addr) 51062306a36Sopenharmony_ci{ 51162306a36Sopenharmony_ci struct sockaddr_in *ipv4 = (struct sockaddr_in *)addr; 51262306a36Sopenharmony_ci struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)addr; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci if (addr->ss_family == AF_INET) 51562306a36Sopenharmony_ci cifs_dbg(FYI, "%s: move to %pI4\n", __func__, &ipv4->sin_addr); 51662306a36Sopenharmony_ci else if (addr->ss_family == AF_INET6) 51762306a36Sopenharmony_ci cifs_dbg(FYI, "%s: move to %pI6\n", __func__, &ipv6->sin6_addr); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci return cifs_swn_reconnect(swnreg->tcon, addr); 52062306a36Sopenharmony_ci} 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ciint cifs_swn_notify(struct sk_buff *skb, struct genl_info *info) 52362306a36Sopenharmony_ci{ 52462306a36Sopenharmony_ci struct cifs_swn_reg *swnreg; 52562306a36Sopenharmony_ci char name[256]; 52662306a36Sopenharmony_ci int type; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci if (info->attrs[CIFS_GENL_ATTR_SWN_REGISTRATION_ID]) { 52962306a36Sopenharmony_ci int swnreg_id; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci swnreg_id = nla_get_u32(info->attrs[CIFS_GENL_ATTR_SWN_REGISTRATION_ID]); 53262306a36Sopenharmony_ci mutex_lock(&cifs_swnreg_idr_mutex); 53362306a36Sopenharmony_ci swnreg = idr_find(&cifs_swnreg_idr, swnreg_id); 53462306a36Sopenharmony_ci mutex_unlock(&cifs_swnreg_idr_mutex); 53562306a36Sopenharmony_ci if (swnreg == NULL) { 53662306a36Sopenharmony_ci cifs_dbg(FYI, "%s: registration id %d not found\n", __func__, swnreg_id); 53762306a36Sopenharmony_ci return -EINVAL; 53862306a36Sopenharmony_ci } 53962306a36Sopenharmony_ci } else { 54062306a36Sopenharmony_ci cifs_dbg(FYI, "%s: missing registration id attribute\n", __func__); 54162306a36Sopenharmony_ci return -EINVAL; 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci if (info->attrs[CIFS_GENL_ATTR_SWN_NOTIFICATION_TYPE]) { 54562306a36Sopenharmony_ci type = nla_get_u32(info->attrs[CIFS_GENL_ATTR_SWN_NOTIFICATION_TYPE]); 54662306a36Sopenharmony_ci } else { 54762306a36Sopenharmony_ci cifs_dbg(FYI, "%s: missing notification type attribute\n", __func__); 54862306a36Sopenharmony_ci return -EINVAL; 54962306a36Sopenharmony_ci } 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci switch (type) { 55262306a36Sopenharmony_ci case CIFS_SWN_NOTIFICATION_RESOURCE_CHANGE: { 55362306a36Sopenharmony_ci int state; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci if (info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_NAME]) { 55662306a36Sopenharmony_ci nla_strscpy(name, info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_NAME], 55762306a36Sopenharmony_ci sizeof(name)); 55862306a36Sopenharmony_ci } else { 55962306a36Sopenharmony_ci cifs_dbg(FYI, "%s: missing resource name attribute\n", __func__); 56062306a36Sopenharmony_ci return -EINVAL; 56162306a36Sopenharmony_ci } 56262306a36Sopenharmony_ci if (info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_STATE]) { 56362306a36Sopenharmony_ci state = nla_get_u32(info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_STATE]); 56462306a36Sopenharmony_ci } else { 56562306a36Sopenharmony_ci cifs_dbg(FYI, "%s: missing resource state attribute\n", __func__); 56662306a36Sopenharmony_ci return -EINVAL; 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci return cifs_swn_resource_state_changed(swnreg, name, state); 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci case CIFS_SWN_NOTIFICATION_CLIENT_MOVE: { 57162306a36Sopenharmony_ci struct sockaddr_storage addr; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci if (info->attrs[CIFS_GENL_ATTR_SWN_IP]) { 57462306a36Sopenharmony_ci nla_memcpy(&addr, info->attrs[CIFS_GENL_ATTR_SWN_IP], sizeof(addr)); 57562306a36Sopenharmony_ci } else { 57662306a36Sopenharmony_ci cifs_dbg(FYI, "%s: missing IP address attribute\n", __func__); 57762306a36Sopenharmony_ci return -EINVAL; 57862306a36Sopenharmony_ci } 57962306a36Sopenharmony_ci return cifs_swn_client_move(swnreg, &addr); 58062306a36Sopenharmony_ci } 58162306a36Sopenharmony_ci default: 58262306a36Sopenharmony_ci cifs_dbg(FYI, "%s: unknown notification type %d\n", __func__, type); 58362306a36Sopenharmony_ci break; 58462306a36Sopenharmony_ci } 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci return 0; 58762306a36Sopenharmony_ci} 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ciint cifs_swn_register(struct cifs_tcon *tcon) 59062306a36Sopenharmony_ci{ 59162306a36Sopenharmony_ci struct cifs_swn_reg *swnreg; 59262306a36Sopenharmony_ci int ret; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci swnreg = cifs_get_swn_reg(tcon); 59562306a36Sopenharmony_ci if (IS_ERR(swnreg)) 59662306a36Sopenharmony_ci return PTR_ERR(swnreg); 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci ret = cifs_swn_send_register_message(swnreg); 59962306a36Sopenharmony_ci if (ret < 0) { 60062306a36Sopenharmony_ci cifs_dbg(VFS, "%s: Failed to send swn register message: %d\n", __func__, ret); 60162306a36Sopenharmony_ci /* Do not put the swnreg or return error, the echo task will retry */ 60262306a36Sopenharmony_ci } 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci return 0; 60562306a36Sopenharmony_ci} 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ciint cifs_swn_unregister(struct cifs_tcon *tcon) 60862306a36Sopenharmony_ci{ 60962306a36Sopenharmony_ci struct cifs_swn_reg *swnreg; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci mutex_lock(&cifs_swnreg_idr_mutex); 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci swnreg = cifs_find_swn_reg(tcon); 61462306a36Sopenharmony_ci if (IS_ERR(swnreg)) { 61562306a36Sopenharmony_ci mutex_unlock(&cifs_swnreg_idr_mutex); 61662306a36Sopenharmony_ci return PTR_ERR(swnreg); 61762306a36Sopenharmony_ci } 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci mutex_unlock(&cifs_swnreg_idr_mutex); 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci cifs_put_swn_reg(swnreg); 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci return 0; 62462306a36Sopenharmony_ci} 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_civoid cifs_swn_dump(struct seq_file *m) 62762306a36Sopenharmony_ci{ 62862306a36Sopenharmony_ci struct cifs_swn_reg *swnreg; 62962306a36Sopenharmony_ci struct sockaddr_in *sa; 63062306a36Sopenharmony_ci struct sockaddr_in6 *sa6; 63162306a36Sopenharmony_ci int id; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci seq_puts(m, "Witness registrations:"); 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci mutex_lock(&cifs_swnreg_idr_mutex); 63662306a36Sopenharmony_ci idr_for_each_entry(&cifs_swnreg_idr, swnreg, id) { 63762306a36Sopenharmony_ci seq_printf(m, "\nId: %u Refs: %u Network name: '%s'%s Share name: '%s'%s Ip address: ", 63862306a36Sopenharmony_ci id, kref_read(&swnreg->ref_count), 63962306a36Sopenharmony_ci swnreg->net_name, swnreg->net_name_notify ? "(y)" : "(n)", 64062306a36Sopenharmony_ci swnreg->share_name, swnreg->share_name_notify ? "(y)" : "(n)"); 64162306a36Sopenharmony_ci switch (swnreg->tcon->ses->server->dstaddr.ss_family) { 64262306a36Sopenharmony_ci case AF_INET: 64362306a36Sopenharmony_ci sa = (struct sockaddr_in *) &swnreg->tcon->ses->server->dstaddr; 64462306a36Sopenharmony_ci seq_printf(m, "%pI4", &sa->sin_addr.s_addr); 64562306a36Sopenharmony_ci break; 64662306a36Sopenharmony_ci case AF_INET6: 64762306a36Sopenharmony_ci sa6 = (struct sockaddr_in6 *) &swnreg->tcon->ses->server->dstaddr; 64862306a36Sopenharmony_ci seq_printf(m, "%pI6", &sa6->sin6_addr.s6_addr); 64962306a36Sopenharmony_ci if (sa6->sin6_scope_id) 65062306a36Sopenharmony_ci seq_printf(m, "%%%u", sa6->sin6_scope_id); 65162306a36Sopenharmony_ci break; 65262306a36Sopenharmony_ci default: 65362306a36Sopenharmony_ci seq_puts(m, "(unknown)"); 65462306a36Sopenharmony_ci } 65562306a36Sopenharmony_ci seq_printf(m, "%s", swnreg->ip_notify ? "(y)" : "(n)"); 65662306a36Sopenharmony_ci } 65762306a36Sopenharmony_ci mutex_unlock(&cifs_swnreg_idr_mutex); 65862306a36Sopenharmony_ci seq_puts(m, "\n"); 65962306a36Sopenharmony_ci} 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_civoid cifs_swn_check(void) 66262306a36Sopenharmony_ci{ 66362306a36Sopenharmony_ci struct cifs_swn_reg *swnreg; 66462306a36Sopenharmony_ci int id; 66562306a36Sopenharmony_ci int ret; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci mutex_lock(&cifs_swnreg_idr_mutex); 66862306a36Sopenharmony_ci idr_for_each_entry(&cifs_swnreg_idr, swnreg, id) { 66962306a36Sopenharmony_ci ret = cifs_swn_send_register_message(swnreg); 67062306a36Sopenharmony_ci if (ret < 0) 67162306a36Sopenharmony_ci cifs_dbg(FYI, "%s: Failed to send register message: %d\n", __func__, ret); 67262306a36Sopenharmony_ci } 67362306a36Sopenharmony_ci mutex_unlock(&cifs_swnreg_idr_mutex); 67462306a36Sopenharmony_ci} 675