@@ -294,34 +294,176 @@ smb2_negotiate_rsize(struct cifs_tcon *tcon, struct smb_vol *volume_info)
294294 return rsize ;
295295}
296296
297- #ifdef CONFIG_CIFS_STATS2
297+
298+ static int
299+ parse_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 ++ ;
409+ next_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+
422+ out :
423+ if (rc ) {
424+ kfree (* iface_list );
425+ * iface_count = 0 ;
426+ * iface_list = NULL ;
427+ }
428+ return rc ;
429+ }
430+
431+
298432static int
299433SMB3_request_interfaces (const unsigned int xid , struct cifs_tcon * tcon )
300434{
301435 int rc ;
302436 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 ;
304441
305442 rc = SMB2_ioctl (xid , tcon , NO_FILE_ID , NO_FILE_ID ,
306443 FSCTL_QUERY_NETWORK_INTERFACE_INFO , true /* is_fsctl */ ,
307444 NULL /* no data input */ , 0 /* no data input */ ,
308445 (char * * )& out_buf , & ret_data_len );
309- if (rc != 0 )
446+ if (rc != 0 ) {
310447 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 )) {
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 ));
448+ goto out ;
320449 }
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+
463+ out :
321464 kfree (out_buf );
322465 return rc ;
323466}
324- #endif /* STATS2 */
325467
326468void
327469smb2_cached_lease_break (struct work_struct * work )
@@ -399,9 +541,7 @@ smb3_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon)
399541 if (rc )
400542 return ;
401543
402- #ifdef CONFIG_CIFS_STATS2
403544 SMB3_request_interfaces (xid , tcon );
404- #endif /* STATS2 */
405545
406546 SMB2_QFS_attr (xid , tcon , fid .persistent_fid , fid .volatile_fid ,
407547 FS_ATTRIBUTE_INFORMATION );
0 commit comments