cifs: fix sockaddr comparison in iface_cmp
authorShyam Prasad N <sprasad@microsoft.com>
Fri, 9 Jun 2023 17:46:59 +0000 (17:46 +0000)
committerSteve French <stfrench@microsoft.com>
Mon, 12 Jun 2023 01:52:49 +0000 (20:52 -0500)
iface_cmp used to simply do a memcmp of the two
provided struct sockaddrs. The comparison needs to do more
based on the address family. Similar logic was already
present in cifs_match_ipaddr. Doing something similar now.

Signed-off-by: Shyam Prasad N <sprasad@microsoft.com>
Reported-by: kernel test robot <lkp@intel.com>
Reported-by: Dan Carpenter <error27@gmail.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/smb/client/cifsglob.h
fs/smb/client/cifsproto.h
fs/smb/client/connect.c
fs/smb/client/smb2ops.c

index 0d84bb1a8cd9d74dba12ace2c6ddc050a655e55c..b212a4e16b39b3d98e870cb03d6ce050f1aeaf62 100644 (file)
@@ -970,43 +970,6 @@ release_iface(struct kref *ref)
        kfree(iface);
 }
 
-/*
- * compare two interfaces a and b
- * return 0 if everything matches.
- * return 1 if a has higher link speed, or rdma capable, or rss capable
- * return -1 otherwise.
- */
-static inline int
-iface_cmp(struct cifs_server_iface *a, struct cifs_server_iface *b)
-{
-       int cmp_ret = 0;
-
-       WARN_ON(!a || !b);
-       if (a->speed == b->speed) {
-               if (a->rdma_capable == b->rdma_capable) {
-                       if (a->rss_capable == b->rss_capable) {
-                               cmp_ret = memcmp(&a->sockaddr, &b->sockaddr,
-                                                sizeof(a->sockaddr));
-                               if (!cmp_ret)
-                                       return 0;
-                               else if (cmp_ret > 0)
-                                       return 1;
-                               else
-                                       return -1;
-                       } else if (a->rss_capable > b->rss_capable)
-                               return 1;
-                       else
-                               return -1;
-               } else if (a->rdma_capable > b->rdma_capable)
-                       return 1;
-               else
-                       return -1;
-       } else if (a->speed > b->speed)
-               return 1;
-       else
-               return -1;
-}
-
 struct cifs_chan {
        unsigned int in_reconnect : 1; /* if session setup in progress for this channel */
        struct TCP_Server_Info *server;
index c1c704990b9861f84379af18be97cff82677e426..d127aded2f2878e75d0f210eca90de27e6295599 100644 (file)
@@ -87,6 +87,7 @@ extern int cifs_handle_standard(struct TCP_Server_Info *server,
                                struct mid_q_entry *mid);
 extern int smb3_parse_devname(const char *devname, struct smb3_fs_context *ctx);
 extern int smb3_parse_opt(const char *options, const char *key, char **val);
+extern int cifs_ipaddr_cmp(struct sockaddr *srcaddr, struct sockaddr *rhs);
 extern bool cifs_match_ipaddr(struct sockaddr *srcaddr, struct sockaddr *rhs);
 extern int cifs_discard_remaining_data(struct TCP_Server_Info *server);
 extern int cifs_call_async(struct TCP_Server_Info *server,
index 1250d156619b750e26d9f371425c8e93cbc2eb63..9d16626e7a669ea69119fb136e4fde1264b40fc7 100644 (file)
@@ -1288,6 +1288,56 @@ next_pdu:
        module_put_and_kthread_exit(0);
 }
 
+int
+cifs_ipaddr_cmp(struct sockaddr *srcaddr, struct sockaddr *rhs)
+{
+       struct sockaddr_in *saddr4 = (struct sockaddr_in *)srcaddr;
+       struct sockaddr_in *vaddr4 = (struct sockaddr_in *)rhs;
+       struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *)srcaddr;
+       struct sockaddr_in6 *vaddr6 = (struct sockaddr_in6 *)rhs;
+
+       switch (srcaddr->sa_family) {
+       case AF_UNSPEC:
+               switch (rhs->sa_family) {
+               case AF_UNSPEC:
+                       return 0;
+               case AF_INET:
+               case AF_INET6:
+                       return 1;
+               default:
+                       return -1;
+               }
+       case AF_INET: {
+               switch (rhs->sa_family) {
+               case AF_UNSPEC:
+                       return -1;
+               case AF_INET:
+                       return memcmp(saddr4, vaddr4,
+                                     sizeof(struct sockaddr_in));
+               case AF_INET6:
+                       return 1;
+               default:
+                       return -1;
+               }
+       }
+       case AF_INET6: {
+               switch (rhs->sa_family) {
+               case AF_UNSPEC:
+               case AF_INET:
+                       return -1;
+               case AF_INET6:
+                       return memcmp(saddr6,
+                                     vaddr6,
+                                     sizeof(struct sockaddr_in6));
+               default:
+                       return -1;
+               }
+       }
+       default:
+               return -1; /* don't expect to be here */
+       }
+}
+
 /*
  * Returns true if srcaddr isn't specified and rhs isn't specified, or
  * if srcaddr is specified and matches the IP address of the rhs argument
index 6e3be58cfe49ff5b5f1bc0bb192488b1589acaa0..8bceb54ab06126d1facb3f199eb2a0f51c89d04c 100644 (file)
@@ -510,6 +510,43 @@ smb3_negotiate_rsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
        return rsize;
 }
 
+/*
+ * compare two interfaces a and b
+ * return 0 if everything matches.
+ * return 1 if a is rdma capable, or rss capable, or has higher link speed
+ * return -1 otherwise.
+ */
+static int
+iface_cmp(struct cifs_server_iface *a, struct cifs_server_iface *b)
+{
+       int cmp_ret = 0;
+
+       WARN_ON(!a || !b);
+       if (a->rdma_capable == b->rdma_capable) {
+               if (a->rss_capable == b->rss_capable) {
+                       if (a->speed == b->speed) {
+                               cmp_ret = cifs_ipaddr_cmp((struct sockaddr *) &a->sockaddr,
+                                                         (struct sockaddr *) &b->sockaddr);
+                               if (!cmp_ret)
+                                       return 0;
+                               else if (cmp_ret > 0)
+                                       return 1;
+                               else
+                                       return -1;
+                       } else if (a->speed > b->speed)
+                               return 1;
+                       else
+                               return -1;
+               } else if (a->rss_capable > b->rss_capable)
+                       return 1;
+               else
+                       return -1;
+       } else if (a->rdma_capable > b->rdma_capable)
+               return 1;
+       else
+               return -1;
+}
+
 static int
 parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf,
                        size_t buf_len, struct cifs_ses *ses, bool in_mount)