Making outgoing IRC DCC work with NAT

In this document, we will cover how to accomplish the above goal using a D-Link DI-614+ router, and mIRC 6.1.

If you're using different equipment, fear not, as the concept itself is what makes all the difference. Regardless, what you're going to need is following:

  1. A NAT router/gateway which permits static port forwarding
  2. An IRC client that permits you to select a port range for outgoing DCCs

It is important that you have a NAT unit that permits static port forwarding. The term "static" is used to describe port forwards (from WAN to LAN) that are not conditional ("triggered"), and do not time out. Using dynamic port forwarding ("triggers") will work, however all routers contain an expiry for such port forwards. So, in the case that you connect to an IRC network and don't attempt an outgoing DCC within the expiry time used by the router, the port forward will essentially become inapplicable until you reconnect to the IRC network. This is one of the core problems with using "triggered" port forwards.

You will also need an IRC client that allows you to configure a port range for outgoing DCCs. There are a lot of great IRC clients out there for different operating systems, but not all of them permit you to do this. If your client does not support this, I recommend you get one which does, or contact your clients' author and demand the feature (it requires very little effort on the author's part, as specifying such is part of the standard libc socket library!). By default, IRC clients will attempt to send outgoing DCCs on a dynamically allocated port number, which simply will not work with NAT (since IRC does not use UPnP).

NOTE: This only applies to outgoing IRC DCC requests. Incoming DCCs should work just fine using NAT (assuming your NAT router has a proper NAT implementation). If incoming DCCs do not work, your problem is elsewhere.

The problem with IRC DCC

The problem with outgoing IRC DCCs -- like most P2P (peer-to-peer) applications -- is that the port numbers are transmitted to the remote end as part of a data packet and not "negotiated" like UPnP provides. IRC DCC was invented long before UPnP, and will probably never be upgraded to support this kind-of feature.

Outgoing IRC DCCs include DCC SENDs, DCC RESUMEs, and DCC CHATs.

Let's take the following scenario and map out exactly what is happening on a network level, so that you can understand exactly where the problem with outgoing IRC DCC lies. In this example, we will be doing a DCC SEND (a file send), and will assume the following information:

192.168.0.0/24   = Local network (LAN)
192.168.0.1      = NAT gateway (on LAN)
222.222.222.222  = NAT WAN-side IP ("Internet IP")
194.68.45.50     = irc.dal.net (IRC server)
SenderNick       = Nickname of individual doing DCC SEND (us)
ReceiverNick     = Nickname of individual receiving file
69.69.69.69      = IP of ReceiverNick
#Source IPSource Port Destination IPDestination Port Packet data / Description
1192.168.0.101026 194.68.45.506667 PRIVMSG ReceiverNick :<0x01>DCC SEND test.txt 192.168.0.10 1027<0x01>

SenderNick tells ReceiverNick "Hey, I have a file to send you. Please connect to 192.168.0.10 on port 1027 so I can begin sending the file."
2192.168.0.10n/a 192.168.0.1n/a The PRIVMSG (DCC SEND) goes through the NAT gateway.
3192.168.0.101027 n/an/a 192.168.0.10 (the LAN IP of SenderNick's PC) begins listening on port 1027 for the incoming DCC connection so it can send the file.
4222.222.222.2224035 194.68.45.506667 PRIVMSG ReceiverNick :<0x01>DCC SEND test.txt 192.168.0.10 1027<0x01>

222.222.222.222 sends the actual PRIVMSG (DCC SEND) to irc.dal.net, which then gets delivered to ReceiverNick.
569.69.69.693097 192.168.0.101027 69.69.69.69 (ReceiverNick) receives the DCC SEND request from irc.dal.net, and therefore attempts to connect to 192.168.0.10 on port 1027.

NOTE: The IP shown in DCC SEND test.txt 192.168.0.10 1027 is not actually a user-readable/plain-text IP number. It is actually the 32-bit Internet-packed representation of the IP number. I used the unpacked version so it would make more sense to readers.

You can see exactly why this doesn't work. The problem is that the actual IP of the sender is contained in the packet which the recipient attempts to connect to. 69.69.69.69 will never be able to connect to 192.168.0.10; it's not on the same network.

The workaround

Thankfully, port forwarding can solve this problem (as can it with most other P2P protocols! This isn't just limited to IRC, you know...)

The trick involves three (3) changes:

  1. Getting the IRC client to use the WAN-side IP address in the actual DCC SEND packet, instead of the LAN-side IP address.
  2. Assigning a range of ports for outgoing DCCs in the IRC client.
  3. Port-forwarding ("port-mapping") those range of ports, via the NAT gateway, to the LAN-side IP address of the PC doing the DCCs.

Something that needs to be made clear here: this solution will not work with DHCP. DHCP, by it's nature, is a dynamic protocol -- and although you may get assigned the same IP for many months, there is no way to guarantee you will get it every time. If you use DHCP, either stop using it and assign your LAN PCs static IPs, or simply rely on other methods of file transfer or IM clients (such as MSN Messenger 6.0, which supports UPnP).

NOTE: D-Link D-614+ firmwares 3.20 (for Revision B models) and 2.20 (for Revision A models) exhibit known bugs in regards to port forwarding (the "Virtual Server," "Application," and "Firewall" settings). The end result is that any incoming connections to ports -- forwarded or not -- will fail and spit back TCP RST ("Connection refused"). D-Link has not acknowledged this problem, but it has been reported by numerous members of the D-Link forum over at BroadbandReports. There are other known problems with the 3.20/2.20 firmwares as well, including spontaneous reboots.

Step 1 - Configuring mIRC to use the WAN-side IP

  1. Start mIRC, and choose Tools... Options from the menu bar.
  2. Expand the Connect tree option, and choose Local Info.
  3. Make sure Lookup method is set to Server.
  4. Make sure On connect, always get... has Local Host checked. The IP Address check-box should be checked, but grayed out.
  5. Double-check your settings by comparing them to the screen-shot below.

    mIRC Settings - Image #1

Step 2 - Configuring mIRC to use a range of ports for outgoing DCCs

  1. Expand the DCC tree option, and choose Options.
  2. Make sure DCC Ports First is 54000 and DCC Ports Last is 54019. This will provide you with the capability of handling up to 20 outgoing DCC file sends. More importantly, this will cause your local PC (on the LAN) to use TCP ports 54000 through 54019 for outgoing DCC sends.

    The port ranges need to be in a very high numbered range. Do not pick port numbers below 1024 (i.e. 1-1023), as this could cause major problems with other protocols (FTP, HTTP, SSH, SMB/CIFS, NFS, and hundreds of others). Stick to port numbers above 40000, and do not exceed 65534. Also, be sure the port ranges you use do not conflict with other applications or games you may use online; many of these games use "high-range" port ranges, so please investigate the network protocols / port ranges of games you use before deploying this workaround.
  3. Make sure Max. DCC Sends is set to 20. This is just to ensure things are consistent with the port ranges you've selected.
  4. Double-check your settings by comparing them to the screen-shot below.

    mIRC Settings - Image #2

Step 3 - Configuring the NAT router for port-forwarding

NOTE: This step is dependant upon what NAT router/gateway you own. The examples shown here are for a D-Link DI-614+. Please refer to your users manual for assistance, or call Technical Support.

  1. Log in to your router and go to the Advanced menu.
  2. Choose the Firewall tab on the left.
  3. Using the input boxes presented, insert the following information:

    NOTE: The IP address used in the example, 192.168.0.10, should be replaced with the LAN-side IP address of the computer doing the DCCs.
              [*] Enabled     [ ] Disabled
    
      Name       IRC file sends (PC#1)
    Action       [*] Allow     [ ] Deny
    
                 Interface   IP Range Start   IP Range End   Protocol   Port Range
    Source       WAN         *
    Destination  LAN         192.168.0.10                    TCP        54000-54019
    
    Schedule     [*] Always
    
  4. Double-check your settings by comparing them to the screen-shot below.

    NAT Settings - Image #1
  5. Click Apply.
  6. Double-check your settings by comparing them to the screen-shot below.

    NAT Settings - Image #2

Step 4 - Testing the results

  1. Sign on to your favourite IRC network with a friend. If you were already connected when you changed your mIRC settings, you will need to re-connect to the IRC server.
  2. Send your friend a file.
  3. Have a coffee or some tea while your file gets sent successfully.
  4. In the middle of a file send, try sending your friend a DCC CHAT. This should also work; you have up to 20 outgoing DCCs that can be going at once, so feel free to make use of them.

Troubleshooting

If you suspect the IP address in the PRIVMSG (DCC SEND) packet is wrong, try sending something to a friend. mIRC will conveniently send a NOTICE message along with any DCC SENDs, which means your friend should see something that resembles the following:

-SenderNick- DCC Send test.txt (222.222.222.222)

Make sure the IP shown (e.g. 222.222.222.222) matches your WAN-side IP address. You can verify this by doing a /WHOIS on yourself and doing a /DNS on the hostname you show up on IRC from, or just by doing a /DNS on your own nickname (i.e. /DNS SenderNick).

If the IP address shown in the NOTICE is your LAN-side IP address, you either did not reconnect to the IRC network, or mIRC is not set to do a "Server" hostname/IP lookup. Double-check your settings.

Multiple LAN PCs using IRC DCC

If you have multiple PCs on the same LAN that need to do IRC DCCs (such as if you live in a household full of geeks who all use IRC), apply the following concepts to the above solution:


Valid XHTML 1.1!