diff --git a/doc/configuration.txt b/doc/configuration.txt index 06d0480f5..7f344d6af 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -7973,7 +7973,7 @@ option forwardfor [ except ] [ header ] [ 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 ] [ header ] 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 diff --git a/include/haproxy/proxy-t.h b/include/haproxy/proxy-t.h index 3fdd09d62..009a7bd00 100644 --- a/include/haproxy/proxy-t.h +++ b/include/haproxy/proxy-t.h @@ -41,6 +41,7 @@ #include #include #include +#include #include /* 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 */ diff --git a/src/cfgparse-listen.c b/src/cfgparse-listen.c index bb57be995..34816c559 100644 --- a/src/cfgparse-listen.c +++ b/src/cfgparse-listen.c @@ -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
[/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
[/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 */ diff --git a/src/http_ana.c b/src/http_ana.c index d18b4ccb0..5536c0402 100644 --- a/src/http_ana.c +++ b/src/http_ana.c @@ -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 diff --git a/src/proxy.c b/src/proxy.c index 5edbbfe23..abca60940 100644 --- a/src/proxy.c +++ b/src/proxy.c @@ -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) {