Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

cifs: Send witness register and unregister commands to userspace daemon

+ Define the generic netlink family commands and message attributes to
communicate with the userspace daemon

+ The register and unregister commands are sent when connecting or
disconnecting a tree. The witness registration keeps a pointer to
the tcon and has the same lifetime.

+ Each registration has an id allocated by an IDR. This id is sent to the
userspace daemon in the register command, and will be included in the
notification messages from the userspace daemon to retrieve from the
IDR the matching registration.

+ The authentication information is bundled in the register message.
If kerberos is used the message just carries a flag.

Signed-off-by: Samuel Cabrero <scabrero@suse.de>
Reviewed-by: Aurelien Aptel <aaptel@suse.com>
Signed-off-by: Steve French <stfrench@microsoft.com>

authored by

Samuel Cabrero and committed by
Steve French
bf80e5d4 e68f4a7b

+489 -3
+1 -1
fs/cifs/Makefile
··· 18 18 19 19 cifs-$(CONFIG_CIFS_DFS_UPCALL) += dns_resolve.o cifs_dfs_ref.o dfs_cache.o 20 20 21 - cifs-$(CONFIG_CIFS_SWN_UPCALL) += netlink.o 21 + cifs-$(CONFIG_CIFS_SWN_UPCALL) += netlink.o cifs_swn.o 22 22 23 23 cifs-$(CONFIG_CIFS_FSCACHE) += fscache.o cache.o 24 24
+421
fs/cifs/cifs_swn.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Witness Service client for CIFS 4 + * 5 + * Copyright (c) 2020 Samuel Cabrero <scabrero@suse.de> 6 + */ 7 + 8 + #include <linux/kref.h> 9 + #include <net/genetlink.h> 10 + #include <uapi/linux/cifs/cifs_netlink.h> 11 + 12 + #include "cifs_swn.h" 13 + #include "cifsglob.h" 14 + #include "cifsproto.h" 15 + #include "fscache.h" 16 + #include "cifs_debug.h" 17 + #include "netlink.h" 18 + 19 + static DEFINE_IDR(cifs_swnreg_idr); 20 + static DEFINE_MUTEX(cifs_swnreg_idr_mutex); 21 + 22 + struct cifs_swn_reg { 23 + int id; 24 + struct kref ref_count; 25 + 26 + const char *net_name; 27 + const char *share_name; 28 + bool net_name_notify; 29 + bool share_name_notify; 30 + bool ip_notify; 31 + 32 + struct cifs_tcon *tcon; 33 + }; 34 + 35 + static int cifs_swn_auth_info_krb(struct cifs_tcon *tcon, struct sk_buff *skb) 36 + { 37 + int ret; 38 + 39 + ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_KRB_AUTH); 40 + if (ret < 0) 41 + return ret; 42 + 43 + return 0; 44 + } 45 + 46 + static int cifs_swn_auth_info_ntlm(struct cifs_tcon *tcon, struct sk_buff *skb) 47 + { 48 + int ret; 49 + 50 + if (tcon->ses->user_name != NULL) { 51 + ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_USER_NAME, tcon->ses->user_name); 52 + if (ret < 0) 53 + return ret; 54 + } 55 + 56 + if (tcon->ses->password != NULL) { 57 + ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_PASSWORD, tcon->ses->password); 58 + if (ret < 0) 59 + return ret; 60 + } 61 + 62 + if (tcon->ses->domainName != NULL) { 63 + ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_DOMAIN_NAME, tcon->ses->domainName); 64 + if (ret < 0) 65 + return ret; 66 + } 67 + 68 + return 0; 69 + } 70 + 71 + /* 72 + * Sends a register message to the userspace daemon based on the registration. 73 + * The authentication information to connect to the witness service is bundled 74 + * into the message. 75 + */ 76 + static int cifs_swn_send_register_message(struct cifs_swn_reg *swnreg) 77 + { 78 + struct sk_buff *skb; 79 + struct genlmsghdr *hdr; 80 + enum securityEnum authtype; 81 + int ret; 82 + 83 + skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 84 + if (skb == NULL) { 85 + ret = -ENOMEM; 86 + goto fail; 87 + } 88 + 89 + hdr = genlmsg_put(skb, 0, 0, &cifs_genl_family, 0, CIFS_GENL_CMD_SWN_REGISTER); 90 + if (hdr == NULL) { 91 + ret = -ENOMEM; 92 + goto nlmsg_fail; 93 + } 94 + 95 + ret = nla_put_u32(skb, CIFS_GENL_ATTR_SWN_REGISTRATION_ID, swnreg->id); 96 + if (ret < 0) 97 + goto nlmsg_fail; 98 + 99 + ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_NET_NAME, swnreg->net_name); 100 + if (ret < 0) 101 + goto nlmsg_fail; 102 + 103 + ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_SHARE_NAME, swnreg->share_name); 104 + if (ret < 0) 105 + goto nlmsg_fail; 106 + 107 + ret = nla_put(skb, CIFS_GENL_ATTR_SWN_IP, sizeof(struct sockaddr_storage), 108 + &swnreg->tcon->ses->server->dstaddr); 109 + if (ret < 0) 110 + goto nlmsg_fail; 111 + 112 + if (swnreg->net_name_notify) { 113 + ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_NET_NAME_NOTIFY); 114 + if (ret < 0) 115 + goto nlmsg_fail; 116 + } 117 + 118 + if (swnreg->share_name_notify) { 119 + ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_SHARE_NAME_NOTIFY); 120 + if (ret < 0) 121 + goto nlmsg_fail; 122 + } 123 + 124 + if (swnreg->ip_notify) { 125 + ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_IP_NOTIFY); 126 + if (ret < 0) 127 + goto nlmsg_fail; 128 + } 129 + 130 + authtype = cifs_select_sectype(swnreg->tcon->ses->server, swnreg->tcon->ses->sectype); 131 + switch (authtype) { 132 + case Kerberos: 133 + ret = cifs_swn_auth_info_krb(swnreg->tcon, skb); 134 + if (ret < 0) { 135 + cifs_dbg(VFS, "%s: Failed to get kerberos auth info: %d\n", __func__, ret); 136 + goto nlmsg_fail; 137 + } 138 + break; 139 + case LANMAN: 140 + case NTLM: 141 + case NTLMv2: 142 + case RawNTLMSSP: 143 + ret = cifs_swn_auth_info_ntlm(swnreg->tcon, skb); 144 + if (ret < 0) { 145 + cifs_dbg(VFS, "%s: Failed to get NTLM auth info: %d\n", __func__, ret); 146 + goto nlmsg_fail; 147 + } 148 + break; 149 + default: 150 + cifs_dbg(VFS, "%s: secType %d not supported!\n", __func__, authtype); 151 + ret = -EINVAL; 152 + goto nlmsg_fail; 153 + } 154 + 155 + genlmsg_end(skb, hdr); 156 + genlmsg_multicast(&cifs_genl_family, skb, 0, CIFS_GENL_MCGRP_SWN, GFP_ATOMIC); 157 + 158 + cifs_dbg(FYI, "%s: Message to register for network name %s with id %d sent\n", __func__, 159 + swnreg->net_name, swnreg->id); 160 + 161 + return 0; 162 + 163 + nlmsg_fail: 164 + genlmsg_cancel(skb, hdr); 165 + nlmsg_free(skb); 166 + fail: 167 + return ret; 168 + } 169 + 170 + /* 171 + * Sends an uregister message to the userspace daemon based on the registration 172 + */ 173 + static int cifs_swn_send_unregister_message(struct cifs_swn_reg *swnreg) 174 + { 175 + struct sk_buff *skb; 176 + struct genlmsghdr *hdr; 177 + int ret; 178 + 179 + skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 180 + if (skb == NULL) 181 + return -ENOMEM; 182 + 183 + hdr = genlmsg_put(skb, 0, 0, &cifs_genl_family, 0, CIFS_GENL_CMD_SWN_UNREGISTER); 184 + if (hdr == NULL) { 185 + ret = -ENOMEM; 186 + goto nlmsg_fail; 187 + } 188 + 189 + ret = nla_put_u32(skb, CIFS_GENL_ATTR_SWN_REGISTRATION_ID, swnreg->id); 190 + if (ret < 0) 191 + goto nlmsg_fail; 192 + 193 + ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_NET_NAME, swnreg->net_name); 194 + if (ret < 0) 195 + goto nlmsg_fail; 196 + 197 + ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_SHARE_NAME, swnreg->share_name); 198 + if (ret < 0) 199 + goto nlmsg_fail; 200 + 201 + ret = nla_put(skb, CIFS_GENL_ATTR_SWN_IP, sizeof(struct sockaddr_storage), 202 + &swnreg->tcon->ses->server->dstaddr); 203 + if (ret < 0) 204 + goto nlmsg_fail; 205 + 206 + if (swnreg->net_name_notify) { 207 + ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_NET_NAME_NOTIFY); 208 + if (ret < 0) 209 + goto nlmsg_fail; 210 + } 211 + 212 + if (swnreg->share_name_notify) { 213 + ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_SHARE_NAME_NOTIFY); 214 + if (ret < 0) 215 + goto nlmsg_fail; 216 + } 217 + 218 + if (swnreg->ip_notify) { 219 + ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_IP_NOTIFY); 220 + if (ret < 0) 221 + goto nlmsg_fail; 222 + } 223 + 224 + genlmsg_end(skb, hdr); 225 + genlmsg_multicast(&cifs_genl_family, skb, 0, CIFS_GENL_MCGRP_SWN, GFP_ATOMIC); 226 + 227 + cifs_dbg(FYI, "%s: Message to unregister for network name %s with id %d sent\n", __func__, 228 + swnreg->net_name, swnreg->id); 229 + 230 + return 0; 231 + 232 + nlmsg_fail: 233 + genlmsg_cancel(skb, hdr); 234 + nlmsg_free(skb); 235 + return ret; 236 + } 237 + 238 + /* 239 + * Try to find a matching registration for the tcon's server name and share name. 240 + * Calls to this funciton must be protected by cifs_swnreg_idr_mutex. 241 + * TODO Try to avoid memory allocations 242 + */ 243 + static struct cifs_swn_reg *cifs_find_swn_reg(struct cifs_tcon *tcon) 244 + { 245 + struct cifs_swn_reg *swnreg; 246 + int id; 247 + const char *share_name; 248 + const char *net_name; 249 + 250 + net_name = extract_hostname(tcon->treeName); 251 + if (IS_ERR_OR_NULL(net_name)) { 252 + int ret; 253 + 254 + ret = PTR_ERR(net_name); 255 + cifs_dbg(VFS, "%s: failed to extract host name from target '%s': %d\n", 256 + __func__, tcon->treeName, ret); 257 + return NULL; 258 + } 259 + 260 + share_name = extract_sharename(tcon->treeName); 261 + if (IS_ERR_OR_NULL(share_name)) { 262 + int ret; 263 + 264 + ret = PTR_ERR(net_name); 265 + cifs_dbg(VFS, "%s: failed to extract share name from target '%s': %d\n", 266 + __func__, tcon->treeName, ret); 267 + kfree(net_name); 268 + return NULL; 269 + } 270 + 271 + idr_for_each_entry(&cifs_swnreg_idr, swnreg, id) { 272 + if (strcasecmp(swnreg->net_name, net_name) != 0 273 + || strcasecmp(swnreg->share_name, share_name) != 0) { 274 + continue; 275 + } 276 + 277 + mutex_unlock(&cifs_swnreg_idr_mutex); 278 + 279 + cifs_dbg(FYI, "Existing swn registration for %s:%s found\n", swnreg->net_name, 280 + swnreg->share_name); 281 + 282 + kfree(net_name); 283 + kfree(share_name); 284 + 285 + return swnreg; 286 + } 287 + 288 + kfree(net_name); 289 + kfree(share_name); 290 + 291 + return NULL; 292 + } 293 + 294 + /* 295 + * Get a registration for the tcon's server and share name, allocating a new one if it does not 296 + * exists 297 + */ 298 + static struct cifs_swn_reg *cifs_get_swn_reg(struct cifs_tcon *tcon) 299 + { 300 + struct cifs_swn_reg *reg = NULL; 301 + int ret; 302 + 303 + mutex_lock(&cifs_swnreg_idr_mutex); 304 + 305 + /* Check if we are already registered for this network and share names */ 306 + reg = cifs_find_swn_reg(tcon); 307 + if (IS_ERR(reg)) { 308 + return reg; 309 + } else if (reg != NULL) { 310 + kref_get(&reg->ref_count); 311 + mutex_unlock(&cifs_swnreg_idr_mutex); 312 + return reg; 313 + } 314 + 315 + reg = kmalloc(sizeof(struct cifs_swn_reg), GFP_ATOMIC); 316 + if (reg == NULL) { 317 + mutex_unlock(&cifs_swnreg_idr_mutex); 318 + return ERR_PTR(-ENOMEM); 319 + } 320 + 321 + kref_init(&reg->ref_count); 322 + 323 + reg->id = idr_alloc(&cifs_swnreg_idr, reg, 1, 0, GFP_ATOMIC); 324 + if (reg->id < 0) { 325 + cifs_dbg(FYI, "%s: failed to allocate registration id\n", __func__); 326 + ret = reg->id; 327 + goto fail; 328 + } 329 + 330 + reg->net_name = extract_hostname(tcon->treeName); 331 + if (IS_ERR(reg->net_name)) { 332 + ret = PTR_ERR(reg->net_name); 333 + cifs_dbg(VFS, "%s: failed to extract host name from target: %d\n", __func__, ret); 334 + goto fail_idr; 335 + } 336 + 337 + reg->share_name = extract_sharename(tcon->treeName); 338 + if (IS_ERR(reg->share_name)) { 339 + ret = PTR_ERR(reg->share_name); 340 + cifs_dbg(VFS, "%s: failed to extract share name from target: %d\n", __func__, ret); 341 + goto fail_net_name; 342 + } 343 + 344 + reg->net_name_notify = true; 345 + reg->share_name_notify = true; 346 + reg->ip_notify = (tcon->capabilities & SMB2_SHARE_CAP_SCALEOUT); 347 + 348 + reg->tcon = tcon; 349 + 350 + mutex_unlock(&cifs_swnreg_idr_mutex); 351 + 352 + return reg; 353 + 354 + fail_net_name: 355 + kfree(reg->net_name); 356 + fail_idr: 357 + idr_remove(&cifs_swnreg_idr, reg->id); 358 + fail: 359 + kfree(reg); 360 + mutex_unlock(&cifs_swnreg_idr_mutex); 361 + return ERR_PTR(ret); 362 + } 363 + 364 + static void cifs_swn_reg_release(struct kref *ref) 365 + { 366 + struct cifs_swn_reg *swnreg = container_of(ref, struct cifs_swn_reg, ref_count); 367 + int ret; 368 + 369 + ret = cifs_swn_send_unregister_message(swnreg); 370 + if (ret < 0) 371 + cifs_dbg(VFS, "%s: Failed to send unregister message: %d\n", __func__, ret); 372 + 373 + idr_remove(&cifs_swnreg_idr, swnreg->id); 374 + kfree(swnreg->net_name); 375 + kfree(swnreg->share_name); 376 + kfree(swnreg); 377 + } 378 + 379 + static void cifs_put_swn_reg(struct cifs_swn_reg *swnreg) 380 + { 381 + mutex_lock(&cifs_swnreg_idr_mutex); 382 + kref_put(&swnreg->ref_count, cifs_swn_reg_release); 383 + mutex_unlock(&cifs_swnreg_idr_mutex); 384 + } 385 + 386 + int cifs_swn_register(struct cifs_tcon *tcon) 387 + { 388 + struct cifs_swn_reg *swnreg; 389 + int ret; 390 + 391 + swnreg = cifs_get_swn_reg(tcon); 392 + if (IS_ERR(swnreg)) 393 + return PTR_ERR(swnreg); 394 + 395 + ret = cifs_swn_send_register_message(swnreg); 396 + if (ret < 0) { 397 + cifs_dbg(VFS, "%s: Failed to send swn register message: %d\n", __func__, ret); 398 + /* Do not put the swnreg or return error, the echo task will retry */ 399 + } 400 + 401 + return 0; 402 + } 403 + 404 + int cifs_swn_unregister(struct cifs_tcon *tcon) 405 + { 406 + struct cifs_swn_reg *swnreg; 407 + 408 + mutex_lock(&cifs_swnreg_idr_mutex); 409 + 410 + swnreg = cifs_find_swn_reg(tcon); 411 + if (swnreg == NULL) { 412 + mutex_unlock(&cifs_swnreg_idr_mutex); 413 + return -EEXIST; 414 + } 415 + 416 + mutex_unlock(&cifs_swnreg_idr_mutex); 417 + 418 + cifs_put_swn_reg(swnreg); 419 + 420 + return 0; 421 + }
+17
fs/cifs/cifs_swn.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * Witness Service client for CIFS 4 + * 5 + * Copyright (c) 2020 Samuel Cabrero <scabrero@suse.de> 6 + */ 7 + 8 + #ifndef _CIFS_SWN_H 9 + #define _CIFS_SWN_H 10 + 11 + struct cifs_tcon; 12 + 13 + extern int cifs_swn_register(struct cifs_tcon *tcon); 14 + 15 + extern int cifs_swn_unregister(struct cifs_tcon *tcon); 16 + 17 + #endif /* _CIFS_SWN_H */
+24 -2
fs/cifs/connect.c
··· 62 62 #include "dfs_cache.h" 63 63 #endif 64 64 #include "fs_context.h" 65 + #ifdef CONFIG_CIFS_SWN_UPCALL 66 + #include "cifs_swn.h" 67 + #endif 65 68 66 69 extern mempool_t *cifs_req_poolp; 67 70 extern bool disable_legacy_dialects; ··· 1947 1944 return; 1948 1945 } 1949 1946 1950 - /* TODO witness unregister */ 1947 + #ifdef CONFIG_CIFS_SWN_UPCALL 1948 + if (tcon->use_witness) { 1949 + int rc; 1950 + 1951 + rc = cifs_swn_unregister(tcon); 1952 + if (rc < 0) { 1953 + cifs_dbg(VFS, "%s: Failed to unregister for witness notifications: %d\n", 1954 + __func__, rc); 1955 + } 1956 + } 1957 + #endif 1951 1958 1952 1959 list_del_init(&tcon->tcon_list); 1953 1960 spin_unlock(&cifs_tcp_ses_lock); ··· 2124 2111 if (ctx->witness) { 2125 2112 if (ses->server->vals->protocol_id >= SMB30_PROT_ID) { 2126 2113 if (tcon->capabilities & SMB2_SHARE_CAP_CLUSTER) { 2127 - /* TODO witness register */ 2114 + /* 2115 + * Set witness in use flag in first place 2116 + * to retry registration in the echo task 2117 + */ 2128 2118 tcon->use_witness = true; 2119 + /* And try to register immediately */ 2120 + rc = cifs_swn_register(tcon); 2121 + if (rc < 0) { 2122 + cifs_dbg(VFS, "Failed to register for witness notifications: %d\n", rc); 2123 + goto out_fail; 2124 + } 2129 2125 } else { 2130 2126 /* TODO: try to extend for non-cluster uses (eg multichannel) */ 2131 2127 cifs_dbg(VFS, "witness requested on mount but no CLUSTER capability on share\n");
+11
fs/cifs/netlink.c
··· 13 13 #include "cifs_debug.h" 14 14 15 15 static const struct nla_policy cifs_genl_policy[CIFS_GENL_ATTR_MAX + 1] = { 16 + [CIFS_GENL_ATTR_SWN_REGISTRATION_ID] = { .type = NLA_U32 }, 17 + [CIFS_GENL_ATTR_SWN_NET_NAME] = { .type = NLA_STRING }, 18 + [CIFS_GENL_ATTR_SWN_SHARE_NAME] = { .type = NLA_STRING }, 19 + [CIFS_GENL_ATTR_SWN_IP] = { .len = sizeof(struct sockaddr_storage) }, 20 + [CIFS_GENL_ATTR_SWN_NET_NAME_NOTIFY] = { .type = NLA_FLAG }, 21 + [CIFS_GENL_ATTR_SWN_SHARE_NAME_NOTIFY] = { .type = NLA_FLAG }, 22 + [CIFS_GENL_ATTR_SWN_IP_NOTIFY] = { .type = NLA_FLAG }, 23 + [CIFS_GENL_ATTR_SWN_KRB_AUTH] = { .type = NLA_FLAG }, 24 + [CIFS_GENL_ATTR_SWN_USER_NAME] = { .type = NLA_STRING }, 25 + [CIFS_GENL_ATTR_SWN_PASSWORD] = { .type = NLA_STRING }, 26 + [CIFS_GENL_ATTR_SWN_DOMAIN_NAME] = { .type = NLA_STRING }, 16 27 }; 17 28 18 29 static struct genl_ops cifs_genl_ops[] = {
+15
include/uapi/linux/cifs/cifs_netlink.h
··· 19 19 }; 20 20 21 21 enum cifs_genl_attributes { 22 + CIFS_GENL_ATTR_UNSPEC, 23 + CIFS_GENL_ATTR_SWN_REGISTRATION_ID, 24 + CIFS_GENL_ATTR_SWN_NET_NAME, 25 + CIFS_GENL_ATTR_SWN_SHARE_NAME, 26 + CIFS_GENL_ATTR_SWN_IP, 27 + CIFS_GENL_ATTR_SWN_NET_NAME_NOTIFY, 28 + CIFS_GENL_ATTR_SWN_SHARE_NAME_NOTIFY, 29 + CIFS_GENL_ATTR_SWN_IP_NOTIFY, 30 + CIFS_GENL_ATTR_SWN_KRB_AUTH, 31 + CIFS_GENL_ATTR_SWN_USER_NAME, 32 + CIFS_GENL_ATTR_SWN_PASSWORD, 33 + CIFS_GENL_ATTR_SWN_DOMAIN_NAME, 22 34 __CIFS_GENL_ATTR_MAX, 23 35 }; 24 36 #define CIFS_GENL_ATTR_MAX (__CIFS_GENL_ATTR_MAX - 1) 25 37 26 38 enum cifs_genl_commands { 39 + CIFS_GENL_CMD_UNSPEC, 40 + CIFS_GENL_CMD_SWN_REGISTER, 41 + CIFS_GENL_CMD_SWN_UNREGISTER, 27 42 __CIFS_GENL_CMD_MAX 28 43 }; 29 44 #define CIFS_GENL_CMD_MAX (__CIFS_GENL_CMD_MAX - 1)