From a86f4d00df8ccbe2b018b94f41b5047280230684 Mon Sep 17 00:00:00 2001 From: hulk Date: Sat, 14 May 2022 11:27:39 +0800 Subject: [PATCH] Support to listen on the IPV6 address (#554) --- kvrocks.conf | 2 +- src/util.cc | 14 +++++++---- src/worker.cc | 65 ++++++++++++++++++++++++++++++++++++--------------- 3 files changed, 57 insertions(+), 24 deletions(-) diff --git a/kvrocks.conf b/kvrocks.conf index 805b84bfdce..d98eed1df3a 100644 --- a/kvrocks.conf +++ b/kvrocks.conf @@ -8,7 +8,7 @@ # Examples: # # bind 192.168.1.100 10.0.0.1 -# bind 127.0.0.1 +# bind 127.0.0.1 ::1 bind 0.0.0.0 # Unix socket. diff --git a/src/util.cc b/src/util.cc index 9bb138c8629..b393597fb23 100644 --- a/src/util.cc +++ b/src/util.cc @@ -306,21 +306,27 @@ Status SockReadLine(int fd, std::string *data) { } int GetPeerAddr(int fd, std::string *addr, uint32_t *port) { + addr->clear(); + sockaddr_storage sa{}; socklen_t sa_len = sizeof(sa); if (getpeername(fd, reinterpret_cast(&sa), &sa_len) < 0) { return -1; } - if (sa.ss_family == AF_INET) { + if (sa.ss_family == AF_INET6) { + char buf[INET6_ADDRSTRLEN]; + auto sa6 = reinterpret_cast(&sa); + inet_ntop(AF_INET6, reinterpret_cast(&sa6->sin6_addr), buf, INET_ADDRSTRLEN); + addr->append(buf); + *port = ntohs(sa6->sin6_port); + } else { auto sa4 = reinterpret_cast(&sa); char buf[INET_ADDRSTRLEN]; inet_ntop(AF_INET, reinterpret_cast(&sa4->sin_addr), buf, INET_ADDRSTRLEN); - addr->clear(); addr->append(buf); *port = ntohs(sa4->sin_port); - return 0; } - return -2; // only support AF_INET currently + return 0; } Status DecimalStringToNum(const std::string &str, int64_t *n, int64_t min, int64_t max) { diff --git a/src/worker.cc b/src/worker.cc index 6caf2fada5d..06faeb44eb9 100644 --- a/src/worker.cc +++ b/src/worker.cc @@ -44,10 +44,11 @@ Worker::Worker(Server *svr, Config *config, bool repl) : svr_(svr) { timeval tm = {10, 0}; evtimer_add(timer_, &tm); + Status s; int port = config->port; auto binds = config->binds; for (const auto &bind : binds) { - Status s = listenTCP(bind, port, config->backlog); + s = listenTCP(bind, port, config->backlog); if (!s.IsOK()) { LOG(ERROR) << "[worker] Failed to listen on: "<< bind << ":" << port << ", encounter error: " << s.Msg(); @@ -154,27 +155,53 @@ void Worker::newUnixSocketConnection(evconnlistener *listener, evutil_socket_t f } Status Worker::listenTCP(const std::string &host, int port, int backlog) { - sockaddr_in sin{}; - sin.sin_family = AF_INET; - evutil_inet_pton(AF_INET, host.data(), &(sin.sin_addr)); - sin.sin_port = htons(port); - int fd = socket(AF_INET, SOCK_STREAM, 0); - int sock_opt = 1; - if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &sock_opt, sizeof(sock_opt)) < 0) { - return Status(Status::NotOK, evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR())); - } - // to support multi-thread binding on macOS - if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &sock_opt, sizeof(sock_opt)) < 0) { - return Status(Status::NotOK, evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR())); + char _port[6]; + int af, rv, fd, sock_opt = 1; + + if (strchr(host.data(), ':')) { + af = AF_INET6; + } else { + af = AF_INET; + } + snprintf(_port, sizeof(_port), "%d", port); + struct addrinfo hints, *srv_info, *p; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = af; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + + if ((rv = getaddrinfo(host.data(), _port, &hints, &srv_info)) != 0) { + return Status(Status::NotOK, gai_strerror(rv)); } - if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) { - return Status(Status::NotOK, evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR())); + + for (p = srv_info; p != nullptr; p = p->ai_next) { + if ((fd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) + continue; + if (af == AF_INET6 && setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &sock_opt, sizeof(sock_opt)) == -1) { + goto error; + } + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &sock_opt, sizeof(sock_opt)) < 0) { + goto error; + } + // to support multi-thread binding on macOS + if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &sock_opt, sizeof(sock_opt)) < 0) { + goto error; + } + if (bind(fd, p->ai_addr, p->ai_addrlen)) { + goto error; + } + evutil_make_socket_nonblocking(fd); + auto lev = evconnlistener_new(base_, newTCPConnection, this, + LEV_OPT_CLOSE_ON_FREE, backlog, fd); + listen_events_.emplace_back(lev); } - evutil_make_socket_nonblocking(fd); - auto lev = evconnlistener_new(base_, newTCPConnection, this, - LEV_OPT_CLOSE_ON_FREE, backlog, fd); - listen_events_.emplace_back(lev); + + freeaddrinfo(srv_info); return Status::OK(); + +error: + freeaddrinfo(srv_info); + return Status(Status::NotOK, evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR())); } Status Worker::ListenUnixSocket(const std::string &path, int perm, int backlog) {