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/



Other Mailing lists | Author Index | Date Index | Subject Index | Thread Index