Hi,

Maik Broemme <mbroe...@plusserver.de> wrote:
> Hi,
> 
> I have attached a patch which will add on every http request a new
> header 'X-Original-Dst'. If you have HAProxy running in transparent mode
> with a big number of SQUID servers behind it, it is very nice to have
> the original destination ip as a common header to make decisions based
> on it.
> 
> The whole thing is configurable with a new option 'originaldst'. I have
> updated the sourcecode as well as the documentation. The 'haproxy-en.txt'
> and 'haproxy-fr.txt' files are untouched, due to lack of my french
> language knowledge. ;)
> 
> Also the patch adds this header for IPv4 only. I haven't any IPv6 test
> environment running here and don't know if getsockopt() with SO_ORIGINAL_DST
> will work on IPv6. If someone knows it and wants to test it I can modify
> the diff. Feel free to ask me questions or things which should be changed. :)
> 

I have attached a new diff, because the first one has two issues:

  - The except ip addresses for X-Original-Dst header were matched
    against 'except_net' instead of 'except_dst' which was wrong.

  - The except ip addresses for X-Original-Dst header were matched
    against the netmask of 'except_mask' which is only valid for
    'except_net'. I have added a new netmask value 'except_mask_dst'
    to fix that.

> --Maik

--Maik
diff -Nur haproxy-1.3.17/doc/architecture.txt 
haproxy-1.3.17-x-original-dst/doc/architecture.txt
--- haproxy-1.3.17/doc/architecture.txt 2009-03-29 15:26:57.000000000 +0200
+++ haproxy-1.3.17-x-original-dst/doc/architecture.txt  2009-04-15 
04:34:08.000000000 +0200
@@ -128,6 +128,15 @@
         option httpclose
         option forwardfor
 
+ - if the application needs to log the original destination IP, use the
+   "originaldst" option which will add an "X-Original-Dst" header with the
+   original destination IP address. You must also use "httpclose" to ensure
+   that you will rewrite every requests and not only the first one of each
+   session :
+
+        option httpclose
+        option originaldst
+
    The web server will have to be configured to use this header instead.
    For example, on apache, you can use LogFormat for this :
 
diff -Nur haproxy-1.3.17/doc/configuration.txt 
haproxy-1.3.17-x-original-dst/doc/configuration.txt
--- haproxy-1.3.17/doc/configuration.txt        2009-03-29 15:26:57.000000000 
+0200
+++ haproxy-1.3.17-x-original-dst/doc/configuration.txt 2009-04-15 
04:30:21.000000000 +0200
@@ -599,6 +599,7 @@
 [no] option dontlognull     X          X         X         -
 [no] option forceclose      X          -         X         X
 option forwardfor           X          X         X         X
+option originaldst          X          X         X         X
 [no] option http_proxy      X          X         X         X
 option httpchk              X          -         X         X
 [no] option httpclose       X          X         X         X
@@ -2038,6 +2039,65 @@
   See also : "option httpclose"
 
 
+option originaldst [ except <network> ] [ header <name> ]
+  Enable insertion of the X-Original-Dst header to requests sent to servers
+  May be used in sections :   defaults | frontend | listen | backend
+                                 yes   |    yes   |   yes  |   yes
+  Arguments :
+    <network> is an optional argument used to disable this option for sources
+              matching <network>
+    <name>    an optional argument to specify a different "X-Original-Dst"
+              header name.  
+
+  Since HAProxy can work in transparent mode, every request from a client can
+  be redirected to the proxy and HAProxy itself can proxy every request to a
+  complex SQUID environment and the destination host from SO_ORIGINAL_DST will
+  be lost. This is annoying when you want access rules based on destination ip
+  addresses. To solve this problem, a new HTTP header "X-Original-Dst" may be
+  added by HAProxy to all requests sent to the server. This header contains a
+  value representing the original destination IP address. Since this must be
+  configured to always use the last occurrence of this header only. Note that
+  only the last occurrence of the header must be used, since it is really
+  possible that the client has already brought one.
+
+  The keyword "header" may be used to supply a different header name to 
replace 
+  the default "X-Original-Dst". This can be useful where you might already
+  have a "X-Original-Dst" header from a different application, and you need
+  preserve it. Also if your backend server doesn't use the "X-Original-Dst"
+  header and requires different one.
+
+  Sometimes, a same HAProxy instance may be shared between a direct client
+  access and a reverse-proxy access (for instance when an SSL reverse-proxy is
+  used to decrypt HTTPS traffic). It is possible to disable the addition of the
+  header for a known source address or network by adding the "except" keyword
+  followed by the network address. In this case, any source IP matching the
+  network will not cause an addition of this header. Most common uses are with
+  private networks or 127.0.0.1.
+
+  This option may be specified either in the frontend or in the backend. If at
+  least one of them uses it, the header will be added. Note that the backend's
+  setting of the header subargument takes precedence over the frontend's if
+  both are defined.
+
+  It is important to note that as long as HAProxy does not support keep-alive
+  connections, only the first request of a connection will receive the header.
+  For this reason, it is important to ensure that "option httpclose" is set
+  when using this option.
+
+  Examples :
+    # Original Destination address
+    frontend www
+        mode http
+        option originaldst except 127.0.0.1
+
+    # Those servers want the IP Address in X-Client-Dst
+    backend www
+        mode http
+        option originaldst header X-Client-Dst
+
+  See also : "option httpclose"
+
+
 option http_proxy
 no option http_proxy
   Enable or disable plain HTTP proxy mode
diff -Nur haproxy-1.3.17/include/common/defaults.h 
haproxy-1.3.17-x-original-dst/include/common/defaults.h
--- haproxy-1.3.17/include/common/defaults.h    2009-03-29 15:26:57.000000000 
+0200
+++ haproxy-1.3.17-x-original-dst/include/common/defaults.h     2009-04-15 
04:05:21.000000000 +0200
@@ -130,6 +130,9 @@
 // X-Forwarded-For header default
 #define DEF_XFORWARDFOR_HDR    "X-Forwarded-For"
 
+// X-Original-Dst header default
+#define DEF_XORIGINALDST_HDR   "X-Original-Dst"
+
 /* Default connections limit.
  *
  * A system limit can be enforced at build time in order to avoid using haproxy
diff -Nur haproxy-1.3.17/include/types/proxy.h 
haproxy-1.3.17-x-original-dst/include/types/proxy.h
--- haproxy-1.3.17/include/types/proxy.h        2009-03-29 15:26:57.000000000 
+0200
+++ haproxy-1.3.17-x-original-dst/include/types/proxy.h 2009-04-15 
18:40:21.000000000 +0200
@@ -105,7 +105,8 @@
 #define PR_O_CONTSTATS 0x10000000      /* continous counters */
 #define PR_O_HTTP_PROXY 0x20000000     /* Enable session to use HTTP proxy 
operations */
 #define PR_O_DISABLE404 0x40000000      /* Disable a server on a 404 response 
to a health-check */
-/* unused: 0x80000000 */
+#define PR_O_ORGDST     0x80000000      /* insert x-original-dst with 
destination address */
+/* unused: 0x80000000 - now used by PR_O_ORGDST */
 
 /* bits for proxy->options2 */
 #define PR_O2_SPLIC_REQ        0x00000001      /* transfer requests using 
linux kernel's splice() */
@@ -229,8 +230,12 @@
        unsigned int fe_maxsps;                 /* max # of new sessions per 
second on the frontend */
        unsigned int fullconn;                  /* #conns on backend above 
which servers are used at full load */
        struct in_addr except_net, except_mask; /* don't x-forward-for for this 
address. FIXME: should support IPv6 */
+       struct in_addr except_dst;              /* don't x-original-dst for 
this address. */
+       struct in_addr except_mask_dst;         /* the netmask for except_dst. 
*/
        char *fwdfor_hdr_name;                  /* header to use - default: 
"x-forwarded-for" */
        int fwdfor_hdr_len;                     /* length of "x-forwarded-for" 
header */
+       char *orgdst_hdr_name;                  /* header to use - default: 
"x-original-dst" */
+       int orgdst_hdr_len;                     /* length of "x-original-dst" 
header */
 
        unsigned down_trans;                    /* up-down transitions */
        unsigned down_time;                     /* total time the proxy was 
down */
diff -Nur haproxy-1.3.17/src/cfgparse.c 
haproxy-1.3.17-x-original-dst/src/cfgparse.c
--- haproxy-1.3.17/src/cfgparse.c       2009-03-29 15:26:57.000000000 +0200
+++ haproxy-1.3.17-x-original-dst/src/cfgparse.c        2009-04-15 
18:41:17.000000000 +0200
@@ -674,6 +674,7 @@
                curproxy->lbprm.algo = defproxy.lbprm.algo;
                curproxy->except_net = defproxy.except_net;
                curproxy->except_mask = defproxy.except_mask;
+               curproxy->except_mask_dst = defproxy.except_mask_dst;
 
                if (defproxy.fwdfor_hdr_len) {
                        curproxy->fwdfor_hdr_len  = defproxy.fwdfor_hdr_len;
@@ -1649,6 +1650,51 @@
                                }
                        } /* end while loop */
                }
+               else if (!strcmp(args[1], "originaldst")) {
+                       int cur_arg;
+
+                       /* insert x-original-dst field, but not for the IP 
address listed as an except.
+                        * set default options (ie: bitfield, header name, etc) 
+                        */
+
+                       curproxy->options |= PR_O_ORGDST;
+
+                       free(curproxy->orgdst_hdr_name);
+                       curproxy->orgdst_hdr_name = 
strdup(DEF_XORIGINALDST_HDR);
+                       curproxy->orgdst_hdr_len  = 
strlen(DEF_XORIGINALDST_HDR);
+
+                       /* loop to go through arguments - start at 2, since 0+1 
= "option" "forwardfor" */
+                       cur_arg = 2;
+                       while (*(args[cur_arg])) {
+                               if (!strcmp(args[cur_arg], "except")) {
+                                       /* suboption except - needs additional 
argument for it */
+                                       if (!*(args[cur_arg+1]) || 
!str2net(args[cur_arg+1], &curproxy->except_dst, &curproxy->except_mask_dst)) {
+                                               Alert("parsing [%s:%d] : '%s %s 
%s' expects <address>[/mask] as argument.\n",
+                                                     file, linenum, args[0], 
args[1], args[cur_arg]);
+                                               return -1;
+                                       }
+                                       /* flush useless bits */
+                                       curproxy->except_dst.s_addr &= 
curproxy->except_mask_dst.s_addr;
+                                       cur_arg += 2;
+                               } else if (!strcmp(args[cur_arg], "header")) {
+                                       /* suboption header - needs additional 
argument for it */
+                                       if (*(args[cur_arg+1]) == 0) {
+                                               Alert("parsing [%s:%d] : '%s %s 
%s' expects <header_name> as argument.\n",
+                                                     file, linenum, args[0], 
args[1], args[cur_arg]);
+                                               return -1;
+                                       }
+                                       free(curproxy->orgdst_hdr_name);
+                                       curproxy->orgdst_hdr_name = 
strdup(args[cur_arg+1]);
+                                       curproxy->orgdst_hdr_len  = 
strlen(curproxy->orgdst_hdr_name);
+                                       cur_arg += 2;
+                               } else {
+                                       /* unknown suboption - catchall */
+                                       Alert("parsing [%s:%d] : '%s %s' only 
supports optional values: 'except' and 'header'.\n",
+                                             file, linenum, args[0], args[1]);
+                                       return -1;
+                               }
+                       } /* end while loop */
+               }
                else {
                        Alert("parsing [%s:%d] : unknown option '%s'.\n", file, 
linenum, args[1]);
                        return -1;
diff -Nur haproxy-1.3.17/src/proto_http.c 
haproxy-1.3.17-x-original-dst/src/proto_http.c
--- haproxy-1.3.17/src/proto_http.c     2009-03-29 15:26:57.000000000 +0200
+++ haproxy-1.3.17-x-original-dst/src/proto_http.c      2009-04-15 
18:41:55.000000000 +0200
@@ -41,6 +41,7 @@
 #include <proto/acl.h>
 #include <proto/backend.h>
 #include <proto/buffers.h>
+#include <proto/client.h>
 #include <proto/dumpstats.h>
 #include <proto/fd.h>
 #include <proto/log.h>
@@ -2196,7 +2197,52 @@
        }
 
        /*
-        * 10: add "Connection: close" if needed and not yet set.
+        * 10: add X-Original-Dst if either the frontend or the backend
+        * asks for it.
+        */
+       if ((s->fe->options | s->be->options) & PR_O_ORGDST) {
+
+               /* FIXME: don't know if IPv6 can handle that case too. */
+               if (s->cli_addr.ss_family == AF_INET) {
+                       /* Add an X-Original-Dst header unless the source IP is
+                        * in the 'except' network range.
+                        */
+                       if (!(s->flags & SN_FRT_ADDR_SET))
+                               get_frt_addr(s);
+
+                       if ((!s->fe->except_mask_dst.s_addr ||
+                            (((struct sockaddr_in 
*)&s->frt_addr)->sin_addr.s_addr & s->fe->except_mask_dst.s_addr)
+                            != s->fe->except_dst.s_addr) &&
+                           (!s->be->except_mask_dst.s_addr ||
+                            (((struct sockaddr_in 
*)&s->frt_addr)->sin_addr.s_addr & s->be->except_mask_dst.s_addr)
+                            != s->be->except_dst.s_addr)) {
+                               int len;
+                               unsigned char *pn;
+                               pn = (unsigned char *)&((struct sockaddr_in 
*)&s->frt_addr)->sin_addr;
+
+                               /* Note: we rely on the backend to get the 
header name to be used for
+                                * x-original-dst, because the header is really 
meant for the backends.
+                                * However, if the backend did not specify any 
option, we have to rely
+                                * on the frontend's header name.
+                                */
+                               if (s->be->orgdst_hdr_len) {
+                                       len = s->be->orgdst_hdr_len;
+                                       memcpy(trash, s->be->orgdst_hdr_name, 
len);
+                               } else {
+                                       len = s->fe->orgdst_hdr_len;
+                                       memcpy(trash, s->fe->orgdst_hdr_name, 
len);
+                                       }
+                               len += sprintf(trash + len, ": %d.%d.%d.%d", 
pn[0], pn[1], pn[2], pn[3]);
+
+                               if (unlikely(http_header_add_tail2(req, 
&txn->req,
+                                                                  
&txn->hdr_idx, trash, len)) < 0)
+                                       goto return_bad_req;
+                       }
+               }
+       }
+
+       /*
+        * 11: add "Connection: close" if needed and not yet set.
         * Note that we do not need to add it in case of HTTP/1.0.
         */
        if (!(s->flags & SN_CONN_CLOSED) &&

Reply via email to