diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc
index 906a7c1..53ff064 100644
--- a/core/includes/bootstrap.inc
+++ b/core/includes/bootstrap.inc
@@ -1881,56 +1881,6 @@ function drupal_set_title($title = NULL, $output = CHECK_PLAIN) {
 }
 
 /**
- * Checks to see if an IP address has been blocked.
- *
- * Blocked IP addresses are stored in the database by default. However for
- * performance reasons we allow an override in settings.php. This allows us
- * to avoid querying the database at this critical stage of the bootstrap if
- * an administrative interface for IP address blocking is not required.
- *
- * @param $ip
- *   IP address to check.
- *
- * @return bool
- *   TRUE if access is denied, FALSE if access is allowed.
- */
-function drupal_is_denied($ip) {
-  // Because this function is called on every page request, we first check
-  // for an array of IP addresses in settings.php before querying the
-  // database.
-  $blocked_ips = variable_get('blocked_ips');
-  $denied = FALSE;
-  if (isset($blocked_ips) && is_array($blocked_ips)) {
-    $denied = in_array($ip, $blocked_ips);
-  }
-  // Only check if database.inc is loaded already. If
-  // $conf['page_cache_without_database'] = TRUE; is set in settings.php,
-  // then the database won't be loaded here so the IPs in the database
-  // won't be denied. However the user asked explicitly not to use the
-  // database and also in this case it's quite likely that the user relies
-  // on higher performance solutions like a firewall.
-  elseif (class_exists('Drupal\Core\Database\Database', FALSE)) {
-    $denied = (bool)db_query("SELECT 1 FROM {blocked_ips} WHERE ip = :ip", array(':ip' => $ip))->fetchField();
-  }
-  return $denied;
-}
-
-/**
- * Handles denied users.
- *
- * @param $ip
- *   IP address to check. Prints a message and exits if access is denied.
- */
-function drupal_block_denied($ip) {
-  // Deny access to blocked IP addresses - t() is not yet available.
-  if (drupal_is_denied($ip)) {
-    header($_SERVER['SERVER_PROTOCOL'] . ' 403 Forbidden');
-    print 'Sorry, ' . check_plain(ip_address()) . ' has been banned.';
-    exit();
-  }
-}
-
-/**
  * Returns a string of highly randomized bytes (over the full 8-bit range).
  *
  * This function is better than simply calling mt_rand() or any other built-in
@@ -2306,7 +2256,6 @@ function _drupal_bootstrap_page_cache() {
     $config = config('system.performance');
     $cache_enabled = $config->get('cache.page.enabled');
   }
-  drupal_block_denied(ip_address());
   // If there is no session cookie and cache is enabled (or forced), try
   // to serve a cached page.
   if (!isset($_COOKIE[session_name()]) && $cache_enabled) {
diff --git a/core/modules/ban/ban.admin.inc b/core/modules/ban/ban.admin.inc
new file mode 100644
index 0000000..7128ee0
--- /dev/null
+++ b/core/modules/ban/ban.admin.inc
@@ -0,0 +1,115 @@
+<?php
+
+/**
+ * @file
+ * Page callback file for the ban module.
+ */
+
+/**
+ * Menu callback. Displays banned IP addresses.
+ *
+ * @param $default_ip
+ *   Optional IP address to be passed on to drupal_get_form() for
+ *   use as the default value of the IP address form field.
+ */
+function ban_page($default_ip = '') {
+  $rows = array();
+  $header = array(t('banned IP addresses'), t('Operations'));
+  $result = db_query('SELECT * FROM {banned_ips}');
+  foreach ($result as $ip) {
+    $rows[] = array(
+      $ip->ip,
+      l(t('delete'), "admin/config/people/ban/delete/$ip->iid"),
+    );
+  }
+
+  $build['ban_ip_form'] = drupal_get_form('ban_ip_form', $default_ip);
+
+  $build['ban_ip_banning_table'] = array(
+    '#theme' => 'table',
+    '#header' => $header,
+    '#rows' => $rows,
+    '#empty' => t('No blocked IP addresses available.'),
+  );
+
+  return $build;
+}
+
+/**
+ * Defines the form for banning IP addresses.
+ *
+ * @see ban_ip_form_validate()
+ * @see ban_ip_form_submit()
+ *
+ * @ingroup forms
+ */
+function ban_ip_form($form, $form_state, $default_ip) {
+  $form['ip'] = array(
+    '#title' => t('IP address'),
+    '#type' => 'textfield',
+    '#size' => 48,
+    '#maxlength' => 40,
+    '#default_value' => $default_ip,
+    '#description' => t('Enter a valid IP address.'),
+  );
+  $form['actions'] = array('#type' => 'actions');
+  $form['actions']['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Add'),
+  );
+  return $form;
+}
+
+/**
+ * Form validation handler for the ban ip form.
+ */
+function ban_ip_form_validate($form, &$form_state) {
+  $ip = trim($form_state['values']['ip']);
+  if (db_query("SELECT * FROM {banned_ips} WHERE ip = :ip", array(':ip' => $ip))->fetchField()) {
+    form_set_error('ip', t('This IP address is already banned.'));
+  }
+  elseif ($ip == ip_address()) {
+    form_set_error('ip', t('You may not ban your own IP address.'));
+  }
+  elseif (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE) == FALSE) {
+    form_set_error('ip', t('Enter a valid IP address.'));
+  }
+}
+
+/**
+ * Form submit handler for the ban ip form.
+ */
+function ban_ip_form_submit($form, &$form_state) {
+  $ip = trim($form_state['values']['ip']);
+  db_insert('banned_ips')
+    ->fields(array('ip' => $ip))
+    ->execute();
+  drupal_set_message(t('The IP address %ip has been banned.', array('%ip' => $ip)));
+  $form_state['redirect'] = 'admin/config/people/ban';
+}
+
+/**
+ * IP deletion confirm page.
+ *
+ * @see ban_ip_delete_submit()
+ */
+function ban_ip_delete($form, &$form_state, $iid) {
+  $form['banned_ip'] = array(
+    '#type' => 'value',
+    '#value' => $iid,
+  );
+  return confirm_form($form, t('Are you sure you want to delete %ip?', array('%ip' => $iid['ip'])), 'admin/config/people/ban', t('This action cannot be undone.'), t('Delete'), t('Cancel'));
+}
+
+/**
+ * Processes ban_ip_delete form submissions.
+ */
+function ban_ip_delete_submit($form, &$form_state) {
+  $banned_ip = $form_state['values']['banned_ip'];
+  db_delete('banned_ips')
+    ->condition('iid', $banned_ip['iid'])
+    ->execute();
+  watchdog('user', 'Deleted %ip', array('%ip' => $banned_ip['ip']));
+  drupal_set_message(t('The IP address %ip was deleted.', array('%ip' => $banned_ip['ip'])));
+  $form_state['redirect'] = 'admin/config/people/ban';
+}
diff --git a/core/modules/ban/ban.info b/core/modules/ban/ban.info
new file mode 100644
index 0000000..e23331c
--- /dev/null
+++ b/core/modules/ban/ban.info
@@ -0,0 +1,6 @@
+name = Ban
+description = Enables banning of IP addresses.
+package = Core
+version = VERSION
+core = 8.x
+configure = admin/config/people/ban
diff --git a/core/modules/ban/ban.install b/core/modules/ban/ban.install
new file mode 100644
index 0000000..51812d4
--- /dev/null
+++ b/core/modules/ban/ban.install
@@ -0,0 +1,37 @@
+<?php
+
+/**
+ * @file
+ * Install, update and uninstall functions for the ban module.
+ */
+
+/**
+ * Implements hook_schema().
+ */
+function ban_schema() {
+  $schema = array();
+  $schema['banned_ips'] = array(
+    'description' => 'Stores banned IP addresses.',
+    'fields' => array(
+       'iid' => array(
+        'description' => 'Primary Key: unique ID for IP addresses.',
+        'type' => 'serial',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+      ),
+      'ip' => array(
+        'description' => 'IP address',
+        'type' => 'varchar',
+        'length' => 40,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+    ),
+    'indexes' => array(
+      'ip' => array('ip'),
+    ),
+    'primary key' => array('iid'),
+  );
+
+  return $schema;
+}
diff --git a/core/modules/ban/ban.module b/core/modules/ban/ban.module
new file mode 100644
index 0000000..d233232
--- /dev/null
+++ b/core/modules/ban/ban.module
@@ -0,0 +1,145 @@
+<?php
+
+/**
+ * @file
+ * Enables banning of IP addresses.
+ */
+
+/**
+ * Implements hook_help().
+ */
+function ban_help($path, $arg) {
+  switch ($path) {
+    case 'admin/config/people/ban':
+      return '<p>' . t('IP addresses listed here are banned from your site. Banned addresses are completely forbidden from accessing the site and instead see a brief message explaining the situation.') . '</p>';
+  }
+}
+
+/**
+ * Implements hook_permission().
+ */
+function ban_permission() {
+  return array(
+    'ban IP addresses' => array(
+      'title' => t('Ban IP addresses'),
+    ),
+  );
+}
+
+/**
+ * Implements hook_menu().
+ */
+function ban_menu() {
+  $items['admin/config/people/ban'] = array(
+    'title' => 'IP address bans',
+    'description' => 'Manage banned IP addresses.',
+    'page callback' => 'ban_page',
+    'access arguments' => array('ban IP addresses'),
+    'file' => 'ban.admin.inc',
+    'weight' => 10,
+  );
+  $items['admin/config/people/ban/delete/%ban_ip'] = array(
+    'title' => 'Delete IP address',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('ban_ip_delete', 5),
+    'access arguments' => array('ban IP addresses'),
+    'file' => 'ban.admin.inc',
+  );
+
+  return $items;
+}
+
+/**
+ * Implements hook_boot().
+ */
+function ban_boot() {
+  ban_block_denied(ip_address());
+}
+
+/**
+ * Checks to see if an IP address has been blocked.
+ *
+ * Blocked IP addresses are stored in the database by default. However for
+ * performance reasons we allow an override in variables.
+ *
+ * @param $ip
+ *   IP address to check.
+ *
+ * @return bool
+ *   TRUE if access is denied, FALSE if access is allowed.
+ */
+function ban_is_denied($ip) {
+  // Because this function is called on every page request, we first check
+  // for an array of IP addresses in settings.php before querying the
+  // database.
+  $blocked_ips = variable_get('blocked_ips');
+  $denied = FALSE;
+  if (isset($blocked_ips) && is_array($blocked_ips)) {
+    $denied = in_array($ip, $blocked_ips);
+  }
+  // Only check if database.inc is loaded already. If
+  // $conf['page_cache_without_database'] = TRUE; is set in settings.php,
+  // then the database won't be loaded here so the IPs in the database
+  // won't be denied. However the user asked explicitly not to use the
+  // database and also in this case it's quite likely that the user relies
+  // on higher performance solutions like a firewall.
+  else {
+    $denied = (bool)db_query("SELECT 1 FROM {banned_ips} WHERE ip = :ip", array(':ip' => $ip))->fetchField();
+  }
+  return $denied;
+}
+
+/**
+ * Handles denied users.
+ *
+ * @param $ip
+ *   IP address to check. Prints a message and exits if access is denied.
+ */
+function ban_block_denied($ip) {
+  // Deny access to blocked IP addresses - t() is not yet available.
+  if (ban_is_denied($ip)) {
+    header($_SERVER['SERVER_PROTOCOL'] . ' 403 Forbidden');
+    print 'Sorry, ' . check_plain(ip_address()) . ' has been banned.';
+    exit();
+  }
+}
+
+/**
+ * Retrieves a banned IP address from the database.
+ *
+ * @param $iid integer
+ *   The ID of the banned IP address to retrieve.
+ *
+ * @return
+ *   The banned IP address from the database as an array.
+ */
+function ban_ip_load($iid) {
+  return db_query("SELECT * FROM {banned_ips} WHERE iid = :iid", array(':iid' => $iid))->fetchAssoc();
+}
+
+/**
+ * Implements hook_action_info().
+ */
+function ban_action_info() {
+  return array(
+    'ban_ip_action' => array(
+      'type' => 'user',
+      'label' => t('Ban IP address of current user'),
+      'configurable' => FALSE,
+      'triggers' => array('any'),
+    ),
+  );
+}
+
+/**
+ * Bans the current user's IP address.
+ *
+ * @ingroup actions
+ */
+function ban_ip_action() {
+  $ip = ip_address();
+  db_insert('blocked_ips')
+    ->fields(array('ip' => $ip))
+    ->execute();
+  watchdog('action', 'Banned IP address %ip', array('%ip' => $ip));
+}
diff --git a/core/modules/ban/lib/Drupal/ban/Tests/IpAddressBlockingTest.php b/core/modules/ban/lib/Drupal/ban/Tests/IpAddressBlockingTest.php
new file mode 100644
index 0000000..7644532
--- /dev/null
+++ b/core/modules/ban/lib/Drupal/ban/Tests/IpAddressBlockingTest.php
@@ -0,0 +1,88 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\ban\Tests\IpAddressBlockingTest.
+ */
+
+namespace Drupal\ban\Tests;
+
+use Drupal\simpletest\WebTestBase;
+
+class IpAddressBlockingTest extends WebTestBase {
+  protected $blocking_user;
+
+  /**
+   * Implement getInfo().
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'IP address banning',
+      'description' => 'Test IP address banning.',
+      'group' => 'Ban'
+    );
+  }
+
+  /**
+   * Implement setUp().
+   */
+  function setUp() {
+    parent::setUp('ban');
+  }
+
+  /**
+   * Test a variety of user input to confirm correct validation and saving of data.
+   */
+  function testIPAddressValidation() {
+    // Create user.
+    $this->banning_user = $this->drupalCreateUser(array('ban IP addresses'));
+    $this->drupalLogin($this->banning_user);
+    $this->drupalGet('admin/config/people/ban');
+
+    // Ban a valid IP address.
+    $edit = array();
+    $edit['ip'] = '192.168.1.1';
+    $this->drupalPost('admin/config/people/ban', $edit, t('Add'));
+    $ip = db_query("SELECT iid from {banned_ips} WHERE ip = :ip", array(':ip' => $edit['ip']))->fetchField();
+    $this->assertTrue($ip, t('IP address found in database.'));
+    $this->assertRaw(t('The IP address %ip has been banned.', array('%ip' => $edit['ip'])), 'IP address was banned.');
+
+    // Try to block an IP address that's already blocked.
+    $edit = array();
+    $edit['ip'] = '192.168.1.1';
+    $this->drupalPost('admin/config/people/ban', $edit, t('Add'));
+    $this->assertText(t('This IP address is already banned.'));
+
+    // Try to block a reserved IP address.
+    $edit = array();
+    $edit['ip'] = '255.255.255.255';
+    $this->drupalPost('admin/config/people/ban', $edit, t('Add'));
+    $this->assertText(t('Enter a valid IP address.'));
+
+    // Try to block a reserved IP address.
+    $edit = array();
+    $edit['ip'] = 'test.example.com';
+    $this->drupalPost('admin/config/people/ban', $edit, t('Add'));
+    $this->assertText(t('Enter a valid IP address.'));
+
+    // Submit an empty form.
+    $edit = array();
+    $edit['ip'] = '';
+    $this->drupalPost('admin/config/people/ban', $edit, t('Add'));
+    $this->assertText(t('Enter a valid IP address.'));
+
+    // Pass an IP address as a URL parameter and submit it.
+    $submit_ip = '1.2.3.4';
+    $this->drupalPost('admin/config/people/ban/' . $submit_ip, NULL, t('Add'));
+    $ip = db_query("SELECT iid from {banned_ips} WHERE ip = :ip", array(':ip' => $submit_ip))->fetchField();
+    $this->assertTrue($ip, 'IP address found in database');
+    $this->assertRaw(t('The IP address %ip has been banned.', array('%ip' => $submit_ip)), 'IP address was banned.');
+
+    // Submit your own IP address. This fails, although it works when testing manually.
+    // TODO: on some systems this test fails due to a bug or inconsistency in cURL.
+    // $edit = array();
+    // $edit['ip'] = ip_address();
+    // $this->drupalPost('admin/config/people/ban', $edit, t('Save'));
+    // $this->assertText(t('You may not ban your own IP address.'));
+  }
+}
diff --git a/core/modules/statistics/lib/Drupal/statistics/Tests/StatisticsBlockVisitorsTest.php b/core/modules/statistics/lib/Drupal/statistics/Tests/StatisticsBlockVisitorsTest.php
index c8786ce..87379f9 100644
--- a/core/modules/statistics/lib/Drupal/statistics/Tests/StatisticsBlockVisitorsTest.php
+++ b/core/modules/statistics/lib/Drupal/statistics/Tests/StatisticsBlockVisitorsTest.php
@@ -13,8 +13,8 @@ namespace Drupal\statistics\Tests;
 class StatisticsBlockVisitorsTest extends StatisticsTestBase {
   public static function getInfo() {
     return array(
-      'name' => 'Top visitor blocking',
-      'description' => 'Tests blocking of IP addresses via the top visitors report.',
+      'name' => 'Top visitor banning',
+      'description' => 'Tests banning of IP addresses via the top visitors report.',
       'group' => 'Statistics'
     );
   }
@@ -27,32 +27,32 @@ class StatisticsBlockVisitorsTest extends StatisticsTestBase {
     $test_ip_address = '192.168.1.1';
 
     // Verify the IP address from accesslog appears on the top visitors page
-    // and that a 'block IP address' link is displayed.
+    // and that a 'ban IP address' link is displayed.
     $this->drupalLogin($this->blocking_user);
     $this->drupalGet('admin/reports/visitors');
-    $this->assertText($test_ip_address, t('IP address found.'));
-    $this->assertText(t('block IP address'), t('Block IP link displayed'));
+    $this->assertText($test_ip_address, 'IP address found.');
+    $this->assertText(t('ban IP address'), 'Ban IP link displayed');
 
     // Block the IP address.
-    $this->clickLink('block IP address');
-    $this->assertText(t('IP address blocking'), t('IP blocking page displayed.'));
+    $this->clickLink('ban IP address');
+    $this->assertText(t('IP address bans'), 'IP banning page displayed.');
     $edit = array();
     $edit['ip'] = $test_ip_address;
-    $this->drupalPost('admin/config/people/ip-blocking', $edit, t('Add'));
-    $ip = db_query("SELECT iid from {blocked_ips} WHERE ip = :ip", array(':ip' => $edit['ip']))->fetchField();
-    $this->assertNotEqual($ip, FALSE, t('IP address found in database'));
-    $this->assertRaw(t('The IP address %ip has been blocked.', array('%ip' => $edit['ip'])), t('IP address was blocked.'));
+    $this->drupalPost('admin/config/people/ban', $edit, t('Add'));
+    $ip = db_query("SELECT iid from {banned_ips} WHERE ip = :ip", array(':ip' => $edit['ip']))->fetchField();
+    $this->assertNotEqual($ip, FALSE, 'IP address found in database');
+    $this->assertRaw(t('The IP address %ip has been banned.', array('%ip' => $edit['ip'])), 'IP address was banned.');
 
     // Verify that the block/unblock link on the top visitors page has been
     // altered.
     $this->drupalGet('admin/reports/visitors');
-    $this->assertText(t('unblock IP address'), t('Unblock IP address link displayed'));
+    $this->assertText(t('unban IP address'), 'Unban IP address link displayed');
 
     // Unblock the IP address.
-    $this->clickLink('unblock IP address');
-    $this->assertRaw(t('Are you sure you want to delete %ip?', array('%ip' => $test_ip_address)), t('IP address deletion confirmation found.'));
+    $this->clickLink('unban IP address');
+    $this->assertRaw(t('Are you sure you want to delete %ip?', array('%ip' => $test_ip_address)), 'IP address deletion confirmation found.');
     $edit = array();
-    $this->drupalPost('admin/config/people/ip-blocking/delete/1', NULL, t('Delete'));
-    $this->assertRaw(t('The IP address %ip was deleted.', array('%ip' => $test_ip_address)), t('IP address deleted.'));
+    $this->drupalPost('admin/config/people/ban/delete/1', NULL, t('Delete'));
+    $this->assertRaw(t('The IP address %ip was deleted.', array('%ip' => $test_ip_address)), 'IP address deleted.');
   }
 }
diff --git a/core/modules/statistics/lib/Drupal/statistics/Tests/StatisticsTestBase.php b/core/modules/statistics/lib/Drupal/statistics/Tests/StatisticsTestBase.php
index b7e2d31..99617dc 100644
--- a/core/modules/statistics/lib/Drupal/statistics/Tests/StatisticsTestBase.php
+++ b/core/modules/statistics/lib/Drupal/statistics/Tests/StatisticsTestBase.php
@@ -34,7 +34,7 @@ abstract class StatisticsTestBase extends WebTestBase {
       'access administration pages',
       'access site reports',
       'access statistics',
-      'block IP addresses',
+      'ban IP addresses',
       'administer blocks',
       'administer statistics',
       'administer users',
diff --git a/core/modules/statistics/statistics.admin.inc b/core/modules/statistics/statistics.admin.inc
index 176e2af..4308bba 100644
--- a/core/modules/statistics/statistics.admin.inc
+++ b/core/modules/statistics/statistics.admin.inc
@@ -131,7 +131,7 @@ function statistics_top_visitors() {
   $query = db_select('accesslog', 'a', array('target' => 'slave'))
     ->extend('Drupal\Core\Database\Query\PagerSelectExtender')
     ->extend('Drupal\Core\Database\Query\TableSortExtender');
-  $query->leftJoin('blocked_ips', 'bl', 'a.hostname = bl.ip');
+  $query->leftJoin('banned_ips', 'b', 'a.hostname = b.ip');
   $query->leftJoin('users', 'u', 'a.uid = u.uid');
 
   $query->addExpression('COUNT(a.uid)', 'hits');
@@ -139,11 +139,11 @@ function statistics_top_visitors() {
   $query
     ->fields('a', array('uid', 'hostname'))
     ->fields('u', array('name'))
-    ->fields('bl', array('iid'))
+    ->fields('b', array('iid'))
     ->groupBy('a.hostname')
     ->groupBy('a.uid')
     ->groupBy('u.name')
-    ->groupBy('bl.iid')
+    ->groupBy('b.iid')
     ->limit(30)
     ->orderByHeader($header);
 
@@ -157,8 +157,8 @@ function statistics_top_visitors() {
   $rows = array();
   $destination = drupal_get_destination();
   foreach ($result as $account) {
-    $ban_link = $account->iid ? l(t('unblock IP address'), "admin/config/people/ip-blocking/delete/$account->iid", array('query' => $destination)) : l(t('block IP address'), "admin/config/people/ip-blocking/$account->hostname", array('query' => $destination));
-    $rows[] = array($account->hits, ($account->uid ? theme('username', array('account' => $account)) : $account->hostname), format_interval(round($account->total / 1000)), (user_access('block IP addresses') && !$account->uid) ? $ban_link : '');
+    $ban_link = $account->iid ? l(t('unban IP address'), "admin/config/people/ban/delete/$account->iid", array('query' => $destination)) : l(t('ban IP address'), "admin/config/people/ban/$account->hostname", array('query' => $destination));
+    $rows[] = array($account->hits, ($account->uid ? theme('username', array('account' => $account)) : $account->hostname), format_interval(round($account->total / 1000)), (user_access('ban IP addresses') && !$account->uid) ? $ban_link : '');
   }
 
   drupal_set_title(t('Top visitors in the past %interval', array('%interval' => format_interval(config('statistics.settings')->get('access_log.max_lifetime')))), PASS_THROUGH);
diff --git a/core/modules/statistics/statistics.info b/core/modules/statistics/statistics.info
index 4b18b8a..ce5b2dd 100644
--- a/core/modules/statistics/statistics.info
+++ b/core/modules/statistics/statistics.info
@@ -4,3 +4,4 @@ package = Core
 version = VERSION
 core = 8.x
 configure = admin/config/system/statistics
+dependencies[] = ban
diff --git a/core/modules/system/lib/Drupal/system/Tests/System/IpAddressBlockingTest.php b/core/modules/system/lib/Drupal/system/Tests/System/IpAddressBlockingTest.php
deleted file mode 100644
index 6d752a2..0000000
--- a/core/modules/system/lib/Drupal/system/Tests/System/IpAddressBlockingTest.php
+++ /dev/null
@@ -1,89 +0,0 @@
-<?php
-
-/**
- * @file
- * Definition of Drupal\system\Tests\System\IpAddressBlockingTest.
- */
-
-namespace Drupal\system\Tests\System;
-
-use Drupal\simpletest\WebTestBase;
-
-class IpAddressBlockingTest extends WebTestBase {
-  protected $blocking_user;
-
-  /**
-   * Implement getInfo().
-   */
-  public static function getInfo() {
-    return array(
-      'name' => 'IP address blocking',
-      'description' => 'Test IP address blocking.',
-      'group' => 'System'
-    );
-  }
-
-  /**
-   * Implement setUp().
-   */
-  function setUp() {
-    parent::setUp();
-
-    // Create user.
-    $this->blocking_user = $this->drupalCreateUser(array('block IP addresses'));
-    $this->drupalLogin($this->blocking_user);
-  }
-
-  /**
-   * Test a variety of user input to confirm correct validation and saving of data.
-   */
-  function testIPAddressValidation() {
-    $this->drupalGet('admin/config/people/ip-blocking');
-
-    // Block a valid IP address.
-    $edit = array();
-    $edit['ip'] = '192.168.1.1';
-    $this->drupalPost('admin/config/people/ip-blocking', $edit, t('Add'));
-    $ip = db_query("SELECT iid from {blocked_ips} WHERE ip = :ip", array(':ip' => $edit['ip']))->fetchField();
-    $this->assertTrue($ip, t('IP address found in database.'));
-    $this->assertRaw(t('The IP address %ip has been blocked.', array('%ip' => $edit['ip'])), t('IP address was blocked.'));
-
-    // Try to block an IP address that's already blocked.
-    $edit = array();
-    $edit['ip'] = '192.168.1.1';
-    $this->drupalPost('admin/config/people/ip-blocking', $edit, t('Add'));
-    $this->assertText(t('This IP address is already blocked.'));
-
-    // Try to block a reserved IP address.
-    $edit = array();
-    $edit['ip'] = '255.255.255.255';
-    $this->drupalPost('admin/config/people/ip-blocking', $edit, t('Add'));
-    $this->assertText(t('Enter a valid IP address.'));
-
-    // Try to block a reserved IP address.
-    $edit = array();
-    $edit['ip'] = 'test.example.com';
-    $this->drupalPost('admin/config/people/ip-blocking', $edit, t('Add'));
-    $this->assertText(t('Enter a valid IP address.'));
-
-    // Submit an empty form.
-    $edit = array();
-    $edit['ip'] = '';
-    $this->drupalPost('admin/config/people/ip-blocking', $edit, t('Add'));
-    $this->assertText(t('Enter a valid IP address.'));
-
-    // Pass an IP address as a URL parameter and submit it.
-    $submit_ip = '1.2.3.4';
-    $this->drupalPost('admin/config/people/ip-blocking/' . $submit_ip, NULL, t('Add'));
-    $ip = db_query("SELECT iid from {blocked_ips} WHERE ip = :ip", array(':ip' => $submit_ip))->fetchField();
-    $this->assertTrue($ip, t('IP address found in database'));
-    $this->assertRaw(t('The IP address %ip has been blocked.', array('%ip' => $submit_ip)), t('IP address was blocked.'));
-
-    // Submit your own IP address. This fails, although it works when testing manually.
-     // TODO: on some systems this test fails due to a bug or inconsistency in cURL.
-     // $edit = array();
-     // $edit['ip'] = ip_address();
-     // $this->drupalPost('admin/config/people/ip-blocking', $edit, t('Save'));
-     // $this->assertText(t('You may not block your own IP address.'));
-  }
-}
diff --git a/core/modules/system/system.admin.inc b/core/modules/system/system.admin.inc
index 2bf4343..c180ad1 100644
--- a/core/modules/system/system.admin.inc
+++ b/core/modules/system/system.admin.inc
@@ -1372,111 +1372,6 @@ function system_modules_uninstall_submit($form, &$form_state) {
 }
 
 /**
- * Menu callback. Display blocked IP addresses.
- *
- * @param $default_ip
- *   Optional IP address to be passed on to drupal_get_form() for
- *   use as the default value of the IP address form field.
- */
-function system_ip_blocking($default_ip = '') {
-  $rows = array();
-  $header = array(t('Blocked IP addresses'), t('Operations'));
-  $result = db_query('SELECT * FROM {blocked_ips}');
-  foreach ($result as $ip) {
-    $rows[] = array(
-      $ip->ip,
-      l(t('delete'), "admin/config/people/ip-blocking/delete/$ip->iid"),
-    );
-  }
-
-  $build['system_ip_blocking_form'] = drupal_get_form('system_ip_blocking_form', $default_ip);
-
-  $build['system_ip_blocking_table'] = array(
-    '#theme' => 'table',
-    '#header' => $header,
-    '#rows' => $rows,
-    '#empty' => t('No blocked IP addresses available.'),
-  );
-
-  return $build;
-}
-
-/**
- * Define the form for blocking IP addresses.
- *
- * @ingroup forms
- * @see system_ip_blocking_form_validate()
- * @see system_ip_blocking_form_submit()
- */
-function system_ip_blocking_form($form, $form_state, $default_ip) {
-  $form['ip'] = array(
-    '#title' => t('IP address'),
-    '#type' => 'textfield',
-    '#size' => 48,
-    '#maxlength' => 40,
-    '#default_value' => $default_ip,
-    '#description' => t('Enter a valid IP address.'),
-  );
-  $form['actions'] = array('#type' => 'actions');
-  $form['actions']['submit'] = array(
-    '#type' => 'submit',
-    '#value' => t('Add'),
-  );
-  $form['#submit'][] = 'system_ip_blocking_form_submit';
-  $form['#validate'][] = 'system_ip_blocking_form_validate';
-  return $form;
-}
-
-function system_ip_blocking_form_validate($form, &$form_state) {
-  $ip = trim($form_state['values']['ip']);
-  if (db_query("SELECT * FROM {blocked_ips} WHERE ip = :ip", array(':ip' => $ip))->fetchField()) {
-    form_set_error('ip', t('This IP address is already blocked.'));
-  }
-  elseif ($ip == ip_address()) {
-    form_set_error('ip', t('You may not block your own IP address.'));
-  }
-  elseif (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE) == FALSE) {
-    form_set_error('ip', t('Enter a valid IP address.'));
-  }
-}
-
-function system_ip_blocking_form_submit($form, &$form_state) {
-  $ip = trim($form_state['values']['ip']);
-  db_insert('blocked_ips')
-    ->fields(array('ip' => $ip))
-    ->execute();
-  drupal_set_message(t('The IP address %ip has been blocked.', array('%ip' => $ip)));
-  $form_state['redirect'] = 'admin/config/people/ip-blocking';
-  return;
-}
-
-/**
- * IP deletion confirm page.
- *
- * @see system_ip_blocking_delete_submit()
- */
-function system_ip_blocking_delete($form, &$form_state, $iid) {
-  $form['blocked_ip'] = array(
-    '#type' => 'value',
-    '#value' => $iid,
-  );
-  return confirm_form($form, t('Are you sure you want to delete %ip?', array('%ip' => $iid['ip'])), 'admin/config/people/ip-blocking', t('This action cannot be undone.'), t('Delete'), t('Cancel'));
-}
-
-/**
- * Process system_ip_blocking_delete form submissions.
- */
-function system_ip_blocking_delete_submit($form, &$form_state) {
-  $blocked_ip = $form_state['values']['blocked_ip'];
-  db_delete('blocked_ips')
-    ->condition('iid', $blocked_ip['iid'])
-    ->execute();
-  watchdog('user', 'Deleted %ip', array('%ip' => $blocked_ip['ip']));
-  drupal_set_message(t('The IP address %ip was deleted.', array('%ip' => $blocked_ip['ip'])));
-  $form_state['redirect'] = 'admin/config/people/ip-blocking';
-}
-
-/**
  * Form builder; The general site information form.
  *
  * @ingroup forms
diff --git a/core/modules/system/system.install b/core/modules/system/system.install
index ac983aa..4328e20 100644
--- a/core/modules/system/system.install
+++ b/core/modules/system/system.install
@@ -626,29 +626,6 @@ function system_schema() {
     ),
   );
 
-  $schema['blocked_ips'] = array(
-    'description' => 'Stores blocked IP addresses.',
-    'fields' => array(
-       'iid' => array(
-        'description' => 'Primary Key: unique ID for IP addresses.',
-        'type' => 'serial',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-      ),
-      'ip' => array(
-        'description' => 'IP address',
-        'type' => 'varchar',
-        'length' => 40,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-    ),
-    'indexes' => array(
-      'blocked_ip' => array('ip'),
-    ),
-    'primary key' => array('iid'),
-  );
-
   $schema['cache_tags'] = array(
     'description' => 'Cache table for tracking cache tags related to the cache bin.',
     'fields' => array(
@@ -1894,6 +1871,42 @@ function system_update_8019() {
 }
 
 /**
+ * Enable the ban module if blocked ips exists or statistics are enabled.
+ */
+function system_update_8020() {
+  $blocked_ips_exists = (bool) db_query_range("SELECT 1 FROM {blocked_ips}", 0, 1)->fetchField();
+  if ($blocked_ips_exists || module_exists('statistics')) {
+    module_enable(array('ban'));
+    // Copy the blocked ips from the old {blocked_ips} table to the new
+    // {banned_ips} table.
+    $result = db_query("SELECT ip FROM {blocked_ips}");
+    $query = db_insert('banned_ips')->fields(array('ip'));
+    foreach ($result as $blocked_ip) {
+      $query->values(array(
+        'ip' => $blocked_ip->ip,
+      ));
+    }
+    $query->execute();
+
+    // Update old permissions to new provided by ban module.
+    db_update('role_permission')
+      ->fields(array(
+        'permission' => 'ban IP addresses',
+        'module' => 'ban',
+      ))
+      ->condition('permission', 'block IP addresses')
+      ->execute();
+
+    // Remove legacy action.
+    db_delete('actions')
+      ->condition('aid', 'system_block_ip_action')
+      ->execute();
+  }
+  // Drop old table.
+  db_drop_table('blocked_ips');
+}
+
+/**
  * @} End of "defgroup updates-7.x-to-8.x".
  * The next series of updates should start at 9000.
  */
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 922b8a7..f99feee 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -138,8 +138,6 @@ function system_help($path, $arg) {
       return $output;
     case 'admin/config/system/actions/configure':
       return t('An advanced action offers additional configuration options which may be filled out below. Changing the <em>Description</em> field is recommended in order to better identify the precise action taking place.');
-    case 'admin/config/people/ip-blocking':
-      return '<p>' . t('IP addresses listed here are blocked from your site. Blocked addresses are completely forbidden from accessing the site and instead see a brief message explaining the situation.') . '</p>';
     case 'admin/reports/status':
       return '<p>' . t("Here you can find a short overview of your site's parameters as well as any problems detected with your installation. It may be useful to copy and paste this information into support requests filed on drupal.org's support forums and project issue queues. Before filing a support request, ensure that your web server meets the <a href=\"@system-requirements\">system requirements.</a>", array('@system-requirements' => 'http://drupal.org/requirements')) . '</p>';
   }
@@ -243,9 +241,6 @@ function system_permission() {
     'access site reports' => array(
       'title' => t('View site reports'),
     ),
-    'block IP addresses' => array(
-      'title' => t('Block IP addresses'),
-    ),
   );
 }
 
@@ -759,23 +754,6 @@ function system_menu() {
     'file' => 'system.admin.inc',
   );
 
-  // IP address blocking.
-  $items['admin/config/people/ip-blocking'] = array(
-    'title' => 'IP address blocking',
-    'description' => 'Manage blocked IP addresses.',
-    'page callback' => 'system_ip_blocking',
-    'access arguments' => array('block IP addresses'),
-    'file' => 'system.admin.inc',
-    'weight' => 10,
-  );
-  $items['admin/config/people/ip-blocking/delete/%blocked_ip'] = array(
-    'title' => 'Delete IP address',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('system_ip_blocking_delete', 5),
-    'access arguments' => array('block IP addresses'),
-    'file' => 'system.admin.inc',
-  );
-
   // Media settings.
   $items['admin/config/media'] = array(
     'title' => 'Media',
@@ -2004,19 +1982,6 @@ function system_stream_wrappers() {
 }
 
 /**
- * Retrieve a blocked IP address from the database.
- *
- * @param $iid integer
- *   The ID of the blocked IP address to retrieve.
- *
- * @return
- *   The blocked IP address from the database as an array.
- */
-function blocked_ip_load($iid) {
-  return db_query("SELECT * FROM {blocked_ips} WHERE iid = :iid", array(':iid' => $iid))->fetchAssoc();
-}
-
-/**
  * Menu item access callback - only enabled themes can be accessed.
  */
 function _system_themes_access($theme) {
@@ -3540,12 +3505,6 @@ function system_action_info() {
       'configurable' => TRUE,
       'triggers' => array('any'),
     ),
-    'system_block_ip_action' => array(
-      'type' => 'user',
-      'label' => t('Ban IP address of current user'),
-      'configurable' => FALSE,
-      'triggers' => array('any'),
-    ),
     'system_goto_action' => array(
       'type' => 'system',
       'label' => t('Redirect to URL'),
@@ -3767,19 +3726,6 @@ function system_goto_action($entity, $context) {
 }
 
 /**
- * Blocks the current user's IP address.
- *
- * @ingroup actions
- */
-function system_block_ip_action() {
-  $ip = ip_address();
-  db_insert('blocked_ips')
-    ->fields(array('ip' => $ip))
-    ->execute();
-  watchdog('action', 'Banned IP address %ip', array('%ip' => $ip));
-}
-
-/**
  * Generate an array of time zones and their local time&date.
  *
  * @param $blank
