V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
F32
V2EX  ›  程序员

使用 Raw Socket 时如何自动获取 Source Address?

  •  
  •   F32 · 2015-06-03 12:41:35 +08:00 · 2747 次点击
    这是一个创建于 3460 天前的主题,其中的信息可能已经有所发展或是发生改变。

    使用普通的 Socket 时,一般由操作系统负责查询本地的路由表,自动填写 IP 报文里的 Source Address 字段,应用程序不用关心用的是哪个本机 IP(很多网络程序是不用绑定端口的)。

    但是有些情况下可能需要使用 Raw Socket,并且需要手动填写 IP 报文里的 Source Address 字段。请问是否有相关的命令 / 系统 API 能够完成这个步骤?必须要用户来指定太不智能了。

    8 条回复    2015-06-23 15:30:54 +08:00
    est
        1
    est  
       2015-06-03 12:43:00 +08:00
    getaddrinfo 这个?
    F32
        2
    F32  
    OP
       2015-06-03 13:56:54 +08:00
    @est 我的问题的本质是路由表查询,getaddrinfo 好像没有处理这个问题把?
    fangjinmin
        3
    fangjinmin  
       2015-06-03 15:28:46 +08:00
    多网卡的时候,只能手动填写吧。
    fangdingjun
        4
    fangdingjun  
       2015-06-03 15:35:14 +08:00   ❤️ 1
    这里有变通的方法啊
    先建一个普通的udp socket,然后connect目标地址,就可以获取到source address了,把这个source address用于raw socket就行了
    TheCure
        5
    TheCure  
       2015-06-03 16:19:19 +08:00
    需要先指定Interface 返回Interface的地址 然后构造数据包 然后协议栈会根据目的地址匹配路由表 不知道src address和路由表有什么关系
    gamexg
        6
    gamexg  
       2015-06-03 16:42:38 +08:00
    @callofmx 楼主的意思是本地有多个ip的时候,不确定对应目的地址使用哪个本地IP。

    不过除非非常复杂的网络环境下开发,一般直接用默认路由同网段的就行。

    @fangdingjun 真是个好主意。
    F32
        7
    F32  
    OP
       2015-06-23 15:30:28 +08:00
    @fangdingjun 方法经测试可用,非常感谢。为了方便其他可能遇到问题的人,这里贴上代码(Linux)

    #include <stdio.h>
    #include <string.h>
    #include <arpa/inet.h>
    #include <sys/socket.h>

    int main()
    {
    &nbsp;&nbsp;&nbsp;&nbsp;// create socket
    &nbsp;&nbsp;&nbsp;&nbsp;int sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
    &nbsp;&nbsp;&nbsp;&nbsp;if (sockfd < 0)
    &nbsp;&nbsp;&nbsp;&nbsp;{
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;perror("create_socket()");
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return -1;
    &nbsp;&nbsp;&nbsp;&nbsp;}

    &nbsp;&nbsp;&nbsp;&nbsp;// prepare target address
    &nbsp;&nbsp;&nbsp;&nbsp;sockaddr_in addr;
    &nbsp;&nbsp;&nbsp;&nbsp;memset(&addr, 0, sizeof(addr));
    &nbsp;&nbsp;&nbsp;&nbsp;addr.sin_family = AF_INET;
    &nbsp;&nbsp;&nbsp;&nbsp;addr.sin_addr.s_addr = inet_addr("8.8.8.8");
    &nbsp;&nbsp;&nbsp;&nbsp;addr.sin_port = htons(33435);

    &nbsp;&nbsp;&nbsp;&nbsp;// connect
    &nbsp;&nbsp;&nbsp;&nbsp;connect(sockfd, (sockaddr *)&addr, sizeof(addr));

    &nbsp;&nbsp;&nbsp;&nbsp;// prepare local address
    &nbsp;&nbsp;&nbsp;&nbsp;sockaddr_in local_addr;
    &nbsp;&nbsp;&nbsp;&nbsp;unsigned int len = sizeof(local_addr);
    &nbsp;&nbsp;&nbsp;&nbsp;getsockname(sockfd, (sockaddr *)&local_addr, &len);

    &nbsp;&nbsp;&nbsp;&nbsp;// output
    &nbsp;&nbsp;&nbsp;&nbsp;printf("local address: %s\n", inet_ntoa(local_addr.sin_addr));

    &nbsp;&nbsp;&nbsp;&nbsp;return 0;
    }
    F32
        8
    F32  
    OP
       2015-06-23 15:30:54 +08:00
    -_-

    #include <stdio.h>
    #include <string.h>
    #include <arpa/inet.h>
    #include <sys/socket.h>

    int main()
    {
    // create socket
    int sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
    if (sockfd < 0)
    {
    perror("create_socket()");
    return -1;
    }

    // prepare target address
    sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr("8.8.8.8");
    addr.sin_port = htons(33435);

    // connect
    connect(sockfd, (sockaddr *)&addr, sizeof(addr));

    // prepare local address
    sockaddr_in local_addr;
    unsigned int len = sizeof(local_addr);
    getsockname(sockfd, (sockaddr *)&local_addr, &len);

    // output
    printf("local address: %s\n", inet_ntoa(local_addr.sin_addr));

    return 0;
    }
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5324 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 07:51 · PVG 15:51 · LAX 23:51 · JFK 02:51
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.