Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.13.7.
  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
 19static DEFINE_IDR(cifs_swnreg_idr);
 20static DEFINE_MUTEX(cifs_swnreg_idr_mutex);
 21
 22struct 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
 35static 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
 46static 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 */
 76static 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	struct sockaddr_storage *addr;
 82	int ret;
 83
 84	skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
 85	if (skb == NULL) {
 86		ret = -ENOMEM;
 87		goto fail;
 88	}
 89
 90	hdr = genlmsg_put(skb, 0, 0, &cifs_genl_family, 0, CIFS_GENL_CMD_SWN_REGISTER);
 91	if (hdr == NULL) {
 92		ret = -ENOMEM;
 93		goto nlmsg_fail;
 94	}
 95
 96	ret = nla_put_u32(skb, CIFS_GENL_ATTR_SWN_REGISTRATION_ID, swnreg->id);
 97	if (ret < 0)
 98		goto nlmsg_fail;
 99
100	ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_NET_NAME, swnreg->net_name);
101	if (ret < 0)
102		goto nlmsg_fail;
103
104	ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_SHARE_NAME, swnreg->share_name);
105	if (ret < 0)
106		goto nlmsg_fail;
107
108	/*
109	 * If there is an address stored use it instead of the server address, because we are
110	 * in the process of reconnecting to it after a share has been moved or we have been
111	 * told to switch to it (client move message). In these cases we unregister from the
112	 * server address and register to the new address when we receive the notification.
113	 */
114	if (swnreg->tcon->ses->server->use_swn_dstaddr)
115		addr = &swnreg->tcon->ses->server->swn_dstaddr;
116	else
117		addr = &swnreg->tcon->ses->server->dstaddr;
118
119	ret = nla_put(skb, CIFS_GENL_ATTR_SWN_IP, sizeof(struct sockaddr_storage), addr);
120	if (ret < 0)
121		goto nlmsg_fail;
122
123	if (swnreg->net_name_notify) {
124		ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_NET_NAME_NOTIFY);
125		if (ret < 0)
126			goto nlmsg_fail;
127	}
128
129	if (swnreg->share_name_notify) {
130		ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_SHARE_NAME_NOTIFY);
131		if (ret < 0)
132			goto nlmsg_fail;
133	}
134
135	if (swnreg->ip_notify) {
136		ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_IP_NOTIFY);
137		if (ret < 0)
138			goto nlmsg_fail;
139	}
140
141	authtype = cifs_select_sectype(swnreg->tcon->ses->server, swnreg->tcon->ses->sectype);
142	switch (authtype) {
143	case Kerberos:
144		ret = cifs_swn_auth_info_krb(swnreg->tcon, skb);
145		if (ret < 0) {
146			cifs_dbg(VFS, "%s: Failed to get kerberos auth info: %d\n", __func__, ret);
147			goto nlmsg_fail;
148		}
149		break;
150	case NTLMv2:
151	case RawNTLMSSP:
152		ret = cifs_swn_auth_info_ntlm(swnreg->tcon, skb);
153		if (ret < 0) {
154			cifs_dbg(VFS, "%s: Failed to get NTLM auth info: %d\n", __func__, ret);
155			goto nlmsg_fail;
156		}
157		break;
158	default:
159		cifs_dbg(VFS, "%s: secType %d not supported!\n", __func__, authtype);
160		ret = -EINVAL;
161		goto nlmsg_fail;
162	}
163
164	genlmsg_end(skb, hdr);
165	genlmsg_multicast(&cifs_genl_family, skb, 0, CIFS_GENL_MCGRP_SWN, GFP_ATOMIC);
166
167	cifs_dbg(FYI, "%s: Message to register for network name %s with id %d sent\n", __func__,
168			swnreg->net_name, swnreg->id);
169
170	return 0;
171
172nlmsg_fail:
173	genlmsg_cancel(skb, hdr);
174	nlmsg_free(skb);
175fail:
176	return ret;
177}
178
179/*
180 * Sends an uregister message to the userspace daemon based on the registration
181 */
182static int cifs_swn_send_unregister_message(struct cifs_swn_reg *swnreg)
183{
184	struct sk_buff *skb;
185	struct genlmsghdr *hdr;
186	int ret;
187
188	skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
189	if (skb == NULL)
190		return -ENOMEM;
191
192	hdr = genlmsg_put(skb, 0, 0, &cifs_genl_family, 0, CIFS_GENL_CMD_SWN_UNREGISTER);
193	if (hdr == NULL) {
194		ret = -ENOMEM;
195		goto nlmsg_fail;
196	}
197
198	ret = nla_put_u32(skb, CIFS_GENL_ATTR_SWN_REGISTRATION_ID, swnreg->id);
199	if (ret < 0)
200		goto nlmsg_fail;
201
202	ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_NET_NAME, swnreg->net_name);
203	if (ret < 0)
204		goto nlmsg_fail;
205
206	ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_SHARE_NAME, swnreg->share_name);
207	if (ret < 0)
208		goto nlmsg_fail;
209
210	ret = nla_put(skb, CIFS_GENL_ATTR_SWN_IP, sizeof(struct sockaddr_storage),
211			&swnreg->tcon->ses->server->dstaddr);
212	if (ret < 0)
213		goto nlmsg_fail;
214
215	if (swnreg->net_name_notify) {
216		ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_NET_NAME_NOTIFY);
217		if (ret < 0)
218			goto nlmsg_fail;
219	}
220
221	if (swnreg->share_name_notify) {
222		ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_SHARE_NAME_NOTIFY);
223		if (ret < 0)
224			goto nlmsg_fail;
225	}
226
227	if (swnreg->ip_notify) {
228		ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_IP_NOTIFY);
229		if (ret < 0)
230			goto nlmsg_fail;
231	}
232
233	genlmsg_end(skb, hdr);
234	genlmsg_multicast(&cifs_genl_family, skb, 0, CIFS_GENL_MCGRP_SWN, GFP_ATOMIC);
235
236	cifs_dbg(FYI, "%s: Message to unregister for network name %s with id %d sent\n", __func__,
237			swnreg->net_name, swnreg->id);
238
239	return 0;
240
241nlmsg_fail:
242	genlmsg_cancel(skb, hdr);
243	nlmsg_free(skb);
244	return ret;
245}
246
247/*
248 * Try to find a matching registration for the tcon's server name and share name.
249 * Calls to this function must be protected by cifs_swnreg_idr_mutex.
250 * TODO Try to avoid memory allocations
251 */
252static struct cifs_swn_reg *cifs_find_swn_reg(struct cifs_tcon *tcon)
253{
254	struct cifs_swn_reg *swnreg;
255	int id;
256	const char *share_name;
257	const char *net_name;
258
259	net_name = extract_hostname(tcon->tree_name);
260	if (IS_ERR(net_name)) {
261		int ret;
262
263		ret = PTR_ERR(net_name);
264		cifs_dbg(VFS, "%s: failed to extract host name from target '%s': %d\n",
265				__func__, tcon->tree_name, ret);
266		return ERR_PTR(-EINVAL);
267	}
268
269	share_name = extract_sharename(tcon->tree_name);
270	if (IS_ERR(share_name)) {
271		int ret;
272
273		ret = PTR_ERR(share_name);
274		cifs_dbg(VFS, "%s: failed to extract share name from target '%s': %d\n",
275				__func__, tcon->tree_name, ret);
276		kfree(net_name);
277		return ERR_PTR(-EINVAL);
278	}
279
280	idr_for_each_entry(&cifs_swnreg_idr, swnreg, id) {
281		if (strcasecmp(swnreg->net_name, net_name) != 0
282		    || strcasecmp(swnreg->share_name, share_name) != 0) {
283			continue;
284		}
285
286		cifs_dbg(FYI, "Existing swn registration for %s:%s found\n", swnreg->net_name,
287				swnreg->share_name);
288
289		kfree(net_name);
290		kfree(share_name);
291
292		return swnreg;
293	}
294
295	kfree(net_name);
296	kfree(share_name);
297
298	return ERR_PTR(-EEXIST);
299}
300
301/*
302 * Get a registration for the tcon's server and share name, allocating a new one if it does not
303 * exists
304 */
305static struct cifs_swn_reg *cifs_get_swn_reg(struct cifs_tcon *tcon)
306{
307	struct cifs_swn_reg *reg = NULL;
308	int ret;
309
310	mutex_lock(&cifs_swnreg_idr_mutex);
311
312	/* Check if we are already registered for this network and share names */
313	reg = cifs_find_swn_reg(tcon);
314	if (!IS_ERR(reg)) {
315		kref_get(&reg->ref_count);
316		mutex_unlock(&cifs_swnreg_idr_mutex);
317		return reg;
318	} else if (PTR_ERR(reg) != -EEXIST) {
319		mutex_unlock(&cifs_swnreg_idr_mutex);
320		return reg;
321	}
322
323	reg = kmalloc(sizeof(struct cifs_swn_reg), GFP_ATOMIC);
324	if (reg == NULL) {
325		mutex_unlock(&cifs_swnreg_idr_mutex);
326		return ERR_PTR(-ENOMEM);
327	}
328
329	kref_init(&reg->ref_count);
330
331	reg->id = idr_alloc(&cifs_swnreg_idr, reg, 1, 0, GFP_ATOMIC);
332	if (reg->id < 0) {
333		cifs_dbg(FYI, "%s: failed to allocate registration id\n", __func__);
334		ret = reg->id;
335		goto fail;
336	}
337
338	reg->net_name = extract_hostname(tcon->tree_name);
339	if (IS_ERR(reg->net_name)) {
340		ret = PTR_ERR(reg->net_name);
341		cifs_dbg(VFS, "%s: failed to extract host name from target: %d\n", __func__, ret);
342		goto fail_idr;
343	}
344
345	reg->share_name = extract_sharename(tcon->tree_name);
346	if (IS_ERR(reg->share_name)) {
347		ret = PTR_ERR(reg->share_name);
348		cifs_dbg(VFS, "%s: failed to extract share name from target: %d\n", __func__, ret);
349		goto fail_net_name;
350	}
351
352	reg->net_name_notify = true;
353	reg->share_name_notify = true;
354	reg->ip_notify = (tcon->capabilities & SMB2_SHARE_CAP_SCALEOUT);
355
356	reg->tcon = tcon;
357
358	mutex_unlock(&cifs_swnreg_idr_mutex);
359
360	return reg;
361
362fail_net_name:
363	kfree(reg->net_name);
364fail_idr:
365	idr_remove(&cifs_swnreg_idr, reg->id);
366fail:
367	kfree(reg);
368	mutex_unlock(&cifs_swnreg_idr_mutex);
369	return ERR_PTR(ret);
370}
371
372static void cifs_swn_reg_release(struct kref *ref)
373{
374	struct cifs_swn_reg *swnreg = container_of(ref, struct cifs_swn_reg, ref_count);
375	int ret;
376
377	ret = cifs_swn_send_unregister_message(swnreg);
378	if (ret < 0)
379		cifs_dbg(VFS, "%s: Failed to send unregister message: %d\n", __func__, ret);
380
381	idr_remove(&cifs_swnreg_idr, swnreg->id);
382	kfree(swnreg->net_name);
383	kfree(swnreg->share_name);
384	kfree(swnreg);
385}
386
387static void cifs_put_swn_reg(struct cifs_swn_reg *swnreg)
388{
389	mutex_lock(&cifs_swnreg_idr_mutex);
390	kref_put(&swnreg->ref_count, cifs_swn_reg_release);
391	mutex_unlock(&cifs_swnreg_idr_mutex);
392}
393
394static int cifs_swn_resource_state_changed(struct cifs_swn_reg *swnreg, const char *name, int state)
395{
396	switch (state) {
397	case CIFS_SWN_RESOURCE_STATE_UNAVAILABLE:
398		cifs_dbg(FYI, "%s: resource name '%s' become unavailable\n", __func__, name);
399		cifs_signal_cifsd_for_reconnect(swnreg->tcon->ses->server, true);
400		break;
401	case CIFS_SWN_RESOURCE_STATE_AVAILABLE:
402		cifs_dbg(FYI, "%s: resource name '%s' become available\n", __func__, name);
403		cifs_signal_cifsd_for_reconnect(swnreg->tcon->ses->server, true);
404		break;
405	case CIFS_SWN_RESOURCE_STATE_UNKNOWN:
406		cifs_dbg(FYI, "%s: resource name '%s' changed to unknown state\n", __func__, name);
407		break;
408	}
409	return 0;
410}
411
412static bool cifs_sockaddr_equal(struct sockaddr_storage *addr1, struct sockaddr_storage *addr2)
413{
414	if (addr1->ss_family != addr2->ss_family)
415		return false;
416
417	if (addr1->ss_family == AF_INET) {
418		return (memcmp(&((const struct sockaddr_in *)addr1)->sin_addr,
419				&((const struct sockaddr_in *)addr2)->sin_addr,
420				sizeof(struct in_addr)) == 0);
421	}
422
423	if (addr1->ss_family == AF_INET6) {
424		return (memcmp(&((const struct sockaddr_in6 *)addr1)->sin6_addr,
425				&((const struct sockaddr_in6 *)addr2)->sin6_addr,
426				sizeof(struct in6_addr)) == 0);
427	}
428
429	return false;
430}
431
432static int cifs_swn_store_swn_addr(const struct sockaddr_storage *new,
433				   const struct sockaddr_storage *old,
434				   struct sockaddr_storage *dst)
435{
436	__be16 port = cpu_to_be16(CIFS_PORT);
437
438	if (old->ss_family == AF_INET) {
439		struct sockaddr_in *ipv4 = (struct sockaddr_in *)old;
440
441		port = ipv4->sin_port;
442	} else if (old->ss_family == AF_INET6) {
443		struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)old;
444
445		port = ipv6->sin6_port;
446	}
447
448	if (new->ss_family == AF_INET) {
449		struct sockaddr_in *ipv4 = (struct sockaddr_in *)new;
450
451		ipv4->sin_port = port;
452	} else if (new->ss_family == AF_INET6) {
453		struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)new;
454
455		ipv6->sin6_port = port;
456	}
457
458	*dst = *new;
459
460	return 0;
461}
462
463static int cifs_swn_reconnect(struct cifs_tcon *tcon, struct sockaddr_storage *addr)
464{
465	int ret = 0;
466
467	/* Store the reconnect address */
468	cifs_server_lock(tcon->ses->server);
469	if (cifs_sockaddr_equal(&tcon->ses->server->dstaddr, addr))
470		goto unlock;
471
472	ret = cifs_swn_store_swn_addr(addr, &tcon->ses->server->dstaddr,
473				      &tcon->ses->server->swn_dstaddr);
474	if (ret < 0) {
475		cifs_dbg(VFS, "%s: failed to store address: %d\n", __func__, ret);
476		goto unlock;
477	}
478	tcon->ses->server->use_swn_dstaddr = true;
479
480	/*
481	 * Unregister to stop receiving notifications for the old IP address.
482	 */
483	ret = cifs_swn_unregister(tcon);
484	if (ret < 0) {
485		cifs_dbg(VFS, "%s: Failed to unregister for witness notifications: %d\n",
486			 __func__, ret);
487		goto unlock;
488	}
489
490	/*
491	 * And register to receive notifications for the new IP address now that we have
492	 * stored the new address.
493	 */
494	ret = cifs_swn_register(tcon);
495	if (ret < 0) {
496		cifs_dbg(VFS, "%s: Failed to register for witness notifications: %d\n",
497			 __func__, ret);
498		goto unlock;
499	}
500
501	cifs_signal_cifsd_for_reconnect(tcon->ses->server, false);
502
503unlock:
504	cifs_server_unlock(tcon->ses->server);
505
506	return ret;
507}
508
509static int cifs_swn_client_move(struct cifs_swn_reg *swnreg, struct sockaddr_storage *addr)
510{
511	struct sockaddr_in *ipv4 = (struct sockaddr_in *)addr;
512	struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)addr;
513
514	if (addr->ss_family == AF_INET)
515		cifs_dbg(FYI, "%s: move to %pI4\n", __func__, &ipv4->sin_addr);
516	else if (addr->ss_family == AF_INET6)
517		cifs_dbg(FYI, "%s: move to %pI6\n", __func__, &ipv6->sin6_addr);
518
519	return cifs_swn_reconnect(swnreg->tcon, addr);
520}
521
522int cifs_swn_notify(struct sk_buff *skb, struct genl_info *info)
523{
524	struct cifs_swn_reg *swnreg;
525	char name[256];
526	int type;
527
528	if (info->attrs[CIFS_GENL_ATTR_SWN_REGISTRATION_ID]) {
529		int swnreg_id;
530
531		swnreg_id = nla_get_u32(info->attrs[CIFS_GENL_ATTR_SWN_REGISTRATION_ID]);
532		mutex_lock(&cifs_swnreg_idr_mutex);
533		swnreg = idr_find(&cifs_swnreg_idr, swnreg_id);
534		mutex_unlock(&cifs_swnreg_idr_mutex);
535		if (swnreg == NULL) {
536			cifs_dbg(FYI, "%s: registration id %d not found\n", __func__, swnreg_id);
537			return -EINVAL;
538		}
539	} else {
540		cifs_dbg(FYI, "%s: missing registration id attribute\n", __func__);
541		return -EINVAL;
542	}
543
544	if (info->attrs[CIFS_GENL_ATTR_SWN_NOTIFICATION_TYPE]) {
545		type = nla_get_u32(info->attrs[CIFS_GENL_ATTR_SWN_NOTIFICATION_TYPE]);
546	} else {
547		cifs_dbg(FYI, "%s: missing notification type attribute\n", __func__);
548		return -EINVAL;
549	}
550
551	switch (type) {
552	case CIFS_SWN_NOTIFICATION_RESOURCE_CHANGE: {
553		int state;
554
555		if (info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_NAME]) {
556			nla_strscpy(name, info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_NAME],
557					sizeof(name));
558		} else {
559			cifs_dbg(FYI, "%s: missing resource name attribute\n", __func__);
560			return -EINVAL;
561		}
562		if (info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_STATE]) {
563			state = nla_get_u32(info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_STATE]);
564		} else {
565			cifs_dbg(FYI, "%s: missing resource state attribute\n", __func__);
566			return -EINVAL;
567		}
568		return cifs_swn_resource_state_changed(swnreg, name, state);
569	}
570	case CIFS_SWN_NOTIFICATION_CLIENT_MOVE: {
571		struct sockaddr_storage addr;
572
573		if (info->attrs[CIFS_GENL_ATTR_SWN_IP]) {
574			nla_memcpy(&addr, info->attrs[CIFS_GENL_ATTR_SWN_IP], sizeof(addr));
575		} else {
576			cifs_dbg(FYI, "%s: missing IP address attribute\n", __func__);
577			return -EINVAL;
578		}
579		return cifs_swn_client_move(swnreg, &addr);
580	}
581	default:
582		cifs_dbg(FYI, "%s: unknown notification type %d\n", __func__, type);
583		break;
584	}
585
586	return 0;
587}
588
589int cifs_swn_register(struct cifs_tcon *tcon)
590{
591	struct cifs_swn_reg *swnreg;
592	int ret;
593
594	swnreg = cifs_get_swn_reg(tcon);
595	if (IS_ERR(swnreg))
596		return PTR_ERR(swnreg);
597
598	ret = cifs_swn_send_register_message(swnreg);
599	if (ret < 0) {
600		cifs_dbg(VFS, "%s: Failed to send swn register message: %d\n", __func__, ret);
601		/* Do not put the swnreg or return error, the echo task will retry */
602	}
603
604	return 0;
605}
606
607int cifs_swn_unregister(struct cifs_tcon *tcon)
608{
609	struct cifs_swn_reg *swnreg;
610
611	mutex_lock(&cifs_swnreg_idr_mutex);
612
613	swnreg = cifs_find_swn_reg(tcon);
614	if (IS_ERR(swnreg)) {
615		mutex_unlock(&cifs_swnreg_idr_mutex);
616		return PTR_ERR(swnreg);
617	}
618
619	mutex_unlock(&cifs_swnreg_idr_mutex);
620
621	cifs_put_swn_reg(swnreg);
622
623	return 0;
624}
625
626void cifs_swn_dump(struct seq_file *m)
627{
628	struct cifs_swn_reg *swnreg;
629	struct sockaddr_in *sa;
630	struct sockaddr_in6 *sa6;
631	int id;
632
633	seq_puts(m, "Witness registrations:");
634
635	mutex_lock(&cifs_swnreg_idr_mutex);
636	idr_for_each_entry(&cifs_swnreg_idr, swnreg, id) {
637		seq_printf(m, "\nId: %u Refs: %u Network name: '%s'%s Share name: '%s'%s Ip address: ",
638				id, kref_read(&swnreg->ref_count),
639				swnreg->net_name, swnreg->net_name_notify ? "(y)" : "(n)",
640				swnreg->share_name, swnreg->share_name_notify ? "(y)" : "(n)");
641		switch (swnreg->tcon->ses->server->dstaddr.ss_family) {
642		case AF_INET:
643			sa = (struct sockaddr_in *) &swnreg->tcon->ses->server->dstaddr;
644			seq_printf(m, "%pI4", &sa->sin_addr.s_addr);
645			break;
646		case AF_INET6:
647			sa6 = (struct sockaddr_in6 *) &swnreg->tcon->ses->server->dstaddr;
648			seq_printf(m, "%pI6", &sa6->sin6_addr.s6_addr);
649			if (sa6->sin6_scope_id)
650				seq_printf(m, "%%%u", sa6->sin6_scope_id);
651			break;
652		default:
653			seq_puts(m, "(unknown)");
654		}
655		seq_printf(m, "%s", swnreg->ip_notify ? "(y)" : "(n)");
656	}
657	mutex_unlock(&cifs_swnreg_idr_mutex);
658	seq_puts(m, "\n");
659}
660
661void cifs_swn_check(void)
662{
663	struct cifs_swn_reg *swnreg;
664	int id;
665	int ret;
666
667	mutex_lock(&cifs_swnreg_idr_mutex);
668	idr_for_each_entry(&cifs_swnreg_idr, swnreg, id) {
669		ret = cifs_swn_send_register_message(swnreg);
670		if (ret < 0)
671			cifs_dbg(FYI, "%s: Failed to send register message: %d\n", __func__, ret);
672	}
673	mutex_unlock(&cifs_swnreg_idr_mutex);
674}