Subject: Re: TCP Port reuse related ...

From: "Ramesh Shankar" <[email-masked]>
Date: Thu, 27 Jan 2000 15:37:28 -0700

Well, if one is talking about well known ports, (0 - 1024), then you have the standard protection that comes with it: only super user can bind to such ports.

In *any case*,  if I had done this:

socket1: bind(*, port #) (SO_REUSEADDR)
socket2: bind(*, port #) (SO_REUSEADDR)
listen(socket1)

Then the sequence would have succeeded. Nothing prevents this from happening. Also, the standard behaviour is to allow the reuse of ports as long as SO_REUSEADDR has been used (i.e. it works this way in BSD, Winsock). With the UNIX variants, every one using a port has to use SO_REUSEADDR. Hence, if the first server that started off didn't use SO_REUSEADDR, this port stealing can't happen anyway. (With Winsock, only the later bind()ers have to specify SO_REUSEADDR, the first one doesn't have to).

There was only one explanation that I could think of on why the Linux folks decided to do this way:

If someone tries to reuse a tuple in the SYN received state for a connect(), then the connect should fail. This is ensured automatically in the way FreeBSD/NetBSD handles inpcbs. Linux has open_request structures for embryonic connections in the listening socket's queue and these consist of SYN received state connections. These are NOT checked for invalid tuple usage when a connect() is made. Also, with SYN cookies, we won't even have these open_request structures and hence there is no real way to identify whether the tuple being used by a connect is valid or not.  Hence, the only way to disallow the connect is to prevent the second bind() from using the same port (except in the case metioned) when the first socket is doing a listen().

About clarification of FTP related comments in the code:

I am not sure what case is being optimised here. Obviously there are two important cases - one is bind() time determination whether a port can be used or not - this is based on SO_REUSEADDR and the second one is at connect() time to decide whether a particular tuple can be used in the connect(). 

In the first case, the tcp_bhash is consulted and in the second case, tcp_ehash (both non-TIME_WAIT part and TIME_WAIT part) are consulted. (Actually the tcp_listening_hash is also consulted without any use!)

What I don't get it is the comments about O(1) allocation for FTP servers etc. The implementation there seems to be related to standard SO_REUSEADDR case and I don't understand how an FTP server fits into this picture. (I even tried to check whether the Linux FTP server gives the same passive port # to multiple FTP clients, it doesn't seem to).

Thanks,

S.R.

>>> Kacheong Poon <[email-masked]> 01/27/00 03:03PM >>>
> socket1: bind(*, port #)
> listen(socket1)
> socket2: bind(IP1, port #)
> 
> For a multihomed host, socket2 (if put on listen()), would field all packets
> destined for IP1, while socket1 will field the others.

Suppose this is allowed and assume that socket1 belongs to a server app.  
Then socket2, which belongs to a malicious app, can steal all service
requests originated from IP1.  Further assume that the malicious app does
the above for all "known" IP addresses in that network, then it basically
hijacks all service requests from the server app.  The bad thing is the
server app does not even know about this...

> If anyone can explain me what the comments in the file include/net/tcp.h
> near the definition of the structure tcp_bind_bucket really mean, it would
> be greatly appreciated!

It is a nice optimization.  What is not clear in that?

							K. Poon.
							[email-masked]

---

From: Kacheong Poon <[email-masked]>
Date: Thu, 27 Jan 2000 15:46:41 -0800 (PST)

> Well, if one is talking about well known ports, (0 - 1024), then you have
> the standard protection that comes with it: only super user can bind to such
> ports.

For example, nfsd is not listening on the above "well known" ports.  It
is listening on 2049, which is still well known to nfs clients.  There are
more well known services than the above 1-1024 port range can allow.  Anyway,
the kernel should provide a way to allow an app to make sure that all
requests to the port it bind() to should be sent to it only.

> socket2: bind(*, port #) (SO_REUSEADDR)

Yup, this is a hole a server app can make.  It should not set SO_REUSEADDR
before doing a listen() if it wants to prevent this from happening.  But this
does not mean that the kernel should not enforce such a rule.  What the kernel
provides is a mechanism for apps to use.  Use it or not depends on what the
app wants to do.

> Then the sequence would have succeeded. Nothing prevents this from
> happening. Also, the standard behaviour is to allow the reuse of ports as
> long as SO_REUSEADDR has been used (i.e. it works this way in BSD, Winsock).
> With the UNIX variants, every one using a port has to use SO_REUSEADDR.
> Hence, if the first server that started off didn't use SO_REUSEADDR, this
> port stealing can't happen anyway. (With Winsock, only the later bind()ers
> have to specify SO_REUSEADDR, the first one doesn't have to).

Correct me if I am wrong.  Last time I checked FreeBSD code, it did not
check whether the original socket has SO_REUSEADDR set or not.  I think
what you mentioned above was not the way it worked.  Is it changed?  I
think it uses the user id to prevent the port stealing problem.  I am not
sure about Winsock.  Anyway, different OSes use different mechanisms to fill
this hole.

> What I don't get it is the comments about O(1) allocation for FTP servers
> etc. The implementation there seems to be related to standard SO_REUSEADDR
> case and I don't understand how an FTP server fits into this picture. (I
> even tried to check whether the Linux FTP server gives the same passive port
> # to multiple FTP clients, it doesn't seem to).

I think ftp server needs to create a data (port 20) connection.  So it has
to bind to port 20 repeatedly and it has to set SO_REUSEADDR.  Think of it
if there are a lot of such data connections in TIME_WAIT state.

							K. Poon.
							[email-masked]