如何在Linux中重新绑定udp套接字 [英] How to re bind a udp socket in Linux

查看:111
本文介绍了如何在Linux中重新绑定udp套接字的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是一位经验丰富的Linux套接字程序员,正在编写具有许多传出接口的服务器应用程序.现在,在此过程开始时,服务器套接字会与INADDR_ANY绑定到随机源端口.

I am an experienced Linux socket programmer and am writing a server application which has many outgoing interfaces. Now server socket binds to a random source port in the start of the process along with INADDR_ANY.

稍后在向特定节点提交响应时,我需要分配一个固定的源ip地址.执行此操作的标准方法是调用bind.但是,将为端口号调用一次bind,成功调用失败,并带有无效的参数错误.

Later at some point when submitting response to a specific node, i need to assign a fixed source ip address. The standard way to do this is calling bind. However, bind is called once for the port number, successive calls fail with invalid argument error.

创建新的套接字并不是一个好选择,因为在响应某些客户时,我将不得不经常这样做.

Creating a new socket is not really a good choice since i will have to be doing this very often upon responding to some clients.

我还研究了SO和许多套接字选项,例如IP_FREEBIND,但它并不完全适合我的情况.

I have also explored SO and a lot of socket options such as IP_FREEBIND, but it doesn't quite suite my scenario.

除非遇到相同的问题,否则也许使用IP_PKT_INFO并设置源地址可能会起作用,即不允许将曾经绑定到INADDRANY的套接字重新绑定到固定源ip上.

Perhaps using IP_PKT_INFO and setting source address might work unless it suffers the same problem i.e. not allowing a socket once bound to INADDRANY to rebind to a fixed source ip latter.

是否有办法解除现有套接字的绑定或设置传出数据包中源ip地址的替代方法?

    int sock = socket(AF_INET, SOCK_DGRAM, 0);

    if(sock < 0)
        printf("Failed creating socket\n");

    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(struct sockaddr_in));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(1500);
    addr.sin_addr.s_addr = INADDR_ANY;

    // first bind succeeds
    if ( (status = bind(sock, (struct sockaddr *) &addr, sizeof(addr))) < 0)
        printf("bind error with port %s\n", strerror(errno));  

    struct sockaddr_in src_addr;
    memset(&src_addr, 0, sizeof(struct sockaddr_in));
    src_addr.sin_family = AF_INET;
    if (inet_aton("10.0.2.17", &(src_addr.sin_addr)) == 0)
        printf("Failed copying address\n");

    // second bind fails
    if((status = bind(sock, (struct sockaddr *)&src_addr, sizeof(src_addr))) < 0)
        printf("re bind error with ip %s\n", strerror(errno));

在这方面的任何想法都将受到高度赞赏.我已经阅读了有关套接字,SO等的大量材料,但还没有成功.

Any ideas in this regard will be highly appreciated. I have gone through considerable material on sockets, SO etc. but no success yet.

推荐答案

我终于找到了自己的解决方案,因此接受了自己的答案(无耻但正确的插件),并补充了代码示例.

我本来想重写传出数据包的源地址,而无需再次创建已绑定套接字的套接字.在这种情况下,多次调用绑定失败,并且(在我的特定情况下),我无法为每个源ip单独使用单独的套接字并使用它.

I originally wanted to rewrite source address of an outgoing packet without creating the socket again where the socket was already bound. Calling bind multiple times fail for this case, and (in my particular situation), i was not able to just have separate sockets for each source ip and use it.

我在 IP_PACKET_INFO 中找到了一些参考,但是要使其正常工作是很痛苦的.以下参考资料很有帮助.

I found some references in IP_PACKET_INFO but it was a pain to get it to work correctly. Following reference was helpful.

设置udp套接字的来源

示例代码

这是一个简单的应用程序,它创建一个udp套接字,将其绑定到本地端口,然后在发送特定消息之前,它附加传​​出的源IP地址.请记住,就我而言,我创建了一个sudo接口并为其分配了另一个IP.如果不是这种情况,发送呼叫将失败.

Here is a trivial application which creates a udp socket, binds it to a local port, then before sending a particular message, it appends the outgoing source ip address. Keeping in mind that in my case, i created a sudo interface and assigned it another ip. The send call will fail if this is not the case.

int status=-1;
int sock = socket(AF_INET, SOCK_DGRAM, 0);

if(sock < 0)
    printf("Failed creating socket\n");

int opt = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

struct sockaddr_in bind_addr;
memset(&bind_addr, 0, sizeof(struct sockaddr_in));
bind_addr.sin_family = AF_INET;
bind_addr.sin_port = htons(44000); // locally bound port

if((status = bind(sock, (struct sockaddr *)&bind_addr, sizeof(bind_addr))) < 0)
    printf("bind error with port %s\n", strerror(errno));

// currently using addr as destination
struct sockaddr_in addr;
memset(&addr, 0, sizeof(struct sockaddr_in));
addr.sin_family = AF_INET;
addr.sin_port = htons(80); // destination port
if (inet_aton("74.125.236.35", &(addr.sin_addr)) == 0)
    printf("Failed copying remote address\n");
else
    printf("Success copying remote address\n");

struct sockaddr_in src_addr;
memset(&src_addr, 0, sizeof(struct sockaddr_in));
src_addr.sin_family = AF_INET;
if (inet_aton("10.0.2.17", &(src_addr.sin_addr)) == 0)
    printf("Failed copying src address\n");
else
    printf("Success copying src address\n");

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

char msg[10] = "hello";
int len = strlen(msg);

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

struct cmsghdr *cmsg;
struct in_pktinfo *pktinfo;

struct iovec iov[1];
iov[0].iov_base = msg;
iov[0].iov_len = len;

mh.msg_name = &addr; // destination address of packet
mh.msg_namelen = sizeof(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 = CMSG_FIRSTHDR(&mh);
cmsg->cmsg_level = IPPROTO_IP;
cmsg->cmsg_type = IP_PKTINFO;
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
pktinfo = (struct in_pktinfo*) CMSG_DATA(cmsg);

//src_interface_index 0 allows choosing interface of the source ip specified
pktinfo->ipi_ifindex = 0;
pktinfo->ipi_spec_dst = src_addr.sin_addr;

int rc = sendmsg(sock, &mh, 0);
printf("Result %d\n", rc);

关键声明是

pktinfo->ipi_spec_dst = src_addr.sin_addr;

我们在其中指定要使用的源IP地址.剩下的诸如 cmsg struct 等之类的东西只是为了能够自己编写ipoktinfo struct

where we are specifying the source ip address to be used. The rest of things like cmsg struct etc. are merely used in order to be able to write ipoktinfo struct ourselves

这篇关于如何在Linux中重新绑定udp套接字的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆