如何使用 C 套接字 API 监听所有 IPV6 地址

发布于 2024-12-05 18:00:15 字数 1306 浏览 1 评论 0原文

我维护 GPSD,这是一个广泛部署的开源服务守护进程,用于监控 GPS 和其他大地测量传感器。它侦听 IPv4 和 IPv6 上端口 2947 上的客户端应用程序连接。为了安全和隐私,它通常只侦听环回地址,但守护程序有一个 -G 选项,旨在使其侦听任何地址。

问题:-G 选项适用于 IPv4,但我不知道如何使其适用于 IPv6。基于各种教程示例应该有效的方法却不起作用,而是产生一个错误,表明该地址已在使用中。我正在向具有 IPv6 网络编程经验的人寻求帮助来解决此问题。

相关代码位于 http://git.berlios.de/cgi-bin/gitweb.cgi?p=gpsd;a=blob;f=gpsd.c;h=ee2156caf03ca23405f57f3e04e9ef306a75686f;hb=HEAD

这个代码在 IPv4 下的 -G 和非 -G 情况下都能正确运行,这很容易验证netstat -l。

现在查看“case AF_INET6:”之后的第 398 行。 Listen_global选项由-G设置;如果为 false,则代码成功。目前有以下评论,继承自未知贡献者,内容如下:

/* else */
        /* BAD:  sat.sa_in6.sin6_addr = in6addr_any;
     * the simple assignment will not work (except as an initializer)
     * because sin6_addr is an array not a simple type
     * we could do something like this:
     * memcpy(sat.sa_in6.sin6_addr, in6addr_any, sizeof(sin6_addr));
     * BUT, all zeros is IPv6 wildcard, and we just zeroed the array
     * so really nothing to do here
     */

根据我查找的各种教程示例,作业“sat.sa_in6.sin6_addr = in6addr_any;”是(尽管有评论)正确的,并且它确实可以编译。但是,使用 -G 启动失败,并声称侦听地址已在使用中。

是赋值“sat.sa_in6.sin6_addr = in6addr_any;”这里名义上正确吗?我还缺少什么(如果有的话)?

I maintain GPSD, a widely-deployed open-source service daemon that monitors GPSes and other geodetic sensors. It listens for client-application connections on port 2947 on both IPv4 and IPv6. For security and privacy it normally listens only on the loopback address, but there is a -G option to the daemon that is intended to cause it to listen on any address.

The problem: the -G option works in IPv4, but I can't figure out how to make it work with IPv6. The method that should work based on various tutorial examples does not, producing instead an error suggesting the address is already in use. I'm seeking help to fix this from people experienced with IPv6 network programming.

Relevant code is at http://git.berlios.de/cgi-bin/gitweb.cgi?p=gpsd;a=blob;f=gpsd.c;h=ee2156caf03ca23405f57f3e04e9ef306a75686f;hb=HEAD

This code operates correctly in both the -G and non -G cases under IPv4, as is easily verified with netstat -l.

Now look around line 398 after "case AF_INET6:". The listen_global option is set by -G; when false, the code succeeds. There is presently a following comment, inherited from an unknown contributor, that reads like this:

/* else */
        /* BAD:  sat.sa_in6.sin6_addr = in6addr_any;
     * the simple assignment will not work (except as an initializer)
     * because sin6_addr is an array not a simple type
     * we could do something like this:
     * memcpy(sat.sa_in6.sin6_addr, in6addr_any, sizeof(sin6_addr));
     * BUT, all zeros is IPv6 wildcard, and we just zeroed the array
     * so really nothing to do here
     */

According to various tutorial examples I have looked up, the assignment "sat.sa_in6.sin6_addr = in6addr_any;" is (despite the comment) correct, and it does compile. However, startup with -G fails claiming the listen address is already in use.

Is the assignment "sat.sa_in6.sin6_addr = in6addr_any;" nominally correct here? What else, if anything, am I missing?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

青萝楚歌 2024-12-12 18:00:15

该地址已被使用的原因是,在许多 IPv6 网络堆栈上,默认情况下 IPv6 套接字将同时侦听 IPv4 和 IPv6。 IPv4 连接将被透明处理并映射到 IPv6 空间的子集< /a>.但是,这意味着您无法在不更改 IPv6 套接字上的设置的情况下将 IPv6 套接字绑定到与 IPv4 套接字相同的端口上。有道理吗?

只需在调用 bind 之前执行此操作(这取自我的一个项目):

int on = 1;
if (addr->sa_family == AF_INET6) {
    r = setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
    if (r)
        /* error */
}

不幸的是,IPV6_V6ONLY 没有跨平台的默认值 - 这基本上意味着您如果您关心的话,总是需要明确地打开或关闭它,除非您不关心其他平台。 Linux 默认情况下将其关闭,Windows 默认情况下将其打开......

The reason the address is already in use is because on many IPv6 networking stacks, by default an IPv6 socket will listen to both IPv4 and IPv6 at the same time. IPv4 connections will be handled transparently and mapped to a subset of the IPv6 space. However, this means you cannot bind to an IPv6 socket on the same port as an IPv4 socket without changing the settings on the IPv6 socket. Make sense?

Just do this before your call to bind (this is taken from one of my projects):

int on = 1;
if (addr->sa_family == AF_INET6) {
    r = setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
    if (r)
        /* error */
}

Unfortunately, there is no default value across platforms for IPV6_V6ONLY -- which basically means you always need to turn it either on or off explicitly if you care, unless you don't care about other platforms. Linux leaves it off by default, Windows leaves it on by default...

丢了幸福的猪 2024-12-12 18:00:15

从随机 Linux 系统的包含文件中看,in6addr_any 的声明如下:

extern const struct in6_addr in6addr_any;        /* :: */
extern const struct in6_addr in6addr_loopback;   /* ::1 */
#define IN6ADDR_ANY_INIT { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } } }

因此,也许与 INIT 数组的接近使在 GPSD 源代码中留下该注释的人感到困惑。实际类型显然是struct in6_addr,它是可赋值的。

我确实环顾四周,发现了一些提示,表明如果 IPv4 已经在侦听“任何”地址,则 IPv6 也不能。也许这就是困扰你的原因。

From a look in a random Linux system's include files, in6addr_any is declared like so:

extern const struct in6_addr in6addr_any;        /* :: */
extern const struct in6_addr in6addr_loopback;   /* ::1 */
#define IN6ADDR_ANY_INIT { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } } }

So, perhaps the nearness to the INIT array confused whoever left that comment in GPSD's sources. The actual type is clearly struct in6_addr, which is assignable.

I did look around, and found some hints that suggested that if IPv4 is already listening to the "any" address, IPv6 can't also. Perhaps that's what's biting you.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文