Re: [PATCH] binddynport.c honor ip_local_reserved_ports

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



For the record, I'm sending an updated patch with static functions.


Signed-off-by: Otto Hollmann <otto.hollmann@xxxxxxxx>
---
 src/binddynport.c | 107 ++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 99 insertions(+), 8 deletions(-)

diff --git a/src/binddynport.c b/src/binddynport.c
index 062629a..90e2748 100644
--- a/src/binddynport.c
+++ b/src/binddynport.c
@@ -37,6 +37,7 @@
 #include <unistd.h>
 #include <errno.h>
 #include <string.h>
+#include <syslog.h>
 
 #include <rpc/rpc.h>
 
@@ -56,6 +57,84 @@ enum {
 	NPORTS		= ENDPORT - LOWPORT + 1,
 };
 
+/*
+ * This function decodes information about given port from provided array and
+ * return if port is reserved or not.
+ *
+ * @reserved_ports an array of size at least "NPORTS / (8*sizeof(char)) + 1".
+ * @port port number within range LOWPORT and ENDPORT
+ *
+ * Returns 0 if port is not reserved, non-negative if port is reserved.
+ */
+static int is_reserved(char *reserved_ports, int port) {
+	port -= LOWPORT;
+	if (port < 0 || port >= NPORTS)
+		return 0;
+	return reserved_ports[port/(8*sizeof(char))] & 1<<(port%(8*sizeof(char)));
+}
+
+/*
+ * This function encodes information about given *reserved* port into provided
+ * array. Don't call this function for ports which are not reserved.
+ *
+ * @reserved_ports an array of size at least "NPORTS / (8*sizeof(char)) + 1".
+ * @port port number within range LOWPORT and ENDPORT
+ *
+ */
+static void set_reserved(char *reserved_ports, int port) {
+	port -= LOWPORT;
+	if (port < 0 || port >= NPORTS)
+		return;
+	reserved_ports[port/(8*sizeof(char))] |= 1<<(port%(8*sizeof(char)));
+}
+
+/*
+ * Parse local reserved ports obtained from
+ * /proc/sys/net/ipv4/ip_local_reserved_ports into bit array.
+ *
+ * @reserved_ports a zeroed array of size at least
+ * "NPORTS / (8*sizeof(char)) + 1". Will be used for bit-wise encoding of
+ * reserved ports.
+ *
+ * On each call, reserved ports are read from /proc and bit-wise stored into
+ * provided array
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+
+static int parse_reserved_ports(char *reserved_ports) {
+	int from, to;
+	char delimiter = ',';
+	int res;
+	FILE * file_ptr = fopen("/proc/sys/net/ipv4/ip_local_reserved_ports","r");
+	if (file_ptr == NULL) {
+		(void) syslog(LOG_ERR,
+			"Unable to open open /proc/sys/net/ipv4/ip_local_reserved_ports.");
+		return -1;
+	}
+	do {
+		if ((res = fscanf(file_ptr, "%d", &to)) != 1) {
+			if (res == EOF) break;
+			goto err;
+		}
+		if (delimiter != '-') {
+			from = to;
+		}
+		for (int i = from; i <= to; ++i) {
+			set_reserved(reserved_ports, i);
+		}
+	} while ((res = fscanf(file_ptr, "%c", &delimiter)) == 1);
+	if (res != EOF)
+		goto err;
+	fclose(file_ptr);
+	return 0;
+err:
+	(void) syslog(LOG_ERR,
+		"An error occurred while parsing ip_local_reserved_ports.");
+	fclose(file_ptr);
+	return -1;
+}
+
 /*
  * Bind a socket to a dynamically-assigned IP port.
  *
@@ -81,7 +160,8 @@ int __binddynport(int fd)
 	in_port_t port, *portp;
 	struct sockaddr *sap;
 	socklen_t salen;
-	int i, res;
+	int i, res, array_size;
+	char *reserved_ports;
 
 	if (__rpc_sockisbound(fd))
 		return 0;
@@ -119,21 +199,32 @@ int __binddynport(int fd)
 		gettimeofday(&tv, NULL);
 		seed = tv.tv_usec * getpid();
 	}
+	array_size = NPORTS / (8*sizeof(char)) + 1;
+	reserved_ports = malloc(array_size);
+	if (!reserved_ports) {
+		goto out;
+	}
+	memset(reserved_ports, 0, array_size);
+	parse_reserved_ports(reserved_ports);
+
 	port = (rand_r(&seed) % NPORTS) + LOWPORT;
 	for (i = 0; i < NPORTS; ++i) {
-		*portp = htons(port++);
-		res = bind(fd, sap, salen);
-		if (res >= 0) {
-			res = 0;
-			break;
+		*portp = htons(port);
+		if (!is_reserved(reserved_ports, port++)) {
+			res = bind(fd, sap, salen);
+			if (res >= 0) {
+				res = 0;
+				break;
+			}
+			if (errno != EADDRINUSE)
+				break;
 		}
-		if (errno != EADDRINUSE)
-			break;
 		if (port > ENDPORT)
 			port = LOWPORT;
 	}
 
 out:
+	free(reserved_ports);
 	mutex_unlock(&port_lock);
 	return res;
 }
-- 
2.26.2




On Wed, 2022-10-05 at 10:13 +0200, Otto Hollmann wrote:
> Hi Steve
> 
> 1) I tested various combinations of ip_local_port_range and
> ip_local_reserved_ports, including edge cases like
> ip_local_port_range
> equal to ip_local_reserved_ports. In all such tests library behaved
> as
> expected. Let me know if I should test anything else.
> 
> No, not yet. We wanted to firstly open discussion, but now we can add
> this patch into our distribution.
> 
> 2) You are right, there is no reason why it shouldn't be declared as
> static. Should I send updated patch or you will do this minor
> modification yourself?
> 
> 
> Otto
> 
> On Thu, 2022-07-14 at 13:01 -0500, Steve Dickson wrote:
> > Hey,
> > 
> > My apologies for taking so long to get to this...
> > 
> > A couple questions:
> > 
> > 1) How well was tested... Is in your distro already?
> > 2) Those new functions the patch introduces...
> >     Don't effect the API? Meaning shouldn't they
> >     declared as static?
> > 
> > steved.
> > 
> > On 6/9/22 4:26 AM, Otto Hollmann wrote:
> > > Read reserved ports from
> > > /proc/sys/net/ipv4/ip_local_reserved_ports,
> > > store them into bit-wise array and before binding to random port
> > > check
> > > if port is not reserved.
> > > 
> > > 
> > > Currently, there is no way how to reserve ports so then will not
> > > be
> > > used by rpcbind.
> > > 
> > > Random ports are opened by rpcbind because of rmtcalls. There is
> > > compile-time flag for disabling them, but in some cases we can
> > > not
> > > simply disable them.
> > > 
> > > One solution would be run time option --enable-rmtcalls as
> > > already
> > > discussed, but it was rejected. So if we want to keep rmtcalls
> > > enabled
> > > and also be able to reserve some ports, there is no other way
> > > than
> > > filtering available ports. The easiest and clearest way seems to
> > > be
> > > just respect kernel list of ip_reserved_ports.
> > > 
> > > Unfortunately there is one known disadvantage/side effect - it
> > > affects
> > > probability of ports which are right after reserved ones. The
> > > bigger
> > > reserved block is, the higher is probability of selecting
> > > following
> > > unreserved port. But if there is no reserved port, impact of this
> > > patch
> > > is minimal/none.
> > > 
> > > Signed-off-by: Otto Hollmann <otto.hollmann@xxxxxxxx>
> > > ---
> > >   src/binddynport.c | 107
> > > ++++++++++++++++++++++++++++++++++++++++++----
> > >   1 file changed, 99 insertions(+), 8 deletions(-)
> > > 
> > > diff --git a/src/binddynport.c b/src/binddynport.c
> > > index 062629a..6f78ebe 100644
> > > --- a/src/binddynport.c
> > > +++ b/src/binddynport.c
> > > @@ -37,6 +37,7 @@
> > >   #include <unistd.h>
> > >   #include <errno.h>
> > >   #include <string.h>
> > > +#include <syslog.h>
> > >   
> > >   #include <rpc/rpc.h>
> > >   
> > > @@ -56,6 +57,84 @@ enum {
> > >         NPORTS          = ENDPORT - LOWPORT + 1,
> > >   };
> > >   
> > > +/*
> > > + * This function decodes information about given port from
> > > provided array and
> > > + * return if port is reserved or not.
> > > + *
> > > + * @reserved_ports an array of size at least "NPORTS /
> > > (8*sizeof(char)) + 1".
> > > + * @port port number within range LOWPORT and ENDPORT
> > > + *
> > > + * Returns 0 if port is not reserved, non-negative if port is
> > > reserved.
> > > + */
> > > +int is_reserved(char *reserved_ports, int port) {
> > > +       port -= LOWPORT;
> > > +       if (port < 0 || port >= NPORTS)
> > > +               return 0;
> > > +       return reserved_ports[port/(8*sizeof(char))] &
> > > 1<<(port%(8*sizeof(char)));
> > > +}
> > > +
> > > +/*
> > > + * This function encodes information about given *reserved* port
> > > into provided
> > > + * array. Don't call this function for ports which are not
> > > reserved.
> > > + *
> > > + * @reserved_ports array TODO .
> > > + * @port port number within range LOWPORT and ENDPORT
> > > + *
> > > + */
> > > +void set_reserved(char *reserved_ports, int port) {
> > > +       port -= LOWPORT;
> > > +       if (port < 0 || port >= NPORTS)
> > > +               return;
> > > +       reserved_ports[port/(8*sizeof(char))] |=
> > > 1<<(port%(8*sizeof(char)));
> > > +}
> > > +
> > > +/*
> > > + * Parse local reserved ports obtained from
> > > + * /proc/sys/net/ipv4/ip_local_reserved_ports into bit array.
> > > + *
> > > + * @reserved_ports a zeroed array of size at least
> > > + * "NPORTS / (8*sizeof(char)) + 1". Will be used for bit-wise
> > > encoding of
> > > + * reserved ports.
> > > + *
> > > + * On each call, reserved ports are read from /proc and bit-wise
> > > stored into
> > > + * provided array
> > > + *
> > > + * Returns 0 on success, -1 on failure.
> > > + */
> > > +
> > > +int parse_reserved_ports(char *reserved_ports) {
> > > +       int from, to;
> > > +       char delimiter = ',';
> > > +       int res;
> > > +       FILE * file_ptr =
> > > fopen("/proc/sys/net/ipv4/ip_local_reserved_ports","r");
> > > +       if (file_ptr == NULL) {
> > > +               (void) syslog(LOG_ERR,
> > > +                       "Unable to open open
> > > /proc/sys/net/ipv4/ip_local_reserved_ports.");
> > > +               return -1;
> > > +       }
> > > +       do {
> > > +               if ((res = fscanf(file_ptr, "%d", &to)) != 1) {
> > > +                       if (res == EOF) break;
> > > +                       goto err;
> > > +               }
> > > +               if (delimiter != '-') {
> > > +                       from = to;
> > > +               }
> > > +               for (int i = from; i <= to; ++i) {
> > > +                       set_reserved(reserved_ports, i);
> > > +               }
> > > +       } while ((res = fscanf(file_ptr, "%c", &delimiter)) ==
> > > 1);
> > > +       if (res != EOF)
> > > +               goto err;
> > > +       fclose(file_ptr);
> > > +       return 0;
> > > +err:
> > > +       (void) syslog(LOG_ERR,
> > > +               "An error occurred while parsing
> > > ip_local_reserved_ports.");
> > > +       fclose(file_ptr);
> > > +       return -1;
> > > +}
> > > +
> > >   /*
> > >    * Bind a socket to a dynamically-assigned IP port.
> > >    *
> > > @@ -81,7 +160,8 @@ int __binddynport(int fd)
> > >         in_port_t port, *portp;
> > >         struct sockaddr *sap;
> > >         socklen_t salen;
> > > -       int i, res;
> > > +       int i, res, array_size;
> > > +       char *reserved_ports;
> > >   
> > >         if (__rpc_sockisbound(fd))
> > >                 return 0;
> > > @@ -119,21 +199,32 @@ int __binddynport(int fd)
> > >                 gettimeofday(&tv, NULL);
> > >                 seed = tv.tv_usec * getpid();
> > >         }
> > > +       array_size = NPORTS / (8*sizeof(char)) + 1;
> > > +       reserved_ports = malloc(array_size);
> > > +       if (!reserved_ports) {
> > > +               goto out;
> > > +       }
> > > +       memset(reserved_ports, 0, array_size);
> > > +       parse_reserved_ports(reserved_ports);
> > > +
> > >         port = (rand_r(&seed) % NPORTS) + LOWPORT;
> > >         for (i = 0; i < NPORTS; ++i) {
> > > -               *portp = htons(port++);
> > > -               res = bind(fd, sap, salen);
> > > -               if (res >= 0) {
> > > -                       res = 0;
> > > -                       break;
> > > +               *portp = htons(port);
> > > +               if (!is_reserved(reserved_ports, port++)) {
> > > +                       res = bind(fd, sap, salen);
> > > +                       if (res >= 0) {
> > > +                               res = 0;
> > > +                               break;
> > > +                       }
> > > +                       if (errno != EADDRINUSE)
> > > +                               break;
> > >                 }
> > > -               if (errno != EADDRINUSE)
> > > -                       break;
> > >                 if (port > ENDPORT)
> > >                         port = LOWPORT;
> > >         }
> > >   
> > >   out:
> > > +       free(reserved_ports);
> > >         mutex_unlock(&port_lock);
> > >         return res;
> > >   }
> > 
> 





[Index of Archives]     [Linux Filesystem Development]     [Linux USB Development]     [Linux Media Development]     [Video for Linux]     [Linux NILFS]     [Linux Audio Users]     [Yosemite Info]     [Linux SCSI]

  Powered by Linux