Blocking incoming forged packets, as discussed previously, is just about the only common use of filtering solely by address. Most other uses of packet filtering involve filtering by service, which is somewhat more complicated.
From a packet filtering point of view, what do the packets associated with particular services look like? As an example, we're going to take a detailed look at Telnet. Telnet allows a user to log in to another system, as if the user had a terminal directly connected to that system. We use Telnet as an example because it is fairly common, fairly simple, and from a packet filtering point of view representative of several other protocols such as SMTP and NNTP. We need to look at both outbound and inbound Telnet service.
For detailed discussions of the packet filtering characteristics of other protocols, see Chapter 8.
Let's look first at outbound Telnet service, in which a local client (a user) is talking to a remote server. We need to handle both outgoing and incoming packets. (Figure 6.8 shows a simplified view of outbound Telnet.)
The outgoing packets for this outbound service contain the user's keystrokes and have the following characteristics:
The IP source address of the outgoing packets is the local host's IP address.
The IP destination address is the remote host's IP address.
Telnet is a TCP-based service, so the IP packet type is TCP.
The TCP destination port is 23; that's the well-known port number Telnet servers use.
The TCP source port number (which we'll call "Y" in this example) is some seemingly random number greater than 1023.
The first outgoing packet, establishing the connection, will not have the ACK bit set; the rest of the outgoing packets will.
The incoming packets for this outbound service contain the data to be displayed on the user's screen (for example, the "login:" prompt) and have the following characteristics:
The IP source address of the incoming packets is the remote host's IP address.
The IP destination address is the local host's IP address.
The IP packet type is TCP.
The TCP source port is 23; that's the port the server uses.
The TCP destination port is the same "Y" we used as the source port for the outgoing packets.
All incoming packets will have the ACK bit set (again, only the first packet, establishing a connection, does not have the ACK bit set; in this example, that first packet was an outgoing packet, not an incoming packet).
Note the similarities between the header fields of the outgoing and incoming packets for Telnet. The same addresses and port numbers are used; they're just exchanged between source and destination. If you compare an outgoing packet to an incoming packet, the source and destination addresses are exchanged, and the source and destination port numbers are exchanged.
Why is the client port - the source port for the outgoing packets, and the destination port for the incoming packets - restricted to being greater than 1023? This is a legacy of the BSD versions of UNIX, to which almost all UNIX networking code can trace its origins. BSD UNIX reserved ports from 0 to 1023 for local use only by root. These ports are normally used only by servers, not clients. (The major exceptions are the BSD "r commands" like rcp and rlogin, as we'll discuss in Chapter 8.) Other operating systems, even those that have no concept analogous to a privileged root user, e.g., Macintosh and MS-DOS systems, have followed this convention. When client programs need a port number for their own use, and any old port number will do, the programs are assigned a port above 1023.
The incoming packets for the inbound Telnet service contain the user's keystrokes and have the following characteristics:
The IP source address of these packets is the remote host's address.
The IP destination address is the local host's address.
The IP packet type is TCP.
The TCP source port is some random port number greater than 1023 (which we'll call "Z" in this example).
The TCP destination port is 23.
The TCP ACK bit will not be set on the very first inbound packet, establishing the connection, but it will be set on all other inbound packets.
The outgoing packets for this inbound Telnet service contain the server responses (the data to be displayed for the user) and have the following characteristics:
The IP source address is the local host's address.
The IP destination address is the remote host's address.
The IP packet type is TCP.
The TCP source port is 23 (these packets are from the Telnet server).
The TCP destination port is the same random port "Z" that was used as the source port for the inbound packets.
The TCP ACK bit will be set on all outgoing packets.
Again, note the similarities between the relevant headers of the incoming and the outgoing packets: the source and destination addresses are exchanged and that the source and destination ports are exchanged.
The following table illustrates the various types of packets involved in inbound and outbound Telnet services.
 The TCP ACK bit will be set on all but the first of these packets, which establishes the connection.
Note that Y and Z are both random (from the packet filtering system's point of view) port numbers above 1023.
If you want to allow outgoing Telnet, but nothing else, you would set up your packet filtering like this:
Rule A allows packets out to remote Telnet servers.
Rule B allows the returning packets to come back in. Because it verifies that the ACK bit is set, rule B can't be abused by an attacker to allow incoming TCP connections from port 23 on the attacker's end to ports above 1023 on your end, e.g., an X11 server on port 6000.
Rule C is the default rule. If none of the preceding rules apply, the packet is blocked. Remember from our discussion above that any blocked packet should be logged, and that it may or may not cause an ICMP message to be returned to the originator.
Making filtering decisions based on source port is not without its risks. There is one fundamental problem with this type of filtering: you can trust the source port only as much as you trust the source machine.
Suppose you mistakenly assume the source port is associated with a particular service. Someone who is in control of the source machine, e.g., someone with root access on a UNIX system (or anyone at all with a networked PC), could run whatever client or server they wanted on a "source port" that you're allowing through your carefully configured packet filtering system. Furthermore, as we've discussed above, you can't necessarily trust the source address to tell you for certain what the source machine is; you can't tell for sure if you're talking to the real machine with that address, or to an attacker who is pretending to be that machine.
What can you do about this situation? You want to restrict the local port numbers as much as possible, regardless of how few remote ports you allow to access them. If you only allow inbound connections to port 23, and if port 23 has a Telnet server on it that is trustworthy (a server that will only do things that a Telnet client should be able to tell it to do), it doesn't actually matter whether the program that is talking to it is a genuine Telnet client or not. Your concern is to limit inbound connections to only ports where you are running trustworthy servers, and to be sure that your servers are genuinely trustworthy. Chapter 8 discusses how you can achieve these ends for various services.
Because many services use random ports above 1023 for clients, and because some services use ports above 1023 for servers, you will often need to accept inbound packets for ports that might have untrustworthy servers on them. In TCP, you can accept inbound packets without accepting inbound connections by requiring the ACK bit to be set. With UDP, you have no such option, because there is no equivalent to the ACK bit. Fortunately, very few protocols used across the Internet are UDP-based.