diff --git a/config/vufind/permissions.ini b/config/vufind/permissions.ini
index b7e68bdf34feb63cecf873f0fae365890709ebd5..04f20ea0d227e0451effd10d2c3d4f3499b05670 100644
--- a/config/vufind/permissions.ini
+++ b/config/vufind/permissions.ini
@@ -20,7 +20,9 @@
 ;
 ; ipRange     - Grant the permission to the single IP adresse or to the range.
 ;               Accepts a single IP adresse or a range with a minus character without
-;               blanks as seperator.
+;               blanks as separator. Also partial addresses can be used (e.g. 192.168
+;               denotes 192.168.0.0-192.168.255.255) and IPv6 addresses are also 
+;               supported (unless PHP is compiled with IPv6 disabled).
 ; ipRegEx     - Grant the permission to IP addresses matching the provided regular
 ;               expression(s). Accepts a string or an array; if an array is passed,
 ;               permission will be granted if ANY one of the expressions matches.
diff --git a/module/VuFind/config/module.config.php b/module/VuFind/config/module.config.php
index fd2f6bf1953e50ce850d0435168bc67903e99dd4..d048bd0ce0a070c8ad1c423af42ed8f7810d06ec 100644
--- a/module/VuFind/config/module.config.php
+++ b/module/VuFind/config/module.config.php
@@ -187,9 +187,10 @@ $config = [
             'VuFind\WorldCatUtils' => 'VuFind\Service\Factory::getWorldCatUtils',
         ],
         'invokables' => [
+            'VuFind\HierarchicalFacetHelper' => 'VuFind\Search\Solr\HierarchicalFacetHelper',
+            'VuFind\IpAddressUtils' => 'VuFind\Net\IpAddressUtils',
             'VuFind\Search'         => 'VuFindSearch\Service',
             'VuFind\Search\Memory'  => 'VuFind\Search\Memory',
-            'VuFind\HierarchicalFacetHelper' => 'VuFind\Search\Solr\HierarchicalFacetHelper'
         ],
         'initializers' => [
             'VuFind\ServiceManager\Initializer::initInstance',
diff --git a/module/VuFind/src/VuFind/Net/IpAddressUtils.php b/module/VuFind/src/VuFind/Net/IpAddressUtils.php
new file mode 100644
index 0000000000000000000000000000000000000000..b699075e21c8f696798574c5317516af389e22f0
--- /dev/null
+++ b/module/VuFind/src/VuFind/Net/IpAddressUtils.php
@@ -0,0 +1,120 @@
+<?php
+/**
+ * IP address utility functions.
+ *
+ * PHP version 5
+ *
+ * Copyright (C) The National Library of Finland 2015.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * @category VuFind2
+ * @package  Authorization
+ * @author   Ere Maijala <ere.maijala@helsinki.fi>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     http://www.vufind.org  Main Page
+ */
+namespace VuFind\Net;
+
+/**
+ * IP address utility functions.
+ *
+ * @category VuFind2
+ * @package  Authorization
+ * @author   Ere Maijala <ere.maijala@helsinki.fi>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     http://www.vufind.org  Main Page
+ */
+class IpAddressUtils
+{
+    /**
+     * Normalize an IP address or a beginning of it to an IPv6 address
+     *
+     * @param string  $ip  IP Address
+     * @param boolean $end Whether to make a partial address  an "end of range"
+     * address
+     *
+     * @return string|false Packed in_addr representation if successful, false
+     * for invalid IP address
+     */
+    public function normalizeIp($ip, $end = false)
+    {
+        // The check for AF_INET6 allows fallback to IPv4 only if necessary.
+        // Hopefully that's not necessary.
+        if (strpos($ip, ':') === false || !defined('AF_INET6')) {
+            // IPv4 address
+
+            // Append parts until complete
+            $addr = explode('.', $ip);
+            for ($i = count($addr); $i < 4; $i++) {
+                $addr[] = $end ? 255 : 0;
+            }
+
+            // Get rid of leading zeros etc.
+            $ip = implode('.', array_map('intval', $addr));
+            if (!defined('AF_INET6')) {
+                return inet_pton($ip);
+            }
+            $ip = "::$ip";
+        } else {
+            // IPv6 address
+
+            // Expand :: with '0:' as many times as necessary for a complete address
+            $count = substr_count($ip, ':');
+            if ($count < 8) {
+                $ip = str_replace(
+                    '::', ':' . str_repeat('0:', 8 - $count), $ip
+                );
+            }
+            if ($ip[0] == ':') {
+                $ip = "0$ip";
+            }
+            // Append ':0' or ':ffff' to complete the address
+            $count = substr_count($ip, ':');
+            if ($count < 7) {
+                $ip .= str_repeat($end ? ':ffff' : ':0', 7 - $count);
+            }
+        }
+        return inet_pton($ip);
+    }
+
+    /**
+     * Check if an IP address is in a range. Works also with mixed IPv4 and IPv6
+     * addresses.
+     *
+     * @param string $ip     IP address to check
+     * @param array  $ranges An array of IP addresses or address ranges to check
+     *
+     * @return bool
+     */
+    public function isInRange($ip, $ranges)
+    {
+        $ip = $this->normalizeIp($ip);
+        foreach ($ranges as $range) {
+            $ips = explode('-', $range, 2);
+            if (!isset($ips[1])) {
+                $ips[1] = $ips[0];
+            }
+            $ips[0] = $this->normalizeIp($ips[0]);
+            $ips[1] = $this->normalizeIp($ips[1], true);
+            if ($ips[0] === false || $ips[1] === false) {
+                continue;
+            }
+            if ($ip >= $ips[0] && $ip <= $ips[1]) {
+                return true;
+            }
+        }
+        return false;
+    }
+}
diff --git a/module/VuFind/src/VuFind/Role/PermissionProvider/Factory.php b/module/VuFind/src/VuFind/Role/PermissionProvider/Factory.php
index 8251bfa1ca90f54e9dac23662de87da6f1756a81..418c9d9e5c25e65071c3c184ee5116470ba4d94f 100644
--- a/module/VuFind/src/VuFind/Role/PermissionProvider/Factory.php
+++ b/module/VuFind/src/VuFind/Role/PermissionProvider/Factory.php
@@ -50,7 +50,10 @@ class Factory
      */
     public static function getIpRange(ServiceManager $sm)
     {
-        return new IpRange($sm->getServiceLocator()->get('Request'));
+        return new IpRange(
+            $sm->getServiceLocator()->get('Request'),
+            $sm->getServiceLocator()->get('VuFind\IpAddressUtils')
+        );
     }
 
     /**
diff --git a/module/VuFind/src/VuFind/Role/PermissionProvider/IpRange.php b/module/VuFind/src/VuFind/Role/PermissionProvider/IpRange.php
index bb910d1b794666d9b67e2cf3145dd46d835c4db9..848de695f85f53e462a8876afafd31e51c3c8788 100644
--- a/module/VuFind/src/VuFind/Role/PermissionProvider/IpRange.php
+++ b/module/VuFind/src/VuFind/Role/PermissionProvider/IpRange.php
@@ -5,6 +5,7 @@
  * PHP version 5
  *
  * Copyright (C) Villanova University 2007.
+ * Copyright (C) The National Library of Finland 2015.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2,
@@ -23,11 +24,13 @@
  * @package  Authorization
  * @author   Demian Katz <demian.katz@villanova.edu>
  * @author   Jochen Lienhard <lienhard@ub.uni-freiburg.de>
+ * @author   Ere Maijala <ere.maijala@helsinki.fi>
  * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
  * @link     http://www.vufind.org  Main Page
  */
 namespace VuFind\Role\PermissionProvider;
 use Zend\Http\PhpEnvironment\Request;
+use VuFind\Net\IpAddressUtils;
 
 /**
  * IpRange permission provider for VuFind.
@@ -36,6 +39,7 @@ use Zend\Http\PhpEnvironment\Request;
  * @package  Authorization
  * @author   Demian Katz <demian.katz@villanova.edu>
  * @author   Jochen Lienhard <lienhard@ub.uni-freiburg.de>
+ * @author   Ere Maijala <ere.maijala@helsinki.fi>
  * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
  * @link     http://www.vufind.org  Main Page
  */
@@ -48,14 +52,23 @@ class IpRange implements PermissionProviderInterface
      */
     protected $request;
 
+    /**
+     * IpAddressUtils object
+     *
+     * @var IpAddressUtils
+     */
+    protected $ipAddressUtils;
+
     /**
      * Constructor
      *
-     * @param Request $request Request object
+     * @param Request        $request Request object
+     * @param IpAddressUtils $ipUtils IpAddressUtils object
      */
-    public function __construct(Request $request)
+    public function __construct(Request $request, IpAddressUtils $ipUtils)
     {
         $this->request = $request;
+        $this->ipAddressUtils = $ipUtils;
     }
 
     /**
@@ -70,7 +83,7 @@ class IpRange implements PermissionProviderInterface
     {
         // Check if any regex matches....
         $ip = $this->request->getServer()->get('REMOTE_ADDR');
-        if ($this->checkIP($ip, $options)) {
+        if ($this->ipAddressUtils->isInRange($ip, (array)$options)) {
             // Match? Grant to all users (guest or logged in).
             return ['guest', 'loggedin'];
         }
@@ -78,40 +91,4 @@ class IpRange implements PermissionProviderInterface
         //  No match? No permissions.
         return [];
     }
-
-    /**
-     * Check if $remoteIP is within $rangeIP
-     *
-     * @param string $remoteIP ip address of the user
-     * @param array  $rangeIP  single ip or range of addresses
-     *
-     * @return bool
-     *
-     * @todo Implement IPv6 check
-     */
-    protected function checkIP($remoteIP, $rangeIP)
-    {
-        $mylist = [];
-        $count = 0;
-        $inList = false;
-        foreach ((array)$rangeIP as $range) {
-            if (preg_match('/-/', $range)) {
-                $tmp = preg_split('/-/', $range);
-                $mylist[$count]['start'] = $tmp[0];
-                $mylist[$count]['end'] = $tmp[1];
-            } else {
-                $mylist[$count]['start'] = $range;
-                $mylist[$count]['end'] = $range;
-            }
-            $count++;
-        }
-        foreach ($mylist as $check) {
-            if (ip2long($remoteIP) >= ip2long($check['start'])
-                && ip2long($remoteIP) <= ip2long($check['end'])
-            ) {
-                $inList = true;
-            }
-        }
-        return $inList;
-    }
-}
\ No newline at end of file
+}
diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Net/IpAddressUtilsTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Net/IpAddressUtilsTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..c16bf2faead74c58bf0ec1dd0b92fe3afbac9702
--- /dev/null
+++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Net/IpAddressUtilsTest.php
@@ -0,0 +1,116 @@
+<?php
+/**
+ * IpAddressUtils Test Class
+ *
+ * PHP version 5
+ *
+ * Copyright (C) The National Library of Finland 2015.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * @category VuFind2
+ * @package  Tests
+ * @author   Ere Maijala <ere.maijala@helsinki.fi>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     http://vufind.org/wiki/vufind2:unit_tests Wiki
+ */
+namespace VuFindTest\Net;
+use VuFind\Net\IpAddressUtils;
+
+/**
+ * IpAddressUtils Test Class
+ *
+ * @category VuFind2
+ * @package  Tests
+ * @author   Ere Maijala <ere.maijala@helsinki.fi>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     http://vufind.org/wiki/vufind2:unit_tests Wiki
+ */
+class IpAddressUtilsTest extends \VuFindTest\Unit\TestCase
+{
+    /**
+     * Test normalizeIp()
+     *
+     * @return void
+     */
+    public function testNormalizeIp()
+    {
+        $utils = new IpAddressUtils();
+        $this->assertEquals(
+            hex2bin('00000000000000000000000000000001'),
+            $utils->normalizeIp('::1')
+        );
+        $this->assertEquals(
+            hex2bin('0000000000000000000000007f000001'),
+            $utils->normalizeIp('127.0.0.1')
+        );
+        // Example from http://www.gestioip.net/docu/ipv6_address_examples.html
+        $this->assertEquals(
+            hex2bin('20010db80a0b12f00000000000000001'),
+            $utils->normalizeIp('2001:db8:a0b:12f0::1')
+        );
+    }
+
+    /**
+     * Test isInRange()
+     *
+     * @return void
+     */
+    public function testIsInRange()
+    {
+        $utils = new IpAddressUtils();
+        $this->assertFalse($utils->isInRange('127.0.0.1', ['127.0.0.0']));
+        $this->assertTrue($utils->isInRange('127.0.0.1', ['127.0.0.1']));
+        $this->assertTrue($utils->isInRange('127.0.0.1', ['127.0.0']));
+        $this->assertFalse($utils->isInRange('127.0.0.1', []));
+        $this->assertFalse($utils->isInRange('127.0.0.1', ['']));
+        $this->assertTrue($utils->isInRange('127.0.0.1', ['127.0.0.0-127.0.0.2']));
+        $this->assertTrue(
+            $utils->isInRange(
+                '127.0.0.1',
+                ['192.168.0.1-192.168.0.2', '127.0.0.0-127.0.0.2']
+            )
+        );
+        $this->assertFalse(
+            $utils->isInRange(
+                '127.0.0.1',
+                ['192.168.0.1-192.168.0.2', '127.0.0.2-127.0.0.4']
+            )
+        );
+        $this->assertTrue(
+            $utils->isInRange(
+                '2001:db8::ef90:1',
+                ['2001:db8::ef90:0-2001:db8::ef90:2']
+            )
+        );
+        $this->assertTrue(
+            $utils->isInRange(
+                '2001:db8::ef90:1',
+                ['2001:0db8::ef90:1']
+            )
+        );
+        $this->assertTrue(
+            $utils->isInRange(
+                '2001:db8::ef90:1',
+                ['2001:0db8']
+            )
+        );
+        $this->assertFalse(
+            $utils->isInRange(
+                '2001:db8::ef90:1',
+                ['2001:0db9']
+            )
+        );
+    }
+}