aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/cifs/smb2ops.c170
1 files changed, 155 insertions, 15 deletions
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index c3648e9b5ec7..b2390e9a6843 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -294,34 +294,176 @@ smb2_negotiate_rsize(struct cifs_tcon *tcon, struct smb_vol *volume_info)
294 return rsize; 294 return rsize;
295} 295}
296 296
297#ifdef CONFIG_CIFS_STATS2 297
298static int
299parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf,
300 size_t buf_len,
301 struct cifs_server_iface **iface_list,
302 size_t *iface_count)
303{
304 struct network_interface_info_ioctl_rsp *p;
305 struct sockaddr_in *addr4;
306 struct sockaddr_in6 *addr6;
307 struct iface_info_ipv4 *p4;
308 struct iface_info_ipv6 *p6;
309 struct cifs_server_iface *info;
310 ssize_t bytes_left;
311 size_t next = 0;
312 int nb_iface = 0;
313 int rc = 0;
314
315 *iface_list = NULL;
316 *iface_count = 0;
317
318 /*
319 * Fist pass: count and sanity check
320 */
321
322 bytes_left = buf_len;
323 p = buf;
324 while (bytes_left >= sizeof(*p)) {
325 nb_iface++;
326 next = le32_to_cpu(p->Next);
327 if (!next) {
328 bytes_left -= sizeof(*p);
329 break;
330 }
331 p = (struct network_interface_info_ioctl_rsp *)((u8 *)p+next);
332 bytes_left -= next;
333 }
334
335 if (!nb_iface) {
336 cifs_dbg(VFS, "%s: malformed interface info\n", __func__);
337 rc = -EINVAL;
338 goto out;
339 }
340
341 if (bytes_left || p->Next)
342 cifs_dbg(VFS, "%s: incomplete interface info\n", __func__);
343
344
345 /*
346 * Second pass: extract info to internal structure
347 */
348
349 *iface_list = kcalloc(nb_iface, sizeof(**iface_list), GFP_KERNEL);
350 if (!*iface_list) {
351 rc = -ENOMEM;
352 goto out;
353 }
354
355 info = *iface_list;
356 bytes_left = buf_len;
357 p = buf;
358 while (bytes_left >= sizeof(*p)) {
359 info->speed = le64_to_cpu(p->LinkSpeed);
360 info->rdma_capable = le32_to_cpu(p->Capability & RDMA_CAPABLE);
361 info->rss_capable = le32_to_cpu(p->Capability & RSS_CAPABLE);
362
363 cifs_dbg(FYI, "%s: adding iface %zu\n", __func__, *iface_count);
364 cifs_dbg(FYI, "%s: speed %zu bps\n", __func__, info->speed);
365 cifs_dbg(FYI, "%s: capabilities 0x%08x\n", __func__,
366 le32_to_cpu(p->Capability));
367
368 switch (p->Family) {
369 /*
370 * The kernel and wire socket structures have the same
371 * layout and use network byte order but make the
372 * conversion explicit in case either one changes.
373 */
374 case INTERNETWORK:
375 addr4 = (struct sockaddr_in *)&info->sockaddr;
376 p4 = (struct iface_info_ipv4 *)p->Buffer;
377 addr4->sin_family = AF_INET;
378 memcpy(&addr4->sin_addr, &p4->IPv4Address, 4);
379
380 /* [MS-SMB2] 2.2.32.5.1.1 Clients MUST ignore these */
381 addr4->sin_port = cpu_to_be16(CIFS_PORT);
382
383 cifs_dbg(FYI, "%s: ipv4 %pI4\n", __func__,
384 &addr4->sin_addr);
385 break;
386 case INTERNETWORKV6:
387 addr6 = (struct sockaddr_in6 *)&info->sockaddr;
388 p6 = (struct iface_info_ipv6 *)p->Buffer;
389 addr6->sin6_family = AF_INET6;
390 memcpy(&addr6->sin6_addr, &p6->IPv6Address, 16);
391
392 /* [MS-SMB2] 2.2.32.5.1.2 Clients MUST ignore these */
393 addr6->sin6_flowinfo = 0;
394 addr6->sin6_scope_id = 0;
395 addr6->sin6_port = cpu_to_be16(CIFS_PORT);
396
397 cifs_dbg(FYI, "%s: ipv6 %pI6\n", __func__,
398 &addr6->sin6_addr);
399 break;
400 default:
401 cifs_dbg(VFS,
402 "%s: skipping unsupported socket family\n",
403 __func__);
404 goto next_iface;
405 }
406
407 (*iface_count)++;
408 info++;
409next_iface:
410 next = le32_to_cpu(p->Next);
411 if (!next)
412 break;
413 p = (struct network_interface_info_ioctl_rsp *)((u8 *)p+next);
414 bytes_left -= next;
415 }
416
417 if (!*iface_count) {
418 rc = -EINVAL;
419 goto out;
420 }
421
422out:
423 if (rc) {
424 kfree(*iface_list);
425 *iface_count = 0;
426 *iface_list = NULL;
427 }
428 return rc;
429}
430
431
298static int 432static int
299SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon) 433SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon)
300{ 434{
301 int rc; 435 int rc;
302 unsigned int ret_data_len = 0; 436 unsigned int ret_data_len = 0;
303 struct network_interface_info_ioctl_rsp *out_buf; 437 struct network_interface_info_ioctl_rsp *out_buf = NULL;
438 struct cifs_server_iface *iface_list;
439 size_t iface_count;
440 struct cifs_ses *ses = tcon->ses;
304 441
305 rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID, 442 rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID,
306 FSCTL_QUERY_NETWORK_INTERFACE_INFO, true /* is_fsctl */, 443 FSCTL_QUERY_NETWORK_INTERFACE_INFO, true /* is_fsctl */,
307 NULL /* no data input */, 0 /* no data input */, 444 NULL /* no data input */, 0 /* no data input */,
308 (char **)&out_buf, &ret_data_len); 445 (char **)&out_buf, &ret_data_len);
309 if (rc != 0) 446 if (rc != 0) {
310 cifs_dbg(VFS, "error %d on ioctl to get interface list\n", rc); 447 cifs_dbg(VFS, "error %d on ioctl to get interface list\n", rc);
311 else if (ret_data_len < sizeof(struct network_interface_info_ioctl_rsp)) { 448 goto out;
312 cifs_dbg(VFS, "server returned bad net interface info buf\n");
313 rc = -EINVAL;
314 } else {
315 /* Dump info on first interface */
316 cifs_dbg(FYI, "Adapter Capability 0x%x\t",
317 le32_to_cpu(out_buf->Capability));
318 cifs_dbg(FYI, "Link Speed %lld\n",
319 le64_to_cpu(out_buf->LinkSpeed));
320 } 449 }
450
451 rc = parse_server_interfaces(out_buf, ret_data_len,
452 &iface_list, &iface_count);
453 if (rc)
454 goto out;
455
456 spin_lock(&ses->iface_lock);
457 kfree(ses->iface_list);
458 ses->iface_list = iface_list;
459 ses->iface_count = iface_count;
460 ses->iface_last_update = jiffies;
461 spin_unlock(&ses->iface_lock);
462
463out:
321 kfree(out_buf); 464 kfree(out_buf);
322 return rc; 465 return rc;
323} 466}
324#endif /* STATS2 */
325 467
326void 468void
327smb2_cached_lease_break(struct work_struct *work) 469smb2_cached_lease_break(struct work_struct *work)
@@ -399,9 +541,7 @@ smb3_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon)
399 if (rc) 541 if (rc)
400 return; 542 return;
401 543
402#ifdef CONFIG_CIFS_STATS2
403 SMB3_request_interfaces(xid, tcon); 544 SMB3_request_interfaces(xid, tcon);
404#endif /* STATS2 */
405 545
406 SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, 546 SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid,
407 FS_ATTRIBUTE_INFORMATION); 547 FS_ATTRIBUTE_INFORMATION);