Country block / allow with iptables

Discussion in 'Tomato Firmware' started by phuque99, Oct 3, 2011.

  1. phuque99

    phuque99 LI Guru Member

    Some folks sent my private messages regarding country access control list. I figured that I'll start a thread to discuss two ways of doing it.

    Using geoIP

    This requires a USB drive to store a copy of the geoip database, a Linux/Unix system or environment with the right *nix tools to create that database. Download the following:

    * GeoIP database
    * GCSV2BIN tool

    Unzip the geoip database. Uncompress and compile the gcsv2bin tool. Then run the following command:
    ./csv2bin ../GeoIPCountryWhois.csv
    This will create two files, geoipdb.bin and geoipdb.idx. Copy both into your USB drive and leave it permanently connected to your USB router. In the Tomato WAN up script, add the following lines (adjust the USB path according to your setup and label of the USB drive):
    [ ! -d /var/geoip ]&&(mkdir /var/geoip;cp /mnt/USB/geoipdb.* /var/geoip;modprobe xt_geoip;service firewall restart)
    Now you can make use of the "geoip" module in iptables, like in the following example that blocks every country except Japan and US from accessing your web server on forward port 80:
    iptables -I wanin -p tcp -d --dport 80 -m geoip ! --src-cc JP,US -j DROP
    The following article discuss the use of geoip database with iptables:

    Quick and dirty country block list

    The dirty way is to download a list of country IP on the fly and add iptables command for each line. Various country IP blocks can be downloaded from "" and you manipulate them into a looping iptables script. See the following examples:

    If you have forwarded SSH port 22 and wish to allow only Nigerian IPs to access that port, you can add the following into Tomato's firewall script:
    iptables -I wanin -p tcp -d --dport 22 -j DROP
    wget -qO- | awk '{system("iptables -I wanin -p tcp -s " $1 " -d --dport 22 -j ACCEPT")}'
    Block US from accessing your web server on port 80:
    wget -qO- | awk '{system("iptables -I wanin -p tcp -s " $1 " -d --dport 80 -j REJECT")}'
    This is quick and dirty because it will add a very long list of iptables rule into your forward chain (depending on country list) and there maybe performance issue on slower routers with small memory)

    Edit: The pipe method above keeps TCP connection open until the all IPs are processed and multiple system() call within awk is expensive and slow. The following loop script is more efficient
    for ip in $(wget -qO-; do iptables -I wanin -p tcp -s $ip -d --dport 80 -j REJECT"; done

    GeoIP is the best method with small number of rule set and is fairly practical. The quick and dirty method is recommended only if you have one or two specific countries to allow / deny access.
    philess and Monk E. Boy like this.
  2. bagu

    bagu Network Guru Member


    I can't compile csv2bin, because i get :
    csv2bin.c:27:13: attention : ‘optind’ redeclared without dllimport attribute: previous dllimport ignored
    I there a way to compile easily csv2bin ?
  3. phuque99

    phuque99 LI Guru Member

    I thought csv2bin is easy enough on any Linux system. It uses standard libraries. Just tested on an Ubuntu system:

    $ make
    gcc -g -c -Wall  -D_GNU_SOURCE csv2bin.c
    gcc -g csv2bin.o -o csv2bin
    $ file csv2bin
    csv2bin: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=0x46d353620512031297901f57e23b8e6ab180eabd, not stripped
  4. bagu

    bagu Network Guru Member

    Ok, with cygwin, it don't work.
    But with ubuntu, everythink work fine. Thanks
  5. Das_OSi

    Das_OSi Reformed Router Member

    Thanks a lot for this Tutorial =)
    Is there a way to easily keep the GeoIP database up to date?
    (without recompiling the whole thing)
  6. jerrm

    jerrm Network Guru Member

    Easiest is probably to compile csv2bin to run on the router, and script monthly downloads of the geoip data file.

    And since this got bumped, the alternative lists mentioned above have not been available for several months now.
  7. Richard Petty

    Richard Petty Reformed Router Member

    I'd love to compile csv2bin to run on my router. Here are my stats:

    Tomato v1.28.0000 MIPSR2-115 K26 USB AIO-64K
    Asus RT-N66U​

    It seems that development libraries are absent on the router so is there a directive I might use on my Linux box to specify the router's architecture?


  8. jerrm

    jerrm Network Guru Member

    It's overkill for csv2bin, but follow the koitsu's docs for setting up a tomato build environment and it will compile OK, or google for "tomatoware" to build on the router.

    An alternative for Shibby builds is to use ipset instead.
  9. Richard Petty

    Richard Petty Reformed Router Member

    You're right, the cross-compilation instructions are a high bar for something as trivial as csv2bin. I was kinda hoping someone had compiled one for my router and just had it laying around.

    I'm not sure that is an option anymore. yields a six month old zones file. web page is not date-stamped and many files are empty.
  10. jerrm

    jerrm Network Guru Member

    No, ipdeny has been down for months as you note.

    I use the geoip lite db and build ipsets with the data. csv2bin is much faster at building the data base, a little less memory usage with the geoip module, but with only monthly updates, I'd rather have a script-only solution.
    Last edited: Jan 27, 2014
  11. mstombs

    mstombs Network Guru Member

    I had a go at using those now defunct ipdeny lists a while ago, it is much faster to use tomato iptables-restore function than the individual iptables call for each entry

    but I think this was obsoleted by ipset

    Attached is a build script and small binary for csv2bin (unmodified source) which seems to run on an older Shibby router, uses tomatousb toolchain that hasn't been updated for a while...

    n66u:/tmp/mnt/usb4gb# chmod +x csv2bin
    root@rtn66u:/tmp/mnt/usb4gb# ./csv2bin
    ./csv2bin: missing input file
    Use -h for help.
    root@rtn66u:/tmp/mnt/usb4gb# ./csv2bin -h
    Usage: ./csv2bin [OPTION] [FILE]
    Convert the comma-separated-values from the inputed Maxmind's (
    FILE to a truncated binary file used by the iptables/netfilter's geoip module.
    The created binary file is outputed in ./geoipdb.bin (the current directory).
    and the index file in ./geoipdb.idx
            -h      Gives this help display.
            -v      Displays this program's version.
    Copyright (C) 2004 CookingLinux
    Nicolas Bouliane <nib at>
    Samuel Jean <sjean at>
    This is free software; see the source for copying conditions. There is NO
    Would be interested to see a script that used it...
  12. jerrm

    jerrm Network Guru Member

    This compiled version reports a "bus error" when run against the geoip csv file and will leave an incomplete bin file. I can check later, but there is a constant - IIRC for the max number of countries - that needs to be increased in the source.

    I'll try to post my compiled version if I get home at a decent hour, maybe with my geolite2ipset script, or a simplified version of it.
  13. jerrm

    jerrm Network Guru Member

    Attached Files:

    Last edited: Jan 28, 2014
  14. mstombs

    mstombs Network Guru Member

    Ah I see, there are 250 unique countries in the csv and the code contains

    #define COUNTRYCOUNT 241
  15. koitsu

    koitsu Network Guru Member

    Cute code (not). Where's the source code to this thing? I wanted to see how this #define is used within the code.

    The only versions I can find via Google are all different and all use different licenses (some claim "LGPL v2 or greater" (what?) and say "read COPYING for License details" yet there is no COPYING file, others claim "GPL v2" (different than LGPL), and others (Python script) use a series of licenses all combined). The binary itself just seems to spew ownership (which has long since disappeared (circa 2004)) but no license, so I was hoping there might be clarification within the source code itself.

    If it's under GPL or LGPL only: you need to provide the source to it (along with the binary, i.e. the source must be included in that .zip file), or alternately you need to provide a link/URL shown within the binary (in its usage syntax is sufficient as I understand it) that points to where the source code lives so people can get it.

    Sorry for getting off-topic and into a licensing discussion, but I don't want to see people hurt if GPL/LGPL is involved and someone claims violation. (Personal opinion, but this is exactly why I use the 2-clause BSD license in my own OSS projects, else recommend people use things like the WTFPL (yes there are actual projects which use this, it's a somewhat tongue-in-cheek license but legitimate)).

    Unrelated to licensing: has anyone looked at the improved xt_geoip.ko (circa 2011, version 1.33) that offers apparently significantly better performance given changes to its search algorithm (see Introduction at top of page)? There's also a tool called xt_geoip_build which does the CSV-to-binary translation, rather than geo2csv. Obviously for Tomato/TomatoUSB the pathnames referenced/used would need to be changed. (That suite, by the way, is under GPL v2 so any source modifications would have to be made available publicly -- yep, that includes pathname changes). EDIT: Oh, never mind, at least for xt_geoip_build -- that isn't a binary at all but a perl script and one which requires Text::CSV_XS which doesn't come with perl either (nor does Text::CSV). *rolls eyes*
    Last edited: Jan 28, 2014
  16. mstombs

    mstombs Network Guru Member

    I used the unmodified ancient sourcecode linked in first post, doesn't seem to be anything much to it, and it clearly has a huge bug in that it makes no attempt to check that the array to store the country list is big enough before writing beyond the end of the declared array. I see many more examples of potential buffer over-runs (surely known about in 2004?), so I think the best use of it is an example of unsafe C-code, and don't recommend deploying on an important router!
  17. jerrm

    jerrm Network Guru Member

    I know, but in this case since the source is posted in the first post and I don't have a copy of it anymore on my system, I'll risk it. There are plenty of serious GPL violations to pursue before they come knocking on my door(hopefully).

    But the license issues are just another reason I went with using ipsets instead - same results, and all scripted with busybox sh and awk.
    Last edited: Jan 28, 2014
  18. jerrm

    jerrm Network Guru Member

    I only found one updated source, and it was just a name change from csv2bin to gcsv2bin. I didn't have warm feelings for it either, part of the reason I went with ipsets.

    Wish we could get Victek and Toastman to include ipsets.
  19. FretNoize

    FretNoize Reformed Router Member


    In light of the fact that the IPDENY website no longer maintains current country lists (most of which are now 0-byte files), can anyone please share a working script using the IPSET method to block specific countries using some other available source for country lists? I would like to implement this on my Asus RT-N66U running Shibby's latest Tomato firmware. Any help would be greatly appreciated.

    Thank you in advance!
  20. jerrm

    jerrm Network Guru Member

    This uses the geolite database to build an ipset. I had meant to post a simplified version, but I'd probably never get around to it. Pick apart the pieces you need.

    Using awk for the deaggregate function is not the speediest operation, but avoids third party dependencies.


    Use at your own risk, consider the script an example, distributed as-is. Update as needed. We use the script at multiple sites, but in environments we know and have control of. The script is not really in a state I would normally consider for public release.

    Basic docs:
    1: Edit countries= to your desired list.
    2: Make sure the folder defined for $etc exists. I use /opt/etc, but any writable folder should do.
    3: execute scriptname start

    start - downloads geolite db and creates ipset data list(if needed), inserts ipset data and rules into iptables
    stop - removes rules and ipset from iptables
    rebuild - rebuilds ipset list and updates iptables - use if you change country list, but do not need to re-download geolite.
    force - force a full download/rebuild
    checkupdate - check for updated geolite file, download, update ipset if needed.
    cron - add a cron entry to run checkupdate daily
    debug - print debug info to console
    Last edited: Jan 31, 2014
    user2k10 and FretNoize like this.
  21. FretNoize

    FretNoize Reformed Router Member

    Thank you, jerrm. I really appreciate your help in this. My router is getting bombarded with port scans from China almost every day and night. I will read through the script and try to understand how it works. I'm just a newbie in Linux, but have many years of programming and IT experience behind me. Hopefully that will help. :D Are there any specific install instructions I should follow or just stick it in as a WAN Up script?

    Best Regards!
  22. jerrm

    jerrm Network Guru Member

    I updated the original post with some basic info.

    wanup should be OK, but I don't load anything from wanup. I load everything from init, after waiting for the wan be live. From init I add "scriptname cron start" to create the cron entry and start.
    FretNoize likes this.
  23. FretNoize

    FretNoize Reformed Router Member

    Thanks, I figured out how to run it after getting some error messages in the log. I had run it without passing it any parameters and didn't change the output file path. After I figured this out, it seems to be running fine without issues. Good suggestion regarding WAN Up - I will move it to Init. It did take a while for it to download the data files and execute the conversion routines. It would be nice to find another source for the data files which are already converted to CIDR format.

    Also, I currently have it running from a CIFS share on my NAS. Would it be better to run it on the local router itself? If so, which is the best read/write path? Some paths seem to be read-only. I guess I can also run it from a folder on /jffs if it fits. Thanks again, jerrm!
  24. jerrm

    jerrm Network Guru Member

    Glad you got it going.

    Nothing necessarily wrong with wanup except some some unneeded work may occur if the wan ip changes.

    I agree in principle, but the geoip list seems to be the best of the free alternatives, and the conversion process really only needs to run once a month. When ipdeny was useful, I ran comparisons logging both, and geoip always was a little better.

    CIFS should be fine, it's not a very disk intensive process and the disk usage is only during startup. We don't have routers without USB or JFFS, so that's what I use, mostly USB.
    FretNoize likes this.
  25. Joenathan

    Joenathan Reformed Router Member

    Trying to get this up and running on my router, running Jerrm's script through the Execute System Commands, running the script I get no error messages, no feedback, I then try to run "geolite start" and get "geolite: not found".

    I must be missing something, any help would be much appreciated.

    the only line I changed in the code is "etc=/cifs1/ipblock"

    cifs1 is mounted and writable and the folder exist.
  26. jerrm

    jerrm Network Guru Member

    Looks like the script is not in your path. Make sure it is executable and call with the full path, ie: /cifs1/ipblock/geolite (or whatever you have it as).
  27. Hienry

    Hienry Network Newbie Member

  28. rs232

    rs232 Network Guru Member

    As an alternative option P2Partisan does country blocking out of the box
    Use the search on this forum to find it
  29. Fishkniktommac

    Fishkniktommac Serious Server Member

    I need some help with a iptables rule. I want to make a rule which block all traffik to a Country:
    I can do this for an ip like this:
    iptables -I FORWARD -d -j DROP

    why does this one not work?
    iptables -I FORWARD -d geoip ! --dst-cc RU -j DROP

    How should the rule looks like?

    OK I figured that out:

    # Blocks all Traffik to Russia , China
    iptables -I FORWARD -m geoip --dst-cc RU,CN -j DROP
    Last edited: Feb 1, 2015
  30. lmerega

    lmerega Serious Server Member

    Hi, I have some problems with attacks from all over the world on my https port.
    I used your idea but it does not work if I use "wanin" as incoming chain, I have to use INPUT.
    So I wrote:
    iptables -I INPUT -m geoip ! --src-cc IT -j DROP (I live in Italy).
    After this command, the router hangs and iptables crashes.
    Is it a common problem since I put too many addresses or is this my RTN66U issue?


  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice