bugreport: multihoming, udp and nc6 (fwd)
Hi,
I just tested a bug with iperf that I reported originally to nc6
developers. The same bug applies to iperf (s/nc6/iperf/).
--
Miika Komu http://www.iki.fi/miika/
---------- Forwarded message ----------
Date: Tue, 21 Aug 2007 10:55:17 +0300 (EEST)
From: Miika Komu <miika --at-- iki.fi>
To: ds6-devel --at-- deepspace6.net
Cc: Antti Louko <alo --at-- louko.com>, Tobias Heer <tobias.heer --at-- gmx.de>,
Joakim Koskela <joakim.koskela --at-- hiit.fi>, Shaohui Li <mail --at-- shaohui.de>,
Samu Varjonen <samu.varjonen --at-- hiit.fi>
Subject: bugreport: multihoming, udp and nc6
Hi!
we have discovered a problem with nc6 and the way it handles UDP with
multihomable nc6 servers (= server with multiple addresses, possible on the
same interface). Below is a description how nc6 operates on UDP based on my
understanding:
* nc6 client basically does a connect(server_ip) which fixes the IP
address of the server and this will drop
packets originating from other IP addresses than the server
* client sends some text to the server
* server receives the text through recvfrom
* server responds using sendmsg
* client and server break with an error:
nc6: error reading from remote: Connection refused
This occurs unless server_ip happens to be the default route to the client.
Namely, the nc6 server does not preserve the server_ip in sendmsg() function,
but rather lets the system to select it. When the server_ip changes in sendmsg
at the server, the client drops the UDP packet because connect() has already a
fixed server address.
There are two ways to fix the problem:
1) Bad way: remove connect from the client and make it just use
send/recvmsg. This is vurnerable to security problems as the client now
accepts UDP packets from ANY host.
2) Better way: remove connect, but check the server IP address after
recvfrom(). Drop the packet if the address has changed.
3) Best way (?): make the server preserve its address from recvmsg to
sendmsg call.
The third option can be implemented using the achillary options in msghdr.
Below is a naive, but working, IPv6-only example how we implemented this in
another project. Sorry, I don't have patch to offer this time!
int main_server_udp(int serversock) {
/* Use recvmsg/sendmsg instead of recvfrom/sendto because
the latter combination may choose a different source
IP for the server */
struct sockaddr_in6 peeraddr;
char control[CMSG_SPACE(256)];
char mylovemostdata[IP_MAXPACKET];
struct iovec iov = { mylovemostdata,
sizeof(mylovemostdata) - 1 };
struct cmsghdr *cmsg;
struct in6_pktinfo *pktinfo_in6;
struct msghdr msg = {
&peeraddr, sizeof(peeraddr),
&iov, 1,
control, sizeof(control), 0
};
int err = 0, on = 1, recvnum, sendnum;
/* XX TODO: IPPROTO_IP and IP_PKTINFO for IPv6 */
err = setsockopt(serversock, IPPROTO_IPV6,
IPV6_2292PKTINFO, &on, sizeof(on));
if (err != 0) {
perror("setsockopt IPV6_RECVPKTINFO");
goto out_err;
}
memset(mylovemostdata, 0, sizeof(mylovemostdata));
memset(&peeraddr, 0, sizeof(peeraddr));
printf("=== Server listening IN6ADDR_ANY ===\n");
while((recvnum = recvmsg(serversock, &msg, 0)) > 0) {
fprintf(stderr,"=== received string: %s ===\n",
mylovemostdata);
fflush(stdout);
/* Local address comes from ancillary data passed
* with msg due to IPV6_PKTINFO socket option */
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg;
cmsg = CMSG_NXTHDR(&msg, cmsg)) {
/* XX TODO: IPPROTO_IP and IP_PKTINFO for IPv6 */
if ((cmsg->cmsg_level == IPPROTO_IPV6) &&
(cmsg->cmsg_type == IPV6_2292PKTINFO)) {
pktinfo_in6 =
(struct in6_pktinfo *) CMSG_DATA(cmsg);
break;
}
}
/* send only the data we received (notice that there is
always a \0 in the end of the string) */
iov.iov_len = strlen(mylovemostdata);
/* send reply using the ORIGINAL src/dst address pair
(preserved in the control field) */
sendnum = sendmsg(serversock, &msg, 0);
if (sendnum < 0) {
perror("send");
err = -1;
break;
}
/* reset all fields for the next round */
memset(mylovemostdata, 0, sizeof(mylovemostdata));
memset(&peeraddr, 0, sizeof(peeraddr));
msg.msg_namelen = sizeof(peeraddr);
memset(control, 0, sizeof(control));
iov.iov_len = sizeof(mylovemostdata);
printf("=== Sent string successfully back ===\n");
printf("=== Server listening IN6ADDR_ANY ===\n");
}
out_err:
return err;
--
Miika Komu http://www.iki.fi/miika/