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:
                        /*
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to