diff options
| author | Henry Hu <huhai@kylinos.cn> | 2026-09-12 00:35:16 +0800 |
|---|---|---|
| committer | Namjae Jeon <linkinjeon@kernel.org> | 2026-09-15 22:28:53 +0900 |
| commit | 9efb5693fa65a5e5496e2e2d85676cfff5e6d3db (patch) | |
| tree | 1569a107484ae99cd4fb49d0c1b9b38f8da59a69 | |
| parent | 285479f4bf93cc7a3c0eb090c9d1baf804b7224c (diff) | |
| download | linux-next-9efb5693fa65a5e5496e2e2d85676cfff5e6d3db.tar.gz linux-next-9efb5693fa65a5e5496e2e2d85676cfff5e6d3db.zip | |
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 <sashiko-bot+sashiko@kernel.org>
Link: https://sashiko.dev/#/patchset/20260910090818.24479-1-15815827059%40163.com
Assisted-by: Codex:gpt-5.6-sol
Signed-off-by: Henry Hu <huhai@kylinos.cn>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
| -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)]; |
