summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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)];