How loyalsoldier/geoip Defines and Processes Special Categories Like Cloudflare, CloudFront, and Facebook
Special categories in loyalsoldier/geoip are defined through three distinct input mechanisms—plain-text IP lists, MaxMind ASN mappings, and hard-coded private CIDRs—and processed as generic lib.Entry instances that the lookup plugin resolves by iterating over all stored prefixes.
The loyalsoldier/geoip repository aggregates IP ranges for major internet services into categorized datasets. Understanding how special categories like cloudflare, cloudfront, and facebook are defined and processed internally requires examining the input plugin architecture and the container lookup implementation.
How Special Categories Are Defined
The repository categorizes IP ranges using three input strategies defined in config.json and specialized plugins.
Plain-Text IP Lists via the Text Input Plugin
Categories like cloudflare, cloudfront, telegram, tor, and cn originate from plain-text CIDR lists. In config.json, these use the "text" input type referencing remote or local files:
{
"type": "text",
"action": "add",
"args": {
"name": "cloudflare",
"uri": "https://www.cloudflare.com/ips-v4"
}
},
{
"type": "text",
"action": "add",
"args": {
"name": "cloudflare",
"uri": "https://www.cloudflare.com/ips-v6"
}
}
The plugin/plaintext/text_in.go converter reads the URI, creates a lib.Entry named "cloudflare", and populates it via Entry.AddPrefix for each CIDR block.
ASN-Based Mappings via MaxMind GeoLite2 ASN CSV
Categories like facebook, google, fastly, netflix, and twitter derive from Autonomous System Number (ASN) mappings. The config.json defines a wantedList (lines 123-174) mapping category names to AS numbers:
"wantedList": {
"cloudflare": [ "AS395747", "AS394536" ],
"facebook": [ "AS63293", "AS54115", "AS32934" ]
}
The plugin/maxmind/maxmind_asn_csv_in.go parser processes the MaxMind CSV, extracts prefixes for the listed AS numbers, and creates entries named after the map keys.
Hard-Coded Private CIDRs via the Special Plugin
The private category is built-in. The plugin/special/private.go file contains a hard-coded slice (lines 15-37):
var privateCIDRs = []string{
"0.0.0.0/8", "10.0.0.0/8", "127.0.0.0/8",
"169.254.0.0/16", "172.16.0.0/12", "192.0.0.0/24",
"192.0.2.0/24", "192.88.99.0/24", "192.168.0.0/16",
"198.18.0.0/15", "198.51.100.0/24", "203.0.113.0/24",
"224.0.0.0/4", "240.0.0.0/4", "255.255.255.255/32",
"::/128", "::1/128", "64:ff9b::/96", "fc00::/7"
}
When config.json specifies { "type": "private", "action": "add" }, the plugin creates an entry named "PRIVATE" and inserts all CIDRs from this slice.
Container Storage and Entry Management
All categories are stored as instances of lib.Entry. After parsing, input plugins invoke:
container.Add(entry, ignoreIPType)
This method, implemented in lib/container.go (lines 63-111), merges the entry into a global map keyed by the upper-cased entry name (e.g., "CLOUDFLARE"). Each entry maintains separate IPv4 and IPv6 prefix sets accessible via GetIPv4Set() and GetIPv6Set().
Runtime Category Resolution via the Lookup Plugin
The plugin/special/lookup.go plugin resolves IP addresses to category names. When executing a lookup:
lists, found, _ := container.Lookup(l.Search, l.SearchList...)
The Container.Lookup method in lib/container.go (lines 195-226) parses the target IP and iterates over all entries:
for entry := range c.Loop() {
if len(searchMap) > 0 && !searchMap[entry.GetName()] {
continue
}
ipset, _ := entry.GetIPv4Set()
if ipset.Contains(addrOrPrefix) {
isfound = true
result = append(result, entry.GetName())
}
}
Key behaviors:
- Entry names are the category names. Matching prefixes return the entry's name (e.g.,
"CLOUDFLARE"). - Optional filtering. If
searchListis provided (e.g.,["CLOUDFLARE","FACEBOOK"]), only those entries are checked against thesearchMap. - Sorted output. Results are sorted alphabetically and returned as lower-case comma-separated strings.
Practical Lookup Examples
To query which categories contain a specific IP:
# Build and lookup a single IP
./geoip lookup --search 1.1.1.1
# Output: cloudflare
To filter results to specific categories:
./geoip lookup --search 1.1.1.1 --searchList cloudflare,facebook
# Output: cloudflare
Under the hood, the lookup iterates through the container entries, checks if 1.1.1.1 falls within the Cloudflare prefix set loaded from the text input, and returns the matching category name.
Summary
- Three definition methods populate special categories: plain-text IP lists for CDNs and anonymity networks, ASN mappings for major tech companies, and hard-coded constants for private ranges.
- Unified storage occurs in
lib/container.go, where entries are keyed by upper-cased names and contain partitioned IPv4/IPv6 prefix sets. - Generic resolution happens in
plugin/special/lookup.goby iterating all entries and testing prefix containment, with optional filtering by category name.
Frequently Asked Questions
How does loyalsoldier/geoip differentiate between Cloudflare IPs defined via text lists versus ASN mappings?
The repository treats both sources as distinct inputs that converge into a single entry. Text-defined entries (like Cloudflare's official IP lists) and ASN-defined entries (mapping AS numbers to Cloudflare) both create lib.Entry instances named "CLOUDFLARE". If both sources are enabled in config.json, the container.Add method merges their prefixes under the same entry name. You can verify the source by checking whether both type: "text" and type: "maxmindGeoLite2ASNCSV" configurations exist for the same name.
Can I add custom special categories to the geoip repository?
Yes. You can define new categories by adding entries to config.json using either the "text" input type (pointing to a CIDR list URL or local file) or the "maxmindGeoLite2ASNCSV" type with a custom wantedList mapping your category name to specific AS numbers. The repository processes these through the existing plugin/plaintext/text_in.go or plugin/maxmind/maxmind_asn_csv_in.go pipelines and stores them as standard entries in the container.
Why is the private category handled by a special plugin instead of a config file?
The private category uses hard-coded RFC 1918 and IPv6 private range constants defined directly in plugin/special/private.go. This ensures the private IP ranges are always available and correctly defined without requiring external file dependencies or network requests, making the build reproducible and eliminating external configuration drift for these standardized ranges.
How does the lookup plugin handle IP addresses that belong to multiple categories?
The container.Lookup method iterates over every entry in the container and collects all matching category names into a result slice. If an IP address falls within both a Cloudflare prefix and a Facebook prefix (possible via ASN overlap or custom configuration), both names are returned as a comma-separated list (e.g., "cloudflare,facebook"). The results are sorted alphabetically before being printed as lower-case output.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →