Tag Archives: GeoIP

Kamailio Bytes – Geoip2

GeoIP2 allows simple Geo IP location parsing using mmdb files, to allow us to map IP addresses to geographic locations in standardized format.

Getting the GeoIP Data

MaxMind provide GeoIP2 formatted data ready for use, albeit with limited accuracy.

We need to download them from MaxMind and extract them for use, so let’s download the file:

#> wget https://geolite.maxmind.com/download/geoip/database/GeoLite2-City.tar.gz

Next we’ll extract the data:

#> tar -xzvf GeoLite2-City.tar.gz

Next we’ll add the below to our Kamailio config (replace the path to the GeoLite2-City.mmdb to your directory).

loadmodule "geoip2.so"
modparam("geoip2", "path", "/home/ubuntu/GeoLite2-City_20190709/GeoLite2-City.mmdb");

If you’re planning on using this in production you probably want to automate the pulling of this data on a regular basis and keep it in a different directory.

I’ve made a very simple example Kamailio config that shows off some of the features of GeoIP2’s logic and what can be shown, so let’s look at the basics of the module:

if(geoip2_match("$si", "src")){
                xlog("Packet received from IP $si");
                xlog("Country is: $gip2(src=>cc)\n");
}

If we put this at the top of our request_route block every time we recieve a new request we can see the country from which the packet came from.

Let’s take a look at the output of syslog (with my IP removed):

#> tail -f /var/log/syslog
ERROR: <script>: Packet received from IP 203.###.###.###
ERROR: <script>: Country is: AU
ERROR: <script>: City is:  Melbourne
ERROR: <script>: ZIP is:  3004
ERROR: <script>: Regc is:  VIC
ERROR: <script>: Regn is:  Victoria
ERROR: <script>: Metro Code is:  <null>

We can add a bunch more smarts to this and get back a bunch more variables, including city, ZIP code, Lat & Long (Approx), timezone, etc.

        if(geoip2_match("$si", "src")){
                xlog("Packet received from IP $si");
                xlog("Country is: $gip2(src=>cc)\n");
                xlog("City is:  $gip2(src=>city)");
                xlog("ZIP is:  $gip2(src=>zip)");
                xlog("Regc is:  $gip2(src=>regc)");
                xlog("Regn is:  $gip2(src=>regn)");
                xlog("Metro Code is:  $gip2(src=>metro)");

                if($gip2(src=>cc)=="AU"){
                        xlog("Traffic is from Australia");
                }
        }else{
                xlog("No GeoIP Match for  $si");
        }
#> tail -f /var/log/syslog
ERROR: <script>: Packet received from IP ###.###.###.###
ERROR: <script>: Country is: AU
ERROR: <script>: City is:  Melbourne
ERROR: <script>: ZIP is:  3004
ERROR: <script>: Regc is:  VIC
ERROR: <script>: Regn is:  Victoria
ERROR: <script>: Metro Code is:  <null>

Using GeoIP2 you could use different rate limits for domestic users vs overseas users, guess the dialling rules based on the location of the caller and generate alerts if accounts are used outside their standard areas.

We’ll touch upon this again in our next post on RTPengine where we’ll use an RTPengine closes to the area in which the traffic originates.

Full example config on GitHub here.

NBNco Australia network map

Kamailio Bytes – Routing to geo local RTPengine Instances with Kamailio

I’m a big fan of RTPengine, and I’ve written a bit about it in the past.

Let’s say we’re building an Australia wide VoIP network. It’s a big country with a lot of nothing in the middle. We’ve got a POP in each of Australia’s capital cities, and two core softswitch clusters, one in Melbourne and one in Sydney.

These two cores will work fine, but a call from a customer in Perth, WA to another customer in Perth, WA would mean their RTP stream will need to go across your inter-caps to Sydney or Melbourne only to route back to Perth.

That’s 3,500Km each way, which is going to lead to higher latency, wasted bandwidth and decreased customer experience.

What if we could have an RTPengine instance in our Perth POP, handling RTP proxying for our Perth customers? Another in Brisbane, Canberra etc, all while keeping our complex expensive core signalling in just the two locations?

RTPengine to the rescue!

Preparing our RTPEngine Instances

In each of our POPs we’ll spin up a box with RTPengine,

We’d set it up in the way outlined in this post,

The only thing we’d do differently is set the listen-ng value to be 0.0.0.0:2223 and the interface to be the IP of the box.

By setting the listen-ng value to 0.0.0.0:2223 it’ll mean that RTPengine’s management port will be bound to any IP, so we can remotely manage it via it’s ng-control protocol, using the rtpengine Kamailio module.

Naturally you’d limit access to port 2223 only to allowed devices inside your network.

Adding Multiple RTP Engines to Kamailio Database

After adding database functionality to our Kamailio instance as we covered in this post, we’ll just need to add the follow lines to our config:

loadmodule "rtpengine.so"
modparam("rtpengine", "db_url", DBURL)
modparam("rtpengine", "table_name", "rtpengine")
modparam("rtpengine", "setid_avp", "$avp(setid)")

Next we’ll need to add the details of each of our RTP engine instances to MySQL, I’ve used a different setid for each of the RTPengines. I’ve chosen to use the first digit of the Zipcode for that state (WA’s Zipcodes / Postcodes are in the format 6xxx while NSW postcodes are look like 2xxx), we’ll use this later when we select which RTPengine instances to use.

I’ve also added localhost with setid of 0, we’ll use this as our fallback route if it’s not coming from Australia.

INSERT INTO `rtpengine` (`id`, `setid`, `url`, `weight`, `disabled`, `stamp`) VALUES (NULL, '6', 'udp:WA-POP.rtpengine.nickvsnetworking.com:2223', '1', '0', NOW());
INSERT INTO `rtpengine` (`id`, `setid`, `url`, `weight`, `disabled`, `stamp`) VALUES (NULL, '2', 'udp:NSW-POP.rtpengine.nickvsnetworking.com:2223', '1', '0', NOW());
INSERT INTO `rtpengine` (`id`, `setid`, `url`, `weight`, `disabled`, `stamp`) VALUES (NULL, '0', 'udp:localhost:2223', '1', '0', NOW());

We’ll restart Kamailio, and check the status of the RTPengines we added:

#> kamcmd rtpengine.show all
{
        url: udp:NSW-POP.rtpengine.nickvsnetworking.com:2223
        set: 2
        index: 1
        weight: 1
        disabled: 0
        recheck_ticks: 0
}
{
        url: udp:WA-POP.rtpengine.nickvsnetworking.com:2223
        set: 6
        index: 3
        weight: 1
        disabled: 0
        recheck_ticks: 0
}
{
        url: udp:localhost:2223
        set: 6
        index: 3
        weight: 1
        disabled: 0
        recheck_ticks: 0
}

Bingo, we’re connected to three RTPengine instances,

Next up we’ll use the Geoip2 module to determine the source of the traffic and route to the correct, I’ve touched upon the Geoip2 module’s basic usage in the past, so if you’re not already familiar with it, read up on it’s usage and we’ll build upon that.

We’ll load GeoIP2 and run some checks in the initial request_route{} block to select the correct RTPengine instance:

        if(geoip2_match("$si", "src")){
                if($gip2(src=>cc)=="AU"){
                        $var(zip) =  $gip2(src=>zip);
                        $avp(setid) = $(var(zip){s.substr,0,1});
                        xlog("rtpengine setID is $avp(setid)");
                }else{
                        xlog("GeoIP not in Australia - Using default RTPengine instance");
                        set_rtpengine_set("0");
                }
        }else{
                xlog("No GeoIP Match - Using default RTPengine instance");
                set_rtpengine_set("0");
        }

In the above example if we have a match on source, and the Country code is Australia, the first digit of the ZIP / Postcode is extracted and assigned to the AVP “setid” so RTPengine knows which set ID to use.

In practice an INVITE from an IP in WA returns setID 6, and uses our RTPengine in WA, while one from NSW returns 2 and uses one in NSW. In production we’d need to setup rules for all the other states / territories, and generally have more than one RTPengine instance in each location (we can have multiple instances with the same setid).

Hopefully you’re starting to get an idea of the fun and relatively painless things you can achieve with RTPengine and Kamailio!