嘉楠科技
直播中

任凭风吹

10年用户 1084经验值
擅长:可编程逻辑 电源/新能源 MEMS/传感技术 制造/封装 模拟技术
私信 关注
[问答]

getsockname是否也需要在内核通过自定义dev来获取啊?

getsockname是否也需要在内核通过自定义dev来获取啊?static int _eXosip_default_gateway_ipv4(char* destination, char* address, int size){    socklen_t          len;    int                sock_rt, on = 1;    struct sockaddr_in iface_out;    struct sockaddr_in remote;    int                type;    printf("n------ [DEBUG] Start _eXosip_default_gateway_ipv4 ------n");    printf("[DEBUG] Input: destination=%s, address=%p, size=%dn", destination ? destination : "NULL", address, size);    // Step 1: 初始化目标地址结构体    memset(&remote, 0, sizeof(struct sockaddr_in));    remote.sin_family      = AF_INET;    remote.sin_addr.s_addr = inet_addr(destination);    remote.sin_port        = htons(11111);    printf("[DEBUG] Remote address set: family=AF_INET, addr=%s, port=11111n", inet_ntoa(remote.sin_addr));    // Step 2: 创建 UDP 套接字    memset(&iface_out, 0, sizeof(iface_out));    type = SOCK_DGRAM;#if defined(SOCK_CLOEXEC)    type = SOCK_CLOEXEC | SOCK_DGRAM;#endif    sock_rt = socket(AF_INET, type, 0);    printf("[DEBUG] socket() called: AF_INET, type=%d, protocol=0n", type);    if (sock_rt == -1) {        printf("[ERROR] socket() failed! errno=%d (%s)n", errno, strerror(errno));        snprintf(address, size, "127.0.0.1");        return OSIP_NO_NETWORK;    }    printf("[DEBUG] socket() returned fd=%dn", sock_rt);    // Step 3: 设置 SO_BROADCAST 选项    if (setsockopt(sock_rt, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) == -1) {        printf("[ERROR] setsockopt(SO_BROADCAST) failed! errno=%d (%s)n", errno, strerror(errno));        _eXosip_closesocket(sock_rt);        snprintf(address, size, "127.0.0.1");        return OSIP_NO_NETWORK;    }    printf("[DEBUG] setsockopt(SO_BROADCAST) successn");    // Step 4: 连接目标地址(模拟路由选择)    printf("[DEBUG] Attempting connect() to %s:11111...n", inet_ntoa(remote.sin_addr));    if (connect(sock_rt, (struct sockaddr*)&remote, sizeof(struct sockaddr_in)) == -1) {        printf("[ERROR] connect() failed! errno=%d (%s)n", errno, strerror(errno));        _eXosip_closesocket(sock_rt);        snprintf(address, size, "127.0.0.1");        return OSIP_NO_NETWORK;    }    printf("[DEBUG] connect() success (UDP pseudo-connection established)n");    // Step 5: 获取本地绑定地址(出口 IP)    len = sizeof(iface_out);    printf("[DEBUG] Calling getsockname()...n");    if (getsockname(sock_rt, (struct sockaddr*)&iface_out, &len) == -1) {        printf("[ERROR] getsockname() failed! errno=%d (%s)n", errno, strerror(errno));        _eXosip_closesocket(sock_rt);        snprintf(address, size, "127.0.0.1");        return OSIP_NO_NETWORK;    }    printf("[DEBUG] getsockname() returned: family=%d, addr=%s, port=%dn", iface_out.sin_family,           inet_ntoa(iface_out.sin_addr), ntohs(iface_out.sin_port));    // iface_out.sin_addr.    // Step 6: 关闭套接字    _eXosip_closesocket(sock_rt);    printf("[DEBUG] Socket closed (fd=%d)n", sock_rt);    // Step 7: 验证获取的 IP 是否有效    if (iface_out.sin_addr.s_addr == 0) {        printf("[WARN] getsockname() returned 0.0.0.0! No valid route to destination?n");        snprintf(address, size, "127.0.0.1");        return OSIP_NO_NETWORK;    }    // Step 8: 返回结果    printf("[DEBUG] Final local interface IP: %sn", inet_ntoa(iface_out.sin_addr));    osip_strncpy(address, inet_ntoa(iface_out.sin_addr), size - 1);    printf("------ [DEBUG] End _eXosip_default_gateway_ipv4 (SUCCESS) ------nn");    return OSIP_SUCCESS;}期待结果和实际结果
软硬件版本信息
错误日志
------ [DEBUG] Start _eXosip_default_gateway_ipv4 ------[DEBUG] Input: destination=217.12.3.11, address=0x100a5cb20, size=128[DEBUG] Remote address set: family=AF_INET, addr=217.12.3.11, port=11111[DEBUG] socket() called: AF_INET, type=524290, protocol=0[DEBUG] socket() returned fd=27[DEBUG] setsockopt(SO_BROADCAST) success[DEBUG] Attempting connect() to 217.12.3.11:11111...[DEBUG] connect() success (UDP pseudo-connection established)[DEBUG] Calling getsockname()...[DEBUG] getsockname() returned: family=2, addr=0.0.0.0, port=49158[DEBUG] Socket closed (fd=113246210)[WARN] getsockname() returned 0.0.0.0! No valid route to destination?[E/sal.skt] not find network interface device by protocol family(16).[E/sal.skt] SAL socket protocol family input failed, return error -3.尝试解决过程

回帖(1)

温暖镜头

2025-6-20 17:40:04

在Linux内核中,getsockname()不需要通过自定义设备(dev)来获取信息。它是标准的系统调用,由内核直接提供支持。以下是详细分析和代码解析:


1. getsockname的实现原理



  • getsockname() 是POSIX标准的系统调用(man 2 getsockname),用于获取套接字绑定的本地地址信息

  • 内核内部实现

    • 当用户调用getsockname()时,内核会查询套接字关联的struct sock对象。

    • 直接从sock->sk_rcv_saddr(IPv4源地址)和sock->sk_num(端口号)等字段提取信息。

    • 无需通过自定义设备驱动;内核的网络协议栈已内置支持此功能。





2. 代码解析:_eXosip_default_gateway_ipv4


您的代码片段是eXosip库中获取默认网关出口IP的方法,其逻辑如下:


static int _eXosip_default_gateway_ipv4(char* destination, char* address, int size) {
    socklen_t len;
    int sock_rt, on = 1;
    struct sockaddr_in iface_out;
    struct sockaddr_in remote;
    int type;
    ...
    // 创建UDP套接字
    sock_rt = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    ...
    // 设置SO_DONTROUTE选项(绕过路由表直接发送)
    setsockopt(sock_rt, SOL_SOCKET, SO_DONTROUTE, (char*)&on, sizeof(on));
    ...
    // 目标地址(通常是公共DNS如8.8.8.8)
    remote.sin_family = AF_INET;
    remote.sin_port = htons(12345);  // 任意端口
    inet_pton(AF_INET, destination, &remote.sin_addr);

    // 连接到此目标(触发路由选择)
    connect(sock_rt, (struct sockaddr*)&remote, sizeof(remote));

    // !!! 关键:使用getsockname获取本地出口地址 !!!
    len = sizeof(iface_out);
    getsockname(sock_rt, (struct sockaddr*)&iface_out, &len);

    // 复制IP到输出缓冲区
    inet_ntop(AF_INET, &iface_out.sin_addr, address, size);
    ...
    close(sock_rt);
    return 0;
}

关键点:



  • getsockname作用:在connect()连接外部地址后,内核会为套接字分配本地出口IP。getsockname()直接读取这个内核分配的地址。

  • 为何不需要自定义设备?

    getsockname()通过系统调用陷入内核,由内核网络子系统直接处理,无需用户空间设备驱动介入。路由决策、地址绑定等均由内核协议栈完成。




3. 路由与地址选择的内部过程


当调用connect()时:



  1. 内核查询路由表确定出口网卡和源IP。

  2. 套接字绑定到该源IP(临时或显式绑定)。

  3. getsockname()从套接字结构体中读取已绑定的地址。




4. 是否需要自定义内核模块?



  • 完全不需要getsockname()是标准功能,内核已实现以下支持:

    • net/socket.c中的sys_getsockname()函数。

    • 协议相关的处理如IPv4在net/ipv4/af_inet.c中的inet_getname()





结论



  1. getsockname()不需要自定义设备驱动,它是内核网络栈的基础功能。

  2. 在您的代码中,getsockname()正确用于获取连接后的本地出口IP。

  3. 若需修改行为(如特殊路由),应通过内核路由表Netfilter等标准机制实现,而非自定义设备。


建议保留此实现方式,它是获取本地出口IP的可靠方法。

举报

更多回帖

发帖
×
20
完善资料,
赚取积分