From 74b7142838017a3a7e322617eb52d93930af8e39 Mon Sep 17 00:00:00 2001
From: Steve Dickson <steved@redhat.com>
Date: Thu, 5 Sep 2019 10:06:18 -0400
Subject: [PATCH] rpcbind: look for an exact match of the ip address for a
 requested service first

the rpcbind program returns the first IP address that matches the hint
IP, i.e.: The first interface found that is UP and on the same network
as the hint IP address (bestmatch).
That could be wrong if there are multiple ips in the same subnet for the
same interface configured.

For instance output of ip addr:
~> ip addr
    inet 10.xxx.xx.133/22 brd 10.xxx.xx.255 scope global mgmt	<------first ip-address which matches the hint
    inet 10.xxx.xx.147/22 scope global eth0	<----actual ip-address where nfs server is configured
    inet 10.xxx.xx.160/22 scope global secondary eth0
~>

rpc mount export: RPC: Timed out
rpc mount export: RPC: Timed out

For multiple IP adresses in the same subnet, it should try to find an
exact match first and only if that fails return the best match.

Signed-off-by: Thomas Blume <thomas.blume@suse.com>
Signed-off-by: Steve Dickson <steved@redhat.com>
(cherry picked from commit b5937e1414e1600d788fd1d689b4f64a09422d03)
---
 src/util.c | 34 ++++++++++++++++++++++++++++------
 1 file changed, 28 insertions(+), 6 deletions(-)

diff --git a/src/util.c b/src/util.c
index a6c835b..0f2d721 100644
--- a/src/util.c
+++ b/src/util.c
@@ -103,7 +103,7 @@ char *
 addrmerge(struct netbuf *caller, char *serv_uaddr, char *clnt_uaddr,
 	  char *netid)
 {
-	struct ifaddrs *ifap, *ifp = NULL, *bestif;
+	struct ifaddrs *ifap, *ifp = NULL, *bestif, *exactif;
 	struct netbuf *serv_nbp = NULL, *hint_nbp = NULL, tbuf;
 	struct sockaddr *caller_sa, *hint_sa, *ifsa, *ifmasksa, *serv_sa;
 	struct sockaddr_storage ss;
@@ -157,7 +157,10 @@ addrmerge(struct netbuf *caller, char *serv_uaddr, char *clnt_uaddr,
 	 * network portion of its address is equal to that of the client.
 	 * If so, we have found the interface that we want to use.
 	 */
-	bestif = NULL;
+	bestif = NULL;  /* first interface UP with same network & family */
+	exactif = NULL; /* the interface requested by the client */
+	u_int8_t maskAllAddrBits[16] = {0xff, 0xff, 0xff, 0xff,  0xff, 0xff, 0xff, 0xff,
+					0xff, 0xff, 0xff, 0xff,  0xff, 0xff, 0xff, 0xff}; /* 16 bytes for IPv6 */
 	for (ifap = ifp; ifap != NULL; ifap = ifap->ifa_next) {
 		ifsa = ifap->ifa_addr;
 		ifmasksa = ifap->ifa_netmask;
@@ -175,8 +178,16 @@ addrmerge(struct netbuf *caller, char *serv_uaddr, char *clnt_uaddr,
 			if (!bitmaskcmp(&SA2SINADDR(ifsa),
 			    &SA2SINADDR(hint_sa), &SA2SINADDR(ifmasksa),
 			    sizeof(struct in_addr))) {
-				bestif = ifap;
-				goto found;
+				if(!bestif) /* for compatibility with previous code */
+				    bestif = ifap;
+				/* Is this an exact match? */
+			        if (!bitmaskcmp(&SA2SINADDR(ifsa),
+			            &SA2SINADDR(hint_sa), maskAllAddrBits,
+			            sizeof(struct in_addr))) {
+				    exactif = ifap;
+				    goto found;
+				}
+				/* else go-on looking for an exact match */
 			}
 			break;
 #ifdef INET6
@@ -197,8 +208,16 @@ addrmerge(struct netbuf *caller, char *serv_uaddr, char *clnt_uaddr,
 			} else if (!bitmaskcmp(&SA2SIN6ADDR(ifsa),
 			    &SA2SIN6ADDR(hint_sa), &SA2SIN6ADDR(ifmasksa),
 			    sizeof(struct in6_addr))) {
-				bestif = ifap;
-				goto found;
+				if(!bestif) /* for compatibility with previous code */
+				    bestif = ifap;
+				/* Is this an exact match? */
+			        if (!bitmaskcmp(&SA2SIN6ADDR(ifsa),
+			            &SA2SIN6ADDR(hint_sa), maskAllAddrBits,
+			            sizeof(struct in6_addr))) {
+				    exactif = ifap;
+				    goto found;
+				}
+				/* else go-on looking for an exact match */
 			}
 			break;
 #endif
@@ -215,10 +234,13 @@ addrmerge(struct netbuf *caller, char *serv_uaddr, char *clnt_uaddr,
 		    (bestif->ifa_flags & (IFF_LOOPBACK | IFF_POINTOPOINT))))
 			bestif = ifap;
 	}
+
 	if (bestif == NULL)
 		goto freeit;
 
 found:
+	if(exactif)
+	    bestif = exactif;
 	/*
 	 * Construct the new address using the the address from
 	 * `bestif', and the port number from `serv_uaddr'.
-- 
2.16.4

