内核模块以UNIX Socket抽象地址连接用户态服务器失败,返回-ECONNREFUSED
问题描述
内核模块作为UNIX Socket客户端,用户态程序作为服务器,使用抽象类型地址通信时内核模块连接失败,错误码-ECONNREFUSED;但使用路径名类型地址时通信正常。
内核代码(原错误版本)
struct sockaddr_un s_addr; ret = sock_create_kern(&init_net, AF_LOCAL, SOCK_STREAM, 0, &cl_sock); if (ret < 0) { /*...*/ } memset(&s_addr, 0, sizeof(struct sockaddr_un)); s_addr.sun_family = AF_LOCAL; s_addr.sun_path[0] = 0; strncpy(s_addr.sun_path + 1, "my_test", sizeof(s_addr.sun_path) - 1); ret = cl_sock->ops->connect(cl_sock, (struct sockaddr *)&s_addr, sizeof(s_addr), 0); if (ret < 0) { /*...*/ }
用户态服务器代码
server_socket = socket_local_server("my_test", ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM); if (server_socket < 0) { /*...*/ } accept(server_socket , NULL, NULL);
问题根源
内核端调用connect时传递的地址长度错误:抽象UNIX Socket的有效地址长度不是整个struct sockaddr_un的大小,而是从sun_family字段开始,到抽象名字末尾的实际字节数。如果传递整个结构体大小,内核会将sun_path中名字后的空字节也当作地址的一部分,导致与服务器绑定的地址不匹配,服务器无法识别该连接请求,返回ECONNREFUSED。
而用户态的socket_local_server在ANDROID_SOCKET_NAMESPACE_ABSTRACT模式下,会自动计算并使用抽象地址的实际长度进行绑定,因此内核端必须使用相同的长度才能匹配。
修复方案
修改内核代码,计算抽象地址的实际长度,并在connect时传入该长度:
struct sockaddr_un s_addr; socklen_t addr_len; ret = sock_create_kern(&init_net, AF_LOCAL, SOCK_STREAM, 0, &cl_sock); if (ret < 0) { /*...*/ } memset(&s_addr, 0, sizeof(struct sockaddr_un)); s_addr.sun_family = AF_LOCAL; s_addr.sun_path[0] = 0; // 预留一个字节给字符串结束符,避免截断 strncpy(s_addr.sun_path + 1, "my_test", sizeof(s_addr.sun_path) - 2); // 计算抽象地址的实际长度:sun_family字段长度 + 空字节(sun_path[0]) + 名字长度 addr_len = offsetof(struct sockaddr_un, sun_path) + 1 + strlen("my_test"); ret = cl_sock->ops->connect(cl_sock, (struct sockaddr *)&s_addr, addr_len, 0); if (ret < 0) { /*...*/ }
关键修改点
- 使用
offsetof(struct sockaddr_un, sun_path)获取sun_family字段的总长度(即结构体开头到sun_path的偏移量)。 - 加上1(对应
sun_path[0]的空字节)和名字的实际长度,得到抽象地址的有效长度。 - 调整
strncpy的长度参数,确保字符串结束符不会被截断。
验证说明
修改后,内核端传递的地址长度与服务器绑定的地址长度完全一致,抽象名字匹配,即可成功建立连接。
内容的提问来源于stack exchange,提问作者yuwezyu
相关产品推荐
相关产品推荐

