Skip to content

fix: enable dual-stack IPv4/IPv6 listening on Linux#1186

Merged
qicosmos merged 4 commits into
alibaba:mainfrom
milestone-17:fix/issue-1185
May 21, 2026
Merged

fix: enable dual-stack IPv4/IPv6 listening on Linux#1186
qicosmos merged 4 commits into
alibaba:mainfrom
milestone-17:fix/issue-1185

Conversation

@milestone-17

@milestone-17 milestone-17 commented May 19, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes #1185

Linux下绑定IPv6地址(如::)时,由于两个原因导致IPv4客户端连不上:

  1. getaddrinfo解析字面量IP在不同glibc版本/容器环境下结果不稳定(AI_ADDRCONFIG标志的影响)
  2. 没有显式设置IPV6_V6ONLY=false,在net.ipv6.bindv6only=1的环境下IPv6 socket拒绝IPv4连接

Changes

  • 新建 include/ylt/coro_io/listen_endpoint.hpp,提取公共的地址解析和dual-stack设置逻辑
    • resolve_listen_endpoint():字面量IP用make_address直接解析,hostname才走resolver
    • set_ipv6_only_false():IPv6 endpoint时关闭v6_only,失败只warn不abort
  • server_acceptor.hppcoro_http_server.hpp 改为调用上述公共函数,消除重复代码

Testing

Windows MSVC 14.50 本地全量回归:

Test Suite Cases Assertions Result
coro_io_test 40 1,171,517 PASS
coro_http_test 121 108,657 PASS
coro_rpc_test 41 200,315 PASS
easylog_test 9 28 PASS
metric_test 44 461 PASS

@CLAassistant

CLAassistant commented May 19, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

When binding to an IPv6 address (e.g. "::"), explicitly set
IPV6_V6ONLY=false so the socket also accepts IPv4 connections.
This fixes the issue where Linux systems with net.ipv6.bindv6only=1
would reject IPv4-mapped connections.

Additionally, for IP literal addresses, use asio::ip::make_address()
directly instead of going through the resolver, which avoids
ambiguous results from getaddrinfo() on different glibc versions.
Falls back to resolver for hostname addresses (e.g. "localhost").

Fixes alibaba#1185

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

for detail, goto summary download Artifacts base-ylt-cov-report(base commit coverage report) and ylt-cov-report(current pull request coverage report)

@milestone-17

Copy link
Copy Markdown
Contributor Author

Hi @qicosmos, thanks for maintaining this great project!

I've addressed the CLA issue and all CI checks are passing now. Would you have a chance to review this PR when convenient? No rush at all — just wanted to make sure it's on your radar.

Happy to make any changes if needed. Thanks!

@qicosmos

Copy link
Copy Markdown
Collaborator

Hi @qicosmos, thanks for maintaining this great project!

I've addressed the CLA issue and all CI checks are passing now. Would you have a chance to review this PR when convenient? No rush at all — just wanted to make sure it's on your radar.

Happy to make any changes if needed. Thanks!

Thanks for your PR, i will review it later.

@qicosmos

qicosmos commented May 20, 2026

Copy link
Copy Markdown
Collaborator

建议新增文件,消除重复代码:

  // include/ylt/coro_io/listen_endpoint.hpp
  #pragma once

  #include <cstdint>
  #include <optional>
  #include <string>

  #include "asio/error_code.hpp"
  #include "asio/ip/address.hpp"
  #include "asio/ip/tcp.hpp"
  #include "asio/ip/v6_only.hpp"

  namespace coro_io::detail {

  template <typename Executor>
  inline std::optional<asio::ip::tcp::endpoint>
  resolve_listen_endpoint(
      const Executor& executor, const std::string& address, uint16_t port,
      asio::error_code& ec) {
    using asio::ip::tcp;

    auto addr = asio::ip::make_address(address, ec);
    if (!ec) {
      return tcp::endpoint(addr, port);
    }

    ec.clear();
    tcp::resolver resolver(executor);
    auto it = resolver.resolve(
        tcp::resolver::query(address, std::to_string(port)), ec);
    if (ec || it == tcp::resolver::iterator{}) {
      return std::nullopt;
    }

    return it->endpoint();
  }

  inline asio::error_code set_ipv6_only_false(
      asio::ip::tcp::acceptor& acceptor,
      const asio::ip::tcp::endpoint& endpoint) {
    asio::error_code ec;
    if (endpoint.protocol() == asio::ip::tcp::v6()) {
      acceptor.set_option(asio::ip::v6_only(false), ec);
    }
    return ec;
  }

  }  // namespace coro_io::detail

server_acceptor.hpp 里:

  #include "listen_endpoint.hpp"

  //然后 listen 里替换成:

  auto endpoint = detail::resolve_listen_endpoint(
      acceptor_->get_executor(), address_, port_, ec);
  if (!endpoint) {
    ELOG_ERROR << "resolve address " << address_
               << " error: " << ec.message();
    return listen_errc::bad_address;
  }

  acceptor_->open(endpoint->protocol(), ec);
  if (ec) {
    ELOG_ERROR << "open failed, error: " << ec.message();
    return listen_errc::open_error;
  }

  #ifdef __GNUC__
  acceptor_->set_option(tcp::acceptor::reuse_address(true), ec);
  #endif

  if (auto opt_ec =
  detail::set_ipv6_only_false(*acceptor_, *endpoint); opt_ec) {
    ELOG_WARN << "set v6_only(false) failed: " << opt_ec.message();
  }

  acceptor_->bind(*endpoint, ec);

coro_http_server.hpp 里:

  #include "ylt/coro_io/listen_endpoint.hpp"

  // 然后 listen 里替换成:

  auto endpoint =
  coro_io::detail::resolve_listen_endpoint(
      acceptor_.get_executor(), address_, port_, ec);
  if (!endpoint) {
    CINATRA_LOG_ERROR << "invalid address: " << address_
                      << " error: " << ec.message();
    if (ec) {
      return ec;
    }
    return std::make_error_code(std::errc::address_not_available);
  }

  acceptor_.open(endpoint->protocol(), ec);
  if (ec) {
    CINATRA_LOG_ERROR << "acceptor open failed"
                      << " error: " << ec.message();
    return ec;
  }

  #ifdef __GNUC__
  acceptor_.set_option(tcp::acceptor::reuse_address(true), ec);
  #endif

  if (auto opt_ec =
  coro_io::detail::set_ipv6_only_false(acceptor_, *endpoint);
      opt_ec) {
    CINATRA_LOG_WARNING << "set v6_only(false) failed:" << opt_ec.message();
  }

  acceptor_.bind(*endpoint, ec);

同时可以删掉两个原文件里直接引入的 asio/ip/ v6_only.hpp 和 asio/ip/address.hpp,因为都在listen_endpoint.hpp 了。这样解析 endpoint 和设置v6_only(false) 都集中,错误日志和返回策略仍然留在各自模块里。

@milestone-17

Copy link
Copy Markdown
Contributor Author

OK,谢谢你的提醒,这是我的疏忽,我现在做出相应的修改

Move resolve_listen_endpoint() and set_ipv6_only_false() into a
dedicated header so that server_acceptor.hpp and coro_http_server.hpp
reuse the same implementation instead of duplicating it.
@milestone-17

Copy link
Copy Markdown
Contributor Author

Done. I extracted resolve_listen_endpoint() and set_ipv6_only_false() into include/ylt/coro_io/listen_endpoint.hpp, and both server_acceptor.hpp and coro_http_server.hpp now call into it instead of duplicating the logic.

Tested locally on MSVC -- all existing test suites pass without regression.

@qicosmos qicosmos requested review from poor-circle and qicosmos May 20, 2026 14:34

@qicosmos qicosmos left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@github-actions

Copy link
Copy Markdown

for detail, goto summary download Artifacts base-ylt-cov-report(base commit coverage report) and ylt-cov-report(current pull request coverage report)

@milestone-17

milestone-17 commented May 20, 2026

Copy link
Copy Markdown
Contributor Author

CI workflows are waiting for approval to run. Could you approve them when you get a chance? Thanks.@qicosmos

@poor-circle

Copy link
Copy Markdown
Collaborator

CI workflows are waiting for approval to run. Could you approve them when you get a chance? Thanks.@qicosmos

done

@github-actions

Copy link
Copy Markdown

for detail, goto summary download Artifacts base-ylt-cov-report(base commit coverage report) and ylt-cov-report(current pull request coverage report)

@github-actions

Copy link
Copy Markdown

for detail, goto summary download Artifacts base-ylt-cov-report(base commit coverage report) and ylt-cov-report(current pull request coverage report)

@milestone-17

Copy link
Copy Markdown
Contributor Author

All checks should be green once CI is approved. Ready to merge from my side — let me know if anything else needs to be changed.

@qicosmos qicosmos merged commit b2057ca into alibaba:main May 21, 2026
31 checks passed
shenxuebing pushed a commit to shenxuebing/yalantinglibs that referenced this pull request Jun 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Linux 无法同时监听ipv4/6

4 participants