[comp.protocols.tcp-ip] ICMP_UNREACH_PORT

wayne@ultra.UUCP (Wayne Hathaway) (05/02/89)

Whilst doing some load-testing of our in-house net, I noticed some
surprising behavior; I'd be interested in some expert comments.

First the configuration:  Several Sun 3's running SunOS 4.0.1,
connected by an UltraNet (our product, but basically irrelevant to the
question).

Next the situation:  We seemed to be having some troubles with the
checksum hardware on one of our cards (in a diskless Sun 3/140, to be
specific), resulting in excessive discarding of datagrams.  To test
it, I started up a process on another Sun which sent a steady stream
of 8K UDP datagrams to a random port on the 3/140.  Since I was
interested in datagrams tossed by the card rather than the SunOS
kernel, I did not bother having anybody do a "recvfrom" on the 3/140
(in other words, the datagrams would be tossed by the 3/140's UDP).
[By the way, the UltraNet MTU is larger than 8K, so these datagrams
are legal.]

Okay, the strangeness:  Looking at counters, I noticed that not only
did the 3/140 receive (say) 854 datagrams, it also SENT exactly 854
datagrams.  It didn't take too much detective work to figure out what
the outgoing datagrams were -- they were ICMP messages with Type 3,
Code 3: "Destination Port Unreachable".  My question is "Why?"

I realize this behavior is perfectly LEGAL (since the ICMP spec [RFC
792] says "If ... the IP module cannot deliver the datagram because
the ... process port is not active, the destination host may send a
destination unreachable message to the source host."), it just seemed
strange to see 4.3 BSD actually doing it.

First of all, I'm a great believer in the idea of "if something weird
happens, build a datagram describing it and launch it towards some
logging host, then forget it."  But if that host's logging program
isn't up, this is going to result in DOUBLE the network traffic (at
least in number of datagrams).  No great "overload" problem, of
course, but it does seem silly, particularly since (for UDP datagrams,
at least) the original sending host isn't really going to be able to
DO anything with the information.

And second, what is IP doing talking about ports anyway???  I mean,
ports are upper layer artifacts, no?  According to my trusty grep, the
word "port" does not even APPEAR in the IP spec (RFC 791)!  But then,
the ICMP spec DOES say "If ... the IP module cannot deliver the
datagram because the ... process PORT is not active" so  SOMEBODY sure
expects IP to be in the port business!  (The spec also mentions ports
in a couple of other places, but they don't seem to be particularly
relevant.)

Actually, what is happening in 4.3 BSD is that UDP is turning around
and causing its ICMP to send the ICMP_UNREACH_PORT message.  Nothing
illegal about this, of course, but it does get us back to the previous
question: why bother?  (The UDP action is in udp_usrreq.c and is
prefaced by the comment "don't send ICMP response for broadcast
packet" -- I would ask why EVER send it?!  Also I note that TCP does
not seem to ever send an ICMP_UNREACH_PORT.)

Anyway, any thoughts from all the IP/ICMP/BSD gurus out there?

  Wayne Hathaway            
  Ultra Network Technologies     domain: wayne@Ultra.COM
  101 Daggett Drive            Internet: ultra!wayne@Ames.ARC.NASA.GOV
  San Jose, CA 95134               uucp: ...!ames!ultra!wayne
  408-922-0100

droid@earth.cray.com (Andy Nicholson) (05/03/89)

> It didn't take too much detective work to figure out what
> the outgoing datagrams were -- they were ICMP messages with Type 3,
> Code 3: "Destination Port Unreachable".  My question is "Why?"

The answer probably is "Because the protocol spec makes it available and
it is probably a good thing to do."  Note that this is more of a
rationalization.  Nowhere that I can think of does the spec say it has
to be done this way.  I would tend to agree that it is probably a good thing
to do.

I find it easy to conceive of an IP implementation on a more advanced operating
system where the IP could receive the ICMP message, make a determination of
the process sending the offending datagram, and asynchronously notify that
process (via a mechanism like signals, and using a signal like SIGPIPE) that
the destination port was not there.  Or the IP could keep a list of
unavailable ports and return a write error to UDP (but only for a short period
of time, as a port may appear later).  There are probably lots of things that
could be done with the message.  It is probably there because it seemed like
"the right thing" to the implementor.

> And second, what is IP doing talking about ports anyway???  I mean,
> ports are upper layer artifacts, no?  According to my trusty grep, the
> word "port" does not even APPEAR in the IP spec (RFC 791)!  But then,
> the ICMP spec DOES say "If ... the IP module cannot deliver the
> datagram because the ... process PORT is not active" so  SOMEBODY sure
> expects IP to be in the port business!  (The spec also mentions ports
> in a couple of other places, but they don't seem to be particularly
> relevant.)

I see you are naive regarding layering of TCP/IP.  They are, but only sometimes.
TCP and UDP protocols make a lot of assumptions about how IP works in order
to gain some performance improvements, like sticking in a partially filled
IP header at the front of the TCP or UDP packet that is passed on to IP.
The fact of the matter is that layering is great from a conceptual view,
but it nukes performance.  Finally, the IP concept of an upper level protocol
port is not the same as the UDP or IP port.  The IP protocol merely makes
the assumption that upper level protocols will have some demultiplexing
mechanism (which they may or may not) and call this the port.  They could
have called this something like endpoint id or demux, or whatever, but port
gets the point across just fine.  Again, although the protocols are layered,
it was pretty much assumed that there was a great likelyhood of finding
TCP and UDP above IP.

> Actually, what is happening in 4.3 BSD is that UDP is turning around
> and causing its ICMP to send the ICMP_UNREACH_PORT message.  Nothing
> illegal about this, of course, but it does get us back to the previous
> question: why bother?  (The UDP action is in udp_usrreq.c and is
> prefaced by the comment "don't send ICMP response for broadcast
> packet" -- I would ask why EVER send it?!  Also I note that TCP does
> not seem to ever send an ICMP_UNREACH_PORT.)

TCP does not send an unreachable port message because it sends a reset segment
instead, as the TCP protocol spec says to.  That is why telnet returns
immediately with "connection refused" if you telnet to a machine that is up
but does not have its telnet server running.

Andy Nicholson			droid@earth.cray.com

barns@GATEWAY.MITRE.ORG (Bill Barns) (05/04/89)

I hold that there is an inherent layering wart in dealing with error
reporting of nonexistent layer-specific address selectors.  You get to
choose the form and location of your wart(s) when you design a protocol
suite, but there will be one somewhere, in some form, if you try to
implement such a function.  The justification for this argument gets
pretty involved; send mail if you want to hear it.  ICMP's approach is
arguably "best" because it provides an optional layer 3 wart which you
may use if you believe it belongs in layer 3, but (since it's optional)
you can implement a layer 4 wart when you design a layer 4 protocol if
you're so inclined, or you can ignore the whole problem by never
reporting such errors.

As for the utility of sending these errors, certainly there is no
knowing in advance whether the recipient will do anything worthwhile
with them, but the forthcoming Host Requirements RFC is attempting to
clarify the issue of service interfaces in such a way that in any
conformant implementation, there will at least be the potential for the
error report to actually be put to use.  For the case of UDP it is
required that the ICMP error be passed back to the application whose
datagram inspired it.  Whether the application makes any intelligent
use of it is an application layer issue.  See sections 3.4 and 4.1.3.3
of the HR RFC draft.  Latest edition of this draft is dated April 17, 1989.

Bill Barns / MITRE-Washington / barns@gateway.mitre.org

romkey@asylum.SF.CA.US (John Romkey) (05/04/89)

In article <8905021609.AA00846@lear.ultra.com> wayne@ultra.UUCP (Wayne
Hathaway) writes about not understanding why he got some ICMP port
unreachables.

The system you tested against was behaving quite properly, both
according to the specs and according to the intentions.

The port unreachables aren't meant to go to some logging host, they're
meant to go back to the system that generated the packets that
couldn't be delivered. Port unreachable is an error indication
mechanism so that UDP-based applications can find out that no one was
home on the other side. Otherwise, there's no way for UDP to tell. You
could do this for TCP, too, but TCP explicitly uses the TCP reset
mechanism, instead.

IP doesn't and shouldn't send these messages. The layering works like
this: UDP gets a packet, checks if there's something listening on the
destination port. If there is, it delivers it, no problem. If there
isn't, it asks ICMP to generate a port unreachable message.

IP should send PROTOCOL unreachables. I don't have a copy of the IP
RFC handy, so I'm not going to double check on what the actual text
says.

Anyway, 4.3 is doing things correctly here.
-- 
			- john romkey
USENET/UUCP: romkey@asylum.sf.ca.us	Internet: romkey@xx.lcs.mit.edu
"We had some good machines/But they don't work no more" - Shriekback

barmar@think.COM (Barry Margolin) (05/04/89)

In article <8905021609.AA00846@lear.ultra.com> wayne@ultra.UUCP (Wayne Hathaway) writes:
>And second, what is IP doing talking about ports anyway???

I think that ICMP is providing this as a service to higher-level
protocols that have no way of doing this on their own.  It's not
necessarily a waste of bandwidth; when using an unreliable datagram
protocol, a client is likely to retransmit the operation if it doesn't
get a response.  One ICMP Port Unreachable message is better than a
half dozen retransmitted UDP packets.  Of course, if the protocol is
one-way with no response expected, this is an extraneous packet;
however, that style is generally only used with broadcasts, which also
don't prompt the port unreachable response, so it's probably OK.

>  Also I note that TCP does
>not seem to ever send an ICMP_UNREACH_PORT.)

TCP doesn't need it, because it has its own protocol for indicating an
invalid port (it sends a RST packet).

Barry Margolin
Thinking Machines Corp.

barmar@think.com
{uunet,harvard}!think!barmar

djm@lupine.UUCP (Dave Mackie) (05/04/89)

	Well, you are a bit off-base on your description of the ICMP
	error logging mechanism. ICMP messages are logged (at least 
	in BSD) down in the IP/ICMP code itself. There is no network 
	error logger program that listens on a special port for ICMP 
	error messages.

	Also, the information contained in an ICMP Port Unreachable message
	is not useless. The problem is that (at least in BSD again) most 
	applications using UDP simply send their packets out using the 
	sendto() system call and use the parameters of that call to specify 
	the destination. That's great if your application is going to be
	sending packets all over creation and you only want to have the 
	overhead/complexity of a single socket. But that's not always the
	case for many applications. The problem with this approach is that the 
	poor networking code doesn't remember where you sent your packets,
	and therefore can't correlate any incoming ICMP port unreachable 
	messages with your application. So what generally happens is the
	application retransmits it's UDP packets for a while and then
	gives up. All the while the kernel is getting ICMP port unreachable's
	from the other host. This results in extra network traffic, and the 
	user waiting around for the !@#$ application to timeout.

	Now the solution to this situation is to have the application
	do a connect() to the destination. Yes, you can do a connect
	on a UDP socket. It just tells the networking code who you will
	be talking to with this socket, without generating any actual
	network traffic. Then the application can use send() instead of
	sendto(). When it attempts to reach a non-existent service on
	the destination host, it *does* get notification of the ICMP
	port unreachable message, and voila it can stop wasting everybody's
	time, and report the problem to the user. Now obviously this model
	doesn't work for every type of application, but it does for quite
	a few.

	In the case of TCP, the TCP spec gives the implementor a nastier
	more direct way of indicating displeasure about the destination
	port number. It sends a RESET back to the source machine. Now
	that should get it's attention!

						Dave Mackie
						Network Computing Devices
						djm@ncd.com