[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.
This commit is contained in:
David du Colombier 2011-03-10 22:26:24 +01:00 committed by Willy Tarreau
parent 827aee913f
commit 6f5ccb1589
14 changed files with 244 additions and 130 deletions

View File

@ -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 <str> 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 <str> 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 <str> to a sockaddr_in*.
* Resolve destination server from URL. Convert <str> 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 <string> replacing all characters tagged in
* <map> 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 */

View File

@ -27,10 +27,10 @@
#include <types/task.h>
#include <proto/stick_table.h>
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);

View File

@ -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 */
};

View File

@ -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 */

View File

@ -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 <addr> */
struct sockaddr_storage check_addr; /* the address to check, if different from <addr> */
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 */

View File

@ -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 */
};

View File

@ -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);

View File

@ -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 <name> and <addr>[:<port>] 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 @@ stats_error_parsing:
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 @@ stats_error_parsing:
}
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 @@ stats_error_parsing:
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 @@ stats_error_parsing:
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 @@ stats_error_parsing:
}
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 @@ stats_error_parsing:
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 @@ stats_error_parsing:
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 @@ stats_error_parsing:
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 @@ stats_error_parsing:
}
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 @@ stats_error_parsing:
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;

View File

@ -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) {

View File

@ -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);

View File

@ -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;

View File

@ -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;
}

View File

@ -100,7 +100,7 @@ static struct protocol proto_tcpv6 = {
};
/* Binds ipv4 address <local> to socket <fd>, unless <flags> is set, in which
/* Binds ipv4/ipv6 address <local> to socket <fd>, unless <flags> is set, in which
* case we try to bind <remote>. <flags> 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 */

View File

@ -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 <str> to a sockaddr_in*.
* Resolve destination server from URL. Convert <str> 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;
}