Messages in this thread | | | Date | Fri, 21 Jan 2000 15:53:59 -0800 (PST) | From | Perry The Cynic <> | Subject | Re: Select and UDP problems |
| |
[Not a kernel problem. But in case you were wondering...]
When you send a UDP packet to a host/port, then either (1) The host isn't reachable (and no router on the way knows this): Your packet will fall into a black hole, and your select() will time out; OR (2) A server is listening on that port and eats your packet; OR (3) The host is reachable but no server listens on that port.
In case (3), the UDP layer on the server will (almost immediately) return an ICMP PORT UNREACHABLE error message to the sending host (yours), and your network stack translates this into an ECONNREFUSED error. Since you selected for errors, your select() fires and returns the error to you.
Why does the send not return an error code? Because *the send succeeds*. UDP has no notion of connections or guaranteed delivery; the packet was sent successfully out your network port, so everything is fine.
On the other hand, the kernel recognizes that the incoming ICMP error message is clearly associated with your (sending) network port, since the ICMP contains (the start of) your sent packet, so it generates an error flag on the port, and when you select() asking for error conditions (your errSet), you get told about it almost immediately.
Note that it's possible (though unlikely) for the ICMP UNREACHABLE to arrive more than five seconds after you send the packet, or to be eaten by a line glitch. Or for your packet to be eaten by a grue. Such are the vagaries of UDP (best effort, no guarantees) packets.
In case you're interested, the code for this is in net/udp.c in the kernel, in the udp_err function.
To demonstrate the difference between cases (2) and (3), try to send your packet to a nonexisting host down your default route (try 1.2.3.4 :-). You can also use a packet sniffer (like tcpdump) to watch for ICMP packets.
As to HP-UX and Solaris, they may be less inclined to tie incoming ICMP errors to UDP ports. I vaguely remember this being tunable to a degree in Solaris, but it's been years since I've touched a Sun box...
Cheers -- perry
On Wed, 19 Jan 2000, Kevin Pearson wrote:
> Hello, > > I have a question reguarding select on a UDP socket. The following > program creates a UDP socket, sends a character, does a select for five > seconds waiting for a response, and recieves a response, if there was one. > > This works fine when there is a service that responds to the request, but > if there isn't, select still returns immediatly and with readSet > set. Then recvfrom return -1 with an errno of 111 (ECONNREFUSED). > > On HP-UX 10.20 and Solaris 5.7, when there is no response, select waits > for 5 seconds and times out. > > I'm using Linux 2.2.12 SMP, glibc-2.1.2. > > I've tried searching the linux-kernel archives, but could find nothing. > > Am I doing something wrong here or is this a defect in select? > Please reply to kevin_pearson@hp.com as I'm not subscribed to the list. > > Thanks in advance, > Kevin Pearson > kevin_pearson@hp.com > > #include <stdio.h> > #include <stdlib.h> > #include <sys/time.h> > #include <sys/types.h> > #include <sys/socket.h> > #include <netinet/in.h> > #include <arpa/inet.h> > #include <netdb.h> > #include <unistd.h> > #include <errno.h> > > int main(int argc, char **argv) > { > int fd = -1; > int dest_port = -1; > int ret = -1; > struct sockaddr_in myaddr; > int addr_size = sizeof(myaddr); > struct hostent *hostname = NULL; > fd_set readSet; > fd_set errSet; > struct timeval timeout; > char buf[512]; > > if (argc < 3) > { > printf("\nUsage: test hostname port\n\n"); > exit(-1); > } > > hostname = gethostbyname(argv[1]); > dest_port = atoi(argv[2]); > > if (hostname == NULL) > { > printf("Could not resolv hostname\n"); > exit(-1); > } > > fd = socket(AF_INET, SOCK_DGRAM, 0); > if (fd < 0) > { > printf("Could not open socket\n"); > } > > addr_size = sizeof(myaddr); > memset(&myaddr, 0, addr_size); > > myaddr.sin_family = AF_INET; > myaddr.sin_port = htons(dest_port); > memcpy(&myaddr.sin_addr, hostname->h_addr_list[0], hostname->h_length); > > ret = sendto(fd, "*", 1, 0, &myaddr, sizeof(myaddr)); > if (ret < 0) > printf("send returned ret=%d, errno=%d\n", ret, errno); > > FD_ZERO(&readSet); > FD_SET(fd, &readSet); > > FD_ZERO(&errSet); > FD_SET(fd, &errSet); > > timeout.tv_sec = 5; > timeout.tv_usec = 0; > > ret = select(fd + 1, &readSet, NULL, &errSet, &timeout); > if (ret < 0) > printf("select returned ret=%d, errno=%d\n", ret, errno); > > if ( FD_ISSET(fd, &readSet) ) > printf("readSet fired\n"); > if ( FD_ISSET(fd, &errSet) ) > printf("errSet fired\n"); > > if ( (ret > 0) && (FD_ISSET(fd, &readSet)) && (!FD_ISSET(fd, &errSet)) ) > { > ret = recvfrom(fd, buf, sizeof(buf), 0, &myaddr, &addr_size); > if (ret < 0) > printf("recvfrom returned ret=%d, errno=%d\n", ret, errno); > else > printf("buff is %s\n", buf); > } > > close(fd); > return 0; > } > > > - > To unsubscribe from this list: send the line "unsubscribe linux-kernel" in > the body of a message to majordomo@vger.rutgers.edu > Please read the FAQ at http://www.tux.org/lkml/ >
--------------------------------------------------------------------------- Perry The Cynic perry@cynic.org To a blind optimist, an optimistic realist must seem like an Accursed Cynic. ---------------------------------------------------------------------------
- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.rutgers.edu Please read the FAQ at http://www.tux.org/lkml/
| |