]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
BUG/MAJOR: fix listening IP address storage for frontends
authorVincent Bernat <vincent@bernat.im>
Wed, 18 May 2016 14:17:44 +0000 (16:17 +0200)
committerWilly Tarreau <w@1wt.eu>
Thu, 19 May 2016 08:43:24 +0000 (10:43 +0200)
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.

include/proto/proto_http.h
src/cfgparse.c
src/connection.c
src/hlua.c
src/proto_http.c
src/proto_tcp.c

index 4d8f5365b625188d46b426dca8be01ae71dd5ee2..0aa6643b98dacbc94f319bd82f6418ca4bf04a68 100644 (file)
@@ -110,7 +110,7 @@ void http_set_status(unsigned int status, struct stream *s);
 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,
index 3fee54e0db1d34fe8712e4e8f45571c93989da79..48e584cf73e7173ae99618d0f5324dd52b77419b 100644 (file)
@@ -287,7 +287,7 @@ int str2listener(char *str, struct proxy *curproxy, struct bind_conf *bind_conf,
                }
 
                /* OK the address looks correct */
-               ss = *ss2;
+               memcpy(&ss, ss2, sizeof(ss));
 
                for (; port <= end; port++) {
                        l = calloc(1, sizeof(*l));
index 330f3efbc995e0b1cf1c24ff063a89fef1c53c91..5515188c6b101ed7385253fb3bad2a90597cffe9 100644 (file)
@@ -744,7 +744,7 @@ int make_proxy_line_v2(char *buf, int buf_len, struct server *srv, struct connec
        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;
 
index f6eb8aa80ee01369cb2e32c05c517919268b3f15..94f97429c8950a92e9f78f473e22102e51ffee05 100644 (file)
@@ -4781,7 +4781,7 @@ __LJMP static int hlua_txn_set_tos(lua_State *L)
        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;
 }
index 21ad131c9f43a85b2c22e748e96c1f7b054260b0..416504247a8df068eef2dcc4325e959b906de41e 100644 (file)
@@ -3189,15 +3189,15 @@ int http_handle_stats(struct stream *s, struct channel *req)
 /* 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 @@ resume_execution:
 
                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 @@ resume_execution:
 
                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:
index a44912af46549c2450b0edfd955e966e9a7b9414..bbe12e2d4c0d9eaa1db9e67b0b789e52fb815cae 100644 (file)
@@ -435,7 +435,7 @@ int tcp_connect_server(struct connection *conn, int data, int delack)
                        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