From: Marko Kreen Date: Wed, 16 Feb 2011 06:57:16 +0000 (+0200) Subject: IPv6 support X-Git-Tag: pgbouncer_1_4_1_rc1~14 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=7bc3c5ad393cc2035c451bc444f60946b5a02a0c;p=pgbouncer IPv6 support Original patch by Hannu Krosing --- diff --git a/include/bouncer.h b/include/bouncer.h index a17aff5..01f037a 100644 --- a/include/bouncer.h +++ b/include/bouncer.h @@ -131,15 +131,31 @@ extern int cf_sbuf_len; /* buffer size for startup noise */ #define STARTUP_BUF 1024 + /* * Remote/local address */ + +/* buffer for pgaddr string conversions */ +#define PGADDR_BUF INET6_ADDRSTRLEN + struct PgAddr { - struct in_addr ip_addr; + unsigned char af; unsigned short port; - bool is_unix; + union { + struct in_addr addr4; + struct in6_addr addr6; + }; }; +static inline bool pga_is_unix(const PgAddr *a) { return a->af == AF_UNIX; } +static inline int pga_port(const PgAddr *a) { return a->port; } + +void pga_set(PgAddr *a, int fam, int port); +void pga_copy(PgAddr *a, const struct sockaddr *sa); +const char *pga_ntop(const PgAddr *a, char *dst, int dstlen); +bool pga_pton(PgAddr *a, const char *s, int port); + /* * Stats, kept per-pool. */ diff --git a/src/admin.c b/src/admin.c index 94c1ee9..6fdcbef 100644 --- a/src/admin.c +++ b/src/admin.c @@ -266,7 +266,7 @@ static bool send_one_fd(PgSocket *admin, msg.msg_iovlen = 1; /* attach a fd */ - if (admin->remote_addr.is_unix && admin->own_user) { + if (pga_is_unix(&admin->remote_addr) && admin->own_user) { msg.msg_control = cntbuf; msg.msg_controllen = sizeof(cntbuf); @@ -303,17 +303,19 @@ static bool show_one_fd(PgSocket *admin, PgSocket *sk) const struct PStr *std_strings = v->var_list[VStdStr]; const struct PStr *datestyle = v->var_list[VDateStyle]; const struct PStr *timezone = v->var_list[VTimeZone]; + char addrbuf[PGADDR_BUF]; mbuf_init_fixed_reader(&tmp, sk->cancel_key, 8); if (!mbuf_get_uint64be(&tmp, &ckey)) return false; + return send_one_fd(admin, sbuf_socket(&sk->sbuf), is_server_socket(sk) ? "server" : "client", sk->auth_user ? sk->auth_user->name : NULL, sk->pool ? sk->pool->db->name : NULL, - addr->is_unix ? "unix" : inet_ntoa(addr->ip_addr), - addr->port, + pga_ntop(addr, addrbuf, sizeof(addrbuf)), + pga_port(addr), ckey, sk->link ? sbuf_socket(&sk->link->sbuf) : 0, client_encoding ? client_encoding->str : NULL, @@ -510,12 +512,7 @@ static void socket_header(PktBuf *buf, bool debug) static void adr2txt(const PgAddr *adr, char *dst, unsigned dstlen) { - if (adr->is_unix) { - safe_strcpy(dst, "unix", dstlen); - } else { - char *tmp = inet_ntoa(adr->ip_addr); - safe_strcpy(dst, tmp, dstlen); - } + pga_ntop(adr, dst, dstlen); } static void socket_row(PktBuf *buf, PgSocket *sk, const char *state, bool debug) @@ -543,8 +540,8 @@ static void socket_row(PktBuf *buf, PgSocket *sk, const char *state, bool debug) is_server_socket(sk) ? "S" :"C", sk->auth_user ? sk->auth_user->name : "(nouser)", sk->pool ? sk->pool->db->name : "(nodb)", - state, r_addr, sk->remote_addr.port, - l_addr, sk->local_addr.port, + state, r_addr, pga_port(&sk->remote_addr), + l_addr, pga_port(&sk->local_addr), sk->connect_time, sk->request_time, ptrbuf, linkbuf, @@ -1114,7 +1111,7 @@ bool admin_pre_login(PgSocket *client) client->own_user = 0; /* tag same uid as special */ - if (client->remote_addr.is_unix) { + if (pga_is_unix(&client->remote_addr)) { res = getpeereid(sbuf_socket(&client->sbuf), &peer_uid, &peer_gid); if (res >= 0 && peer_uid == getuid() && strcmp("pgbouncer", username) == 0) diff --git a/src/objects.c b/src/objects.c index 475176e..ddf1c57 100644 --- a/src/objects.c +++ b/src/objects.c @@ -824,12 +824,9 @@ static void connect_server(struct PgSocket *server, const struct sockaddr *sa, i /* fill remote_addr */ memset(&server->remote_addr, 0, sizeof(server->remote_addr)); if (sa->sa_family == AF_UNIX) { - server->remote_addr.port = server->pool->db->port; - server->remote_addr.is_unix = true; - } else if (sa->sa_family == AF_INET) { - struct sockaddr_in *in = (struct sockaddr_in *)sa; - server->remote_addr.port = ntohs(in->sin_port); - server->remote_addr.ip_addr = in->sin_addr; + pga_set(&server->remote_addr, AF_UNIX, server->pool->db->port); + } else { + pga_copy(&server->remote_addr, sa); } if (cf_log_connections) @@ -873,6 +870,7 @@ static void dns_connect(struct PgSocket *server) { struct sockaddr_un sa_un; struct sockaddr_in sa_in; + struct sockaddr_in6 sa_in6; struct sockaddr *sa; struct PgDatabase *db = server->pool->db; const char *host = db->host; @@ -893,7 +891,15 @@ static void dns_connect(struct PgSocket *server) "%s/.s.PGSQL.%d", unix_dir, db->port); sa = (struct sockaddr *)&sa_un; sa_len = sizeof(sa_un); - } else if (host[0] >= '0' && host[0] <= '9') { + } else if (strchr(host, ':')) { // assume IPv6 address on any : in addr + slog_noise(server, "inet6 socket: %s", db->host); + memset(&sa_in6, 0, sizeof(sa_in6)); + sa_in6.sin6_family = AF_INET6; + inet_pton(AF_INET6, db->host, (void *) sa_in6.sin6_addr.s6_addr); + sa_in6.sin6_port = htons(db->port); + sa = (struct sockaddr *)&sa_in6; + sa_len = sizeof(sa_in6); + } else if (host[0] >= '0' && host[0] <= '9') { // else try IPv4 slog_noise(server, "inet socket: %s", db->host); memset(&sa_in, 0, sizeof(sa_in)); sa_in.sin_family = AF_INET; @@ -1125,7 +1131,7 @@ bool use_client_socket(int fd, PgAddr *addr, PgSocket *client; PktBuf tmp; - client = accept_client(fd, NULL, addr->is_unix); + client = accept_client(fd, NULL, pga_is_unix(addr)); if (client == NULL) return false; client->suspended = 1; @@ -1184,7 +1190,7 @@ bool use_server_socket(int fd, PgAddr *addr, if (!server) return false; - res = sbuf_accept(&server->sbuf, fd, addr->is_unix); + res = sbuf_accept(&server->sbuf, fd, pga_is_unix(addr)); if (!res) return false; @@ -1194,8 +1200,8 @@ bool use_server_socket(int fd, PgAddr *addr, server->connect_time = server->request_time = get_cached_time(); server->query_start = 0; - fill_remote_addr(server, fd, addr->is_unix); - fill_local_addr(server, fd, addr->is_unix); + fill_remote_addr(server, fd, pga_is_unix(addr)); + fill_local_addr(server, fd, pga_is_unix(addr)); if (linkfd) { server->ready = 0; diff --git a/src/pooler.c b/src/pooler.c index 6455ceb..0acefc4 100644 --- a/src/pooler.c +++ b/src/pooler.c @@ -24,6 +24,10 @@ static int fd_net = 0; static int fd_unix = 0; +#ifdef HAVE_IPV6 +static int fd_net_v6 = 0; +#endif + static struct event ev_net; static struct event ev_unix; @@ -159,27 +163,49 @@ void pooler_tune_accept(bool on) static int create_net_socket(const char *listen_addr, int listen_port) { int sock; - struct sockaddr_in sa; + struct sockaddr_in sa4; + struct sockaddr_in6 sa6; + struct sockaddr * sa; + int sa_size = 0; int res; int val; - /* create socket */ - sock = socket(AF_INET, SOCK_STREAM, 0); - if (sock < 0) - fatal_perror("socket"); - /* parse address */ - memset(&sa, 0, sizeof(sa)); - sa.sin_family = AF_INET; - sa.sin_port = htons(cf_listen_port); + /* parse address as IPv4 */ + memset(&sa4, 0, sizeof(sa)); + sa = (struct sockaddr *)&sa4; + sa_size = sizeof(sa4); + sa4.sin_family = AF_INET; + sa4.sin_port = htons(cf_listen_port); if (strcmp(listen_addr, "*") == 0) { - sa.sin_addr.s_addr = htonl(INADDR_ANY); + sa4.sin_addr.s_addr = htonl(INADDR_ANY); } else { - sa.sin_addr.s_addr = inet_addr(listen_addr); - if (sa.sin_addr.s_addr == INADDR_NONE) + sa4.sin_addr.s_addr = inet_addr(listen_addr); + } + if (sa4.sin_addr.s_addr == INADDR_NONE) { + /* IPv4 addr not foundm re-parse address as IPv6 */ + log_info("trying to parse %s as IPv6 address", listen_addr); + sa = (struct sockaddr *)&sa6; + sa_size = sizeof(sa6); + memset(&sa6, 0, sizeof(sa6)); + sa6.sin6_family = AF_INET6; + sa6.sin6_port = htons(cf_listen_port); + + if (inet_pton(AF_INET6, listen_addr, (void *) sa6.sin6_addr.s6_addr) <= 0) fatal("cannot parse addr: '%s'", listen_addr); } + /* create socket of right type */ //NB! turn if() into x?y:z + if (sa6.sin6_family == AF_INET6){ + sock = socket(AF_INET6, SOCK_STREAM, 0); + log_info("created AF_INET6 socket"); + } else { + sock = socket(AF_INET, SOCK_STREAM, 0); + log_info("created AF_INET socket"); + } + if (sock < 0) + fatal_perror("socket"); + /* relaxed binding */ val = 1; res = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)); @@ -187,7 +213,7 @@ static int create_net_socket(const char *listen_addr, int listen_port) fatal_perror("setsockopt"); /* bind to address */ - res = bind(sock, (struct sockaddr *)&sa, sizeof(sa)); + res = bind(sock, sa, sa_size); if (res < 0) fatal_perror("bind"); @@ -215,19 +241,20 @@ static void err_wait_func(int sock, short flags, void *arg) static const char *addrpair(const PgAddr *src, const PgAddr *dst) { - static char ip1buf[64], ip2buf[64], buf[256]; + static char ip1buf[INET6_ADDRSTRLEN], ip2buf[INET6_ADDRSTRLEN], + buf[INET6_ADDRSTRLEN+INET6_ADDRSTRLEN+128]; const char *ip1, *ip2; - if (src->is_unix) + if (pga_is_unix(src)) return "unix->unix"; - ip1 = inet_ntop(AF_INET, &src->ip_addr, ip1buf, sizeof(ip1buf)); + ip1 = pga_ntop(src, ip1buf, sizeof(ip1buf)); if (!ip1) ip1 = strerror(errno); - ip2 = inet_ntop(AF_INET, &dst->ip_addr, ip2buf, sizeof(ip2buf)); + ip2 = pga_ntop(src, ip2buf, sizeof(ip2buf)); if (!ip2) ip2 = strerror(errno); snprintf(buf, sizeof(buf), "%s:%d -> %s:%d", - ip1, src->port, ip2, dst->port); + ip1, pga_port(src), ip2, pga_port(dst)); return buf; } diff --git a/src/server.c b/src/server.c index 01c6106..6870de0 100644 --- a/src/server.c +++ b/src/server.c @@ -308,7 +308,7 @@ static bool handle_connect(PgSocket *server) bool res = false; PgPool *pool = server->pool; - fill_local_addr(server, sbuf_socket(&server->sbuf), server->remote_addr.is_unix); + fill_local_addr(server, sbuf_socket(&server->sbuf), pga_is_unix(&server->remote_addr)); if (!statlist_empty(&pool->cancel_req_list)) { slog_debug(server, "use it for pending cancel req"); diff --git a/src/takeover.c b/src/takeover.c index 8dbf065..ad1a76b 100644 --- a/src/takeover.c +++ b/src/takeover.c @@ -111,12 +111,10 @@ static void takeover_load_fd(struct MBuf *pkt, const struct cmsghdr *cmsg) client_enc ? client_enc : "NULL"); /* fill address */ - addr.is_unix = strcmp(saddr, "unix") == 0 ? true : false; - if (addr.is_unix) { - addr.port = cf_listen_port; + if (strcmp(saddr, "unix") == 0) { + pga_set(&addr, AF_UNIX, cf_listen_port); } else { - addr.ip_addr.s_addr = inet_addr(saddr); - addr.port = port; + pga_pton(&addr, saddr, port); } /* decide what to do with it */ @@ -127,7 +125,7 @@ static void takeover_load_fd(struct MBuf *pkt, const struct cmsghdr *cmsg) res = use_server_socket(fd, &addr, db, user, ckey, oldfd, linkfd, client_enc, std_string, datestyle, timezone); else if (strcmp(task, "pooler") == 0) - res = use_pooler_socket(fd, addr.is_unix); + res = use_pooler_socket(fd, pga_is_unix(&addr)); else fatal("unknown task: %s", task); diff --git a/src/util.c b/src/util.c index 3f8a624..f3a5390 100644 --- a/src/util.c +++ b/src/util.c @@ -29,6 +29,7 @@ int log_socket_prefix(enum LogLevel lev, void *ctx, char *dst, unsigned int dstl { const struct PgSocket *sock = ctx; char *user, *db, *host; + char host6[INET6_ADDRSTRLEN]; int port; /* no prefix */ @@ -38,12 +39,12 @@ int log_socket_prefix(enum LogLevel lev, void *ctx, char *dst, unsigned int dstl /* format prefix */ db = sock->pool ? sock->pool->db->name : "(nodb)"; user = sock->auth_user ? sock->auth_user->name : "(nouser)"; - if (sock->remote_addr.is_unix) { + if (pga_is_unix(&sock->remote_addr)) { host = "unix"; } else { - host = inet_ntoa(sock->remote_addr.ip_addr); + pga_ntop(&sock->remote_addr, host6, INET6_ADDRSTRLEN); } - port = sock->remote_addr.port; + port = pga_port(&sock->remote_addr); return snprintf(dst, dstlen, "%c-%p: %s/%s@%s:%d ", is_server_socket(sock) ? 'S' : 'C', @@ -223,23 +224,16 @@ loop: void fill_remote_addr(PgSocket *sk, int fd, bool is_unix) { PgAddr *dst = &sk->remote_addr; - struct sockaddr_in adr; - socklen_t len = sizeof(adr); + socklen_t len = sizeof(PgAddr); int err; - dst->ip_addr.s_addr = INADDR_ANY; - dst->port = 0; - dst->is_unix = is_unix; if (is_unix) { - dst->port = cf_listen_port; + pga_set(dst, AF_UNIX, cf_listen_port); } else { - err = getpeername(fd, (struct sockaddr *)&adr, &len); + err = getpeername(fd, (struct sockaddr *)dst, &len); if (err < 0) { log_error("fill_remote_addr: getpeername(%d) = %s", fd, strerror(errno)); - } else { - dst->ip_addr = adr.sin_addr; - dst->port = ntohs(adr.sin_port); } } } @@ -247,23 +241,16 @@ void fill_remote_addr(PgSocket *sk, int fd, bool is_unix) void fill_local_addr(PgSocket *sk, int fd, bool is_unix) { PgAddr *dst = &sk->local_addr; - struct sockaddr_in adr; - socklen_t len = sizeof(adr); + socklen_t len = sizeof(PgAddr); int err; - dst->ip_addr.s_addr = INADDR_ANY; - dst->port = 0; - dst->is_unix = is_unix; if (is_unix) { - dst->port = cf_listen_port; + pga_set(dst, AF_UNIX, cf_listen_port); } else { - err = getsockname(fd, (struct sockaddr *)&adr, &len); + err = getsockname(fd, (struct sockaddr *)dst, &len); if (err < 0) { log_error("fill_local_addr: getsockname(%d) = %s", fd, strerror(errno)); - } else { - dst->ip_addr = adr.sin_addr; - dst->port = ntohs(adr.sin_port); } } } @@ -311,3 +298,71 @@ void rescue_timers(void) } } +/* + * PgAddr operations + */ + +/* set family and port */ +void pga_set(PgAddr *a, int af, int port) +{ + a->af = af; + a->port = port; +} + +/* copy sockaddr_in/in6 to PgAddr */ +void pga_copy(PgAddr *a, const struct sockaddr *sa) +{ + const struct sockaddr_in *sa4; + const struct sockaddr_in6 *sa6; + + a->af = sa->sa_family; + switch (a->af) { + case AF_INET: + sa4 = (struct sockaddr_in *)sa; + a->port = ntohs(sa4->sin_port); + a->addr4 = sa4->sin_addr; + break; + case AF_INET6: + sa6 = (struct sockaddr_in6 *)sa; + a->port = ntohs(sa6->sin6_port); + a->addr6 = sa6->sin6_addr; + break; + } +} + +/* convert pgaddr to string */ +const char *pga_ntop(const PgAddr *a, char *dst, int dstlen) +{ + switch (a->af) { + case AF_UNIX: + strlcpy(dst, "unix", dstlen); + return dst; + case AF_INET: + return inet_ntop(a->af, &a->addr4, dst, dstlen); + case AF_INET6: + return inet_ntop(a->af, &a->addr6, dst, dstlen); + default: + return NULL; + } +} + +/* parse address from string */ +bool pga_pton(PgAddr *a, const char *s, int port) +{ + int res; + if (strcmp(s, "unix") == 0) { + pga_set(a, AF_UNIX, port); + return true; + } + if (strchr(s, ':')) { + pga_set(a, AF_INET6, port); + res = inet_pton(AF_INET6, s, &a->addr6); + } else { + pga_set(a, AF_INET, port); + res = inet_pton(AF_INET, s, &a->addr4); + } + if (res == 0) + errno = EINVAL; + return res > 0; +} +