summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHenry Hu <huhai@kylinos.cn>2026-09-12 00:35:16 +0800
committerNamjae Jeon <linkinjeon@kernel.org>2026-09-15 22:28:53 +0900
commit9efb5693fa65a5e5496e2e2d85676cfff5e6d3db (patch)
tree1569a107484ae99cd4fb49d0c1b9b38f8da59a69
parent285479f4bf93cc7a3c0eb090c9d1baf804b7224c (diff)
downloadlinux-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.c127
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)];