Introduction to ICMP
ICMP is a protocol within the TCP/IP stack that exist basically to provide control, troubleshooting, and error messages. It runs over IP, like TCP and UDP do, but is a network-layer protocol, like IP, rather than a transport layer protocol like TCP and UDP are. (Yes, this is kind of weird, that ICMP is encapsulated within IP while being on the same layer as IP. But then again, you can encapsulate IP within IP as well.) [See RFC 792 for the full ICMP specification. See this Geocities page for a chart explaining the functions of each OSI model network layer. See this page for a nice chart showing various protocol stacks in relationship to the OSI model.]
Message types and codes
With IPv4 (the current version of IP that's most in use... there are people using IPv6, but it's not in general deployment over the Internet at large), there are different message types within ICMP. The message type identifies what sort of ICMP message it is (echo request for ping vs. router solicitation vs. redirect...). Each ICMP message type also has a message code, that lets you know the exact meaning. So an ICMP packet with a message type 3 (Destination Unreachable) and a message code 3 (Port Unreachable) lets you know that the machine you tried to reach is not listening on this port. When you nmap a machine on a port it's not listening on, it sends back an ICMP packet like this to let you know that it's not listening on that port (if the port is not firewalled. If it is, then what happens depends on the config of your firewall).
Error and query messages
There are lots of types of ICMP error and query messages. Note that although an ICMP reply can be sent to an ICMP query, ICMP error messages are never (or should never be) sent in reply to ICMP error messages. This keeps infinite loops of ICMP error messages from filling the Internet. [grin] Most of the various ICMP types aren't in common usage anymore. But let's take a quick tour so you know what would be a good idea to let through your firewall and what would be a good idea to block.
When you ping someone, you are sending an ICMP packet with message code 8 (Echo Request) to them. They reply with an ICMP packet with message code 0 (Echo Reply). So if you want to allow people behind your firewall to ping external systems and get a reply, you have to allow ICMP message code 0 packets incoming, like Coldfire said. If you want to allow people to ping you, you have to allow ICMP message code 8 requests incoming.
Of course, you can filter the opposite way outgoing, too, if you want. I just find it easier to block incoming packets that I don't want than to block the replies from my system. It's more efficient to catch things before they get to my machines, and when it's TCP or UDP rather than ICMP, it's a lot safer too.
When you traceroute to someone, you send (under most implementations; we'll get to tcptraceroute and its friends later) three UDP packets with a time-to-live of one towards your destination. The first system it hits sees the packets, sees the expired TTL, and sends back three ICMP message type 11 (Time Exceeded), message code 0 (Time-To-Live Exceeded in Transit) packets to the originating system. The system notes that, and sends out three packets with a TTL of 2. It gets the ICMP errors from the next system down the line, and sends out three UDP packets with a TTL of three... and so on until it gets back the ICMP errors from the destination system.
So if you want people behind your firewall to be able to traceroute to external systems, you want to allow those ICMP message code 11 packets through.
Path MTU discovery
You also want to know when you can't get to something. So you probably want to allow ICMP message type 3 (Destination Unreachable) error messages through your firewall. Of particular importance is ICMP message type 3, message code 4 (Fragmentation Required but DF Bit Is Set). This error message is necessary for Path MTU discovery.
There's a feature of TCP/IP where it tries to auto-negotiate the size of packets that can be sent between a given set of machines. This is called Path MTU discovery. (MTU = Maximum Transmit Unit) One machine will send the biggest packet it can with the DF (Don't Fragment) bit set. With the DF bit set, this packet will not be broken up into smaller packets. It has to be transmitted at its current size or not at all. If any system along the path has a smaller MTU, it will drop the packet with the DF bit set, and send an ICMP error message back to the origin system saying that it couldn't send packets that big, but DF was set so it couldn't fragment it.
Path MTU discovery depends on getting these error messages. If there is no error message, the end system doesn't realize that it's sending packets too big for its path, and will merrily keep on doing so. (If the end system does get the ICMP error, it reduces the size of the packets it's sending until it doesn't get the error any more.) This causes transmission problems for you.
IMO, most of the rest of the ICMP error messages can be safely blocked. (Anyone with arguments to the contrary, please chime in.)
Copyright (c) 2002 by Raven Alder. This material may be distributed only subject to the terms and conditions set forth in the Open Publication License, v1.0 or later (the latest version is presently available at http://www.opencontent.org/openpub/).