diff --git a/scripts/base/init-default.bro b/scripts/base/init-default.bro index fb3048165a..b12adf83a8 100644 --- a/scripts/base/init-default.bro +++ b/scripts/base/init-default.bro @@ -12,6 +12,7 @@ @load base/utils/directions-and-hosts @load base/utils/exec @load base/utils/files +@load base/utils/geoip-distance @load base/utils/numbers @load base/utils/paths @load base/utils/patterns diff --git a/scripts/base/utils/geoip-distance.bro b/scripts/base/utils/geoip-distance.bro new file mode 100644 index 0000000000..068e2a8b3e --- /dev/null +++ b/scripts/base/utils/geoip-distance.bro @@ -0,0 +1,26 @@ +##! Functions to calculate distance between two locations, based on GeoIP data. + +## Returns the distance between two IP addresses using the haversine formula, +## based on GeoIP database locations. Requires Bro to be built with libgeoip. +## +## a1: First IP address. +## +## a2: Second IP address. +## +## Returns: The distance between *a1* and *a2* in miles, or -1.0 if GeoIP data +## is not available for either of the IP addresses. +## +## .. bro:see:: haversine_distance lookup_location +function haversine_distance_ip(a1: addr, a2: addr): double + { + local loc1 = lookup_location(a1); + local loc2 = lookup_location(a2); + local miles: double; + + if (loc1?$latitude && loc1?$longitude && loc2?$latitude && loc2?$longitude) + miles = haversine_distance(loc1$latitude, loc1$longitude, loc2$latitude, loc2$longitude); + else + miles = -1.0; + + return miles; + } diff --git a/src/bro.bif b/src/bro.bif index ee3add586d..445b08fca6 100644 --- a/src/bro.bif +++ b/src/bro.bif @@ -3787,6 +3787,35 @@ function lookup_asn%(a: addr%) : count return new Val(0, TYPE_COUNT); %} +## Calculates distance between two geographic locations using the haversine +## formula. Latitudes and longitudes must be given in degrees, where southern +## hemispere latitudes are negative and western hemisphere longitudes are +## negative. +## +## lat1: Latitude (in degrees) of location 1. +## +## long1: Longitude (in degrees) of location 1. +## +## lat2: Latitude (in degrees) of location 2. +## +## long2: Longitude (in degrees) of location 2. +## +## Returns: Distance in miles. +## +## .. bro:see:: haversine_distance_ip +function haversine_distance%(lat1: double, long1: double, lat2: double, long2: double%): double + %{ + const double PI = 3.14159; + const double RADIUS = 3958.8; // Earth's radius in miles. + + double s1 = sin((lat2 - lat1) * PI/360); + double s2 = sin((long2 - long1) * PI/360); + double a = s1 * s1 + cos(lat1 * PI/180) * cos(lat2 * PI/180) * s2 * s2; + double distance = 2 * RADIUS * asin(sqrt(a)); + + return new Val(distance, TYPE_DOUBLE); + %} + ## Converts UNIX file permissions given by a mode to an ASCII string. ## ## mode: The permissions (an octal number like 0644 converted to decimal). diff --git a/testing/btest/Baseline/bifs.haversine_distance/out b/testing/btest/Baseline/bifs.haversine_distance/out new file mode 100644 index 0000000000..80707cf02e --- /dev/null +++ b/testing/btest/Baseline/bifs.haversine_distance/out @@ -0,0 +1,7 @@ +5.8481e+03 +5.8481e+03 +1.9193e-02 +1.5136e-02 +9.2419e-01 +1.2437e+04 +1.2437e+04 diff --git a/testing/btest/bifs/haversine_distance.bro b/testing/btest/bifs/haversine_distance.bro new file mode 100644 index 0000000000..b0a87a2c2d --- /dev/null +++ b/testing/btest/bifs/haversine_distance.bro @@ -0,0 +1,30 @@ +# +# @TEST-EXEC: bro -b %INPUT >out +# @TEST-EXEC: btest-diff out + +function test(la1: double, lo1: double, la2: double, lo2: double) + { + print fmt("%.4e", haversine_distance(la1, lo1, la2, lo2)); + } + +event bro_init() + { + # Test two arbitrary locations. + test(37.866798, -122.253601, 48.25, 11.65); + # Swap the order of locations to verify the distance doesn't change. + test(48.25, 11.65, 37.866798, -122.253601); + + # Distance of one second of latitude (crossing the equator). + test(.0001388889, 0, -.0001388889, 0); + + # Distance of one second of longitude (crossing the prime meridian). + test(38, 0.000138999, 38, -0.000138999); + + # Distance of one minute of longitude (test extreme longitude values). + test(38, 180, 38, -179.98333); + + # Two locations on opposite ends of the Earth. + test(45, -90, -45, 90); + # Same, but verify that extreme latitude values work. + test(90, 0, -90, 0); + }