Skip to content

ravenscourt/xylem-ip-db

Repository files navigation

Xylem IP Database

IP range lists for countries with internet restrictions, sourced from RIPE NCC with community-maintained overrides. Lists are updated automatically every 6 hours.

Available as plain-text CIDR lists and ready-to-import RouterOS scripts.

Available Lists

Country Plain Text RouterOS sing-box
Iran (IR) ipv4 ipv6 ipv4 ipv6 ipv4 ipv6
Russia (RU) ipv4 ipv6 ipv4 ipv6 ipv4 ipv6
China (CN) ipv4 ipv6 ipv4 ipv6 ipv4 ipv6

Usage

Raw Lists

Each .txt file in the lists/ directory contains one CIDR range per line with a metadata header. Use these with any firewall, VPN, or routing tool that accepts CIDR notation.

Direct links:

https://raw.githubusercontent.com/ravenscourt/xylem-ip-db/main/lists/ir.ipv4.txt
https://raw.githubusercontent.com/ravenscourt/xylem-ip-db/main/lists/ir.ipv6.txt

Replace ir with ru or cn for other countries.

RouterOS

Manual (Initial Seed / Offline Transfer)

Use the .rsc files for first-time setup or when transferring to a router without internet access.

  1. Download the .rsc file for your country from the routeros/ directory.
  2. Upload it to your router (WinBox drag-and-drop, SCP, or FTP).
  3. Import:
/import file-name=ir.ipv4.rsc

The .rsc file clears the existing address list and rebuilds it. Safe to re-run.

Automatic Updates (Recommended)

Create a script on the router that periodically fetches the plain-text list and rebuilds the address list locally. This is more secure than importing .rsc files because only raw IP ranges are downloaded -- your router never executes foreign scripts.

Step 1 -- Create update scripts

IPv4 example (Iran):

/system script add name=update-irv4 source={
  :local country "ir"
  :local listName "IRv4"
  :local fileName ($country . ".ipv4.txt")
  :local url ("https://raw.githubusercontent.com/ravenscourt/xylem-ip-db/main/lists/" . $fileName)
  :do {
    /tool fetch url=$url dst-path=$fileName mode=https
    :delay 3s
    :if ([:len [/file find name=$fileName]] > 0) do={
      :if ([/file get $fileName size] > 0) do={
        /ip firewall address-list remove [/ip firewall address-list find list=$listName]
        :local content [/file get $fileName contents]
        :local comment ""
        :while ([:len $content] > 0) do={
          :local pos [:find $content "\n"]
          :local line ""
          :if ($pos = nil) do={
            :set line $content
            :set content ""
          } else={
            :set line [:pick $content 0 $pos]
            :set content [:pick $content ($pos + 1) [:len $content]]
          }
          :if (([:len $line] > 0) && ([:pick $line ([:len $line] - 1) [:len $line]] = "\r")) do={
            :set line [:pick $line 0 ([:len $line] - 1)]
          }
          :if ([:len $line] = 0) do={
            :set comment ""
          } else={
            :if ([:pick $line 0 1] = "#") do={
              :set comment [:pick $line 2 [:len $line]]
            } else={
              :if ([:len $comment] > 0) do={
                :do { /ip firewall address-list add list=$listName address=$line comment=$comment } on-error={}
                :set comment ""
              } else={
                :do { /ip firewall address-list add list=$listName address=$line } on-error={}
              }
            }
          }
        }
        /file remove $fileName
        :log info "$listName address list synced successfully"
      } else={
        :log warning "Download failed or empty file - keeping old address list"
        /file remove $fileName
      }
    } else={
      :log warning "File not found after fetch - download likely failed"
    }
  } on-error={
    :log error "Failed to sync $listName address list"
  }
}

For IPv6, use /ipv6 firewall address-list instead:

/system script add name=update-irv6 source={
  :local country "ir"
  :local listName "IRv6"
  :local fileName ($country . ".ipv6.txt")
  :local url ("https://raw.githubusercontent.com/ravenscourt/xylem-ip-db/main/lists/" . $fileName)
  :do {
    /tool fetch url=$url dst-path=$fileName mode=https
    :delay 3s
    :if ([:len [/file find name=$fileName]] > 0) do={
      :if ([/file get $fileName size] > 0) do={
        /ipv6 firewall address-list remove [/ipv6 firewall address-list find list=$listName]
        :local content [/file get $fileName contents]
        :local comment ""
        :while ([:len $content] > 0) do={
          :local pos [:find $content "\n"]
          :local line ""
          :if ($pos = nil) do={
            :set line $content
            :set content ""
          } else={
            :set line [:pick $content 0 $pos]
            :set content [:pick $content ($pos + 1) [:len $content]]
          }
          :if (([:len $line] > 0) && ([:pick $line ([:len $line] - 1) [:len $line]] = "\r")) do={
            :set line [:pick $line 0 ([:len $line] - 1)]
          }
          :if ([:len $line] = 0) do={
            :set comment ""
          } else={
            :if ([:pick $line 0 1] = "#") do={
              :set comment [:pick $line 2 [:len $line]]
            } else={
              :if ([:len $comment] > 0) do={
                :do { /ipv6 firewall address-list add list=$listName address=$line comment=$comment } on-error={}
                :set comment ""
              } else={
                :do { /ipv6 firewall address-list add list=$listName address=$line } on-error={}
              }
            }
          }
        }
        /file remove $fileName
        :log info "$listName address list synced successfully"
      } else={
        :log warning "Download failed or empty file - keeping old address list"
        /file remove $fileName
      }
    } else={
      :log warning "File not found after fetch - download likely failed"
    }
  } on-error={
    :log error "Failed to sync $listName address list"
  }
}

Change country and listName for other countries. See the Available Lists table for values.

Step 2 -- Schedule automatic runs

/system scheduler add name=schedule-irv4 interval=12h on-event="/system script run update-irv4" start-time=startup
/system scheduler add name=schedule-irv6 interval=12h on-event="/system script run update-irv6" start-time=startup

Adjust interval to your preference. start-time=startup ensures the list is refreshed on router boot as well.

OpenWrt (PBR)

If you use Policy-Based Routing (pbr) on OpenWrt, you can create a custom user file that fetches the IP lists and adds them to the PBR nft sets. This routes matching traffic through a specific interface (e.g. a VPN tunnel).

Step 1 -- Create the user file

Create /usr/share/pbr/pbr.user.xylem (or any name starting with pbr.user.):

#!/bin/sh
# Fetches xylem-ip-db lists and adds them to PBR nft sets.
# Change COUNTRY and TARGET_INTERFACE to match your setup.

COUNTRY="ir"
TARGET_INTERFACE="wg0"
TARGET_TABLE="inet fw4"
BASE_URL="https://raw.githubusercontent.com/ravenscourt/xylem-ip-db/main/lists"

for ver in 4 6; do
  url="${BASE_URL}/${COUNTRY}.ipv${ver}.txt"
  tmp="/tmp/pbr_xylem_${COUNTRY}_v${ver}.txt"
  nftset="pbr_${TARGET_INTERFACE}_${ver}_dst_ip_user"

  uclient-fetch -qO- "$url" | grep -v '^#' | grep -v '^$' > "$tmp" 2>/dev/null
  [ -s "$tmp" ] || continue

  elements=$(paste -sd, "$tmp")
  nft "add element ${TARGET_TABLE} ${nftset} { ${elements} }" 2>/dev/null
  rm -f "$tmp"
done

Set COUNTRY to the country code you need (e.g. ir, ru, cn) and TARGET_INTERFACE to your VPN/tunnel interface. Create a separate user file for each country if needed.

Step 2 -- Enable it in PBR config

Via UCI:

uci add pbr include
uci set pbr.@include[-1].path='/usr/share/pbr/pbr.user.xylem'
uci set pbr.@include[-1].enabled='1'
uci commit pbr
service pbr restart

Or add it manually to /etc/config/pbr:

config include
  option path '/usr/share/pbr/pbr.user.xylem'
  option enabled '1'

The script runs automatically whenever PBR starts or reloads. IPv6 sets are only populated if ipv6_enabled is set to 1 in your PBR config.

pf (FreeBSD / macOS)

The .txt files work directly with pf tables.

Load a list into a table:

curl -s https://raw.githubusercontent.com/ravenscourt/xylem-ip-db/main/lists/ir.ipv4.txt \
  | pfctl -t ir_ipv4 -T replace -f -

Reference the table in your pf.conf:

table <ir_ipv4> persist
pass out quick on egress to <ir_ipv4> route-to (tun0)

To auto-update, add the curl | pfctl command to a cron job.

iptables + ipset (Linux)

Create a hash:net ipset and load ranges from the list:

COUNTRY="ir"
SET_NAME="ir_ipv4"
URL="https://raw.githubusercontent.com/ravenscourt/xylem-ip-db/main/lists/${COUNTRY}.ipv4.txt"

ipset create "$SET_NAME" hash:net -exist
ipset flush "$SET_NAME"
curl -s "$URL" | grep -v '^#' | grep -v '^$' | while read -r range; do
  ipset add "$SET_NAME" "$range" -exist
done

Then match it in iptables:

iptables -A FORWARD -m set --match-set ir_ipv4 dst -j MARK --set-mark 1

For IPv6, use ipset create ir_ipv6 hash:net family inet6 and ip6tables.

This also works on systems running UFW, since UFW uses iptables/netfilter under the hood. ipset rules coexist with UFW without conflict.

nftables (Linux)

Create a named set and load ranges:

COUNTRY="ir"
SET_NAME="ir_ipv4"
URL="https://raw.githubusercontent.com/ravenscourt/xylem-ip-db/main/lists/${COUNTRY}.ipv4.txt"

nft add table inet filter 2>/dev/null
nft "add set inet filter ${SET_NAME} { type ipv4_addr; flags interval; }" 2>/dev/null
nft flush set inet filter "$SET_NAME"

elements=$(curl -s "$URL" | grep -v '^#' | grep -v '^$' | paste -sd,)
nft "add element inet filter ${SET_NAME} { ${elements} }"

Then reference the set in a rule:

nft add rule inet filter forward ip daddr @ir_ipv4 meta mark set 1

For IPv6, change the set type to ipv6_addr and use ip6 daddr.

Clash / Mihomo

The .txt files work directly as rule provider sources.

Add a rule provider to your config:

rule-providers:
  ir-ipv4:
    type: http
    behavior: ipcidr
    format: text
    url: "https://raw.githubusercontent.com/ravenscourt/xylem-ip-db/main/lists/ir.ipv4.txt"
    interval: 43200
    path: ./rules/ir-ipv4.txt

rules:
  - RULE-SET,ir-ipv4,DIRECT

Replace ir with the country code you need. For IPv6, use the .ipv6.txt URL.

sing-box

Pre-built rule sets are available in the sing-box/ directory in source JSON format.

Use them as remote rule sets in your config:

{
  "route": {
    "rule_set": [
      {
        "tag": "ir-ipv4",
        "type": "remote",
        "format": "source",
        "url": "https://raw.githubusercontent.com/ravenscourt/xylem-ip-db/main/sing-box/ir.ipv4.json"
      }
    ],
    "rules": [
      {
        "rule_set": "ir-ipv4",
        "outbound": "direct"
      }
    ]
  }
}

V2Ray / Xray

V2Ray and Xray can use inline CIDRs in routing rules for small lists, but for the large lists in this repository the recommended approach is to use a custom geoip.dat file. Projects like v2ray-rules-dat generate these from CIDR sources. Our .txt files can be used as input for such generators.

Contributing

To request a change to a country's IP list, open an issue using the appropriate template:

Development

Requires Python 3.13+ and just.

just setup

This creates a venv, installs dev dependencies, and sets up pre-commit hooks (lint + tests run automatically on each commit).

Other useful commands: just lint, just fmt, just test, just check, just generate.

Acknowledgements

This project relies on data from:

About

Curated IP range lists for split tunneling in countries with internet restrictions. Updated every 6 hours.

Topics

Resources

License

Stars

Watchers

Forks

Contributors