diff options
| -rw-r--r-- | fs/smb/server/smb2pdu.c | 127 |
1 files changed, 70 insertions, 57 deletions
diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index a36d9407cc69..71237e74acc4 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -10492,6 +10492,27 @@ static __be32 idev_ipv4_address(struct in_device *idev) return addr; } +static struct network_interface_info_ioctl_rsp * +ksmbd_iface_entry_init(struct smb2_ioctl_rsp *rsp, int nbytes, + struct net_device *netdev, unsigned long long speed) +{ + struct network_interface_info_ioctl_rsp *nii_rsp; + + nii_rsp = (struct network_interface_info_ioctl_rsp *)&rsp->Buffer[nbytes]; + nii_rsp->IfIndex = cpu_to_le32(netdev->ifindex); + nii_rsp->Capability = 0; + if (netdev->real_num_tx_queues > 1) + nii_rsp->Capability |= RSS_CAPABLE; + if (ksmbd_rdma_capable_netdev(netdev)) + nii_rsp->Capability |= RDMA_CAPABLE; + nii_rsp->Next = cpu_to_le32(152); + nii_rsp->Reserved = 0; + nii_rsp->LinkSpeed = cpu_to_le64(speed); + memset(nii_rsp->SockAddr_Storage, 0, 128); + + return nii_rsp; +} + static int fsctl_query_iface_info_ioctl(struct ksmbd_conn *conn, struct smb2_ioctl_rsp *rsp, unsigned int out_buf_len) @@ -10506,7 +10527,12 @@ static int fsctl_query_iface_info_ioctl(struct ksmbd_conn *conn, rtnl_lock(); for_each_netdev(&init_net, netdev) { - bool ipv4_set = false; + struct inet6_ifaddr *ifa; + struct inet6_dev *idev6; + struct in_device *idev; + struct in6_addr ip6 = { }; + bool have_ip6 = false; + __be32 ip4 = 0; if (netdev->type == ARPHRD_LOOPBACK) continue; @@ -10517,25 +10543,6 @@ static int fsctl_query_iface_info_ioctl(struct ksmbd_conn *conn, flags = netif_get_flags(netdev); if (!(flags & IFF_RUNNING)) continue; -ipv6_retry: - if (out_buf_len < - nbytes + sizeof(struct network_interface_info_ioctl_rsp)) { - rtnl_unlock(); - return -ENOSPC; - } - - nii_rsp = (struct network_interface_info_ioctl_rsp *) - &rsp->Buffer[nbytes]; - nii_rsp->IfIndex = cpu_to_le32(netdev->ifindex); - - nii_rsp->Capability = 0; - if (netdev->real_num_tx_queues > 1) - nii_rsp->Capability |= RSS_CAPABLE; - if (ksmbd_rdma_capable_netdev(netdev)) - nii_rsp->Capability |= RDMA_CAPABLE; - - nii_rsp->Next = cpu_to_le32(152); - nii_rsp->Reserved = 0; if (!__ethtool_get_link_ksettings(netdev, &cmd) && cmd.base.speed && cmd.base.speed != SPEED_UNKNOWN) { @@ -10545,60 +10552,66 @@ ipv6_retry: "speed is unknown, defaulting to 1Gb/sec"); speed = SPEED_1000; } - speed *= 1000000; - nii_rsp->LinkSpeed = cpu_to_le64(speed); - sockaddr_storage = (struct sockaddr_storage_rsp *) - nii_rsp->SockAddr_Storage; - memset(sockaddr_storage, 0, 128); + /* + * Query IPv4 and IPv6 independently; emit an entry only when a + * usable address exists, so an interface missing one family is + * still reported for the other and 0.0.0.0 / :: placeholders are + * never advertised. + */ + idev = __in_dev_get_rtnl(netdev); + if (idev) + ip4 = idev_ipv4_address(idev); - if (!ipv4_set) { - struct in_device *idev; + idev6 = __in6_dev_get(netdev); + if (idev6) { + rcu_read_lock(); + list_for_each_entry_rcu(ifa, &idev6->addr_list, if_list) { + if (ifa->flags & (IFA_F_TENTATIVE | IFA_F_DEPRECATED)) + continue; + memcpy(&ip6, ifa->addr.s6_addr, sizeof(ip6)); + have_ip6 = true; + break; + } + rcu_read_unlock(); + } + if (ip4) { + if (out_buf_len < + nbytes + sizeof(struct network_interface_info_ioctl_rsp)) { + rtnl_unlock(); + return -ENOSPC; + } + + nii_rsp = ksmbd_iface_entry_init(rsp, nbytes, netdev, speed); + sockaddr_storage = (struct sockaddr_storage_rsp *)nii_rsp->SockAddr_Storage; sockaddr_storage->Family = INTERNETWORK; sockaddr_storage->addr4.Port = 0; - - idev = __in_dev_get_rtnl(netdev); - if (!idev) - continue; - sockaddr_storage->addr4.IPv4Address = - idev_ipv4_address(idev); + sockaddr_storage->addr4.IPv4Address = ip4; nbytes += sizeof(struct network_interface_info_ioctl_rsp); - ipv4_set = true; - goto ipv6_retry; - } else { - struct inet6_dev *idev6; - struct inet6_ifaddr *ifa; - __u8 *ipv6_addr = sockaddr_storage->addr6.IPv6Address; + } + + if (have_ip6) { + if (out_buf_len < + nbytes + sizeof(struct network_interface_info_ioctl_rsp)) { + rtnl_unlock(); + return -ENOSPC; + } + nii_rsp = ksmbd_iface_entry_init(rsp, nbytes, netdev, speed); + sockaddr_storage = (struct sockaddr_storage_rsp *)nii_rsp->SockAddr_Storage; sockaddr_storage->Family = INTERNETWORKV6; sockaddr_storage->addr6.Port = 0; sockaddr_storage->addr6.FlowInfo = 0; - - idev6 = __in6_dev_get(netdev); - if (!idev6) - continue; - - rcu_read_lock(); - list_for_each_entry_rcu(ifa, &idev6->addr_list, if_list) { - if (ifa->flags & (IFA_F_TENTATIVE | - IFA_F_DEPRECATED)) - continue; - memcpy(ipv6_addr, ifa->addr.s6_addr, 16); - break; - } - rcu_read_unlock(); + memcpy(sockaddr_storage->addr6.IPv6Address, ip6.s6_addr, 16); sockaddr_storage->addr6.ScopeId = 0; nbytes += sizeof(struct network_interface_info_ioctl_rsp); } } rtnl_unlock(); - /* - * nii_rsp may point to an entry not included in nbytes if the IPv4 - * or IPv6 device lookup failed. Clear Next in the last returned entry. - */ + /* Clear Next of the last committed entry to terminate the list. */ if (nbytes > 0) { nii_rsp = (struct network_interface_info_ioctl_rsp *) &rsp->Buffer[nbytes - sizeof(*nii_rsp)]; |
