내 서버에있는 모든 IP에서 패킷을 수신 대기하도록 INADDR_ANY에 바인딩 된 UDP 소켓이 있습니다. 나는 같은 소켓을 통해 응답을 보내고있다.UDP 소켓의 소스 IP 설정
지금 서버는 패킷을 보낼 때 원본 IP로 사용되는 IP를 자동으로 선택하지만 나가는 원본 IP를 직접 설정할 수 있기를 원합니다.
각 IP에 대해 별도의 소켓을 만들지 않고도 그렇게 할 수 있습니까?
내 서버에있는 모든 IP에서 패킷을 수신 대기하도록 INADDR_ANY에 바인딩 된 UDP 소켓이 있습니다. 나는 같은 소켓을 통해 응답을 보내고있다.UDP 소켓의 소스 IP 설정
지금 서버는 패킷을 보낼 때 원본 IP로 사용되는 IP를 자동으로 선택하지만 나가는 원본 IP를 직접 설정할 수 있기를 원합니다.
각 IP에 대해 별도의 소켓을 만들지 않고도 그렇게 할 수 있습니까?
니콜라이 (Nikolai)는 각 주소에 대해 별도의 소켓 및 bind (2)를 사용하거나 라우팅 테이블을 망가 뜨리는 것이 가능하지 않은 경우가 많습니다. 동적 주소. 단일 IP_ADDRANY
- 바운드 UDP 서버는 패킷이 수신되는 동일한 동적으로 할당 된 IP 주소로 응답하는 것처럼 보일 수 있어야합니다.
운 좋게도 다른 방법이 있습니다. 시스템의 지원에 따라 IP_PKTINFO
소켓 옵션을 사용하여 메시지에 대한 보조 데이터를 설정하거나 수신 할 수 있습니다. 보조 데이터 (cmsg(3)
경유)는 comp.os.linux.development.system에 IP_PKTINFO
에 관련된 전체 코드 샘플을 가지고 있지만 온라인으로 많은 곳에서 다루어집니다.
cmsg(3)
데이터에서 UDP 메시지의 대상 주소를 얻기 위해 링크의 코드는 IP_PKTINFO
(또는 플랫폼에 따라 IP_RECVDSTADDR
)을 사용합니다. 여기에 바꿔 치기 :
struct msghdr msg;
struct cmsghdr *cmsg;
struct in_addr addr;
// after recvmsg(sd, &msg, flags);
for(cmsg = CMSG_FIRSTHDR(&msg);
cmsg != NULL;
cmsg = CMSG_NXTHDR(&msg, cmsg)) {
if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) {
addr = ((struct in_pktinfo*)CMSG_DATA(cmsg))->ipi_addr;
printf("message received on address %s\n", inet_ntoa(addr));
}
}
진, 나가는 패킷에 발신지 주소를 설정하는 방법을 묻는 질문. IP_PKTINFO
을 사용하면 sendmsg(2)
으로 전달되는 보조 데이터에 struct in_pktinfo
의 ipi_spec_dst
필드를 설정할 수 있습니다. struct msghdr
에서 보조 데이터를 만들고 조작하는 방법에 대한 지침은 위에 언급 된 게시물 cmsg(3)
및 sendmsg(2)
을 참조하십시오. 예 (여기 보장 할) 수 있습니다 :이 IPv6를에서 다른
struct msghdr msg;
struct cmsghdr *cmsg;
struct in_pktinfo *pktinfo;
// after initializing msghdr & control data to CMSG_SPACE(sizeof(struct in_pktinfo))
cmsg = CMSG_FIRSTHDR(&msg);
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);
pktinfo->ipi_ifindex = src_interface_index;
pktinfo->ipi_spec_dst = src_addr;
// bytes_sent = sendmsg(sd, &msg, flags);
참고하십시오 recvmsg 및 sendmsg 경우 모두에서 struct in6_pktinfo::ipi6_addr
를 사용합니다.
Windows는 in_pktinfo 구조체의 ipi_spec_dst에 해당하는 것을 지원하지 않으므로이 메서드를 사용하여 나가는 winsock2 패킷의 원본 주소를 설정할 수는 없습니다.
(참조 맨 페이지 - 약 1 하이퍼 링크 제한을 받고는)
http:// linux.die.net/man/2/sendmsg
http:// linux.die.net/man/3/cmsg
각 인터페이스 주소에 bind(2)
을 입력하고 여러 소켓을 관리하거나 커널에 INADDR_ANY
이라는 암시 적 소스 IP 할당을 수행하게하십시오. 다른 방법은 없습니다.
제 질문은 - 왜 이것을 필요로합니까? 정상적인 IP 라우팅이 작동하지 않습니까?
난 제레미의 IPv6에 대해이 작업을 수행하는 방법에 대한 확장 거라고 생각했다. Jeremy는 많은 세부 사항을 생략하고 일부 문서 (Linux의 ipv6 용 man 페이지와 같은)는 단순한 잘못입니다.우선 몇 가지 배포판에 당신이 그렇지 않으면 IPv6의 물건 중 일부는 정의되지 않은, _GNU_SOURCE를 정의해야합니다 : 다음, 즉, 모든 IP 패킷 (를 수신 상당히 표준적인 방법으로 소켓을 설정
#define _GNU_SOURCE
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
모두 IPv4 및 IPv6) 특정 호스트의 UDP 포트에 대한 IP 및 IPv6 옵션을 설정합니다. 위의 코드는 IPv6 소켓에 대한 IP 및 IPv6 옵션을 모두 설정합니다. IPv4 주소에 패킷이 도착하면 IPv6 소켓 임에도 불구하고 IP_PKTINFO (즉 IPv4) cmsg가 표시되고 사용하지 않으면 전송되지 않습니다. 또한 IPV6_RECPKTINFO 옵션이 설정되어 있는지 확인하십시오 (man 7 ipv6에 언급되지 않음). IPV6_PKTINFO는 (man 7 ipv6에 잘못 설명되어 있습니다.) 이제 UDP 패킷을 수신 :
다음 단계는 인터페이스를 추출하고 UDP 패킷이는 cmsg에서의 수신 처리하는 것입니다int bytes_received;
struct sockaddr_in6 from;
struct iovec iovec[1];
struct msghdr msg;
char msg_control[1024];
char udp_packet[1500];
iovec[0].iov_base = udp_packet;
iovec[0].iov_len = sizeof(udp_packet);
msg.msg_name = &from;
msg.msg_namelen = sizeof(from);
msg.msg_iov = iovec;
msg.msg_iovlen = sizeof(iovec)/sizeof(*iovec);
msg.msg_control = msg_control;
msg.msg_controllen = sizeof(msg_control);
msg.msg_flags = 0;
bytes_received = recvmsg(soc, &msg, 0);
:
struct in_pktinfo in_pktinfo;
struct in6_pktinfo in6_pktinfo;
int have_in_pktinfo = 0;
int have_in6_pktinfo = 0;
struct cmsghdr* cmsg;
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != 0; cmsg = CMSG_NXTHDR(&msg, cmsg))
{
if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO)
{
in_pktinfo = *(struct in_pktinfo*)CMSG_DATA(cmsg);
have_in_pktinfo = 1;
}
if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO)
{
in6_pktinfo = *(struct in6_pktinfo*)CMSG_DATA(cmsg);
have_in6_pktinfo = 1;
}
}
마지막으로 우리가 응답을 보내기 위해서는, 동일한 목적지를 사용합니다.
int cmsg_space;
iovec[0].iov_base = udp_response;
iovec[0].iov_len = udp_response_length;
msg.msg_name = &from;
msg.msg_namelen = sizeof(from);
msg.msg_iov = iovec;
msg.msg_iovlen = sizeof(iovec)/sizeof(*iovec);
msg.msg_control = msg_control;
msg.msg_controllen = sizeof(msg_control);
msg.msg_flags = 0;
cmsg_space = 0;
cmsg = CMSG_FIRSTHDR(&msg);
if (have_in6_pktinfo)
{
cmsg->cmsg_level = IPPROTO_IPV6;
cmsg->cmsg_type = IPV6_PKTINFO;
cmsg->cmsg_len = CMSG_LEN(sizeof(in6_pktinfo));
*(struct in6_pktinfo*)CMSG_DATA(cmsg) = in6_pktinfo;
cmsg_space += CMSG_SPACE(sizeof(in6_pktinfo));
}
if (have_in_pktinfo)
{
cmsg->cmsg_level = IPPROTO_IP;
cmsg->cmsg_type = IP_PKTINFO;
cmsg->cmsg_len = CMSG_LEN(sizeof(in_pktinfo));
*(struct in_pktinfo*)CMSG_DATA(cmsg) = in_pktinfo;
cmsg_space += CMSG_SPACE(sizeof(in_pktinfo));
}
msg.msg_controllen = cmsg_space;
ret = sendmsg(soc, &msg, 0);
는 다시 패킷이 IPv4를 통해 들어온 경우 우리가는 cmsg의 그것이 AF_INET6 소켓 경우에도로는 IPv4 옵션을 넣어 가지고 어떻게 알 수 있습니다. 적어도 리눅스에서해야 할 일입니다.
놀라 울 정도의 작업이지만 실제로 생각할 수있는 모든 Linux 환경에서 작동하는 견고한 UDP 서버를 만들기 위해서는 최소한의 노력이 필요합니다. 그것의 대부분은 멀티 호밍을 투명하게 처리하기 때문에 TCP에는 필요하지 않습니다.
최근에 같은 문제가 발생했습니다.
나는이 문제가
예 :
struct ifreq ifr;
...
recvmsg(fd, &msg...)
...
if (msg.msg_controllen >= sizeof(struct cmsghdr))
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO)
{
iface_index = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_ifindex;
}
if_indextoname(iface_index , ifr.ifr_name);
mret=setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr));
sendmsg(...);
memset(&ifr, 0, sizeof(ifr));
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "");
mret=setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr));
IBM 지식 센터에서 소스 IP 선택에 대한 링크를 찾았습니다 : [https://www.ibm.com/support/knowledgecenter/SSLTBW_2.1.0/com.ibm.zos.v2r1.halz002/tcpip_source_ip_addr_selection.htm] https://www.ibm.com/support/knowledgecenter/SSLTBW_2.1.0/com.ibm.zos.v2r1.halz002/tcpip_source_ip_addr_selection.htm) –
감사합니다. IP 라우팅이 작동합니다. 벌금과 패킷이 목적지로 도착하지만 유감스럽게도 클라이언트는 모두 특정 서버 IP에 연결하고 프로토콜은이 특정 IP에서 응답을 얻도록 요구합니다. 현재 모든 고객은 동일한 IP에서 응답을 얻습니다. –
내 의심이 라우팅 테이블이 될 것입니다 - 단일 기본 경로/게이트웨이가 있습니까? 클라이언트 주소에 특정한 경로를 추가하는 것이 도움이 될 수 있습니다. –
예, 호스트 경로를 추가하면 도움이되지만 내 프로그램에서 호스트 경로를 사용하는 것이 좋습니다. –