NAME

    gdnsd-plugin-geoip - gdnsd meta-plugin for GSLB + failover via MaxMind's
    GeoIP databases

SYNOPSIS

    Minimal example gdnsd config file using this plugin:

      zones => { example.com => {} }
      plugins => { geoip => {
        maps => {
          my_prod_map => {
            geoip_db => /var/gdnsd/GeoIPCity.dat,
            datacenters => [dc-03, dc-02, dc-01],
            map => {
                EU => {
                    DE => [dc-03, dc-01],
                    CH => [dc-01, dc-03]
                },
                NA => { MX => [dc-02] }
            }
          },
          my_auto_map => {
            geoip_db => /var/gdnsd/GeoIPCityv6.dat,
            datacenters => [dc1, dc2],
            auto_dc_coords => {
               dc1 => [ 38.9, -77 ],
               dc2 => [ 50.1, 8.7 ],
            }
          }
        },
        resources => {
          prod_www => {
            map => my_prod_map
            service_types => default
            dcmap => {
              dc-01 => 192.0.2.1,
              dc-02 => { lb01 => 192.0.2.2, lb02 => 192.0.2.3 },
              dc-03 => [ 192.0.2.4, 192.0.2.5, 192.0.2.6 ]
            }
          }
          corp_www => {
            map => my_auto_map
            dcmap => {
              dc1 => 192.0.2.100,
              dc2 => 192.0.2.101
            }
          }
        }
      }}

    Example zonefile RRs:

      www      600 DYNA geoip!prod_www
      www-dc01 600 DYNA geoip!prod_www/dc-01
      www.corp 600 DYNA geoip!corp_www

DESCRIPTION

    gdnsd-plugin-geoip uses MaxMind's GeoIP binary databases to map address
    and CNAME results based on geography and (in the address case) monitored
    service availability.

    Like the older gdnsd-plugin-georeg, this plugin can operate in an
    automatic distance-based mode (using City-level coordinate information
    rather than an external file and a Region-level db). It can also operate
    coordinate-free and rely on the user to configure a hierarchical map of
    cascading default user_location-to-datacenter mappings, starting at the
    continent level. The two modes can also be effectively mixed at
    geographic boundaries. This avoids the previous complications
    surrounding coordinate math, the georeg.locations file, and lengthy
    manual "overrides" sections to correct real-world load distribution and
    network topography vs rough geographic guesswork.

    Other key improvements over gdnsd-plugin-georeg:

    *   Supports multiple geographic mappings and datacenter-sets

    *   Full support for IPv4 and IPv6 Country, Region, and City databases

    *   Fully supports IPv6 from every angle

    *   Reloads GeoIP database updates without restarts or query delays

    *   Supports the emerging edns-client-subnet standard very optimally

    *   Does not require libGeoIP as a dependency

    *   Easy to expand for future alternative database format support and
        methods of geographical mapping without breaking configuration
        compatibility

    *   Delegates out the per-datacenter work to other various plugins,
        using the same code as the metafo plugin

    The edns-client-subnet optimizations are worth mentioning in particular.
    For each "map" you define (which maps geographic location codes to
    preference-ordered lists of your datacenter locations), this plugin
    merges all of the raw GeoIP subnets into the largest possible supernets
    which contain identical responses in your configuration. These in turn
    are used to set larger edns-client-ip scope masks than you'd see simply
    returning raw GeoIP results.

PLUGIN_METAFO

    The documentation for gdnsd-plugin-metafo(8) is required reading for
    understanding the geoip plugin documentation here. The geoip plugin is
    an exact superset of the metafo plugin, and re-uses almost all of the
    metafo plugin's source code. What plugin_geoip adds on top of the
    functionality of metafo is the ability to have the order of the
    datacenter failover list become dynamic per-request based on geographic
    hints derived from the client's network address, as well as the ability
    to do geographic selection of DYNC (CNAME) resources.

CONFIGURATION - TOP-LEVEL

    The top level of the geoip plugin's configuration (i.e. "plugins => {
    geoip => { ... } }") supports only two special keys, both of which are
    required and expanded upon in detail in the next two sections: "maps",
    and "resources". The "maps" section defines one or more named mappings
    of location information from GeoIP binary databases to ordered subsets
    of datacenter names. The "resources" section defines one or more named
    resources, each of which references one of the named maps and resolves
    datacenter names to specific sets of addresses or CNAMEs.

    Any other keys present at this level will be inherited down inside of
    each per-resource hash inside the "resources" stanza, acting as
    per-resource defaults for anything not defined explicitly there.

CONFIGURATION - MAPS

    The "maps" stanza supports one special configuration key at the top
    level:

  "city_region_names = /path/to/fips_include"

    String, default unset. GeoIP City databases use FIPS 10-4 codes for the
    names of Regions outside of the US and Canada. For example the Geneve
    region of Switzerland is identified as 07 in the database. By default
    you would have to use these relatively confusing region codes in your
    hierarchical maps that make use of Region-level information (e.g. "EU =>
    { CH => { 07 => { Geneva => [ ... ] } } } }". If this option is
    specified, it points to a text file that maps these FIPS codes to
    canonical, memorable full names for clearer map configuration (e.g. "EU
    => { CH => { Geneve => { Geneva => [ ... ] } } } }".

    The file format is a simple subset of the CSV format with 3 fields: ISO
    3166-1 country code, FIPS 10-4 region code, and the region name in
    double-quotes. It is recommended you download this file directly from
    MaxMind's reference copy in this format. As of this writing, it is
    available from them at the following URL:
    <http://www.maxmind.com/app/fips_include>

CONFIGURATION - PER-MAP

    All other "maps"-level configuration keys are the names of the maps you
    choose to define. A map, conceptually, is a mapping between geography
    and/or network topology to varying ordered datacenter sub-sets. The
    value of each named map must be a hash, and the following configuration
    keys apply within:

  "geoip_db = /path/to/GeoIP.dat"

    String, pathname, required. This is the filesystem path to one of the
    supported MaxMind GeoIP database types. It will be loaded and converted
    to an internal representation for this particular map once at startup
    time, and re-loaded again if it changes during the daemon's lifetime.

    In the common case of the gdnsd daemon being started by root and
    securing itself via "setuid()", "chroot()", etc, the "geoip_db" pathname
    must lie within the daemon's chroot path in order for runtime reloads to
    work. If it does not, the plugin will still function correctly in all
    other respects, but database updates can only be reloaded by restarting
    the whole daemon. The default gdnsd chroot path is /var/gdnsd/, or
    something similar depending on the installation prefix (e.g.
    /usr/local/var/gdnsd/).

  "geoip_db_v4_overlay = /path/to/GeoIP.dat"

    String, pathname, optional. This specifies an optional IPv4-level GeoIP
    database to overwrite the IPv4 sub-space of the IPv6 data loaded from
    "geoip_db". It must be a V4-format database, and "geoip_db" must be a
    V6-format database. When runtime reloads are performed, if either
    database changes, both will be reloaded.

    As of this writing, MaxMind doesn't sell a commercial GeoIPv6 database.
    What they offer are free IPv6 GeoLite database downloads, which include
    the IPv4 subset in the less-accurate GeoLite form. This option allows
    you to use these GeoLitev6 databases for IPv6 coverage, and then overlay
    your paid commercial GeoIPv4 data on top for more accurate IPv4 results.

  "datacenters = [ one, two, three, ... ]"

    Array of strings, required. This is the total set of datacenter names
    used by this map. You must define at least one datacenter name (although
    2 or more would be infinitely more useful). At this time, there is a
    maximum limit of 254 datacenter names per map, although this could be
    raised if anyone requires it. The order specified here is the fallback
    default result ordering in various default cases (e.g. if no explicit
    top-level map default list is given).

  "city_no_region = true"

    Boolean, default "false". If this key is set to "true" and "geoip_db"
    references a City-level database, the Region-level information within it
    will be completely ignored for mapping purposes. Your hierarchical map
    structure will now be "continent => country => city" rather than
    "continent => country => region => city".

  "city_no_city = true"

    Boolean, default "false". Very similar to the above, but ignores the
    final, city-level information instead, leaving you with only the
    information to map your hierarchy as "continent => country => region".
    If both options were set to "true", you would only have the ability to
    map by "continent => country", as if it were a Country-level database.

  "nets = { ... }"

    Key-value hash, optional. If specified, the contents should be key-value
    pairs of "network/netmask" mapped to a datacenter name (or an array of
    datacenter names). This is intended for doing one-off overrides of
    special networks (to you), such as private space. Entries specified here
    will override any GeoIP-based mapping from "map" below. Note that this
    is implemented in a simplistic way which isn't optimized for large sets
    of "nets". If you really need a giant custom map, consider generating a
    custom GeoIP.dat instead (there's some related perl code in gdnsd's test
    suite...). Example:

        nets => {
            10.0.0.0/8 => [ dc1, dc2 ],
            192.0.2.128/25 => dc3
            2001:DB8::/32 => [ dc4, dc5, dc6 ],
        }

    Note that IPv6 entries in "nets" are illegal unless the "geoip_db" is
    also IPv6-enabled. Regardless of the order specified in the config, the
    networks in 'nets' will be sorted in an order that prevents larger
    subnets from obliterating the effects of smaller ones. Specifically,
    IPv6 networks will be applied before IPv4 networks, and within each type
    the networks are sorted by ascending network number.

    Also note that you cannot directly specify IPv6 networks within the
    trivially-translatable v4-compat, v4-mapped, SIIT, Teredo, or 6to4
    address spaces here. Such assignments would be ineffective because the
    lookup-time code always translates these addresses into a canonical IPv4
    address first before traversing the network tree. For related reasons,
    IPv6 supernets of these spaces are also not allowed as nets entries.

    Always just specify the IPv4 network itself to cover all of these cases.
    The following are the IPv4-like IPv6 address spaces referenced above,
    with "NNNN:NNNN" representing the 32 bits the effective IPv4 address is
    taken from:

       ::NNNN:NNNN/96        # v4-compat
       ::FFFF:NNNN:NNNN/96   # v4-mapped
       ::FFFF:0:NNNN:NNNN/96 # SIIT
       2001::NNNN:NNNN/32    # Teredo (NNNN:NNNN is xor'd with FFFF:FFFF)
       2002:NNNN:NNNN::/16   # 6to4

  "map = { ... }"

    Key-value hash, optional. This is the heart of a named map: the map
    itself, which maps places to ordered lists of datacenters.

    This is a nested key-value hash. At each level, the keys are location
    codes (continent, country, region, or city information depending on
    depth), and the values are either an ordered datacenter array (e.g. "[
    dc03, dc01, dc04 ]"), or a sub-hash containing a deeper level of
    distinction. At each layer, a special key named "default" is available,
    which sets the default for everything within the current scope. The
    top-level default itself defaults to the ordered list from "datacenters"
    in the normal case. If the entire "map" stanza is missing or empty, you
    just get the default behavior of "default". A datacenter array can also
    be empty, which implies that this location is mapped to receive no
    response data (the server will still respond to the query, and will not
    issue an NXDOMAIN. It will simply be a NODATA/NOERROR response like
    you'd get if there were no records of this type, but could be records of
    other types for the same name).

    The meaningful location keys at the top level are continent codes, of
    which there are primarily seven in MaxMind's databases: "AF" for Africa,
    "AS" for Asia, "NA" for North America, "SA" for South America, "EU" for
    Europe, "OC" for Oceania, and "AN" for Antarctica. There is also an
    eighth continent-level code which is, literally, "--". This is a sort of
    fallback "no information available" continent code, and it contains the
    special country codes "A1", "A2", "O1", and "--", which represent
    Anonymous Proxies, Satellite Providers, Other, and Unknown,
    respsectively.

    The next layer (the sub-hash beneath any continent code) maps ISO-3166-1
    2-letter country codes, which as with continents can map directly to
    datacenters, or to yet another recursive layer.

    The next two layers deep are for Region and City level information, only
    available from the Region and City type databases. The Region database
    type only provides region information for the US and Canada, using the
    standard local 2-letter abbrevations (e.g. AB for Alberta, OK for
    Oklahama). The City databases use those same region abbrevations for the
    US and Canada, but use either FIPS 10-4 2-letter codes or full region
    names for the rest of the world's regions (as detailed earlier in, and
    controlled by, the "city_region_names" option).

    The actual City names at the final layer appear to be encoded using some
    form of ISO8859-1 and/or CP1252 character set in the databases
    themselves, and your map entries will have to match byte-for-byte in the
    case of non-ASCII characters. May come up with a better solution for
    this down the road.

    There is also one other special key (aside from "default") available at
    all levels of the map hierarchy, a boolean named "skip_level", default
    "false". If set within the hierarchical "map" at any layer, it causes
    the next layer of detail to be skipped for this portion of the map. For
    example, setting this at the very top layer would mean that the top
    layer would contain country-level codes directly, without an enclosing
    continent-level hierarchy. Setting it within a country would mean that
    city names are used directly within that country, without an intervening
    layer of region names. This option is not aware of the "city_no_region"
    option, so e.g. setting that option and specifying "skip_level" at the
    country-level would result in no further information being available
    within that country (as "skip_level" would skip the remaining layer of
    city data).

CONFIGURATION - MAPS - CITY AUTO MODE

    "City-auto-mode" is a special mode of operation that automatically maps
    out the world to your datacenters based on coordinate math, so that you
    don't have to manually construct a complex hierarchical "map". It can
    still be mixed with "map" of course, allowing you to use auto-mode for
    only select geographic areas if you wish (or disabling it for select
    areas by specifying manual lists). The key parameter is
    "auto_dc_coords", which enables city-auto-mode.

    "auto_dc_coords = { ... }"
        Key-value hash, optional. If this option is specified, the whole
        map's basic mode of operation changes to "city-auto-mode". The
        contents of the hash must be a key for each datacenter named in
        "datacenters", with their values set to an array of "[lat, lon]" in
        decimal degree units. When city-auto-mode is enabled by this, the
        following configuration-validation changes occur from the default,
        static-mapping mode: the loaded GeoIP database(s) are required be
        City-level databases, and the special keyword "auto" becomes a legal
        "datacenter list" in the "map" stanza.

        With city-auto-mode enabled, the top-level map "default" defaults to
        "auto", but can be overriden with a manual list. For any location
        that maps to "auto", the coordinates specified here in
        "auto_dc_coords" will be compared with the coordinates from the
        City-level database(s) to determine an automatic distance-sorted
        datacenter list.

    "auto_dc_limit = N"
        Unsigned integer, optional, default 3. When city-auto-mode is in
        effect, this is the upper length limit for auto-generated lists. 3
        is a reasonable default even if you have a considerably longer set
        of datacenters, as this provides a primary as well as two fallbacks.
        Raising this to a large number in the presence of a long datacenter
        list will cause the set of unique result datacenter lists to
        increase rapidly, and thus reduce the optimization of the final
        result database for edns-client-subnet purposes. It's really not
        worth raising this value in almost any case, unless you really need
        to handle more than 3 random datacenters going offline at the same
        time and still have clients fail elsewhere. The value zero is
        treated as unlimited (highly un-recommended).

    Under city-auto-mode, when the top-level default is (explicitly or
    implicitly) "auto", there is still a fallback static ordering which is
    the whole ordered "datacenters" list, which is the normal static default
    "default" when not in city-auto-mode. This fallback is used when no
    location information is available at all (e.g. IPv6 client vs IPv4 GeoIP
    DB, Anonymous Proxies, etc).

MAP TESTING

    A binary program "gdnsd_geoip_test" is included. This can be used
    directly from the commandline, parses the relevant bits of your gdnsd
    config file for geoip map info, and then provides datacenter list
    results for IP address + map combinations supplied by the user. Useful
    for debugging your maps and testing the mapping of client IPs. It has a
    separate manpage gdnsd_geoip_test(1).

CONFIGURATION - RESOURCES

    Resource-level configuration within the "resources" stanza is nearly
    identical to the resources configuration of the metafo plugin, with all
    of the same basic behaviors about synthesizing or directly referencing
    the configuration of other plugins per-datacenter. Only the key
    differences will be covered here:

    *   metafo's per-resource "datacenters" array is replaced with "map =>
        mapname", which references one of the maps defined in the "maps"
        stanza, described in detail earlier. The set of defined datacenters
        in the "dcmap" stanza must match the total set of datacenters
        defined by the referenced map.

    *   metafo's restriction to just address-based (DYNA) results is lifted.
        If the per-resource sub-plugin configurations used by a geoip
        resource support CNAME (DYNC) results, then that geoip resource will
        also support DYNC results. Because there is no DYNC monitoring, only
        the first datacenter from each datacenter sub-list in the "map" will
        be used for the result; there is no failover, only geographic
        differentiation.

    *   For the common case of a single CNAME result per-datacenter, the
        CNAME data can be supplied directly as the singular string value of
        each datacenter in the "dcmap", without involving a sub-plugin at
        all.

META-PLUGIN INTERACTION

    Both of the meta-plugins ("metafo" and "geoip") can reference their own
    as well as each others' resources by direct reference within a "dcmap",
    so long as a resource does not directly refer to itself. This allows
    plugin-layering configurations such as geoip -> metafo -> weighted, or
    metafo -> geoip -> multifo, or even metafo -> metafo -> simplefo, etc.

    Bear in mind that once you begin using inter-meta-plugin references, you
    could create a reference loop. gdnsd does not currently detect or
    prevent such loops, and they will cause complete runtime failure when
    queried, probably by running out of stack space during recursion.

    Additionally, "geoip" can synthesize configuration for "metafo"
    resources, but the reverse does not hold; "metafo" cannot synthesize
    configuration for "geoip" resources.

ANOTHER CONFIG EXAMPLE

    A relatively-maximal example config, showing the interaction of valid
    "maps" and "resources" sections:

      zones => { example.com => {} }
      service_types => {
        xmpp_svc => { plugin => "tcp_connect", ... }
        www_svc => { plugin => "http_status", ... }
      }
      plugins => {
        geoip => {
          maps => {
            city_region_names => /etc/fips_include,
            my_prod_map => {
              geoip_db => /var/gdnsd/GeoIPCityv6.dat,
              geoip_db_v4_overlay => /var/gdnsd/GeoIPCity.dat,
              city_no_region => false, # default
              city_no_city => false, # default
              datacenters => [us-01, de-01, sg-01],
              map => {
                  # Hierarchy is Continent -> Country -> Region -> City
                  NA => {
                    US => {
                      skip_level => 1, # skip past region level
                      Dallas => [sg-01],
                    }
                  }
                  SA => [us-01, sg-01, de-01],
                  EU => {
                    default => [eu-01, us-01, sg-01],
                    CH => {
                      Geneve => {
                        Geneva => [sg-01],
                      }
                    }
                  }
                  AF => [eu-01, us-01, sg-01],
                  AS => [sg-01, eu-01, us-01],
                  OC => [sg-01, us-01, eu-01],
              }
              nets => {
                  10.0.0.0/8 => [ eu-01 ],
                  2001:DB8::/32 => [ us-01 ],
              }
            }
            my_auto_map => {
              geoip_db => /var/gdnsd/GeoIPCityv6.dat,
              geoip_db_v4_overlay => /var/gdnsd/GeoIPCity.dat,
              datacenters => [us-01, de-01, sg-01],
              auto_dc_coords => {
                 us-01 => [ 38.9, -77 ],
                 de-01 => [ 50.1, 8.7 ],
                 sg-01 => [ 1.3, 103.9 ],
              }
            }
          }
          resources => {
            prod_app => {
              map => my_auto_map
              # these two are inherited multifo config keys
              #  for all of the dcmap below:
              service_types => [www_svc, xmpp_svc],
              up_thresh => 0.4,
              dcmap => {
                us-01 => {
                  lb01 => 192.0.2.1,
                  lb02 => 192.0.2.2,
                  lb03 => 192.0.2.3,
                  lb01.v6 => 2001:DB8::1,
                  lb02.v6 => 2001:DB8::2,
                  lb03.v6 => 2001:DB8::3,
                },
                sg-01 => {
                  lb01 => 192.0.2.4,
                  lb02 => 192.0.2.5,
                  lb03 => 192.0.2.6,
                  lb01.v6 => 2001:DB8::4,
                  lb02.v6 => 2001:DB8::5,
                  lb03.v6 => 2001:DB8::6,
                },
                de-01 => {
                  lb01 => 192.0.2.7,
                  lb02 => 192.0.2.8,
                  lb03 => 192.0.2.9,
                  lb01.v6 => 2001:DB8::7,
                  lb02.v6 => 2001:DB8::8,
                  lb03.v6 => 2001:DB8::9,
                },
              }
            },
            prod_cdn => {
              map => my_prod_map,
              dcmap => {
                us-01 => us-cdn-provider.example.com.
                sg-01 => asia-cdn-provider.example.com.
                de-01 => europe-cdn-provider.example.com.
              }
            }
          }
        }
      }

    Example zonefile RRs:

      app     600 DYNA geoip!prod_app
      app.us  600 DYNA geoip!prod_app/us-01
      app.sg  600 DYNA geoip!prod_app/sg-01
      app.de  600 DYNA geoip!prod_app/de-01
      content 600 DYNC geoip!prod_cdn

SEE ALSO

    gdnsd-plugin-metafo(8), gdnsd_geoip_test(1), gdnsd.config(5),
    gdnsd.zonefile(5), gdnsd(8)

    The gdnsd manual.

COPYRIGHT AND LICENSE

    Copyright (c) 2012 Brandon L Black <blblack@gmail.com>

    This file is part of gdnsd.

    gdnsd is free software: you can redistribute it and/or modify it under
    the terms of the GNU General Public License as published by the Free
    Software Foundation, either version 3 of the License, or (at your
    option) any later version.

    gdnsd is distributed in the hope that it will be useful, but WITHOUT ANY
    WARRANTY; without even the implied warranty of MERCHANTABILITY or
    FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
    more details.

    You should have received a copy of the GNU General Public License along
    with gdnsd. If not, see <http://www.gnu.org/licenses/>.

