From 9efb5693fa65a5e5496e2e2d85676cfff5e6d3db Mon Sep 17 00:00:00 2001 From: Henry Hu Date: Sat, 12 Sep 2026 00:35:16 +0800 Subject: ksmbd: report IPv4 and IPv6 addresses independently in iface query fsctl_query_iface_info_ioctl() appends an entry before checking that a usable address was found, so it advertises unspecified-address placeholders: an entry containing 0.0.0.0 when an in_device exists but has no primary IPv4 address, and an entry containing :: when an inet6_dev exists but its address list is empty or every address is tentative or deprecated. The loop also couples IPv6 reporting to IPv4 success: when __in_dev_get_rtnl() returns NULL it continues to the next interface, skipping the IPv6 pass, so an interface without an in_device is dropped even if it has a usable IPv6 address. Query IPv4 and IPv6 independently and append an entry only when an address is found. Factor out the common entry initialization and query the link speed once per interface instead of once per address family. Fixes: e2f34481b24d ("cifsd: add server-side procedures for SMB3") Reported-by: Sashiko Link: https://sashiko.dev/#/patchset/20260910090818.24479-1-15815827059%40163.com Assisted-by: Codex:gpt-5.6-sol Signed-off-by: Henry Hu Signed-off-by: Namjae Jeon --- fs/smb/server/smb2pdu.c | 127 ++++++++++++++++++++++++++---------------------- 1 file 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)]; -- cgit v1.2.3