【问题标题】:Dynamically changing the source IP address动态更改源 IP 地址
【发布时间】:2013-10-15 02:56:18
【问题描述】:

我的服务器有几个不同的 IP 地址。此时,每个 IP 都可以收到一个 UDP 请求,但回复的始终是同一个 IP,这是请求者不喜欢的。

长话短说,这是所有基本代码:

int sock;
socklen_t clilen;
struct sockaddr_in serv_addr, cli_addr;
memset((char*)&serv_addr, 0, sizeof(serv_addr));

sock = socket(AF_INET, SOCK_DGRAM, 0);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(PORT);
...
recvfrom(sock, buffer, BUFLEN, 0, (struct sockaddr *)&cli_addr, &clilen);
...
sendto(sock, resData, resLen, 0, (struct sockaddr *)&cli_addr, sizeof(cli_addr));

我希望能够以某种方式指定使用哪个 IP 来发回我的数据包(每个请求都可能不同),但我不知道如何以及是否可以使用套接字来完成。我在这个领域并不是很有经验,因此非常感谢我能得到的所有帮助。

在下方编辑

我在这里接受的答案中找到了一个潜在的解决方案:How to re bind a udp socket in Linux

但是,出现了一个新问题。我如何知道哪个 IP/接口收到了请求?这样我就可以使用该 IP/接口做出响应。

【问题讨论】:

  • 您的问题不清楚。您的机器上是否有多个接口,并且您希望每次都从不同的接口发送。还是您想将 IP 地址欺骗为数据包中的假 IP 地址?
  • 我有多个接口,我不想恶搞。

标签: c++ c sockets udp ip-address


【解决方案1】:

您需要bind() 到本地IP 地址,即您要使用的接口的IP。

阅读有关bind()的指南

Beej's Guide To Networking#bind

【讨论】:

  • 这就是问题所在。我不想绑定到特定的接口或 IP 地址。
【解决方案2】:

我已经解决了我的问题,并且必须以良好的方式发布解决方案!我没有使用 SOCK_RAW,也没有将我的接口绑定到本地 IP 等。这是 google 上的 100 页和一些 stackoverflow 的混合体,所以我有点遗憾我没有保存链接以给出正确的学分。

由于我不是专家,代码中可能存在明显的缺陷,但这是我想出的解决方案,并且它有效。我刚刚开始清理代码(当你尝试从 100 个不同的页面中获取内容并将它们组合起来时,它会变得很混乱)。无论如何,这里是:

接收部分:

int sock;
socklen_t clilen;
struct sockaddr_in serv_addr, cli_addr;
char buffer[BUFLEN];

// Next
memset((char*)&serv_addr, 0, sizeof(serv_addr));
sock = socket(AF_INET, SOCK_DGRAM, 0);
serv_addr.sin_family = AF_INET;
//serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(PORT);

clilen = sizeof(cli_addr);

if (bind(sock, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
    error("ERROR on binding");
}

bool opt = true;
setsockopt(sock, IPPROTO_IP, IP_PKTINFO, (char *)&opt, sizeof(opt));

// Packet data will be saved in buffer
bzero(buffer, BUFLEN);
struct iovec vector;
vector.iov_base = buffer;
vector.iov_len = sizeof(buffer);

struct msghdr msg;
msg.msg_name = &cli_addr;
msg.msg_namelen = sizeof(cli_addr);
msg.msg_iov = &vector;
msg.msg_iovlen = 1;
int flags = 0;

// Not sure what controlBuffer contains at this point
char controlBuffer[1024];
msg.msg_control = controlBuffer;
msg.msg_controllen = 1024;

// Recv packet
int bytes = ::recvmsg(sock, &msg, flags);

struct cmsghdr *cmsg;
struct in_pktinfo *dest_ip_ptr;
int dest_ip = 0;

// Loop through IP header messages
cmsg = CMSG_FIRSTHDR(&msg);
for ( cmsg = CMSG_FIRSTHDR(&msg);
    cmsg != NULL;
    cmsg = CMSG_NXTHDR( &msg, cmsg ) )
{
    if (cmsg->cmsg_level != IPPROTO_IP ||
        cmsg->cmsg_type != IP_PKTINFO)
    {
        continue;
    }

    // Get IP (int)
    struct in_pktinfo *dest_ip_ptr = (struct in_pktinfo*)CMSG_DATA(cmsg);
    dest_ip = dest_ip_ptr->ipi_addr.s_addr;
}

// Format IP
unsigned char ipParts[4];
ipParts[0] = dest_ip & 0xFF;
ipParts[1] = (dest_ip >> 8) & 0xFF;
ipParts[2] = (dest_ip >> 16) & 0xFF;
ipParts[3] = (dest_ip >> 24) & 0xFF;

发送部分:

// Build source sockaddr
struct sockaddr_in src_addr;
memset(&src_addr, 0, sizeof(struct sockaddr_in));
src_addr.sin_family = AF_INET;

// Save IP into a char array
char destIp[16];
memset(destIp, 0, sizeof(destIp));
sprintf(destIp, "%d.%d.%d.%d", ipParts[0], ipParts[1], ipParts[2], ipParts[3]);
inet_aton(destIp, &(src_addr.sin_addr));

char cmbuf[CMSG_SPACE(sizeof(struct in_pktinfo))];

bzero(buffer, BUFLEN);
int len = dp.getRaw(buffer);

struct msghdr mh;
memset(&mh, 0, sizeof(mh));

struct cmsghdr *cmsg_send;
struct in_pktinfo *pktinfo;

struct iovec iov[1];
iov[0].iov_base = buffer;
iov[0].iov_len = BUFLEN;

mh.msg_name = &cli_addr; // destination address of packet
mh.msg_namelen = sizeof(cli_addr);
mh.msg_control = cmbuf;
mh.msg_controllen = sizeof(cmbuf);
mh.msg_flags = 0;
mh.msg_iov = iov;
mh.msg_iovlen = 1;

// after initializing msghdr & control data to CMSG_SPACE(sizeof(struct in_pktinfo))
cmsg_send = CMSG_FIRSTHDR(&mh);
cmsg_send->cmsg_level = IPPROTO_IP;
cmsg_send->cmsg_type = IP_PKTINFO;
cmsg_send->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
pktinfo = (struct in_pktinfo*) CMSG_DATA(cmsg_send);
pktinfo->ipi_ifindex = 0;
pktinfo->ipi_spec_dst = src_addr.sin_addr;

int rc = sendmsg(sock, &mh, 0);

【讨论】:

    【解决方案3】:

    典型的 TCP/UDP 套接字在执行 Rx/Tx 之前绑定到特定的 IP/端口。 尝试使用原始套接字作为您的方法。

    代码比较大,请参考以下链接 http://sock-raw.org/papers/sock_raw

    创建一个原始套接字 - 通过原始套接字接收 - 现在您将从 L2 接收整个帧 通过原始套接字发送 - 修改相关字段后从 L3 标头传输

    【讨论】:

    • 你有任何描述这个的链接或一个如何使用 SOCK_RAW 的小例子吗?如果您不是 IP 和网络方面的专家,它看起来真的很先进。
    猜你喜欢
    • 2015-05-05
    • 2020-06-12
    • 2019-01-31
    • 2014-05-06
    • 1970-01-01
    • 2016-08-27
    • 2017-01-05
    • 1970-01-01
    • 2022-11-21
    相关资源
    最近更新 更多