MEDIUM: http-ana: Add IPv6 support for forwardfor and orignialto options
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
diff --git a/src/cfgparse-listen.c b/src/cfgparse-listen.c
index bb57be9..34816c5 100644
--- a/src/cfgparse-listen.c
+++ b/src/cfgparse-listen.c
@@ -2107,20 +2107,35 @@
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 @@
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 */
diff --git a/src/http_ana.c b/src/http_ana.c
index d18b4cc..5536c04 100644
--- a/src/http_ana.c
+++ b/src/http_ana.c
@@ -673,12 +673,8 @@
/* 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 @@
}
}
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 @@
* 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 @@
* 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 5edbbfe..abca609 100644
--- a/src/proxy.c
+++ b/src/proxy.c
@@ -1229,10 +1229,8 @@
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) {