Re: [PATCH 0/8] Add IPv6 support to the ipmask converter

2018-01-21 Thread Jarno Huuskonen
Hi,

On Sun, Jan 21, Tim Düsterhus wrote:
> Jarno,
> 
> Am 15.01.2018 um 14:28 schrieb Jarno Huuskonen:
> > I'll try to test later (might be couple of days).
> 
> Did you find time, yet?

I did some brief testing on saturday and with this silly config
frontend test4
bind ipv4@127.0.0.1:8080
bind ipv6@::1:8080

http-request track-sc1 src,ipmask(32,128) table test_be

http-request track-sc0 req.hdr_ip(X,1),ipmask(24,64) table test_be
http-request set-var(sess.myx) req.hdr_ip(X,1),ipmask(24,64)
http-response set-header X-MY %[var(sess.myx)]
default_backend test_be

backend test_be
stick-table type ipv6 size 20 expire 180s store gpc0,conn_cnt
server wp1 some.ip.addr.ess:80 id 1

and curl -4|-6 -H "X: ipv4.or.ipv6.address" http://localhost:8080/

everything I tested seemed to work (I tested Tim's patch with for size_t outside
of loop).

-Jarno

-- 
Jarno Huuskonen



[PATCH] BUG/MEDIUM: standard: Fix memory leak in str2ip2()

2018-01-21 Thread Tim Duesterhus
Hi

attached is a patch that fixes a memory leak in str2ip2. I wasn't sure
about the severity of this bug (it's only 140 Bytes per call for me) and
opted for MEDIUM. Change if you think MAJOR (?) is more warranted for a
memory leak.

Also I wasn't sure how I would structure the code best. I did not want to
copy the freeaddrinfo into every case, because don't repeat yourself, but
I am not sure whether the `success` variable is any better.

Best regards
Tim Düsterhus

Apply with `git am --scissors` to automatically cut the commit message.
-- >8 --
Subject: [PATCH] BUG/MEDIUM: standard: Fix memory leak in str2ip2()

An haproxy compiled with:

> make -j4 all TARGET=linux2628 USE_GETADDRINFO=1

And running with a configuration like this:

  defaults
log global
modehttp
option  httplog
option  dontlognull
timeout connect 5000
timeout client  5
timeout server  5

  frontend fe
bind :::8080 v4v6

default_backend be

  backend be
server s example.com:80 check

Will leak memory inside `str2ip2()`, because the list `result` is not
properly freed in success cases:

==18875== 140 (76 direct, 64 indirect) bytes in 1 blocks are definitely lost in 
loss record 87 of 111
==18875==at 0x4C2DB8F: malloc (in 
/usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==18875==by 0x537A565: gaih_inet (getaddrinfo.c:1223)
==18875==by 0x537DD5D: getaddrinfo (getaddrinfo.c:2425)
==18875==by 0x4868E5: str2ip2 (standard.c:733)
==18875==by 0x43F28B: srv_set_addr_via_libc (server.c:3767)
==18875==by 0x43F50A: srv_iterate_initaddr (server.c:3879)
==18875==by 0x43F50A: srv_init_addr (server.c:3944)
==18875==by 0x475B30: init (haproxy.c:1595)
==18875==by 0x40406D: main (haproxy.c:2479)

The exists as long as the usage of getaddrinfo in that function exists,
it was introduced in commit:
d5f4328efd5f4eaa7c89cad9773124959195430a

v1.5-dev8 is the first tag containing this comment, the fix
should be backported to haproxy 1.5 and newer.
---
 src/standard.c | 14 +++---
 1 file changed, 11 insertions(+), 3 deletions(-)

diff --git a/src/standard.c b/src/standard.c
index acd136c27..4f89a9217 100644
--- a/src/standard.c
+++ b/src/standard.c
@@ -722,6 +722,7 @@ struct sockaddr_storage *str2ip2(const char *str, struct 
sockaddr_storage *sa, i
 #ifdef USE_GETADDRINFO
if (global.tune.options & GTUNE_USE_GAI) {
struct addrinfo hints, *result;
+   int success = 0;
 
memset(&result, 0, sizeof(result));
memset(&hints, 0, sizeof(hints));
@@ -733,23 +734,30 @@ struct sockaddr_storage *str2ip2(const char *str, struct 
sockaddr_storage *sa, i
if (getaddrinfo(str, NULL, &hints, &result) == 0) {
if (!sa->ss_family || sa->ss_family == AF_UNSPEC)
sa->ss_family = result->ai_family;
-   else if (sa->ss_family != result->ai_family)
+   else if (sa->ss_family != result->ai_family) {
+   freeaddrinfo(result);
goto fail;
+   }
 
switch (result->ai_family) {
case AF_INET:
memcpy((struct sockaddr_in *)sa, 
result->ai_addr, result->ai_addrlen);
set_host_port(sa, port);
-   return sa;
+   success = 1;
+   break;
case AF_INET6:
memcpy((struct sockaddr_in6 *)sa, 
result->ai_addr, result->ai_addrlen);
set_host_port(sa, port);
-   return sa;
+   success = 1;
+   break;
}
}
 
if (result)
freeaddrinfo(result);
+
+   if (success)
+   return sa;
}
 #endif
/* try to resolve an IPv4/IPv6 hostname */
-- 
2.15.1




Re: [PATCH 0/8] Add IPv6 support to the ipmask converter

2018-01-21 Thread Tim Düsterhus
Jarno,
Willy,

Am 15.01.2018 um 14:28 schrieb Jarno Huuskonen:
> On Sun, Jan 14, Tim Düsterhus wrote:
>>> Have you tested that req.hdr_ip / stick tables work w/both masks ? I
>>> used something like:
>>> http-request track-sc0 req.hdr_ip(X,1),ipmask(24,64) table test_be
>>> http-request set-var(sess.myx) req.hdr_ip(X,1),ipmask(24,64)
>>> http-response set-header X-MY %[var(sess.myx)]
>>>
>>> backend test_be
>>>   stick-table type ipv6 size 20 expire 180s store gpc0,conn_cnt
>>

I just read up on stick tables and think I figured it out:

haproxy.cfg
> defaults
>   log global
>   modehttp
>   option  httplog
>   option  dontlognull
>   timeout connect 5000
>   timeout client  5
>   timeout server  5
> 
> frontend fe
>   bind :::8080 v4v6
> 
>   http-request track-sc0 src,ipmask(24,64) table be
>   # Masked IPv4 for IPv4, empty for IPv6 (with and without this commit)
>   http-response set-header Test %[src,ipmask(24)]
>   # Correctly masked IP addresses for both IPv4 and IPv6
>   http-response set-header Test2 %[src,ipmask(24,:::::)]
>   # Correctly masked IP addresses for both IPv4 and IPv6
>   http-response set-header Test3 %[src,ipmask(24,64)]
>   http-response set-header Stick %[sc0_conn_cnt(be)]
> 
>   default_backend be
> 
> backend be
>   stick-table type ipv6 size 20 expire 180s store gpc0,conn_cnt
>   server s example.com:80

Calls:

> [timwolla@/s/haproxy (master)]http --headers 127.0.0.3:8080 |grep Stick
> Stick: 3
> [timwolla@/s/haproxy (master)]http --headers 127.0.0.1:8080 |grep Stick
> Stick: 1
> [timwolla@/s/haproxy (master)]http --headers 127.0.0.2:8080 |grep Stick
> Stick: 2
> [timwolla@/s/haproxy (master)]http --headers 127.0.0.3:8080 |grep Stick
> Stick: 3
> [timwolla@/s/haproxy (master)]http --headers 192.168.178.38:8080 |grep Stick
> Stick: 1
> [timwolla@/s/haproxy (master)]http --headers [::1]:8080 |grep Stick
> Stick: 1
> [timwolla@/s/haproxy (master)]http --headers [::1]:8080 |grep Stick
> Stick: 2

I think this is looking good, no?

Best regards
Tim Düsterhus



Re: Show: haproxy-auth-request - HTTP access control using subrequests

2018-01-21 Thread Ciprian Dorin Craciun
On Sun, Jan 21, 2018 at 4:17 PM, Tim Düsterhus  wrote:
>> Quick question though:  does this script actually block HAproxy's
>> event loop while waiting for the response from the backend server?
>
> haproxy's Socket class is documented to be non-blocking, as explained
> here: https://www.arpalert.org/haproxy-lua.html#h204 ("Non blocking
> design"). Most of my article focused on getting the Socket class of
> haproxy to work, instead of using the native Socket class of Lua for
> that reason.


OK, so it seems that "non-blocking" in the context of Lua for HAProxy
it is actually understood "asynchronous", in the sense that if / when
the call would block, the HAProxy event loop takes over and restarts
the Lua code only when the blocking operation can be retried without
blocking.

So for the purpose of developing in Lua, the code seems to be
"blocking" and "synchronous", but behind the scenes HAProxy controls
the Lua VM to transform the Lua code exeguhiot into and
"asynchronous".

Thanks,
Ciprian.



Re: Show: haproxy-auth-request - HTTP access control using subrequests

2018-01-21 Thread Tim Düsterhus
Ciprian,

Am 21.01.2018 um 13:26 schrieb Ciprian Dorin Craciun:
> Nice little tool!

Thank you.

> Quick question though:  does this script actually block HAproxy's
> event loop while waiting for the response from the backend server?
> 
> (I've read quickly through your article describing the script, but
> failed to get a clear answer to this question.  Moreover looking at
> the script, unless HAProxy does something "funky" with the Lua
> interpreter to give the impression of a synchronous API, I would say
> it does block the HAProxy thread that calls this script.)

haproxy's Socket class is documented to be non-blocking, as explained
here: https://www.arpalert.org/haproxy-lua.html#h204 ("Non blocking
design"). Most of my article focused on getting the Socket class of
haproxy to work, instead of using the native Socket class of Lua for
that reason.

Best regards
Tim Düsterhus



Re: [PATCH 0/8] Add IPv6 support to the ipmask converter

2018-01-21 Thread Tim Düsterhus
Willy,

Am 21.01.2018 um 14:07 schrieb Willy Tarreau:
> There's no problem here because these ones are word-aligned anyway.
> There are other such occurrences elsewhere in the code, so don't
> worry for this one.

Okay, I sent an updated patch that uses the cast to uint32_t.

> From what I've seen till now your patches look fine but like you I'm
> interested in Jarno's feedback ;-)

Could you possibly review pick the following list of patches (all of
them bugfixes only; mostly for docs) already?

- v2 1/8
-2/8
-3/8
-4/8

Possibly also:

-5/8
-6/8
-7/8

which don't really touch live code (apart from 5).

It would allow me to clean up a bit of my local repository.

Best regards
Tim Düsterhus



[PATCH v4 8/8] MEDIUM: sample: Add IPv6 support to the ipmask converter

2018-01-21 Thread Tim Duesterhus
Add an optional second parameter to the ipmask converter that specifies
the number of bits to mask off IPv6 addresses.

If the second parameter is not given IPv6 addresses fail to mask (resulting
in an empty string), preserving backwards compatibility: Previously
a sample like `src,ipmask(24)` failed to give a result for IPv6 addresses.

This feature can be tested like this:

  defaults
log global
modehttp
option  httplog
option  dontlognull
timeout connect 5000
timeout client  5
timeout server  5

  frontend fe
bind :::8080 v4v6

# Masked IPv4 for IPv4, empty for IPv6 (with and without this commit)
http-response set-header Test %[src,ipmask(24)]
# Correctly masked IP addresses for both IPv4 and IPv6
http-response set-header Test2 %[src,ipmask(24,:::::)]
# Correctly masked IP addresses for both IPv4 and IPv6
http-response set-header Test3 %[src,ipmask(24,64)]

default_backend be

  backend be
server s example.com:80
---
 doc/configuration.txt | 11 +++
 src/sample.c  | 27 ++-
 2 files changed, 29 insertions(+), 9 deletions(-)

diff --git a/doc/configuration.txt b/doc/configuration.txt
index 827506788..eaaf0f71f 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -12870,11 +12870,14 @@ in_table()
   the presence of a certain key in a table tracking some elements (e.g. whether
   or not a source IP address or an Authorization header was already seen).
 
-ipmask()
-  Apply a mask to an IPv4 address, and use the result for lookups and storage.
+ipmask(, [])
+  Apply a mask to an IP address, and use the result for lookups and storage.
   This can be used to make all hosts within a certain mask to share the same
-  table entries and as such use the same server. The mask can be passed in
-  dotted form (e.g. 255.255.255.0) or in CIDR form (e.g. 24).
+  table entries and as such use the same server. The mask4 can be passed in
+  dotted form (e.g. 255.255.255.0) or in CIDR form (e.g. 24). The mask6 can
+  be passed in quadruplet form (e.g. :::) or in CIDR form (e.g. 64).
+  If no mask6 is given IPv6 addresses will fail to convert for backwards
+  compatibility reasons.
 
 json([])
   Escapes the input string and produces an ASCII output string ready to use as 
a
diff --git a/src/sample.c b/src/sample.c
index 04cec4fc8..0155b3d69 100644
--- a/src/sample.c
+++ b/src/sample.c
@@ -1603,11 +1603,28 @@ static int sample_conv_str2upper(const struct arg 
*arg_p, struct sample *smp, vo
return 1;
 }
 
-/* takes the netmask in arg_p */
-static int sample_conv_ipmask(const struct arg *arg_p, struct sample *smp, 
void *private)
+/* takes the IPv4 mask in args[0] and an optional IPv6 mask in args[1] */
+static int sample_conv_ipmask(const struct arg *args, struct sample *smp, void 
*private)
 {
-   smp->data.u.ipv4.s_addr &= arg_p->data.ipv4.s_addr;
-   smp->data.type = SMP_T_IPV4;
+   /* Attempt to convert to IPv4 to apply the correct mask. */
+   c_ipv62ip(smp);
+
+   if (smp->data.type == SMP_T_IPV4) {
+   smp->data.u.ipv4.s_addr &= args[0].data.ipv4.s_addr;
+   smp->data.type = SMP_T_IPV4;
+   }
+   else if (smp->data.type == SMP_T_IPV6) {
+   /* IPv6 cannot be converted without an IPv6 mask. */
+   if (args[1].type != ARGT_IPV6)
+   return 0;
+
+   *(uint32_t*)&smp->data.u.ipv6.s6_addr[0]  &= 
*(uint32_t*)&args[1].data.ipv6.s6_addr[0];
+   *(uint32_t*)&smp->data.u.ipv6.s6_addr[4]  &= 
*(uint32_t*)&args[1].data.ipv6.s6_addr[4];
+   *(uint32_t*)&smp->data.u.ipv6.s6_addr[8]  &= 
*(uint32_t*)&args[1].data.ipv6.s6_addr[8];
+   *(uint32_t*)&smp->data.u.ipv6.s6_addr[12] &= 
*(uint32_t*)&args[1].data.ipv6.s6_addr[12];
+   smp->data.type = SMP_T_IPV6;
+   }
+
return 1;
 }
 
@@ -2797,7 +2814,7 @@ static struct sample_conv_kw_list sample_conv_kws = {ILH, 
{
{ "length", sample_conv_length,0,NULL, SMP_T_STR,  
SMP_T_SINT },
{ "hex",sample_conv_bin2hex,   0,NULL, SMP_T_BIN,  
SMP_T_STR  },
{ "hex2i",  sample_conv_hex2int,   0,NULL, SMP_T_STR,  
SMP_T_SINT },
-   { "ipmask", sample_conv_ipmask,ARG1(1,MSK4), NULL, SMP_T_IPV4, 
SMP_T_IPV4 },
+   { "ipmask", sample_conv_ipmask,ARG2(1,MSK4,MSK6), NULL, SMP_T_ADDR, 
SMP_T_IPV4 },
{ "ltime",  sample_conv_ltime, ARG2(1,STR,SINT), NULL, SMP_T_SINT, 
SMP_T_STR },
{ "utime",  sample_conv_utime, ARG2(1,STR,SINT), NULL, SMP_T_SINT, 
SMP_T_STR },
{ "crc32",  sample_conv_crc32, ARG1(0,SINT), NULL, SMP_T_BIN,  
SMP_T_SINT  },
-- 
2.15.1




Re: [PATCH 0/8] Add IPv6 support to the ipmask converter

2018-01-21 Thread Willy Tarreau
Hi Tim,

On Sun, Jan 21, 2018 at 01:24:06PM +0100, Tim Düsterhus wrote:
> Jarno,
> 
> Am 15.01.2018 um 14:28 schrieb Jarno Huuskonen:
> > I'll try to test later (might be couple of days).
> 
> Did you find time, yet?
> 
> > I'll get a compilation error on centos7:
> > src/sample.c: In function 'sample_conv_ipmask':
> > src/sample.c:1623:3: error: 'for' loop initial declarations are only 
> > allowed in C99 mode
> >for (size_t i = 0; i < 16; i++)
> >^
> > src/sample.c:1623:3: note: use option -std=c99 or -std=gnu99 to compile 
> > your code
> > 
> > So maybe move size_t i outside of loop or unroll loop, something like

Yes, please avoid declaring variables inside the for() loop or in the middle
of the code, over time they make code maintenance more painful because when
you look for the type of a variable you have to scrutinize the code backwards
till the beginning of the function instead of quickly glancing at the beginning
of each block.

> > +   *(uint32_t*)&smp->data.u.ipv6.s6_addr[0] &= 
> > *(uint32_t*)&arg_p[1].data.ipv6.s6_addr[0];
> > +   *(uint32_t*)&smp->data.u.ipv6.s6_addr[4] &= 
> > *(uint32_t*)&arg_p[1].data.ipv6.s6_addr[4];
> > +   *(uint32_t*)&smp->data.u.ipv6.s6_addr[8] &= 
> > *(uint32_t*)&arg_p[1].data.ipv6.s6_addr[8];
> > +   *(uint32_t*)&smp->data.u.ipv6.s6_addr[12] &= 
> > *(uint32_t*)&arg_p[1].data.ipv6.s6_addr[12];
> > 
> 
> Thinking about this for some time: I am not sure whether casting a char
> pointer to a uint32_t pointer is safe from a language lawyer perspective
> in all cases. I certainly can update the patch when Willy tells me his
> preference (just move the declaration up or perform the casting).

There's no problem here because these ones are word-aligned anyway.
There are other such occurrences elsewhere in the code, so don't
worry for this one.

>From what I've seen till now your patches look fine but like you I'm
interested in Jarno's feedback ;-)

Cheers,
Willy



Re: Show: haproxy-auth-request - HTTP access control using subrequests

2018-01-21 Thread Ciprian Dorin Craciun
On Fri, Jan 19, 2018 at 9:23 PM, Tim Düsterhus  wrote:
> https://github.com/TimWolla/haproxy-auth-request
>
> This Lua script reimplements the idea behind nginx'
> ngx_http_auth_request_module in haproxy: It allows you to decide whether
> an HTTP request should be allowed or not based on the result of an HTTP
> request to a backend service.



Nice little tool!

Quick question though:  does this script actually block HAproxy's
event loop while waiting for the response from the backend server?

(I've read quickly through your article describing the script, but
failed to get a clear answer to this question.  Moreover looking at
the script, unless HAProxy does something "funky" with the Lua
interpreter to give the impression of a synchronous API, I would say
it does block the HAProxy thread that calls this script.)

Thanks,
Ciprian.



Re: [PATCH 0/8] Add IPv6 support to the ipmask converter

2018-01-21 Thread Tim Düsterhus
Jarno,

Am 15.01.2018 um 14:28 schrieb Jarno Huuskonen:
> I'll try to test later (might be couple of days).

Did you find time, yet?

> I'll get a compilation error on centos7:
> src/sample.c: In function ‘sample_conv_ipmask’:
> src/sample.c:1623:3: error: ‘for’ loop initial declarations are only allowed 
> in C99 mode
>for (size_t i = 0; i < 16; i++)
>^
> src/sample.c:1623:3: note: use option -std=c99 or -std=gnu99 to compile your 
> code
> 
> So maybe move size_t i outside of loop or unroll loop, something like
> +   *(uint32_t*)&smp->data.u.ipv6.s6_addr[0] &= 
> *(uint32_t*)&arg_p[1].data.ipv6.s6_addr[0];
> +   *(uint32_t*)&smp->data.u.ipv6.s6_addr[4] &= 
> *(uint32_t*)&arg_p[1].data.ipv6.s6_addr[4];
> +   *(uint32_t*)&smp->data.u.ipv6.s6_addr[8] &= 
> *(uint32_t*)&arg_p[1].data.ipv6.s6_addr[8];
> +   *(uint32_t*)&smp->data.u.ipv6.s6_addr[12] &= 
> *(uint32_t*)&arg_p[1].data.ipv6.s6_addr[12];
> 

Thinking about this for some time: I am not sure whether casting a char
pointer to a uint32_t pointer is safe from a language lawyer perspective
in all cases. I certainly can update the patch when Willy tells me his
preference (just move the declaration up or perform the casting).

Best regards
Tim Düsterhus