svn commit: r203710 - head/usr.sbin/rpcbind

Warner Losh imp at FreeBSD.org
Tue Feb 9 18:10:56 UTC 2010


Author: imp
Date: Tue Feb  9 18:10:56 2010
New Revision: 203710
URL: http://svn.freebsd.org/changeset/base/203710

Log:
  When you have multiple addresses on the same network on different
  interfaces (such as when you are part of a carp pool), and you run
  rpcbind -h to restrict which interfaces have rpc services, rpcbind can
  none-the-less return addresses that aren't in the -h list.  This patch
  enforces the rule that when you specify -h on the command line, then
  services returned from rpcbind must be to one of the addresses listed
  in -h, or be a loopback address (since localhost is implicit when
  running -h).
  
  The root cause of this is the assumption in addrmerge that there can
  be only one interface that matches a given network IP address.  This
  turns out not to be the case.  To retain historical behavior, I didn't
  try to fix the routine to prefer the address that the request came
  into, since I didn't know the side effects that might cause in the
  normal case.  My quick analysis suggests that it wouldn't be a
  problem, but since this code is tricky I opted for the more
  conservative patch of only restricting the reply when -h is in effect.
  
  Hence, this change will have no effect when you are running rpcbind
  without -h.
  
  Reviewed by:	alfred@
  Sponsored by:	iX Systems
  MFC after:	2 weeks

Modified:
  head/usr.sbin/rpcbind/rpcbind.c
  head/usr.sbin/rpcbind/rpcbind.h
  head/usr.sbin/rpcbind/util.c

Modified: head/usr.sbin/rpcbind/rpcbind.c
==============================================================================
--- head/usr.sbin/rpcbind/rpcbind.c	Tue Feb  9 17:29:04 2010	(r203709)
+++ head/usr.sbin/rpcbind/rpcbind.c	Tue Feb  9 18:10:56 2010	(r203710)
@@ -92,6 +92,7 @@ int oldstyle_local = 0;
 int verboselog = 0;
 
 char **hosts = NULL;
+struct sockaddr **bound_sa;
 int ipv6_only = 0;
 int nhosts = 0;
 int on = 1;
@@ -119,6 +120,7 @@ static void rbllist_add(rpcprog_t, rpcve
 			     struct netbuf *);
 static void terminate(int);
 static void parseargs(int, char *[]);
+static void update_bound_sa(void);
 
 int
 main(int argc, char *argv[])
@@ -130,6 +132,8 @@ main(int argc, char *argv[])
 
 	parseargs(argc, argv);
 
+	update_bound_sa();
+
 	/* Check that another rpcbind isn't already running. */
 	if ((rpcbindlockfd = (open(RPCBINDDLOCK,
 	    O_RDONLY|O_CREAT, 0444))) == -1)
@@ -323,8 +327,7 @@ init_transport(struct netconfig *nconf)
 	     * If no hosts were specified, just bind to INADDR_ANY.
 	     * Otherwise  make sure 127.0.0.1 is added to the list.
 	     */
-	    nhostsbak = nhosts;
-	    nhostsbak++;
+	    nhostsbak = nhosts + 1;
 	    hosts = realloc(hosts, nhostsbak * sizeof(char *));
 	    if (nhostsbak == 1)
 	        hosts[0] = "*";
@@ -657,6 +660,75 @@ error:
 	return (1);
 }
 
+/*
+ * Create the list of addresses that we're bound to.  Normally, this
+ * list is empty because we're listening on the wildcard address
+ * (nhost == 0).  If -h is specified on the command line, then
+ * bound_sa will have a list of the addresses that the program binds
+ * to specifically.  This function takes that list and converts them to
+ * struct sockaddr * and stores them in bound_sa.
+ */
+static void
+update_bound_sa(void)
+{
+	struct addrinfo hints, *res = NULL;
+	int i;
+
+	if (nhosts == 0)
+		return;
+	bound_sa = malloc(sizeof(*bound_sa) * nhosts);
+	memset(&hints, 0, sizeof(hints));
+	hints.ai_family = PF_UNSPEC;
+	for (i = 0; i < nhosts; i++)  {
+		if (getaddrinfo(hosts[i], NULL, &hints, &res) != 0)
+			continue;
+		bound_sa[i] = malloc(res->ai_addrlen);
+		memcpy(bound_sa[i], res->ai_addr, res->ai_addrlen);
+	}
+}
+
+/*
+ * Match the sa against the list of addresses we've bound to.  If
+ * we've not specifically bound to anything, we match everything.
+ * Otherwise, if the IPv4 or IPv6 address matches one of the addresses
+ * in bound_sa, we return true.  If not, we return false.
+ */
+int
+listen_addr(const struct sockaddr *sa)
+{
+	int i;
+
+	/*
+	 * If nhosts == 0, then there were no -h options on the
+	 * command line, so all addresses are addresses we're
+	 * listening to.
+	 */
+	if (nhosts == 0)
+		return 1;
+	for (i = 0; i < nhosts; i++) {
+		if (bound_sa[i] == NULL ||
+		    sa->sa_family != bound_sa[i]->sa_family)
+			continue;
+		switch (sa->sa_family) {
+		case AF_INET:
+		  	if (memcmp(&SA2SINADDR(sa), &SA2SINADDR(bound_sa[i]),
+			    sizeof(struct in_addr)) == 0)
+				return (1);
+			break;
+#ifdef INET6
+		case AF_INET6:
+		  	if (memcmp(&SA2SIN6ADDR(sa), &SA2SIN6ADDR(bound_sa[i]),
+			    sizeof(struct in6_addr)) == 0)
+				return (1);
+			break;
+#endif
+		default:
+			break;
+		}
+	}
+	return (0);
+}
+
 static void
 rbllist_add(rpcprog_t prog, rpcvers_t vers, struct netconfig *nconf,
 	    struct netbuf *addr)

Modified: head/usr.sbin/rpcbind/rpcbind.h
==============================================================================
--- head/usr.sbin/rpcbind/rpcbind.h	Tue Feb  9 17:29:04 2010	(r203709)
+++ head/usr.sbin/rpcbind/rpcbind.h	Tue Feb  9 18:10:56 2010	(r203710)
@@ -134,6 +134,7 @@ void read_warmstart(void);
 
 char *addrmerge(struct netbuf *caller, char *serv_uaddr, char *clnt_uaddr,
 		     char *netid);
+int listen_addr(const struct sockaddr *sa);
 void network_init(void);
 struct sockaddr *local_sa(int);
 
@@ -141,4 +142,12 @@ struct sockaddr *local_sa(int);
 #define	RPCB_ALLVERS 0
 #define	RPCB_ONEVERS 1
 
+/* To convert a struct sockaddr to IPv4 or IPv6 address */
+#define	SA2SIN(sa)	((struct sockaddr_in *)(sa))
+#define	SA2SINADDR(sa)	(SA2SIN(sa)->sin_addr)
+#ifdef INET6
+#define	SA2SIN6(sa)	((struct sockaddr_in6 *)(sa))
+#define	SA2SIN6ADDR(sa)	(SA2SIN6(sa)->sin6_addr)
+#endif
+
 #endif /* rpcbind_h */

Modified: head/usr.sbin/rpcbind/util.c
==============================================================================
--- head/usr.sbin/rpcbind/util.c	Tue Feb  9 17:29:04 2010	(r203709)
+++ head/usr.sbin/rpcbind/util.c	Tue Feb  9 18:10:56 2010	(r203710)
@@ -58,13 +58,6 @@
 
 #include "rpcbind.h"
 
-#define	SA2SIN(sa)	((struct sockaddr_in *)(sa))
-#define	SA2SINADDR(sa)	(SA2SIN(sa)->sin_addr)
-#ifdef INET6
-#define	SA2SIN6(sa)	((struct sockaddr_in6 *)(sa))
-#define	SA2SIN6ADDR(sa)	(SA2SIN6(sa)->sin6_addr)
-#endif
-
 static struct sockaddr_in *local_in4;
 #ifdef INET6
 static struct sockaddr_in6 *local_in6;
@@ -176,9 +169,13 @@ addrmerge(struct netbuf *caller, char *s
 		goto freeit;
 
 	/*
-	 * Loop through all interfaces. For each interface, see if the
-	 * network portion of its address is equal to that of the client.
-	 * If so, we have found the interface that we want to use.
+	 * Loop through all interfaces. For each interface, see if it
+	 * is either the loopback interface (which we always listen
+	 * on) or is one of the addresses the program bound to (the
+	 * wildcard by default, or a subset if -h is specified) and
+	 * the 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;
 	for (ifap = ifp; ifap != NULL; ifap = ifap->ifa_next) {
@@ -189,6 +186,9 @@ addrmerge(struct netbuf *caller, char *s
 		    !(ifap->ifa_flags & IFF_UP))
 			continue;
 
+		if (!(ifap->ifa_flags & IFF_LOOPBACK) && !listen_addr(ifsa))
+			continue;
+
 		switch (hint_sa->sa_family) {
 		case AF_INET:
 			/*


More information about the svn-src-head mailing list