From 6f5ccb15896173fc12f4ea183b0fb2e35b185d84 Mon Sep 17 00:00:00 2001 From: David du Colombier Date: Thu, 10 Mar 2011 22:26:24 +0100 Subject: [PATCH] [MEDIUM] add internal support for IPv6 server addresses This patch turns internal server addresses to sockaddr_storage to store IPv6 addresses, and makes the connect() function use it. This code already works but some caveats with getaddrinfo/gethostbyname still need to be sorted out while the changes had to be merged at this stage of internal architecture changes. So for now the config parser will not emit an IPv6 address yet so that user experience remains unchanged. This change should have absolutely zero user-visible effect, otherwise it's a bug introduced during the merge, that should be reported ASAP. --- include/common/standard.h | 28 ++++++++++--- include/proto/proto_tcp.h | 4 +- include/types/peers.h | 2 +- include/types/proxy.h | 6 +-- include/types/server.h | 8 ++-- include/types/stream_interface.h | 4 +- src/backend.c | 69 ++++++++++++++++++++------------ src/cfgparse.c | 60 ++++++++++++++++++--------- src/checks.c | 64 ++++++++++++++++++++--------- src/dumpstats.c | 18 +++++++-- src/peers.c | 2 +- src/proto_http.c | 6 +-- src/proto_tcp.c | 49 +++++++++++++++-------- src/standard.c | 54 +++++++++++++------------ 14 files changed, 244 insertions(+), 130 deletions(-) diff --git a/include/common/standard.h b/include/common/standard.h index 3990e6ffa..712d94128 100644 --- a/include/common/standard.h +++ b/include/common/standard.h @@ -158,7 +158,7 @@ struct sockaddr_un *str2sun(const char *str); * The format is "addr:port", where "addr" can be a dotted IPv4 address, * a host name, or empty or "*" to indicate INADDR_ANY. */ -struct sockaddr_in *str2sa(char *str); +struct sockaddr_storage *str2sa(char *str); /* * converts to a struct sockaddr_in* which is locally allocated, and a @@ -169,7 +169,7 @@ struct sockaddr_in *str2sa(char *str); * "addr[:port[-port]]", where "addr" can be a dotted IPv4 address, a host * name, or empty or "*" to indicate INADDR_ANY. */ -struct sockaddr_in *str2sa_range(char *str, int *low, int *high); +struct sockaddr_storage *str2sa_range(char *str, int *low, int *high); /* converts to a struct in_addr containing a network mask. It can be * passed in dotted form (255.255.255.0) or in CIDR form (24). It returns 1 @@ -188,12 +188,12 @@ int str2net(const char *str, struct in_addr *addr, struct in_addr *mask); /* * Parse IP address found in url. */ -int url2ip(const char *addr, struct in_addr *dst); +int url2ipv4(const char *addr, struct in_addr *dst); /* - * Resolve destination server from URL. Convert to a sockaddr_in*. + * Resolve destination server from URL. Convert to a sockaddr_storage*. */ -int url2sa(const char *url, int ulen, struct sockaddr_in *addr); +int url2sa(const char *url, int ulen, struct sockaddr_storage *addr); /* will try to encode the string replacing all characters tagged in * with the hexadecimal representation of their ASCII-code (2 digits) @@ -466,4 +466,22 @@ static inline unsigned int __full_hash(unsigned int a) return a * 3221225473U; } +/* returns non-zero if addr has a valid and non-null IPv4 or IPv6 address, + * otherwise zero. + */ +static inline int is_addr(struct sockaddr_storage *addr) +{ + int i; + + switch (addr->ss_family) { + case AF_INET: + return *(int *)&((struct sockaddr_in *)&addr)->sin_addr; + case AF_INET6: + for (i = 0; i < sizeof(struct in6_addr) / sizeof(int); i++) + if (((int *)&((struct sockaddr_in6 *)addr)->sin6_addr)[i] != 0) + return ((int *)&((struct sockaddr_in6 *)addr)->sin6_addr)[i]; + } + return 0; +} + #endif /* _COMMON_STANDARD_H */ diff --git a/include/proto/proto_tcp.h b/include/proto/proto_tcp.h index b871003a4..26e06df8c 100644 --- a/include/proto/proto_tcp.h +++ b/include/proto/proto_tcp.h @@ -27,10 +27,10 @@ #include #include -int tcpv4_bind_socket(int fd, int flags, struct sockaddr_in *local, struct sockaddr_in *remote); +int tcp_bind_socket(int fd, int flags, struct sockaddr_storage *local, struct sockaddr_storage *remote); void tcpv4_add_listener(struct listener *listener); void tcpv6_add_listener(struct listener *listener); -int tcpv4_connect_server(struct stream_interface *si); +int tcp_connect_server(struct stream_interface *si); int tcp_inspect_request(struct session *s, struct buffer *req, int an_bit); int tcp_inspect_response(struct session *s, struct buffer *rep, int an_bit); int tcp_persist_rdp_cookie(struct session *s, struct buffer *req, int an_bit); diff --git a/include/types/peers.h b/include/types/peers.h index b5b92c2b7..24ab804b1 100644 --- a/include/types/peers.h +++ b/include/types/peers.h @@ -72,7 +72,7 @@ struct peer { int line; /* line where the section appears */ } conf; /* config information */ time_t last_change; - struct sockaddr_in addr; /* peer address */ + struct sockaddr_storage addr; /* peer address */ struct peer *next; /* next peer in the list */ }; diff --git a/include/types/proxy.h b/include/types/proxy.h index e724a25b4..95736b183 100644 --- a/include/types/proxy.h +++ b/include/types/proxy.h @@ -182,7 +182,7 @@ struct proxy { unsigned int fe_req_ana, be_req_ana; /* bitmap of common request protocol analysers for the frontend and backend */ unsigned int fe_rsp_ana, be_rsp_ana; /* bitmap of common response protocol analysers for the frontend and backend */ int mode; /* mode = PR_MODE_TCP, PR_MODE_HTTP or PR_MODE_HEALTH */ - struct sockaddr_in dispatch_addr; /* the default address to connect to */ + struct sockaddr_storage dispatch_addr; /* the default address to connect to */ union { struct proxy *be; /* default backend, or NULL if none set */ char *name; /* default backend name during config parse */ @@ -271,9 +271,9 @@ struct proxy { int conn_retries; /* maximum number of connect retries */ int cap; /* supported capabilities (PR_CAP_*) */ - struct sockaddr_in source_addr; /* the address to which we want to bind for connect() */ + struct sockaddr_storage source_addr; /* the address to which we want to bind for connect() */ #if defined(CONFIG_HAP_CTTPROXY) || defined(CONFIG_HAP_LINUX_TPROXY) - struct sockaddr_in tproxy_addr; /* non-local address we want to bind to for connect() */ + struct sockaddr_storage tproxy_addr; /* non-local address we want to bind to for connect() */ char *bind_hdr_name; /* bind to this header name if defined */ int bind_hdr_len; /* length of the name of the header above */ int bind_hdr_occ; /* occurrence number of header above: >0 = from first, <0 = from end, 0=disabled */ diff --git a/include/types/server.h b/include/types/server.h index 00251d8ec..c697457ea 100644 --- a/include/types/server.h +++ b/include/types/server.h @@ -102,10 +102,10 @@ struct server { struct list pendconns; /* pending connections */ struct task *check; /* the task associated to the health check processing */ - struct sockaddr_in addr; /* the address to connect to */ - struct sockaddr_in source_addr; /* the address to which we want to bind for connect() */ + struct sockaddr_storage addr; /* the address to connect to */ + struct sockaddr_storage source_addr; /* the address to which we want to bind for connect() */ #if defined(CONFIG_HAP_CTTPROXY) || defined(CONFIG_HAP_LINUX_TPROXY) - struct sockaddr_in tproxy_addr; /* non-local address we want to bind to for connect() */ + struct sockaddr_storage tproxy_addr; /* non-local address we want to bind to for connect() */ char *bind_hdr_name; /* bind to this header name if defined */ int bind_hdr_len; /* length of the name of the header above */ int bind_hdr_occ; /* occurrence number of header above: >0 = from first, <0 = from end, 0=disabled */ @@ -116,7 +116,7 @@ struct server { struct server *tracknext, *tracked; /* next server in a tracking list, tracked server */ char *trackit; /* temporary variable to make assignment deferrable */ - struct sockaddr_in check_addr; /* the address to check, if different from */ + struct sockaddr_storage check_addr; /* the address to check, if different from */ short check_port; /* the port to use for the health checks */ int health; /* 0->rise-1 = bad; rise->rise+fall-1 = good */ int consecutive_errors; /* current number of consecutive errors */ diff --git a/include/types/stream_interface.h b/include/types/stream_interface.h index 39caf8fe7..16a261fb5 100644 --- a/include/types/stream_interface.h +++ b/include/types/stream_interface.h @@ -183,8 +183,8 @@ struct stream_interface { struct sockaddr_storage to; /* the address reached by the client if SN_FRT_ADDR_SET is set */ } c; /* client side */ struct { - struct sockaddr_in from; /* the address to spoof when connecting to the server (transparent mode) */ - struct sockaddr_in to; /* the address to connect to */ + struct sockaddr_storage from; /* the address to spoof when connecting to the server (transparent mode) */ + struct sockaddr_storage to; /* the address to connect to */ } s; /* server side */ } addr; /* addresses of the remote side */ }; diff --git a/src/backend.c b/src/backend.c index 7a9e0c6b3..e2ad5b2b4 100644 --- a/src/backend.c +++ b/src/backend.c @@ -621,7 +621,8 @@ int assign_server(struct session *s) else if ((s->be->options2 & PR_O2_DISPATCH) || (s->be->options & PR_O_TRANSP)) { set_target_proxy(&s->target, s->be); } - else if ((s->be->options & PR_O_HTTP_PROXY) && s->req->cons->addr.s.to.sin_addr.s_addr) { + else if ((s->be->options & PR_O_HTTP_PROXY) && + is_addr(&s->req->cons->addr.s.to)) { /* in proxy mode, we need a valid destination address */ set_target_proxy(&s->target, s->be); } @@ -677,7 +678,7 @@ int assign_server_address(struct session *s) s->req->cons->addr.s.to = target_srv(&s->target)->addr; - if (!s->req->cons->addr.s.to.sin_addr.s_addr) { + if (!is_addr(&s->req->cons->addr.s.to)) { /* if the server has no address, we use the same address * the client asked, which is handy for remapping ports * locally on multiple addresses at once. @@ -686,22 +687,36 @@ int assign_server_address(struct session *s) get_frt_addr(s); if (s->req->prod->addr.c.to.ss_family == AF_INET) { - s->req->cons->addr.s.to.sin_addr = ((struct sockaddr_in *)&s->req->prod->addr.c.to)->sin_addr; + ((struct sockaddr_in *)&s->req->cons->addr.s.to)->sin_addr = ((struct sockaddr_in *)&s->req->prod->addr.c.to)->sin_addr; + } else if (s->req->prod->addr.c.to.ss_family == AF_INET6) { + ((struct sockaddr_in6 *)&s->req->cons->addr.s.to)->sin6_addr = ((struct sockaddr_in6 *)&s->req->prod->addr.c.to)->sin6_addr; } } /* if this server remaps proxied ports, we'll use * the port the client connected to with an offset. */ if (target_srv(&s->target)->state & SRV_MAPPORTS) { + int base_port; + if (!(s->be->options & PR_O_TRANSP) && !(s->flags & SN_FRT_ADDR_SET)) get_frt_addr(s); - if (s->req->prod->addr.c.to.ss_family == AF_INET) { - s->req->cons->addr.s.to.sin_port = htons(ntohs(s->req->cons->addr.s.to.sin_port) + - ntohs(((struct sockaddr_in *)&s->req->prod->addr.c.to)->sin_port)); + + /* First, retrieve the port from the incoming connection */ + if (s->req->prod->addr.c.to.ss_family == AF_INET) + base_port = ntohs(((struct sockaddr_in *)&s->req->prod->addr.c.to)->sin_port); + else if (s->req->prod->addr.c.to.ss_family == AF_INET6) + base_port = ntohs(((struct sockaddr_in6 *)&s->req->prod->addr.c.to)->sin6_port); + else + base_port = 0; + + /* Second, assign the outgoing connection's port */ + if (s->req->cons->addr.c.to.ss_family == AF_INET) { + ((struct sockaddr_in *)&s->req->cons->addr.s.to)->sin_port = + htons(base_port + ntohs(((struct sockaddr_in *)&s->req->cons->addr.s.to)->sin_port)); } else if (s->req->prod->addr.c.to.ss_family == AF_INET6) { - s->req->cons->addr.s.to.sin_port = htons(ntohs(s->req->cons->addr.s.to.sin_port) + - ntohs(((struct sockaddr_in6 *)&s->req->prod->addr.c.to)->sin6_port)); + ((struct sockaddr_in6 *)&s->req->cons->addr.s.to)->sin6_port = + htons(base_port + ntohs(((struct sockaddr_in6 *)&s->req->cons->addr.s.to)->sin6_port)); } } } @@ -714,7 +729,7 @@ int assign_server_address(struct session *s) if (!(s->flags & SN_FRT_ADDR_SET)) get_frt_addr(s); - if (s->req->prod->addr.c.to.ss_family == AF_INET) { + if (s->req->prod->addr.c.to.ss_family == AF_INET || s->req->prod->addr.c.to.ss_family == AF_INET6) { memcpy(&s->req->cons->addr.s.to, &s->req->prod->addr.c.to, MIN(sizeof(s->req->cons->addr.s.to), sizeof(s->req->prod->addr.c.to))); } /* when we support IPv6 on the backend, we may add other tests */ @@ -869,22 +884,23 @@ static void assign_tproxy_address(struct session *s) if (srv && srv->state & SRV_BIND_SRC) { switch (srv->state & SRV_TPROXY_MASK) { case SRV_TPROXY_ADDR: - s->req->cons->addr.s.from = *(struct sockaddr_in *)&srv->tproxy_addr; + s->req->cons->addr.s.from = srv->tproxy_addr; break; case SRV_TPROXY_CLI: case SRV_TPROXY_CIP: /* FIXME: what can we do if the client connects in IPv6 or unix socket ? */ - s->req->cons->addr.s.from = *(struct sockaddr_in *)&s->req->prod->addr.c.from; + s->req->cons->addr.s.from = s->req->prod->addr.c.from; break; case SRV_TPROXY_DYN: if (srv->bind_hdr_occ) { /* bind to the IP in a header */ - s->req->cons->addr.s.from.sin_port = 0; - s->req->cons->addr.s.from.sin_addr.s_addr = htonl(get_ip_from_hdr2(&s->txn.req, - srv->bind_hdr_name, - srv->bind_hdr_len, - &s->txn.hdr_idx, - srv->bind_hdr_occ)); + ((struct sockaddr_in *)&s->req->cons->addr.s.from)->sin_port = 0; + ((struct sockaddr_in *)&s->req->cons->addr.s.from)->sin_addr.s_addr = + htonl(get_ip_from_hdr2(&s->txn.req, + srv->bind_hdr_name, + srv->bind_hdr_len, + &s->txn.hdr_idx, + srv->bind_hdr_occ)); } break; default: @@ -894,22 +910,23 @@ static void assign_tproxy_address(struct session *s) else if (s->be->options & PR_O_BIND_SRC) { switch (s->be->options & PR_O_TPXY_MASK) { case PR_O_TPXY_ADDR: - s->req->cons->addr.s.from = *(struct sockaddr_in *)&s->be->tproxy_addr; + s->req->cons->addr.s.from = s->be->tproxy_addr; break; case PR_O_TPXY_CLI: case PR_O_TPXY_CIP: /* FIXME: what can we do if the client connects in IPv6 or socket unix? */ - s->req->cons->addr.s.from = *(struct sockaddr_in *)&s->req->prod->addr.c.from; + s->req->cons->addr.s.from = s->req->prod->addr.c.from; break; case PR_O_TPXY_DYN: if (s->be->bind_hdr_occ) { /* bind to the IP in a header */ - s->req->cons->addr.s.from.sin_port = 0; - s->req->cons->addr.s.from.sin_addr.s_addr = htonl(get_ip_from_hdr2(&s->txn.req, - s->be->bind_hdr_name, - s->be->bind_hdr_len, - &s->txn.hdr_idx, - s->be->bind_hdr_occ)); + ((struct sockaddr_in *)&s->req->cons->addr.s.from)->sin_port = 0; + ((struct sockaddr_in *)&s->req->cons->addr.s.from)->sin_addr.s_addr = + htonl(get_ip_from_hdr2(&s->txn.req, + s->be->bind_hdr_name, + s->be->bind_hdr_len, + &s->txn.hdr_idx, + s->be->bind_hdr_occ)); } break; default: @@ -951,7 +968,7 @@ int connect_server(struct session *s) * session's freshly assigned target with the stream interface's. */ stream_sock_prepare_interface(s->req->cons); - s->req->cons->connect = tcpv4_connect_server; + s->req->cons->connect = tcp_connect_server; copy_target(&s->req->cons->target, &s->target); assign_tproxy_address(s); diff --git a/src/cfgparse.c b/src/cfgparse.c index 18cfda39d..aeec87ba2 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -937,13 +937,13 @@ int cfg_parse_global(const char *file, int linenum, char **args, int kwm) logsrv.u.un = *sk; logsrv.u.addr.sa_family = AF_UNIX; } else { - struct sockaddr_in *sk = str2sa(args[1]); - if (!sk) { + struct sockaddr_storage *sk = str2sa(args[1]); + if (!sk || sk->ss_family != AF_INET) { Alert("parsing [%s:%d] : Unknown host in '%s'\n", file, linenum, args[1]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } - logsrv.u.in = *sk; + logsrv.u.in = *(struct sockaddr_in *)sk; logsrv.u.addr.sa_family = AF_INET; if (!logsrv.u.in.sin_port) logsrv.u.in.sin_port = htons(SYSLOG_PORT); @@ -1233,7 +1233,7 @@ int cfg_parse_peers(const char *file, int linenum, char **args, int kwm) else if (strcmp(args[0], "peer") == 0) { /* peer definition */ char *rport, *raddr; short realport = 0; - struct sockaddr_in *sk; + struct sockaddr_storage *sk; if (!*args[2]) { Alert("parsing [%s:%d] : '%s' expects and [:] as arguments.\n", @@ -1287,7 +1287,15 @@ int cfg_parse_peers(const char *file, int linenum, char **args, int kwm) goto out; } newpeer->addr = *sk; - newpeer->addr.sin_port = htons(realport); + + switch (newpeer->addr.ss_family) { + case AF_INET: + ((struct sockaddr_in *)&newpeer->addr)->sin_port = htons(realport); + break; + case AF_INET6: + ((struct sockaddr_in6 *)&newpeer->addr)->sin6_port = htons(realport); + break; + } if (strcmp(newpeer->id, localpeer) == 0) { /* Current is local peer, it define a frontend */ @@ -3843,7 +3851,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm) curproxy->grace = val; } else if (!strcmp(args[0], "dispatch")) { /* dispatch address */ - struct sockaddr_in *sk; + struct sockaddr_storage *sk; if (curproxy == &defproxy) { Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; @@ -3929,7 +3937,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm) } if (!defsrv) { - struct sockaddr_in *sk; + struct sockaddr_storage *sk; if ((newsrv = (struct server *)calloc(1, sizeof(struct server))) == NULL) { Alert("parsing [%s:%d] : out of memory.\n", file, linenum); @@ -3975,7 +3983,15 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm) goto out; } newsrv->addr = *sk; - newsrv->addr.sin_port = htons(realport); + + switch (newsrv->addr.ss_family) { + case AF_INET: + ((struct sockaddr_in *)&newsrv->addr)->sin_port = htons(realport); + break; + case AF_INET6: + ((struct sockaddr_in6 *)&newsrv->addr)->sin6_port = htons(realport); + break; + } newsrv->check_port = curproxy->defsrv.check_port; newsrv->inter = curproxy->defsrv.inter; @@ -4135,7 +4151,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm) cur_arg += 2; } else if (!defsrv && !strcmp(args[cur_arg], "addr")) { - struct sockaddr_in *sk = str2sa(args[cur_arg + 1]); + struct sockaddr_storage *sk = str2sa(args[cur_arg + 1]); if (!sk) { Alert("parsing [%s:%d] : Unknown host in '%s'\n", file, linenum, args[cur_arg + 1]); err_code |= ERR_ALERT | ERR_FATAL; @@ -4280,7 +4296,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm) } else if (!defsrv && !strcmp(args[cur_arg], "source")) { /* address to which we bind when connecting */ int port_low, port_high; - struct sockaddr_in *sk; + struct sockaddr_storage *sk; if (!*args[cur_arg + 1]) { #if defined(CONFIG_HAP_CTTPROXY) || defined(CONFIG_HAP_LINUX_TPROXY) @@ -4381,7 +4397,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm) goto out; } } else { - struct sockaddr_in *sk = str2sa(args[cur_arg + 1]); + struct sockaddr_storage *sk = str2sa(args[cur_arg + 1]); if (!sk) { Alert("parsing [%s:%d] : Unknown host in '%s'\n", file, linenum, args[cur_arg + 1]); err_code |= ERR_ALERT | ERR_FATAL; @@ -4458,8 +4474,16 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm) goto out; } - if (!newsrv->check_port && newsrv->check_addr.sin_port) - newsrv->check_port = newsrv->check_addr.sin_port; + switch (newsrv->check_addr.ss_family) { + case AF_INET: + if (!newsrv->check_port && ((struct sockaddr_in *)&newsrv->check_addr)->sin_port) + newsrv->check_port = ntohs(((struct sockaddr_in *)&newsrv->check_addr)->sin_port); + break; + case AF_INET6: + if (!newsrv->check_port && ((struct sockaddr_in6 *)&newsrv->check_addr)->sin6_port) + newsrv->check_port = ntohs(((struct sockaddr_in6 *)&newsrv->check_addr)->sin6_port); + break; + } if (!newsrv->check_port && !(newsrv->state & SRV_MAPPORTS)) newsrv->check_port = realport; /* by default */ @@ -4557,13 +4581,13 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm) logsrv.u.un = *sk; logsrv.u.addr.sa_family = AF_UNIX; } else { - struct sockaddr_in *sk = str2sa(args[1]); - if (!sk) { + struct sockaddr_storage *sk = str2sa(args[1]); + if (!sk || sk->ss_family != AF_INET) { Alert("parsing [%s:%d] : Unknown host in '%s'\n", file, linenum, args[1]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } - logsrv.u.in = *sk; + logsrv.u.in = *(struct sockaddr_in *)sk; logsrv.u.addr.sa_family = AF_INET; if (!logsrv.u.in.sin_port) { logsrv.u.in.sin_port = @@ -4598,7 +4622,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm) } else if (!strcmp(args[0], "source")) { /* address to which we bind when connecting */ int cur_arg; - struct sockaddr_in *sk; + struct sockaddr_storage *sk; if (warnifnotcap(curproxy, PR_CAP_BE, file, linenum, args[0], NULL)) err_code |= ERR_WARN; @@ -4690,7 +4714,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm) goto out; } } else { - struct sockaddr_in *sk = str2sa(args[cur_arg + 1]); + struct sockaddr_storage *sk = str2sa(args[cur_arg + 1]); if (!sk) { Alert("parsing [%s:%d] : Unknown host in '%s'\n", file, linenum, args[cur_arg + 1]); err_code |= ERR_ALERT | ERR_FATAL; diff --git a/src/checks.c b/src/checks.c index ba3e745e9..e4982ca5a 100644 --- a/src/checks.c +++ b/src/checks.c @@ -810,10 +810,21 @@ static int event_srv_chk_w(int fd) * - connected (EISCONN, 0) */ - struct sockaddr_in sa; + struct sockaddr_storage sa; - sa = (s->check_addr.sin_addr.s_addr) ? s->check_addr : s->addr; - sa.sin_port = htons(s->check_port); + if (is_addr(&s->check_addr)) + sa = s->check_addr; + else + sa = s->addr; + + switch (s->check_addr.ss_family) { + case AF_INET: + ((struct sockaddr_in *)&sa)->sin_port = htons(s->check_port); + break; + case AF_INET6: + ((struct sockaddr_in6 *)&sa)->sin6_port = htons(s->check_port); + break; + } if (connect(fd, (struct sockaddr *)&sa, sizeof(sa)) == 0) errno = 0; @@ -1193,7 +1204,7 @@ struct task *process_chk(struct task *t) { int attempts = 0; struct server *s = t->context; - struct sockaddr_in sa; + struct sockaddr_storage sa; int fd; int rv; @@ -1223,7 +1234,7 @@ struct task *process_chk(struct task *t) /* we'll initiate a new check */ set_server_check_status(s, HCHK_STATUS_START, NULL); - if ((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) != -1) { + if ((fd = socket(s->addr.ss_family, SOCK_STREAM, IPPROTO_TCP)) != -1) { if ((fd < global.maxsock) && (fcntl(fd, F_SETFL, O_NONBLOCK) != -1) && (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &one, sizeof(one)) != -1)) { @@ -1233,28 +1244,35 @@ struct task *process_chk(struct task *t) /* We don't want to useless data */ setsockopt(fd, SOL_SOCKET, SO_LINGER, (struct linger *) &nolinger, sizeof(struct linger)); } - - if (s->check_addr.sin_addr.s_addr) + + if (is_addr(&s->check_addr)) /* we'll connect to the check addr specified on the server */ sa = s->check_addr; else /* we'll connect to the addr on the server */ sa = s->addr; - /* we'll connect to the check port on the server */ - sa.sin_port = htons(s->check_port); - + switch (s->check_addr.ss_family) { + case AF_INET: + /* we'll connect to the check port on the server */ + ((struct sockaddr_in *)&sa)->sin_port = htons(s->check_port); + break; + case AF_INET6: + /* we'll connect to the check port on the server */ + ((struct sockaddr_in6 *)&sa)->sin6_port = htons(s->check_port); + break; + } /* allow specific binding : * - server-specific at first * - proxy-specific next */ if (s->state & SRV_BIND_SRC) { - struct sockaddr_in *remote = NULL; + struct sockaddr_storage *remote = NULL; int ret, flags = 0; #if defined(CONFIG_HAP_CTTPROXY) || defined(CONFIG_HAP_LINUX_TPROXY) if ((s->state & SRV_TPROXY_MASK) == SRV_TPROXY_ADDR) { - remote = (struct sockaddr_in *)&s->tproxy_addr; + remote = &s->tproxy_addr; flags = 3; } #endif @@ -1266,7 +1284,7 @@ struct task *process_chk(struct task *t) #endif if (s->sport_range) { int bind_attempts = 10; /* should be more than enough to find a spare port */ - struct sockaddr_in src; + struct sockaddr_storage src; ret = 1; src = s->source_addr; @@ -1287,13 +1305,21 @@ struct task *process_chk(struct task *t) break; fdinfo[fd].port_range = s->sport_range; - src.sin_port = htons(fdinfo[fd].local_port); - ret = tcpv4_bind_socket(fd, flags, &src, remote); + switch (src.ss_family) { + case AF_INET: + ((struct sockaddr_in *)&src)->sin_port = htons(fdinfo[fd].local_port); + break; + case AF_INET6: + ((struct sockaddr_in6 *)&src)->sin6_port = htons(fdinfo[fd].local_port); + break; + } + + ret = tcp_bind_socket(fd, flags, &src, remote); } while (ret != 0); /* binding NOK */ } else { - ret = tcpv4_bind_socket(fd, flags, &s->source_addr, remote); + ret = tcp_bind_socket(fd, flags, &s->source_addr, remote); } if (ret) { @@ -1311,12 +1337,12 @@ struct task *process_chk(struct task *t) } } else if (s->proxy->options & PR_O_BIND_SRC) { - struct sockaddr_in *remote = NULL; + struct sockaddr_storage *remote = NULL; int ret, flags = 0; #if defined(CONFIG_HAP_CTTPROXY) || defined(CONFIG_HAP_LINUX_TPROXY) if ((s->proxy->options & PR_O_TPXY_MASK) == PR_O_TPXY_ADDR) { - remote = (struct sockaddr_in *)&s->proxy->tproxy_addr; + remote = &s->proxy->tproxy_addr; flags = 3; } #endif @@ -1326,7 +1352,7 @@ struct task *process_chk(struct task *t) setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, s->proxy->iface_name, s->proxy->iface_len + 1); #endif - ret = tcpv4_bind_socket(fd, flags, &s->proxy->source_addr, remote); + ret = tcp_bind_socket(fd, flags, &s->proxy->source_addr, remote); if (ret) { set_server_check_status(s, HCHK_STATUS_SOCKERR, NULL); switch (ret) { diff --git a/src/dumpstats.c b/src/dumpstats.c index ed577ad67..b04b297fa 100644 --- a/src/dumpstats.c +++ b/src/dumpstats.c @@ -2111,10 +2111,20 @@ int stats_dump_proxy(struct stream_interface *si, struct proxy *px, struct uri_a chunk_printf(&msg, " title=\"IP: "); /* IP */ - if (inet_ntop(sv->addr.sin_family, &sv->addr.sin_addr, str, sizeof(str))) - chunk_printf(&msg, "%s:%d", str, htons(sv->addr.sin_port)); - else - chunk_printf(&msg, "(%s)", strerror(errno)); + switch (sv->addr.ss_family) { + case AF_INET: + if (inet_ntop(AF_INET, (const void *)&((struct sockaddr_in *)&sv->addr)->sin_addr, str, sizeof(str))) + chunk_printf(&msg, "%s:%d", str, htons(((struct sockaddr_in *)&sv->addr)->sin_port)); + else + chunk_printf(&msg, "(%s)", strerror(errno)); + break; + case AF_INET6: + if (inet_ntop(AF_INET6, (const void *)&((struct sockaddr_in6 *)&sv->addr)->sin6_addr, str, sizeof(str))) + chunk_printf(&msg, "%s:%d", str, htons(((struct sockaddr_in6 *)&sv->addr)->sin6_port)); + else + chunk_printf(&msg, "(%s)", strerror(errno)); + break; + } /* id */ chunk_printf(&msg, ", id: %d", sv->puid); diff --git a/src/peers.c b/src/peers.c index 5b8e7490a..739c09b05 100644 --- a/src/peers.c +++ b/src/peers.c @@ -1174,7 +1174,7 @@ struct session *peer_session_create(struct peer *peer, struct peer_session *ps) s->si[1].conn_retries = p->conn_retries; s->si[1].err_type = SI_ET_NONE; s->si[1].err_loc = NULL; - s->si[1].connect = tcpv4_connect_server; + s->si[1].connect = tcp_connect_server; set_target_proxy(&s->si[1].target, s->be); s->si[1].exp = TICK_ETERNITY; s->si[1].flags = SI_FL_NONE; diff --git a/src/proto_http.c b/src/proto_http.c index cbfdca287..ff2f44cac 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -8037,9 +8037,9 @@ acl_fetch_hdr_ip(struct proxy *px, struct session *l4, void *l7, char *sol, test->flags |= ACL_TEST_F_FETCH_MORE; test->flags |= ACL_TEST_F_VOL_HDR; /* Same optimization as url_ip */ - memset(&l4->req->cons->addr.s.to.sin_addr, 0, sizeof(l4->req->cons->addr.s.to.sin_addr)); - url2ip((char *)ctx->line + ctx->val, &l4->req->cons->addr.s.to.sin_addr); - test->ptr = (void *)&l4->req->cons->addr.s.to.sin_addr; + memset(&((struct sockaddr_in *)&l4->req->cons->addr.s.to)->sin_addr, 0, sizeof(((struct sockaddr_in *)&l4->req->cons->addr.s.to)->sin_addr)); + url2ipv4((char *)ctx->line + ctx->val, &((struct sockaddr_in *)&l4->req->cons->addr.s.to)->sin_addr); + test->ptr = (void *)&((struct sockaddr_in *)&l4->req->cons->addr.s.to)->sin_addr; test->i = AF_INET; return 1; } diff --git a/src/proto_tcp.c b/src/proto_tcp.c index 2d79e22d2..04562f104 100644 --- a/src/proto_tcp.c +++ b/src/proto_tcp.c @@ -100,7 +100,7 @@ static struct protocol proto_tcpv6 = { }; -/* Binds ipv4 address to socket , unless is set, in which +/* Binds ipv4/ipv6 address to socket , unless is set, in which * case we try to bind . is a 2-bit field consisting of : * - 0 : ignore remote address (may even be a NULL pointer) * - 1 : use provided address @@ -114,9 +114,9 @@ static struct protocol proto_tcpv6 = { * This function returns 0 when everything's OK, 1 if it could not bind, to the * local address, 2 if it could not bind to the foreign address. */ -int tcpv4_bind_socket(int fd, int flags, struct sockaddr_in *local, struct sockaddr_in *remote) +int tcp_bind_socket(int fd, int flags, struct sockaddr_storage *local, struct sockaddr_storage *remote) { - struct sockaddr_in bind_addr; + struct sockaddr_storage bind_addr; int foreign_ok = 0; int ret; @@ -132,10 +132,20 @@ int tcpv4_bind_socket(int fd, int flags, struct sockaddr_in *local, struct socka #endif if (flags) { memset(&bind_addr, 0, sizeof(bind_addr)); - if (flags & 1) - bind_addr.sin_addr = remote->sin_addr; - if (flags & 2) - bind_addr.sin_port = remote->sin_port; + switch (remote->ss_family) { + case AF_INET: + if (flags & 1) + ((struct sockaddr_in *)&bind_addr)->sin_addr = ((struct sockaddr_in *)remote)->sin_addr; + if (flags & 2) + ((struct sockaddr_in *)&bind_addr)->sin_port = ((struct sockaddr_in *)remote)->sin_port; + break; + case AF_INET6: + if (flags & 1) + ((struct sockaddr_in6 *)&bind_addr)->sin6_addr = ((struct sockaddr_in6 *)remote)->sin6_addr; + if (flags & 2) + ((struct sockaddr_in6 *)&bind_addr)->sin6_port = ((struct sockaddr_in6 *)remote)->sin6_port; + break; + } } setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof(one)); @@ -198,7 +208,7 @@ int tcpv4_bind_socket(int fd, int flags, struct sockaddr_in *local, struct socka * Additionnally, in the case of SN_ERR_RESOURCE, an emergency log will be emitted. */ -int tcpv4_connect_server(struct stream_interface *si) +int tcp_connect_server(struct stream_interface *si) { int fd; struct server *srv; @@ -217,7 +227,7 @@ int tcpv4_connect_server(struct stream_interface *si) return SN_ERR_INTERNAL; } - if ((fd = si->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { + if ((fd = si->fd = socket(si->addr.s.to.ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1) { qfprintf(stderr, "Cannot get a server socket.\n"); if (errno == ENFILE) @@ -284,7 +294,7 @@ int tcpv4_connect_server(struct stream_interface *si) if (srv->sport_range) { int attempts = 10; /* should be more than enough to find a spare port */ - struct sockaddr_in src; + struct sockaddr_storage src; ret = 1; src = srv->source_addr; @@ -305,13 +315,20 @@ int tcpv4_connect_server(struct stream_interface *si) break; fdinfo[fd].port_range = srv->sport_range; - src.sin_port = htons(fdinfo[fd].local_port); + switch (src.ss_family) { + case AF_INET: + ((struct sockaddr_in *)&src)->sin_port = htons(fdinfo[fd].local_port); + break; + case AF_INET6: + ((struct sockaddr_in6 *)&src)->sin6_port = htons(fdinfo[fd].local_port); + break; + } - ret = tcpv4_bind_socket(fd, flags, &src, (struct sockaddr_in *)&si->addr.s.from); + ret = tcp_bind_socket(fd, flags, &src, &si->addr.s.from); } while (ret != 0); /* binding NOK */ } else { - ret = tcpv4_bind_socket(fd, flags, &srv->source_addr, (struct sockaddr_in *)&si->addr.s.from); + ret = tcp_bind_socket(fd, flags, &srv->source_addr, &si->addr.s.from); } if (ret) { @@ -354,7 +371,7 @@ int tcpv4_connect_server(struct stream_interface *si) if (be->iface_name) setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, be->iface_name, be->iface_len + 1); #endif - ret = tcpv4_bind_socket(fd, flags, &be->source_addr, (struct sockaddr_in *)&si->addr.s.from); + ret = tcp_bind_socket(fd, flags, &be->source_addr, &si->addr.s.from); if (ret) { close(fd); if (ret == 1) { @@ -389,7 +406,7 @@ int tcpv4_connect_server(struct stream_interface *si) if (global.tune.server_rcvbuf) setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &global.tune.server_rcvbuf, sizeof(global.tune.server_rcvbuf)); - if ((connect(fd, (struct sockaddr *)&si->addr.s.to, sizeof(struct sockaddr_in)) == -1) && + if ((connect(fd, (struct sockaddr *)&si->addr.s.to, sizeof(struct sockaddr_storage)) == -1) && (errno != EINPROGRESS) && (errno != EALREADY) && (errno != EISCONN)) { if (errno == EAGAIN || errno == EADDRINUSE) { @@ -432,7 +449,7 @@ int tcpv4_connect_server(struct stream_interface *si) fdtab[fd].cb[DIR_WR].b = si->ob; fdinfo[fd].peeraddr = (struct sockaddr *)&si->addr.s.to; - fdinfo[fd].peerlen = sizeof(struct sockaddr_in); + fdinfo[fd].peerlen = sizeof(struct sockaddr_storage); fd_insert(fd); EV_FD_SET(fd, DIR_WR); /* for connect status */ diff --git a/src/standard.c b/src/standard.c index 1ab219412..27fa374ea 100644 --- a/src/standard.c +++ b/src/standard.c @@ -219,10 +219,10 @@ const char *invalid_domainchar(const char *name) { * a host name, or empty or "*" to indicate INADDR_ANY. NULL is returned * if the host part cannot be resolved. */ -struct sockaddr_in *str2sa(char *str) +struct sockaddr_storage *str2sa(char *str) { - static struct sockaddr_in sa; - struct sockaddr_in *ret = NULL; + static struct sockaddr_storage sa; + struct sockaddr_storage *ret = NULL; char *c; int port; @@ -238,17 +238,17 @@ struct sockaddr_in *str2sa(char *str) else port = 0; + sa.ss_family = AF_INET; + ((struct sockaddr_in *)&sa)->sin_port = htons(port); if (*str == '*' || *str == '\0') { /* INADDR_ANY */ - sa.sin_addr.s_addr = INADDR_ANY; + ((struct sockaddr_in *)&sa)->sin_addr.s_addr = INADDR_ANY; } - else if (!inet_pton(AF_INET, str, &sa.sin_addr)) { + else if (!inet_pton(sa.ss_family, str, &((struct sockaddr_in *)&sa)->sin_addr)) { struct hostent *he = gethostbyname(str); if (!he) goto out; - sa.sin_addr = *(struct in_addr *) *(he->h_addr_list); + ((struct sockaddr_in *)&sa)->sin_addr = *(struct in_addr *) *(he->h_addr_list); } - sa.sin_port = htons(port); - sa.sin_family = AF_INET; ret = &sa; out: free(str); @@ -265,10 +265,10 @@ struct sockaddr_in *str2sa(char *str) * name, or empty or "*" to indicate INADDR_ANY. NULL is returned if the host * part cannot be resolved. */ -struct sockaddr_in *str2sa_range(char *str, int *low, int *high) +struct sockaddr_storage *str2sa_range(char *str, int *low, int *high) { - static struct sockaddr_in sa; - struct sockaddr_in *ret = NULL; + static struct sockaddr_storage sa; + struct sockaddr_storage *ret = NULL; char *c; int portl, porth; @@ -293,17 +293,17 @@ struct sockaddr_in *str2sa_range(char *str, int *low, int *high) porth = 0; } + sa.ss_family = AF_INET; + ((struct sockaddr_in *)&sa)->sin_port = htonl(portl); if (*str == '*' || *str == '\0') { /* INADDR_ANY */ - sa.sin_addr.s_addr = INADDR_ANY; + ((struct sockaddr_in *)&sa)->sin_addr.s_addr = INADDR_ANY; } - else if (!inet_pton(AF_INET, str, &sa.sin_addr)) { + else if (!inet_pton(sa.ss_family, str, &((struct sockaddr_in *)&sa)->sin_addr)) { struct hostent *he = gethostbyname(str); if (!he) goto out; - sa.sin_addr = *(struct in_addr *) *(he->h_addr_list); + ((struct sockaddr_in *)&sa)->sin_addr = *(struct in_addr *) *(he->h_addr_list); } - sa.sin_port = htons(portl); - sa.sin_family = AF_INET; ret = &sa; *low = portl; @@ -387,9 +387,9 @@ int str2net(const char *str, struct in_addr *addr, struct in_addr *mask) /* - * Parse IP address found in url. + * Parse IPv4 address found in url. */ -int url2ip(const char *addr, struct in_addr *dst) +int url2ipv4(const char *addr, struct in_addr *dst) { int saw_digit, octets, ch; u_char tmp[4], *tp; @@ -430,18 +430,20 @@ int url2ip(const char *addr, struct in_addr *dst) } /* - * Resolve destination server from URL. Convert to a sockaddr_in*. + * Resolve destination server from URL. Convert to a sockaddr_storage*. */ -int url2sa(const char *url, int ulen, struct sockaddr_in *addr) +int url2sa(const char *url, int ulen, struct sockaddr_storage *addr) { const char *curr = url, *cp = url; int ret, url_code = 0; unsigned int http_code = 0; /* Cleanup the room */ - addr->sin_family = AF_INET; - addr->sin_addr.s_addr = 0; - addr->sin_port = 0; + + /* FIXME: assume IPv4 only for now */ + ((struct sockaddr_in *)addr)->sin_family = AF_INET; + ((struct sockaddr_in *)addr)->sin_addr.s_addr = 0; + ((struct sockaddr_in *)addr)->sin_port = 0; /* Firstly, try to find :// pattern */ while (curr < url+ulen && url_code != 0x3a2f2f) { @@ -467,12 +469,12 @@ int url2sa(const char *url, int ulen, struct sockaddr_in *addr) * be warned this can slow down global daemon performances * while handling lagging dns responses. */ - ret = url2ip(curr, &addr->sin_addr); + ret = url2ipv4(curr, &((struct sockaddr_in *)&addr)->sin_addr); if (!ret) return -1; curr += ret; - addr->sin_port = (*curr == ':') ? str2uic(++curr) : 80; - addr->sin_port = htons(addr->sin_port); + ((struct sockaddr_in *)addr)->sin_port = (*curr == ':') ? str2uic(++curr) : 80; + ((struct sockaddr_in *)addr)->sin_port = htons(((struct sockaddr_in *)&addr)->sin_port); } return 0; }