On 10/07/15 14:05, Martin Pieuchot wrote:
> On 01/10/15(Thu) 19:40, Vincent Gross wrote:
>> Although the sysctls controlling the port range are labelled 
>> "port(hi)?first" and
>> "port(hi)?last", no ordering is enforced and you can have portfirst > 
>> portlast.
>> in_pcbbind() (and in6_pcbsetport()) work around this by duplicating the loop 
>> looking
>> for an available port.
> 
> What about a small regress test?
> 
>> This diff introduce temporary bounds and compare them to guarantee that
>> first <= last, thus allowing deduplication of the port scan loop.
> 
> Makes sense to me, I'd keep the comment though.
> 
>>
>> Tested on my laptop with a narrow port range and heavy cheezburger browsing, 
>> no fault
>> detected. Deeper testing welcome.
>>
>> Should I include in6_pcbsetport() changes right now or should ipv4 be 
>> validated first ?
> 
> I prefer when both version are keep in sync, so yes a in6_pcbsetport()
> diff would be nice.  Plus if it's possible to have a regress test it
> would be awesome.
>

Ok ?


Index: sys/netinet/in_pcb.c
===================================================================
RCS file: /cvs/src/sys/netinet/in_pcb.c,v
retrieving revision 1.181
diff -u -p -r1.181 in_pcb.c
--- sys/netinet/in_pcb.c        9 Oct 2015 01:10:27 -0000       1.181
+++ sys/netinet/in_pcb.c        15 Oct 2015 12:08:06 -0000
@@ -360,67 +360,47 @@ in_pcbbind(struct inpcb *inp, struct mbu
                inp->inp_laddr = sin->sin_addr;
        }
        if (lport == 0) {
-               u_int16_t first, last;
+               u_int16_t bound_a, bound_b, first, last;
                int count;
 
                if (inp->inp_flags & INP_HIGHPORT) {
-                       first = ipport_hifirstauto;     /* sysctl */
-                       last = ipport_hilastauto;
+                       bound_a = ipport_hifirstauto;   /* sysctl */
+                       bound_b = ipport_hilastauto;
                } else if (inp->inp_flags & INP_LOWPORT) {
                        if ((error = suser(p, 0)))
                                return (EACCES);
-                       first = IPPORT_RESERVED-1; /* 1023 */
-                       last = 600;                /* not IPPORT_RESERVED/2 */
+                       bound_a = IPPORT_RESERVED-1; /* 1023 */
+                       bound_b = 600;             /* not IPPORT_RESERVED/2 */
                } else {
-                       first = ipport_firstauto;       /* sysctl */
-                       last  = ipport_lastauto;
+                       bound_a = ipport_firstauto;     /* sysctl */
+                       bound_b = ipport_lastauto;
+               }
+               if (bound_a < bound_b) {
+                       first = bound_a;
+                       last  = bound_b;
+               } else {
+                       first = bound_b;
+                       last  = bound_a;
                }
 
                /*
                 * Simple check to ensure all ports are not used up causing
                 * a deadlock here.
-                *
-                * We split the two cases (up and down) so that the direction
-                * is not being tested on each round of the loop.
                 */
 
-               if (first > last) {
-                       /*
-                        * counting down
-                        */
-                       count = first - last;
-                       if (count)
-                               lastport = first - arc4random_uniform(count);
+               count = last - first;
+               lastport = first + arc4random_uniform(count);
 
-                       do {
-                               if (count-- < 0)        /* completely used? */
-                                       return (EADDRNOTAVAIL);
-                               --lastport;
-                               if (lastport > first || lastport < last)
-                                       lastport = first;
-                               lport = htons(lastport);
-                       } while (in_baddynamic(lastport, 
so->so_proto->pr_protocol) ||
-                           in_pcblookup(table, &zeroin_addr, 0,
-                           &inp->inp_laddr, lport, wild, inp->inp_rtableid));
-               } else {
-                       /*
-                        * counting up
-                        */
-                       count = last - first;
-                       if (count)
-                               lastport = first + arc4random_uniform(count);
-
-                       do {
-                               if (count-- < 0)        /* completely used? */
-                                       return (EADDRNOTAVAIL);
-                               ++lastport;
-                               if (lastport < first || lastport > last)
-                                       lastport = first;
-                               lport = htons(lastport);
-                       } while (in_baddynamic(lastport, 
so->so_proto->pr_protocol) ||
-                           in_pcblookup(table, &zeroin_addr, 0,
-                           &inp->inp_laddr, lport, wild, inp->inp_rtableid));
-               }
+               do {
+                       if (count-- < 0)        /* completely used? */
+                               return (EADDRNOTAVAIL);
+                       ++lastport;
+                       if (lastport < first || lastport > last)
+                               lastport = first;
+                       lport = htons(lastport);
+               } while (in_baddynamic(lastport, so->so_proto->pr_protocol) ||
+                   in_pcblookup(table, &zeroin_addr, 0,
+                   &inp->inp_laddr, lport, wild, inp->inp_rtableid));
        }
        inp->inp_lport = lport;
        in_pcbrehash(inp);
Index: sys/netinet6/in6_pcb.c
===================================================================
RCS file: /cvs/src/sys/netinet6/in6_pcb.c,v
retrieving revision 1.77
diff -u -p -r1.77 in6_pcb.c
--- sys/netinet6/in6_pcb.c      15 Oct 2015 10:27:18 -0000      1.77
+++ sys/netinet6/in6_pcb.c      15 Oct 2015 12:08:06 -0000
@@ -293,7 +293,7 @@ in6_pcbsetport(struct in6_addr *laddr, s
 {
        struct socket *so = inp->inp_socket;
        struct inpcbtable *table = inp->inp_table;
-       u_int16_t first, last;
+       u_int16_t bound_a, bound_b, first, last;
        u_int16_t lastport = 0;
        u_int16_t lport = 0;
        int count;
@@ -308,63 +308,43 @@ in6_pcbsetport(struct in6_addr *laddr, s
                wild |= INPLOOKUP_WILDCARD;
 
        if (inp->inp_flags & INP_HIGHPORT) {
-               first = ipport_hifirstauto;     /* sysctl */
-               last = ipport_hilastauto;
+               bound_a = ipport_hifirstauto;   /* sysctl */
+               bound_b = ipport_hilastauto;
        } else if (inp->inp_flags & INP_LOWPORT) {
                if ((error = suser(p, 0)))
                        return (EACCES);
-               first = IPPORT_RESERVED-1; /* 1023 */
-               last = 600;                /* not IPPORT_RESERVED/2 */
+               bound_a = IPPORT_RESERVED-1; /* 1023 */
+               bound_b = 600;             /* not IPPORT_RESERVED/2 */
        } else {
-               first = ipport_firstauto;       /* sysctl */
-               last  = ipport_lastauto;
+               bound_a = ipport_firstauto;     /* sysctl */
+               bound_b = ipport_lastauto;
+       }
+       if (bound_a < bound_b) {
+               first = bound_a;
+               last  = bound_b;
+       } else {
+               first = bound_b;
+               last  = bound_a;
        }
 
        /*
         * Simple check to ensure all ports are not used up causing
         * a deadlock here.
-        *
-        * We split the two cases (up and down) so that the direction
-        * is not being tested on each round of the loop.
         */
 
-       if (first > last) {
-               /*
-                * counting down
-                */
-               count = first - last;
-               if (count)
-                       lastport = first - arc4random_uniform(count);
-
-               do {
-                       if (count-- < 0)        /* completely used? */
-                               return (EADDRNOTAVAIL);
-                       --lastport;
-                       if (lastport > first || lastport < last)
-                               lastport = first;
-                       lport = htons(lastport);
-               } while (in_baddynamic(lastport, so->so_proto->pr_protocol) ||
-                   in_pcblookup(table, &zeroin6_addr, 0,
-                   &inp->inp_laddr6, lport, wild, inp->inp_rtableid));
-       } else {
-               /*
-                * counting up
-                */
-               count = last - first;
-               if (count)
-                       lastport = first + arc4random_uniform(count);
-
-               do {
-                       if (count-- < 0)        /* completely used? */
-                               return (EADDRNOTAVAIL);
-                       ++lastport;
-                       if (lastport < first || lastport > last)
-                               lastport = first;
-                       lport = htons(lastport);
-               } while (in_baddynamic(lastport, so->so_proto->pr_protocol) ||
-                   in_pcblookup(table, &zeroin6_addr, 0,
-                   &inp->inp_laddr6, lport, wild, inp->inp_rtableid));
-       }
+       count = last - first;
+       lastport = first + arc4random_uniform(count);
+
+       do {
+               if (count-- < 0)        /* completely used? */
+                       return (EADDRNOTAVAIL);
+               ++lastport;
+               if (lastport < first || lastport > last)
+                       lastport = first;
+               lport = htons(lastport);
+       } while (in_baddynamic(lastport, so->so_proto->pr_protocol) ||
+           in_pcblookup(table, &zeroin6_addr, 0,
+           &inp->inp_laddr6, lport, wild, inp->inp_rtableid));
 
        inp->inp_lport = lport;
        in_pcbrehash(inp);

Reply via email to