]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: http-ana: Add IPv6 support for forwardfor and orignialto options
authorChristopher Faulet <cfaulet@haproxy.com>
Fri, 26 Feb 2021 08:19:15 +0000 (09:19 +0100)
committerChristopher Faulet <cfaulet@haproxy.com>
Fri, 26 Feb 2021 12:52:48 +0000 (13:52 +0100)
A network may be specified to avoid header addition for "forwardfor" and
"orignialto" option via the "except" parameter. However, only IPv4
networks/addresses are supported. This patch adds the support of IPv6.

To do so, the net_addr structure is used to store the parameter value in the
proxy structure. And ipcmp2net() function is used to perform the comparison.

This patch should fix the issue #1145. It depends on the following commit:

  * c6ce0ab MINOR: tools: Add function to compare an address to a network address
  * 5587287 MINOR: tools: Add net_addr structure describing a network addess

doc/configuration.txt
include/haproxy/proxy-t.h
src/cfgparse-listen.c
src/http_ana.c
src/proxy.c

index 06d0480f5d4e8f3d254d905cfac664e76a83ae0d..7f344d6afd099d282a667385a428b3e38423432a 100644 (file)
@@ -7973,7 +7973,7 @@ option forwardfor [ except <network> ] [ header <name> ] [ if-none ]
   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.
+  private networks or 127.0.0.1. IPv4 and IPv6 are both supported.
 
   Alternatively, the keyword "if-none" states that the header will only be
   added if it is not present. This should only be used in perfectly trusted
@@ -8773,7 +8773,7 @@ option originalto [ except <network> ] [ header <name> ]
   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.
+  private networks or 127.0.0.1.  IPv4 and IPv6 are both supported.
 
   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
index 3fdd09d625419313838041a9b01a3b122dfaee80..009a7bd00bf7b2e76fd9a0608a3e955b82a45417 100644 (file)
@@ -41,6 +41,7 @@
 #include <haproxy/stats-t.h>
 #include <haproxy/tcpcheck-t.h>
 #include <haproxy/thread-t.h>
+#include <haproxy/tools-t.h>
 #include <haproxy/uri_auth-t.h>
 
 /* values for proxy->mode */
@@ -341,9 +342,8 @@ struct proxy {
        unsigned int fe_sps_lim;                /* limit on new sessions per second on the frontend */
        unsigned int fullconn;                  /* #conns on backend above which servers are used at full load */
        unsigned int tot_fe_maxconn;            /* #maxconn of frontends linked to that backend, it is used to compute fullconn */
-       struct in_addr except_net, except_mask; /* don't x-forward-for for this address. FIXME: should support IPv6 */
-       struct in_addr except_to;               /* don't x-original-to for this address. */
-       struct in_addr except_mask_to;          /* the netmask for except_to. */
+       struct net_addr except_xff_net;         /* don't x-forward-for for this address. */
+       struct net_addr except_xot_net;         /* don't x-original-to for this address. */
        char *fwdfor_hdr_name;                  /* header to use - default: "x-forwarded-for" */
        char *orgto_hdr_name;                   /* header to use - default: "x-original-to" */
        int fwdfor_hdr_len;                     /* length of "x-forwarded-for" header */
index bb57be9957dbb169505454465614e37d61fa733c..34816c5593480c39596f48036c5676e6b839008b 100644 (file)
@@ -2107,20 +2107,35 @@ stats_error_parsing:
                        free(curproxy->fwdfor_hdr_name);
                        curproxy->fwdfor_hdr_name = strdup(DEF_XFORWARDFOR_HDR);
                        curproxy->fwdfor_hdr_len  = strlen(DEF_XFORWARDFOR_HDR);
+                       curproxy->except_xff_net.family = AF_UNSPEC;
 
                        /* 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") == 0) {
+                                       unsigned char mask;
+                                       int i;
+
                                        /* suboption except - needs additional argument for it */
-                                       if (!*(args[cur_arg+1]) || !str2net(args[cur_arg+1], 1, &curproxy->except_net, &curproxy->except_mask)) {
+                                       if (*(args[cur_arg+1]) &&
+                                           str2net(args[cur_arg+1], 1, &curproxy->except_xff_net.addr.v4.ip, &curproxy->except_xff_net.addr.v4.mask)) {
+                                               curproxy->except_xff_net.family = AF_INET;
+                                               curproxy->except_xff_net.addr.v4.ip.s_addr &= curproxy->except_xff_net.addr.v4.mask.s_addr;
+                                       }
+                                       else if (*(args[cur_arg+1]) &&
+                                                str62net(args[cur_arg+1], &curproxy->except_xff_net.addr.v6.ip, &mask)) {
+                                               curproxy->except_xff_net.family = AF_INET6;
+                                               len2mask6(mask, &curproxy->except_xff_net.addr.v6.mask);
+                                               for (i = 0; i < 16; i++)
+                                                       curproxy->except_xff_net.addr.v6.ip.s6_addr[i] &= curproxy->except_xff_net.addr.v6.mask.s6_addr[i];
+                                       }
+                                       else {
                                                ha_alert("parsing [%s:%d] : '%s %s %s' expects <address>[/mask] as argument.\n",
                                                         file, linenum, args[0], args[1], args[cur_arg]);
                                                err_code |= ERR_ALERT | ERR_FATAL;
                                                goto out;
                                        }
                                        /* flush useless bits */
-                                       curproxy->except_net.s_addr &= curproxy->except_mask.s_addr;
                                        cur_arg += 2;
                                } else if (strcmp(args[cur_arg], "header") == 0) {
                                        /* suboption header - needs additional argument for it */
@@ -2158,20 +2173,34 @@ stats_error_parsing:
                        free(curproxy->orgto_hdr_name);
                        curproxy->orgto_hdr_name = strdup(DEF_XORIGINALTO_HDR);
                        curproxy->orgto_hdr_len  = strlen(DEF_XORIGINALTO_HDR);
+                       curproxy->except_xot_net.family = AF_UNSPEC;
 
                        /* loop to go through arguments - start at 2, since 0+1 = "option" "originalto" */
                        cur_arg = 2;
                        while (*(args[cur_arg])) {
                                if (strcmp(args[cur_arg], "except") == 0) {
+                                       unsigned char mask;
+                                       int i;
+
                                        /* suboption except - needs additional argument for it */
-                                       if (!*(args[cur_arg+1]) || !str2net(args[cur_arg+1], 1, &curproxy->except_to, &curproxy->except_mask_to)) {
+                                       if (*(args[cur_arg+1]) &&
+                                           str2net(args[cur_arg+1], 1, &curproxy->except_xot_net.addr.v4.ip, &curproxy->except_xot_net.addr.v4.mask)) {
+                                               curproxy->except_xot_net.family = AF_INET;
+                                               curproxy->except_xot_net.addr.v4.ip.s_addr &= curproxy->except_xot_net.addr.v4.mask.s_addr;
+                                       }
+                                       else if (*(args[cur_arg+1]) &&
+                                                str62net(args[cur_arg+1], &curproxy->except_xot_net.addr.v6.ip, &mask)) {
+                                               curproxy->except_xot_net.family = AF_INET6;
+                                               len2mask6(mask, &curproxy->except_xot_net.addr.v6.mask);
+                                               for (i = 0; i < 16; i++)
+                                                       curproxy->except_xot_net.addr.v6.ip.s6_addr[i] &= curproxy->except_xot_net.addr.v6.mask.s6_addr[i];
+                                       }
+                                       else {
                                                ha_alert("parsing [%s:%d] : '%s %s %s' expects <address>[/mask] as argument.\n",
                                                         file, linenum, args[0], args[1], args[cur_arg]);
                                                err_code |= ERR_ALERT | ERR_FATAL;
                                                goto out;
                                        }
-                                       /* flush useless bits */
-                                       curproxy->except_to.s_addr &= curproxy->except_mask_to.s_addr;
                                        cur_arg += 2;
                                } else if (strcmp(args[cur_arg], "header") == 0) {
                                        /* suboption header - needs additional argument for it */
index d18b4ccb0c184ca29d1ba7ffca4b19c004836d95..5536c0402f9a3515afd1f42c115ce4f422aa38a1 100644 (file)
@@ -673,12 +673,8 @@ int http_process_request(struct stream *s, struct channel *req, int an_bit)
                        /* Add an X-Forwarded-For header unless the source IP is
                         * in the 'except' network range.
                         */
-                       if ((!sess->fe->except_mask.s_addr ||
-                            (((struct sockaddr_in *)cli_conn->src)->sin_addr.s_addr & sess->fe->except_mask.s_addr)
-                            != sess->fe->except_net.s_addr) &&
-                           (!s->be->except_mask.s_addr ||
-                            (((struct sockaddr_in *)cli_conn->src)->sin_addr.s_addr & s->be->except_mask.s_addr)
-                            != s->be->except_net.s_addr)) {
+                       if (ipcmp2net(cli_conn->src, &sess->fe->except_xff_net) &&
+                           ipcmp2net(cli_conn->src, &s->be->except_xff_net)) {
                                unsigned char *pn = (unsigned char *)&((struct sockaddr_in *)cli_conn->src)->sin_addr;
 
                                /* Note: we rely on the backend to get the header name to be used for
@@ -692,23 +688,26 @@ int http_process_request(struct stream *s, struct channel *req, int an_bit)
                        }
                }
                else if (cli_conn && conn_get_src(cli_conn) && cli_conn->src->ss_family == AF_INET6) {
-                       /* FIXME: for the sake of completeness, we should also support
-                        * 'except' here, although it is mostly useless in this case.
+                       /* Add an X-Forwarded-For header unless the source IP is
+                        * in the 'except' network range.
                         */
-                       char pn[INET6_ADDRSTRLEN];
+                       if (ipcmp2net(cli_conn->src, &sess->fe->except_xff_net) &&
+                           ipcmp2net(cli_conn->src, &s->be->except_xff_net)) {
+                               char pn[INET6_ADDRSTRLEN];
 
-                       inet_ntop(AF_INET6,
-                                 (const void *)&((struct sockaddr_in6 *)(cli_conn->src))->sin6_addr,
-                                 pn, sizeof(pn));
+                               inet_ntop(AF_INET6,
+                                         (const void *)&((struct sockaddr_in6 *)(cli_conn->src))->sin6_addr,
+                                         pn, sizeof(pn));
 
-                       /* Note: we rely on the backend to get the header name to be used for
-                        * x-forwarded-for, 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.
-                        */
-                       chunk_printf(&trash, "%s", pn);
-                       if (unlikely(!http_add_header(htx, hdr, ist2(trash.area, trash.data))))
-                               goto return_int_err;
+                               /* Note: we rely on the backend to get the header name to be used for
+                                * x-forwarded-for, 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.
+                                */
+                               chunk_printf(&trash, "%s", pn);
+                               if (unlikely(!http_add_header(htx, hdr, ist2(trash.area, trash.data))))
+                                       goto return_int_err;
+                       }
                }
        }
 
@@ -717,20 +716,15 @@ int http_process_request(struct stream *s, struct channel *req, int an_bit)
         * asks for it.
         */
        if ((sess->fe->options | s->be->options) & PR_O_ORGTO) {
+               struct ist hdr = ist2(s->be->orgto_hdr_len ? s->be->orgto_hdr_name : sess->fe->orgto_hdr_name,
+                                     s->be->orgto_hdr_len ? s->be->orgto_hdr_len  : sess->fe->orgto_hdr_len);
 
-               /* FIXME: don't know if IPv6 can handle that case too. */
                if (cli_conn && conn_get_dst(cli_conn) && cli_conn->dst->ss_family == AF_INET) {
                        /* Add an X-Original-To header unless the destination IP is
                         * in the 'except' network range.
                         */
-                       if (cli_conn->dst->ss_family == AF_INET &&
-                           ((!sess->fe->except_mask_to.s_addr ||
-                             (((struct sockaddr_in *)cli_conn->dst)->sin_addr.s_addr & sess->fe->except_mask_to.s_addr)
-                             != sess->fe->except_to.s_addr) &&
-                            (!s->be->except_mask_to.s_addr ||
-                             (((struct sockaddr_in *)cli_conn->dst)->sin_addr.s_addr & s->be->except_mask_to.s_addr)
-                             != s->be->except_to.s_addr))) {
-                               struct ist hdr;
+                       if (ipcmp2net(cli_conn->dst, &sess->fe->except_xot_net) &&
+                           ipcmp2net(cli_conn->dst, &s->be->except_xot_net)) {
                                unsigned char *pn = (unsigned char *)&((struct sockaddr_in *)cli_conn->dst)->sin_addr;
 
                                /* Note: we rely on the backend to get the header name to be used for
@@ -738,16 +732,33 @@ int http_process_request(struct stream *s, struct channel *req, int an_bit)
                                 * However, if the backend did not specify any option, we have to rely
                                 * on the frontend's header name.
                                 */
-                               if (s->be->orgto_hdr_len)
-                                       hdr = ist2(s->be->orgto_hdr_name, s->be->orgto_hdr_len);
-                               else
-                                       hdr = ist2(sess->fe->orgto_hdr_name, sess->fe->orgto_hdr_len);
-
                                chunk_printf(&trash, "%d.%d.%d.%d", pn[0], pn[1], pn[2], pn[3]);
                                if (unlikely(!http_add_header(htx, hdr, ist2(trash.area, trash.data))))
                                        goto return_int_err;
                        }
                }
+               else if (cli_conn && conn_get_dst(cli_conn) && cli_conn->dst->ss_family == AF_INET6) {
+                       /* Add an X-Original-To header unless the source IP is
+                        * in the 'except' network range.
+                        */
+                       if (ipcmp2net(cli_conn->dst, &sess->fe->except_xot_net) &&
+                           ipcmp2net(cli_conn->dst, &s->be->except_xot_net)) {
+                               char pn[INET6_ADDRSTRLEN];
+
+                               inet_ntop(AF_INET6,
+                                         (const void *)&((struct sockaddr_in6 *)(cli_conn->dst))->sin6_addr,
+                                         pn, sizeof(pn));
+
+                               /* Note: we rely on the backend to get the header name to be used for
+                                * x-forwarded-for, 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.
+                                */
+                               chunk_printf(&trash, "%s", pn);
+                               if (unlikely(!http_add_header(htx, hdr, ist2(trash.area, trash.data))))
+                                       goto return_int_err;
+                       }
+               }
        }
 
        /* If we have no server assigned yet and we're balancing on url_param
index 5edbbfe238b42de51d90659f5a9cdf6ec97caac0..abca60940576c867cd85be931fcf5a7c2e94f65f 100644 (file)
@@ -1229,10 +1229,8 @@ struct proxy *alloc_new_proxy(const char *name, unsigned int cap, const char *fi
        curproxy->no_options = defproxy->no_options;
        curproxy->no_options2 = defproxy->no_options2;
        curproxy->bind_proc = defproxy->bind_proc;
-       curproxy->except_net = defproxy->except_net;
-       curproxy->except_mask = defproxy->except_mask;
-       curproxy->except_to = defproxy->except_to;
-       curproxy->except_mask_to = defproxy->except_mask_to;
+       curproxy->except_xff_net = defproxy->except_xff_net;
+       curproxy->except_xot_net = defproxy->except_xot_net;
        curproxy->retry_type = defproxy->retry_type;
 
        if (defproxy->fwdfor_hdr_len) {