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

2015-06-03 12:41:35 +08:00
 F32

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

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

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

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

@fangdingjun 真是个好主意。
F32
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
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;
}

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/195770

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX