BUG/MAJOR: fix listening IP address storage for frontends
When compiled with GCC 6, the IP address specified for a frontend was
ignored and HAProxy was listening on all addresses instead. This is
caused by an incomplete copy of a "struct sockaddr_storage".
With the GNU Libc, "struct sockaddr_storage" is defined as this:
struct sockaddr_storage
{
sa_family_t ss_family;
unsigned long int __ss_align;
char __ss_padding[(128 - (2 * sizeof (unsigned long int)))];
};
Doing an aggregate copy (ss1 = ss2) is different than using memcpy():
only members of the aggregate have to be copied. Notably, padding can be
or not be copied. In GCC 6, some optimizations use this fact and if a
"struct sockaddr_storage" contains a "struct sockaddr_in", the port and
the address are part of the padding (between sa_family and __ss_align)
and can be not copied over.
Therefore, we replace any aggregate copy by a memcpy(). There is another
place using the same pattern. We also fix a function receiving a "struct
sockaddr_storage" by copy instead of by reference. Since it only needs a
read-only copy, the function is converted to request a reference.
diff --git a/include/proto/proto_http.h b/include/proto/proto_http.h
index 4d8f536..0aa6643 100644
--- a/include/proto/proto_http.h
+++ b/include/proto/proto_http.h
@@ -110,7 +110,7 @@
int http_transform_header_str(struct stream* s, struct http_msg *msg, const char* name,
unsigned int name_len, const char *str, struct my_regex *re,
int action);
-void inet_set_tos(int fd, struct sockaddr_storage from, int tos);
+void inet_set_tos(int fd, const struct sockaddr_storage *from, int tos);
void http_perform_server_redirect(struct stream *s, struct stream_interface *si);
void http_return_srv_error(struct stream *s, struct stream_interface *si);
void http_capture_bad_message(struct error_snapshot *es, struct stream *s,
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 3fee54e..48e584c 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -287,7 +287,7 @@
}
/* OK the address looks correct */
- ss = *ss2;
+ memcpy(&ss, ss2, sizeof(ss));
for (; port <= end; port++) {
l = calloc(1, sizeof(*l));
diff --git a/src/connection.c b/src/connection.c
index 330f3ef..5515188 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -744,7 +744,7 @@
const char pp2_signature[] = PP2_SIGNATURE;
int ret = 0;
struct proxy_hdr_v2 *hdr = (struct proxy_hdr_v2 *)buf;
- struct sockaddr_storage null_addr = {0};
+ struct sockaddr_storage null_addr = { .ss_family = 0 };
struct sockaddr_storage *src = &null_addr;
struct sockaddr_storage *dst = &null_addr;
diff --git a/src/hlua.c b/src/hlua.c
index f6eb8aa..94f9742 100644
--- a/src/hlua.c
+++ b/src/hlua.c
@@ -4781,7 +4781,7 @@
tos = MAY_LJMP(luaL_checkinteger(L, 2));
if ((cli_conn = objt_conn(htxn->s->sess->origin)) && conn_ctrl_ready(cli_conn))
- inet_set_tos(cli_conn->t.sock.fd, cli_conn->addr.from, tos);
+ inet_set_tos(cli_conn->t.sock.fd, &cli_conn->addr.from, tos);
return 0;
}
diff --git a/src/proto_http.c b/src/proto_http.c
index 21ad131..4165042 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -3189,15 +3189,15 @@
/* Sets the TOS header in IPv4 and the traffic class header in IPv6 packets
* (as per RFC3260 #4 and BCP37 #4.2 and #5.2).
*/
-void inet_set_tos(int fd, struct sockaddr_storage from, int tos)
+void inet_set_tos(int fd, const struct sockaddr_storage *from, int tos)
{
#ifdef IP_TOS
- if (from.ss_family == AF_INET)
+ if (from->ss_family == AF_INET)
setsockopt(fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos));
#endif
#ifdef IPV6_TCLASS
- if (from.ss_family == AF_INET6) {
- if (IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)&from)->sin6_addr))
+ if (from->ss_family == AF_INET6) {
+ if (IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)from)->sin6_addr))
/* v4-mapped addresses need IP_TOS */
setsockopt(fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos));
else
@@ -3363,7 +3363,7 @@
case ACT_HTTP_SET_TOS:
if ((cli_conn = objt_conn(sess->origin)) && conn_ctrl_ready(cli_conn))
- inet_set_tos(cli_conn->t.sock.fd, cli_conn->addr.from, rule->arg.tos);
+ inet_set_tos(cli_conn->t.sock.fd, &cli_conn->addr.from, rule->arg.tos);
break;
case ACT_HTTP_SET_MARK:
@@ -3646,7 +3646,7 @@
case ACT_HTTP_SET_TOS:
if ((cli_conn = objt_conn(sess->origin)) && conn_ctrl_ready(cli_conn))
- inet_set_tos(cli_conn->t.sock.fd, cli_conn->addr.from, rule->arg.tos);
+ inet_set_tos(cli_conn->t.sock.fd, &cli_conn->addr.from, rule->arg.tos);
break;
case ACT_HTTP_SET_MARK:
diff --git a/src/proto_tcp.c b/src/proto_tcp.c
index a44912a..bbe12e2 100644
--- a/src/proto_tcp.c
+++ b/src/proto_tcp.c
@@ -435,7 +435,7 @@
struct sockaddr_storage sa;
ret = 1;
- sa = src->source_addr;
+ memcpy(&sa, &src->source_addr, sizeof(sa));
do {
/* note: in case of retry, we may have to release a previously