Fix IPv4 Geolocation After a Transfer: Step-by-Step

Fix IPv4 Geolocation After a Transfer: Step-by-Step

Fix IPv4 Geolocation After a Transfer: Step-by-Step

Quick answer: after a transfer, IPv4 geolocation can still show the wrong country (the previous one) because the registry record, the BGP announcement and each commercial GeoIP database are separate systems. Changing the registry does not rewrite the databases. The working fix is to publish your own location file (an RFC 8805 geofeed), reference it from your RIPE Database object and send a correction to each provider, keeping the ticket ids. No provider guarantees a date. Checked on 2026-10-14. The registry steps are in our RIPE NCC transfer guide; this post starts after the block is yours.

Four records, four different questions

Record Answers Who sets it What it can affect
RIR record (WHOIS) Who holds the block The holder, in the registry Holder identity. country: is not a location
BGP origin and ROA Who announces it, and is that authorized You, on your routers and in RPKI Reachability and route validity, not the country
Geofeed (RFC 8805) Where the operator says each prefix is used You, in a file you host A hint for providers that read it
Commercial GeoIP database Where that vendor believes the address is The vendor, from its own sources What websites and apps see

The fifth thing, where your servers really are, is what the other four try to model. The RIPE Database documentation says it has never been specified what country: represents, so it cannot be used in any reliable way to map IP addresses to countries, and the RIPE NCC states that it is not a geolocation provider. A block can be registered to a company in one country and run in another, and both records are correct. The same gap at world scale, registry files against a geolocation map: if IPv4 were land.

Diagnose it with one checklist and one script

Check Command Affects
CIDR and registry record whois -h whois.ripe.net -r -T inetnum 192.0.2.0/24 Holder, country:, the geofeed: line
Geofeed file curl -s <url from geofeed:> The location hint for providers that read it
Origin AS and ROA RIPEstat prefix-overview, rpki-validation Routing and route validity, not the country
Provider records The provider's public lookup page The country your users and partners see
Reverse DNS dig -x 192.0.2.1 Mail checks and log readability; no checked source uses it for country

192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24 and 2001:db8::/32 are documentation prefixes: use them for syntax only, because they are not announced and the RIPE query answers with their non-RIPE parent block. For live output we use DCXV's own prefix and AS204057 (run on 2026-10-14; Linux, whois 5.6, jq 1.8; add -p 43 to whois on a system without a services file):

$ whois -h whois.ripe.net -r -T inetnum 178.23.189.0/24 | grep -E '^(inetnum|netname|country|geofeed)'
inetnum:        178.23.189.0 - 178.23.189.255
netname:        CY-DCXV-20210903
country:        CY
geofeed:        https://dcxv.com/geofeed.csv
$ curl -s 'https://stat.ripe.net/data/prefix-overview/data.json?resource=178.23.189.0/24' | jq -c '.data.asns'
[{"asn":204057,"holder":"DCXV-AS DCXV INTERNATIONAL LTD"}]
$ curl -s 'https://stat.ripe.net/data/rpki-validation/data.json?resource=204057&prefix=178.23.189.0/24' | jq -r '.data.status'
valid

The registry says CY because the holder is a Cyprus company, while the servers are elsewhere. The origin AS and the ROA are valid, and a geofeed: attribute is present. That mix is normal and is exactly why a provider can show a country the registry does not. DCXV publishes its network data here. If you hold a block with this problem, you can Ask About IPv4 Transfer Assistance.

The script below (Python 3.8+, standard library only, tested on 3.14, RIPE-region resources only) prints the registry object, origin AS, RPKI status and the most specific geofeed line for a prefix, using the RIPE Database and RIPEstat. We ran it on DCXV prefixes, on documentation prefixes and on its failure cases. Feed matching was also checked offline on a sample file.

#!/usr/bin/env python3
"""geocheck.py PREFIX - registry country, origin ASN, RPKI status and geofeed line for a prefix.
Python 3.8+, standard library only. Data: RIPE Database REST API and RIPEstat (RIPE region only)."""
import csv, io, ipaddress, json, sys, time, urllib.error, urllib.parse, urllib.request

def get(url):
    req = urllib.request.Request(url, headers={'Accept': 'application/json', 'User-Agent': 'geocheck/1.0'})
    for attempt in range(4):
        try:
            with urllib.request.urlopen(req, timeout=20) as r:
                return r.read(5_000_000).decode('utf-8-sig', 'replace')  # cap the read at 5 MB
        except OSError as e:  # includes HTTP 429 and connection resets
            if isinstance(e, urllib.error.HTTPError) and 400 <= e.code < 500 and e.code != 429:
                sys.exit('request refused for %s: %s' % (url, e))  # retrying a 4xx will not help
            if attempt == 3:
                sys.exit('network error for %s: %s' % (url, e))
            time.sleep(2 ** attempt)

def stat(call, **q):
    return json.loads(get('https://stat.ripe.net/data/%s/data.json?%s' % (call, urllib.parse.urlencode(q))))['data']

def registry(net):
    # best match: the most specific inetnum / inet6num that covers the prefix
    t = 'inet6num' if net.version == 6 else 'inetnum'
    q = urllib.parse.urlencode([('query-string', str(net)), ('source', 'ripe'), ('type-filter', t),
                                ('flags', 'no-referenced'), ('flags', 'no-irt')])
    try:
        obj = json.loads(get('https://rest.db.ripe.net/search.json?' + q))['objects']['object'][0]
    except (ValueError, KeyError, IndexError):
        return None
    return {a['name']: a['value'] for a in obj['attributes']['attribute']}

def feed_match(url, net):
    # most specific feed entry that covers the prefix; RFC 8805 columns: prefix,country,region,city
    best = None
    for row in csv.reader(io.StringIO(get(url))):
        if not row or row[0].lstrip().startswith('#'):
            continue
        try:
            p = ipaddress.ip_network(row[0].strip(), strict=False)
        except ValueError:
            continue
        if p.version == net.version and net.subnet_of(p) and (best is None or p.prefixlen > best[0].prefixlen):
            best = (p, row)
    return best[1] if best else None

def main(arg):
    try:
        net = ipaddress.ip_network(arg, strict=False)
    except ValueError:
        sys.exit('usage: geocheck.py PREFIX   (for example 192.0.2.0/24)')
    if net.prefixlen == net.max_prefixlen:
        sys.exit('give the prefix, not a single address: a /32 or /128 gives a misleading RPKI result')
    if net.prefixlen == 0:
        sys.exit('give a real prefix, not the default route')
    print('prefix           ', net)
    reg = registry(net)
    top = reg and ((reg.get('netname') or '').startswith('NON-RIPE-NCC-MANAGED')
                   or (reg.get('inetnum') or '').startswith('0.0.0.0 -') or reg.get('inet6num') == '::/0')
    if not reg or top:
        print('registry object   outside the RIPE NCC: query that RIR instead')
    else:
        print('registry object  ', reg.get('inetnum') or reg.get('inet6num'), '| netname', reg.get('netname'), '| status', reg.get('status'))
        print('registry country ', reg.get('country'), '(country: is not a location)')
    ov = stat('prefix-overview', resource=str(net))
    asns = [str(a['asn']) for a in ov.get('asns', [])]
    print('origin ASN       ', ', '.join('AS' + a for a in asns) or 'not announced (no origin in RIPEstat)')
    for a in asns:
        print('RPKI AS%-9s' % a, stat('rpki-validation', resource=a, prefix=str(net))['status'])
    url = None if top else (reg or {}).get('geofeed')
    if not url:
        print('geofeed          none (no geofeed: attribute on the best-match object)')
        return
    print('geofeed URL      ', url)
    if not url.startswith('https://'):
        sys.exit('geofeed URL is not https://, not fetched')
    row = feed_match(url, net)
    print('geofeed line     ', ','.join(row[:4]) if row else 'NO entry covers this prefix')

if __name__ == '__main__':
    if len(sys.argv) != 2:
        sys.exit('usage: geocheck.py PREFIX')
    main(sys.argv[1])

You want valid and a feed line for your prefix. "NO entry covers this prefix" means providers reading your feed skip the block.

Where to send corrections

Use each provider's current channel; fixing one does not fix the others. Documentation checked on 2026-10-14.

Provider Channel What its documentation says
MaxMind One-time forms; GeoIP Exchange for operators Accepts RFC 8805 and RFC 9632 geofeeds, reviewed once per business day; no release date
IPinfo Corrections page; geofeeds Its community post (2026-02-05): measurement first, geofeeds selectively
DB-IP Report link on the result; batch by contact form A changed RIR WHOIS record usually triggers a refresh; a geofeed is another route
IP2Location Contact and the data accuracy report Release dates per licence; no public correction or geofeed procedure found
Google IP report form in Search Help Updates "may take more than a month"; no individual follow-up

Publish a geofeed and link it

A geofeed is a CSV file defined by RFC 8805: ip_prefix,alpha2code,region,city. Nested prefixes are allowed and the most specific wins. The RFC asks for UTF-8 and CRLF line breaks, deprecates the postal code, so omit it, and lets consumers treat the data as a hint only. A minimal file with documentation prefixes:

# prefix,country,region,city
192.0.2.0/24,CZ,CZ-10,Prague
2001:db8::/32,CZ,CZ-10,Prague

Host it on HTTPS and add one geofeed: https://example.net/geofeed.csv line to your inetnum or inet6num; the attribute is optional and single. RFC 9632 tells consumers to use the most specific object that has a geofeed and then ignore other hints such as country:. It also defines an optional RPKI signature for the file; we did not verify which providers check it. After a transfer, read the object: a geofeed: line left by the seller points at a file you do not control.

A step plan for the new holder

  1. Collect evidence. The registry record showing you as holder, the CIDR list, and proof of where the servers run.
  2. Fix your side. Correct the geofeed: line, publish the file, run the script.
  3. Submit per provider. One request each, with the feed URL. State the real location only: providers may cross-check it against their own measurements.
  4. Keep a log. Ticket id, date, provider, prefix, answer.
  5. Re-check later. Nobody promises a date, so do not plan a launch around one.

Symptom, cause, check, action

Symptom Possible cause Verify Action
Several providers show the previous country No geofeed, or not read yet The script: geofeed: line and a matching row? Publish the feed, then one correction per provider
One provider follows the registry, not your feed That provider does not use feeds Its documentation and a lookup Send a correction with the feed URL
Feed is live, old data remains Release lag, fetch failure, or measurement disagrees HTTPS reachable, prefix covered, no conflicting nested row Ask the provider, keep the ticket, re-check later
Only one CDN, bank or payment service is wrong It runs its own data: Cloudflare, for one, updates its database several times a week and cannot guarantee a match with other providers; risk engines may also weigh other signals Ask which source it uses Fix that source, then report to the service
Right country, wrong city The feed has no city, or measurement differs The city column in the feed Add region and city, then ask again

Flowchart: five checks in order for a wrong country on an IPv4 block, with the action for each

Estimate the risk before you buy

Blacklists and abuse are in the due diligence checklist; here is only geolocation. Look the prefix up at a few providers now, read its inetnum for country: and any geofeed: URL, and ask who controls that file. Agree in the contract that the seller hands over or removes the feed reference at closing.

Next step

Not sure where the gap is? Ask About IPv4 Transfer Assistance or write to ipv4@dcxv.com with the prefix and the symptom. We make no promise about when a provider will update.

DNS Propagation Time: Cloudflare's 3 Seconds vs Your TTL
networkingcloudinfrastructure

DNS Propagation Time: Cloudflare's 3 Seconds vs Your TTL

Cloudflare cut its DNS build delay to about 3.3 s. A cutover still waits on TTLs, negative caching and a 2-day .com delegation. We measured them on two zones.

IPv4 for Ukrainian Companies: RIPE LIR, PI or Lease in 2026
ipv4ripe-ncctransferbroker

IPv4 for Ukrainian Companies: RIPE LIR, PI or Lease in 2026

Ukrainian companies and RIPE IPv4 in 2026: own LIR, PI via a sponsoring LIR or addresses from an LIR, the fees, registry data for Ukraine and the paperwork.

If IPv4 Were Land: Who Holds the Addresses and Why
ipv4transferbroker

If IPv4 Were Land: Who Holds the Addresses and Why

The IP Trust map of IPv4 as land, checked against the five RIR files: who holds the addresses, where transfer supply comes from and what short regions can do.

Inter-RIR Transfers: Moving IPv4 from ARIN to RIPE in 2026
ipv4ripe-ncctransferbroker

Inter-RIR Transfers: Moving IPv4 from ARIN to RIPE in 2026

ARIN to RIPE NCC IPv4 transfers in 2026: 748 by 28 October, the NRPM 8.4 and RIPE-807 rules, the $500 ARIN fee, the steps, and a script that counts the flows.

RIPE IPv4 Waiting List vs Buying a /24: 2026 Cost Math
ipv4ripe-nccpricing

RIPE IPv4 Waiting List vs Buying a /24: 2026 Cost Math

RIPE NCC's IPv4 waiting list: 811 LIRs and a 492-day wait on 28 October 2026. What waiting costs vs buying or leasing a /24, with a script that reads the queue.