1040 lines
36 KiB
PHP
1040 lines
36 KiB
PHP
<?php
|
|
/**
|
|
* ---------------------------------------------------------------------
|
|
* GLPI - Gestionnaire Libre de Parc Informatique
|
|
* Copyright (C) 2015-2020 Teclib' and contributors.
|
|
*
|
|
* http://glpi-project.org
|
|
*
|
|
* based on GLPI - Gestionnaire Libre de Parc Informatique
|
|
* Copyright (C) 2003-2014 by the INDEPNET Development Team.
|
|
*
|
|
* ---------------------------------------------------------------------
|
|
*
|
|
* LICENSE
|
|
*
|
|
* This file is part of GLPI.
|
|
*
|
|
* GLPI 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 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* GLPI 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 GLPI. If not, see <http://www.gnu.org/licenses/>.
|
|
* ---------------------------------------------------------------------
|
|
* */
|
|
|
|
if (!defined('GLPI_ROOT')) {
|
|
die("Sorry. You can't access this file directly");
|
|
}
|
|
|
|
/// Class IPNetwork : Represent an IPv4 or an IPv6 network.
|
|
/// It fully use IPAddress and IPNetmask to check validity and change representation from binary
|
|
/// to textual values.
|
|
/// \anchor parameterType Moreover, attributes of checking and retrieving functions allways allows
|
|
/// both binary (ie: array of 4 bytes) or IPAddress Object. As such, $version is only use (and
|
|
/// checked) with binary format of parameters.
|
|
/// \anchor ipAddressToNetwork We have to notice that checking regarding an IP address is the same
|
|
/// thing than checking regarding a network with all bits of the netmask set to 1
|
|
/// @since 0.84
|
|
class IPNetwork extends CommonImplicitTreeDropdown {
|
|
|
|
public $dohistory = true;
|
|
|
|
static $rightname = 'internet';
|
|
|
|
|
|
|
|
static function getTypeName($nb = 0) {
|
|
return _n('IP network', 'IP networks', $nb);
|
|
}
|
|
|
|
|
|
function rawSearchOptions() {
|
|
$tab = parent::rawSearchOptions();
|
|
|
|
$tab[] = [
|
|
'id' => '10',
|
|
'table' => $this->getTable(),
|
|
'field' => 'version',
|
|
'name' => __('IP version'),
|
|
'massiveaction' => false,
|
|
'datatype' => 'number'
|
|
];
|
|
|
|
$tab[] = [
|
|
'id' => '11',
|
|
'table' => $this->getTable(),
|
|
'field' => 'address',
|
|
'name' => IPAddress::getTypeName(1),
|
|
'massiveaction' => false,
|
|
'datatype' => 'string'
|
|
];
|
|
|
|
$tab[] = [
|
|
'id' => '12',
|
|
'table' => $this->getTable(),
|
|
'field' => 'netmask',
|
|
'name' => IPNetmask::getTypeName(1),
|
|
'massiveaction' => false,
|
|
'datatype' => 'string'
|
|
];
|
|
|
|
$tab[] = [
|
|
'id' => '17',
|
|
'table' => $this->getTable(),
|
|
'field' => 'gateway',
|
|
'name' => __('Gateway'),
|
|
'massiveaction' => false,
|
|
'datatype' => 'string',
|
|
'autocomplete' => true,
|
|
];
|
|
|
|
$tab[] = [
|
|
'id' => '18',
|
|
'table' => $this->getTable(),
|
|
'field' => 'addressable',
|
|
'name' => __('Addressable network'),
|
|
'datatype' => 'bool'
|
|
];
|
|
|
|
return $tab;
|
|
}
|
|
|
|
|
|
function getAddress() {
|
|
|
|
if (!isset($this->address)) {
|
|
$this->address = new IPAddress();
|
|
if (!$this->address->setAddressFromArray($this->fields, "version", "address", "address")) {
|
|
return false;
|
|
}
|
|
}
|
|
return $this->address;
|
|
}
|
|
|
|
|
|
function getNetmask() {
|
|
|
|
if (!isset($this->netmask)) {
|
|
$this->netmask = new IPNetmask();
|
|
if (!$this->netmask->setAddressFromArray($this->fields, "version", "netmask", "netmask")) {
|
|
return false;
|
|
}
|
|
}
|
|
return $this->netmask;
|
|
}
|
|
|
|
|
|
function getGateway() {
|
|
|
|
if (!isset($this->gateway)) {
|
|
$this->gateway = new IPAddress();
|
|
if (!$this->gateway->setAddressFromArray($this->fields, "version", "gateway", "gateway")) {
|
|
return false;
|
|
}
|
|
}
|
|
return $this->gateway;
|
|
}
|
|
|
|
|
|
/**
|
|
* When we load the object, we fill the "network" field with the correct address/netmask values
|
|
**/
|
|
function post_getFromDB () {
|
|
|
|
// Be sure to remove addresses, otherwise reusing will provide old objects for getAddress, ...
|
|
unset($this->address);
|
|
unset($this->netmask);
|
|
unset($this->gateway);
|
|
|
|
if (isset($this->fields["address"])
|
|
&& isset($this->fields["netmask"])) {
|
|
if ($this->fields["version"] == 4) {
|
|
$this->fields["network"] = sprintf(__('%1$s / %2$s'), $this->fields["address"],
|
|
$this->fields["netmask"]);
|
|
} else { // IPv6
|
|
$this->fields["network"] = sprintf(__('%1$s / %2$s'), $this->fields["address"],
|
|
$this->fields["netmask"]);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
function getAdditionalFields() {
|
|
|
|
return [['name' => 'network',
|
|
'label' => self::getTypeName(1),
|
|
'type' => 'text',
|
|
'list' => true,
|
|
'comment' => __('Set the network using notation address/mask')],
|
|
['name' => 'gateway',
|
|
'label' => __('Gateway'),
|
|
'type' => 'text',
|
|
'list' => true],
|
|
['name' => 'addressable',
|
|
'label' => __('Addressable network'),
|
|
'comment' => __('An addressable network is a network defined on an equipment'),
|
|
'type' => 'bool']];
|
|
}
|
|
|
|
|
|
function getNewAncestor() {
|
|
|
|
if (isset($this->data_for_implicit_update)) {
|
|
$params = ["address" => $this->data_for_implicit_update['address'],
|
|
"netmask" => $this->data_for_implicit_update['netmask']];
|
|
|
|
if (isset($this->fields['id'])) {
|
|
$params['exclude IDs'] = $this->fields['id'];
|
|
}
|
|
|
|
$parents = self::searchNetworks("contains", $params,
|
|
$this->data_for_implicit_update['entities_id']);
|
|
|
|
if ((is_array($parents)) && (count($parents) > 0)) {
|
|
return $parents[0];
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* @param $input
|
|
**/
|
|
function prepareInput($input) {
|
|
|
|
// In case of entity transfer, $input['network'] is not defined
|
|
if (!isset($input['network']) && isset($this->fields['network'])) {
|
|
$input['network'] = $this->fields['network'];
|
|
}
|
|
|
|
// In case of entity transfer, $input['gateway'] is not defined
|
|
if (!isset($input['gateway']) && isset($this->fields['gateway'])) {
|
|
$input['gateway'] = $this->fields['gateway'];
|
|
}
|
|
|
|
// If $this->fields["id"] is not set then, we are adding a new network
|
|
// Or if $this->fields["network"] != $input["network"] we a updating the network
|
|
$address = new IPAddress();
|
|
$netmask = new IPNetmask();
|
|
// Don't validate an empty network
|
|
if (empty($input["network"])) {
|
|
return ['error' => __('Invalid network address'),
|
|
'input' => false];
|
|
}
|
|
if (!isset($this->fields["id"])
|
|
|| !isset($this->fields["network"])
|
|
|| ($input["network"] != $this->fields["network"])) {
|
|
$network = explode ("/", $input["network"]);
|
|
if (count($network) != 2) {
|
|
return ['error' => __('Invalid input format for the network'),
|
|
'input' => false];
|
|
}
|
|
if (!$address->setAddressFromString(trim($network[0]))) {
|
|
return ['error' => __('Invalid network address'),
|
|
'input' => false];
|
|
}
|
|
if (!$netmask->setNetmaskFromString(trim($network[1]), $address->getVersion())) {
|
|
return ['error' => __('Invalid subnet mask'),
|
|
'input' => false];
|
|
}
|
|
|
|
// After checking that address and netmask are valid, modify the address to be the "real"
|
|
// network address : the first address of the network. This is not required for SQL, but
|
|
// that looks better for the human
|
|
self::computeNetworkRangeFromAdressAndNetmask($address, $netmask, $address);
|
|
|
|
// Now, we look for already existing same network inside the database
|
|
$params = ["address" => $address,
|
|
"netmask" => $netmask];
|
|
if (isset($this->fields["id"])) {
|
|
$params["exclude IDs"] = $this->fields["id"];
|
|
}
|
|
|
|
if (isset($this->fields["entities_id"])) {
|
|
$entities_id = $this->fields["entities_id"];
|
|
} else if (isset($input["entities_id"])) {
|
|
$entities_id = $input["entities_id"];
|
|
} else {
|
|
$entities_id = -1;
|
|
}
|
|
|
|
// TODO : what is the best way ? recursive or not ?
|
|
$sameNetworks = self::searchNetworks("equals", $params, $entities_id, false);
|
|
// Check unicity !
|
|
if ($sameNetworks && (count($sameNetworks) > 0)) {
|
|
return ['error' => __('Network already defined in visible entities'),
|
|
'input' => false];
|
|
}
|
|
|
|
// Then, update $input to reflect the network and the netmask
|
|
$input = $address->setArrayFromAddress($input, "version", "address", "address");
|
|
$input = $netmask->setArrayFromAddress($input, "", "netmask", "netmask");
|
|
|
|
// We check to see if the network is modified
|
|
$previousAddress = new IPAddress();
|
|
$previousAddress->setAddressFromArray($this->fields, "version", "address", "address");
|
|
$previousNetmask = new IPNetmask();
|
|
$previousNetmask->setAddressFromArray($this->fields, "version", "netmask", "netmask");
|
|
|
|
if ($previousAddress->equals($address)
|
|
&& $previousNetmask->equals($netmask)) {
|
|
$this->networkUpdate = false;
|
|
} else {
|
|
$this->networkUpdate = true;
|
|
}
|
|
|
|
} else {
|
|
// If netmask and address are not modified, then, load them from DB to check the validity
|
|
// of the gateway
|
|
$this->networkUpdate = false;
|
|
$address->setAddressFromArray($this->fields, "version", "address", "address");
|
|
$netmask->setAddressFromArray($this->fields, "version", "netmask", "netmask");
|
|
$entities_id = $this->fields['entities_id'];
|
|
}
|
|
|
|
// Update class for the CommonImplicitTree update ...
|
|
$this->data_for_implicit_update = ['address' => $address,
|
|
'netmask' => $netmask,
|
|
'entities_id' => $entities_id];
|
|
|
|
$returnValue = [];
|
|
// If the gateway has been altered, or the network information (address or netmask) changed,
|
|
// then, we must revalidate the gateway !
|
|
if (!isset($this->fields["gateway"])
|
|
|| ($input["gateway"] != $this->fields["gateway"])
|
|
|| $this->networkUpdate) {
|
|
$gateway = new IPAddress();
|
|
|
|
if (!empty($input["gateway"])) {
|
|
if (!$gateway->setAddressFromString($input["gateway"])
|
|
|| !self::checkIPFromNetwork($gateway, $address, $netmask)) {
|
|
$returnValue['error'] = __('Invalid gateway address');
|
|
|
|
if (!empty($this->fields["gateway"])) {
|
|
if (!$gateway->setAddressFromString($this->fields["gateway"])
|
|
|| !self::checkIPFromNetwork($gateway, $address, $netmask)) {
|
|
$gateway->disableAddress();
|
|
}
|
|
} else {
|
|
$gateway->disableAddress();
|
|
}
|
|
}
|
|
}
|
|
$input = $gateway->setArrayFromAddress($input, "", "gateway", "gateway");
|
|
}
|
|
|
|
$returnValue['input'] = $input;
|
|
|
|
return $returnValue;
|
|
}
|
|
|
|
|
|
function prepareInputForAdd($input) {
|
|
|
|
$preparedInput = $this->prepareInput($input);
|
|
|
|
if (isset($preparedInput['error'])) {
|
|
Session::addMessageAfterRedirect($preparedInput['error'], false, ERROR);
|
|
}
|
|
|
|
$input = $preparedInput['input'];
|
|
|
|
if (is_array($input)) {
|
|
return parent::prepareInputForAdd($input);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
function prepareInputForUpdate($input) {
|
|
|
|
$preparedInput = $this->prepareInput($input);
|
|
|
|
if (isset($preparedInput['error'])) {
|
|
Session::addMessageAfterRedirect($preparedInput['error'], false, ERROR);
|
|
}
|
|
|
|
$input = $preparedInput['input'];
|
|
|
|
if (is_array($input)) {
|
|
return parent::prepareInputForUpdate($input);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
function post_addItem() {
|
|
|
|
if ($this->networkUpdate) {
|
|
IPAddress_IPNetwork::linkIPAddressFromIPNetwork($this);
|
|
}
|
|
|
|
unset($this->networkUpdate);
|
|
parent::post_addItem();
|
|
}
|
|
|
|
|
|
function post_updateItem($history = 1) {
|
|
|
|
if ($this->networkUpdate) {
|
|
IPAddress_IPNetwork::linkIPAddressFromIPNetwork($this);
|
|
}
|
|
|
|
unset($this->networkUpdate);
|
|
parent::post_updateItem($history);
|
|
}
|
|
|
|
|
|
function cleanDBonPurge() {
|
|
|
|
$this->deleteChildrenAndRelationsFromDb(
|
|
[
|
|
IPAddress_IPNetwork::class,
|
|
IPNetwork_Vlan::class,
|
|
]
|
|
);
|
|
}
|
|
|
|
|
|
function getPotentialSons() {
|
|
|
|
if (isset($this->data_for_implicit_update)) {
|
|
$params = ["address" => $this->data_for_implicit_update['address'],
|
|
"netmask" => $this->data_for_implicit_update['netmask'],
|
|
"exclude IDs" => $this->getID()];
|
|
|
|
$mysons = self::searchNetworks("is contained by", $params,
|
|
$this->data_for_implicit_update['entities_id']);
|
|
|
|
if (is_array($mysons)) {
|
|
return $mysons;
|
|
}
|
|
}
|
|
|
|
return [];
|
|
}
|
|
|
|
|
|
/**
|
|
* \brief Search any networks that contains the given IP
|
|
* \ref ipAddressToNetwork
|
|
*
|
|
* @param IPAddress|string|integer[] $IP (see \ref parameterType) given IP
|
|
* @param integer $entityID scope of the search (parents and childrens are check)
|
|
* @param boolean $recursive set to false to only search in current entity,
|
|
* otherwise, all visible entities will be search
|
|
* @param string|array $fields list of fields to return in the result (default : only ID of the networks)
|
|
* @param string $where search criteria
|
|
*
|
|
* @return array|false list of networks (see searchNetworks())
|
|
**/
|
|
static function searchNetworksContainingIP($IP, $entityID = -1, $recursive = true,
|
|
$fields = "", $where = "") {
|
|
|
|
return self::searchNetworks('contains', ['address' => $IP,
|
|
'netmask' => [0xffffffff, 0xffffffff,
|
|
0xffffffff, 0xffffffff],
|
|
'fields' => $fields,
|
|
'where' => $where],
|
|
$entityID, $recursive);
|
|
}
|
|
|
|
|
|
/**
|
|
* Search networks relative to a given network
|
|
*
|
|
* @param string $relation type of relation ("is contained by", "equals" or "contains")
|
|
* regarding the networks given as parameter
|
|
* @param array $condition array of elements to select the good arrays (see Parameters above)
|
|
* - fields : the fields of the network we wish to retrieve (single field or array of
|
|
* fields). This parameter will impact the result of the function
|
|
* - address (see \ref parameterType) : the address for the query
|
|
* - netmask (see \ref parameterType) : the netmask for the query
|
|
* - exclude IDs : the IDs to exclude from the query (for instance, $this->getID())
|
|
* - where : filters to add to the SQL request
|
|
*
|
|
* @param integer $entityID the entity on which the selection should occur (-1 => the current active
|
|
* entity) (default -1)
|
|
* @param boolean $recursive set to false to only search in current entity, otherwise, all visible
|
|
* entities will be search (true by default)
|
|
* @param integer $version version of IP to look (only use when using arrays or string as input for
|
|
* address or netmask n(default 0)
|
|
*
|
|
* @return array of networks found. If we want request several field, the return value will be
|
|
* an array of array
|
|
*
|
|
* \warning The order of the elements inside the result are ordered from the nearest one to the
|
|
* further. (ie. 0.0.0.0 is the further of whatever network if you lool for ones that
|
|
* contains the current network.
|
|
**/
|
|
static function searchNetworks($relation, $condition, $entityID = -1, $recursive = true,
|
|
$version = 0) {
|
|
global $DB;
|
|
|
|
if (empty($relation)) {
|
|
return false;
|
|
}
|
|
|
|
if (empty($condition["fields"])) {
|
|
$fields = 'id';
|
|
} else {
|
|
$fields = $condition["fields"];
|
|
}
|
|
|
|
if (!is_array($fields)) {
|
|
$fields = [$fields];
|
|
}
|
|
|
|
$startIndex = (($version == 4) ? 3 : 1);
|
|
|
|
$addressDB = ['address_0', 'address_1', 'address_2', 'address_3'];
|
|
$netmaskDB = ['netmask_0', 'netmask_1', 'netmask_2', 'netmask_3'];
|
|
|
|
$WHERE = [];
|
|
if (isset($condition["address"])
|
|
&& isset($condition["netmask"])) {
|
|
$addressPa = new IPAddress($condition["address"]);
|
|
|
|
// Check version equality ...
|
|
if ($version != $addressPa->getVersion()) {
|
|
if ($version != 0) {
|
|
return false;
|
|
}
|
|
$version = $addressPa->getVersion();
|
|
}
|
|
|
|
$netmaskPa = new IPNetmask($condition["netmask"], $version);
|
|
|
|
// Get the array of the adresses
|
|
$addressPa = $addressPa->getBinary();
|
|
$netmaskPa = $netmaskPa->getBinary();
|
|
|
|
// Check the binary is valid
|
|
if (!is_array($addressPa) || (count($addressPa) != 4)) {
|
|
return false;
|
|
}
|
|
if (!is_array($netmaskPa) || (count($netmaskPa) != 4)) {
|
|
return false;
|
|
}
|
|
|
|
$startIndex = (($version == 4) ? 3 : 0);
|
|
|
|
if ($relation == "equals") {
|
|
for ($i = $startIndex; $i < 4; ++$i) {
|
|
$WHERE = [
|
|
new \QueryExpression("(".$DB->quoteName($addressDB[$i]) . " & " . $DB->quoteValue($netmaskPa[$i]) . ") = (".$DB->quoteValue($addressPa[$i])." & ".$DB->quoteValue($netmaskPa[$i]).")"),
|
|
$netmaskDB[$i] => $netmaskPa[$i]
|
|
];
|
|
}
|
|
} else {
|
|
for ($i = $startIndex; $i < 4; ++$i) {
|
|
if ($relation == "is contained by") {
|
|
$globalNetmask = $DB->quoteValue($netmaskPa[$i]);
|
|
} else {
|
|
$globalNetmask = $DB->quoteName($netmaskDB[$i]);
|
|
}
|
|
|
|
$WHERE = [
|
|
new \QueryExpression("(".$DB->quoteName($addressDB[$i])." & $globalNetmask) = (".$DB->quoteValue($addressPa[$i])." & $globalNetmask)"),
|
|
new \QueryExpression("(".$DB->quoteValue($netmaskPa[$i])." & ".$DB->quoteName($netmaskDB[$i]).")=$globalNetmask")
|
|
];
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($entityID < 0) {
|
|
$entityID = $_SESSION['glpiactive_entity'];
|
|
}
|
|
$entitiesID = [];
|
|
switch ($relation) {
|
|
case "is contained by" :
|
|
$ORDER_ORIENTATION = 'ASC';
|
|
if ($recursive) {
|
|
$entitiesID = getSonsOf('glpi_entities', $entityID);
|
|
}
|
|
break;
|
|
|
|
case "contains" :
|
|
$ORDER_ORIENTATION = 'DESC';
|
|
if ($recursive) {
|
|
$entitiesID = getAncestorsOf('glpi_entities', $entityID);
|
|
}
|
|
break;
|
|
|
|
case "equals" :
|
|
$ORDER_ORIENTATION = '';
|
|
if ($recursive) {
|
|
$entitiesID = getSonsAndAncestorsOf('glpi_entities', $entityID);
|
|
}
|
|
break;
|
|
}
|
|
|
|
$entitiesID[] = $entityID;
|
|
$WHERE['entities_id'] = $entitiesID;
|
|
$WHERE['version'] = $version;
|
|
|
|
if (!empty($condition["exclude IDs"])) {
|
|
if (is_array($condition["exclude IDs"])) {
|
|
if (count($condition["exclude IDs"]) > 1) {
|
|
$WHERE['NOT'] = ['id' => $condition['exclude IDs']];
|
|
} else {
|
|
$WHERE['id'] = ['<>', $condition['exclude IDs'][0]];
|
|
}
|
|
} else {
|
|
$WHERE['id'] = ['<>', $condition['exclude IDs']];
|
|
}
|
|
}
|
|
|
|
$ORDER = [];
|
|
// By ordering on the netmask, we ensure that the first element is the nearest one (ie:
|
|
// the last should be 0.0.0.0/0.0.0.0 of x.y.z.a/255.255.255.255 regarding the interested
|
|
// element)
|
|
for ($i = $startIndex; $i < 4; ++$i) {
|
|
$ORDER[] = new \QueryExpression("BIT_COUNT(".$DB->quoteName($netmaskDB[$i]).") $ORDER_ORIENTATION");
|
|
}
|
|
|
|
if (!empty($condition["where"])) {
|
|
$WHERE .= " AND " . $condition["where"];
|
|
}
|
|
|
|
$iterator = $DB->request([
|
|
'SELECT' => $fields,
|
|
'FROM' => self::getTable(),
|
|
'WHERE' => $WHERE,
|
|
'ORDER' => $ORDER
|
|
]);
|
|
|
|
$returnValues = [];
|
|
while ($data = $iterator->next()) {
|
|
if (count($fields) > 1) {
|
|
$returnValue = [];
|
|
foreach ($fields as $field) {
|
|
$returnValue[$field] = $data[$field];
|
|
}
|
|
} else {
|
|
$returnValue = $data[$fields[0]];
|
|
}
|
|
$returnValues[] = $returnValue;
|
|
}
|
|
return $returnValues;
|
|
}
|
|
|
|
|
|
function defineTabs($options = []) {
|
|
|
|
$ong = [];
|
|
$this->addDefaultFormTab($ong);
|
|
$this->addStandardTab('IPNetwork_Vlan', $ong, $options);
|
|
$this->addStandardTab('IPAddress', $ong, $options);
|
|
$this->addStandardTab('Log', $ong, $options);
|
|
|
|
return $ong;
|
|
}
|
|
|
|
|
|
/**
|
|
* Get SQL WHERE criteria for requesting elements that are contained inside the current network
|
|
*
|
|
* @since 9.5.0
|
|
*
|
|
* @param string $tableName name of the table containing the element
|
|
* (for instance : glpi_ipaddresses)
|
|
* @param string $binaryFieldPrefix prefix of the binary version of IP address
|
|
* (binary for glpi ipaddresses)
|
|
* @param string $versionField the name of the field containing the version inside the database
|
|
*
|
|
* @return array
|
|
**/
|
|
function getCriteriaForMatchingElement($tableName, $binaryFieldPrefix, $versionField) {
|
|
global $DB;
|
|
|
|
$version = $this->fields["version"];
|
|
$start = null;
|
|
$this->computeNetworkRange($start);
|
|
|
|
$result = [];
|
|
for ($i = ($version == 4 ? 3 : 0); $i < 4; ++$i) {
|
|
$result[] = new \QueryExpression(
|
|
"({$DB->quoteName($tableName.'.'.$binaryFieldPrefix.'_'.$i)} & " . $this->fields["netmask_$i"] . ") = ({$start[$i]})"
|
|
);
|
|
}
|
|
$result["$tableName.version"] = $version;
|
|
|
|
return $result;
|
|
}
|
|
|
|
|
|
/**
|
|
* Check to see if an IP is inside a given network
|
|
* See : \ref ipAddressToNetwork
|
|
*
|
|
* @param IPAddress|integer[] $address (see \ref parameterType) the IP address to check
|
|
* @param IPAddress|integer[] $networkAddress (see \ref parameterType) the address of the network
|
|
* @param IPAddress|integer[] $networkNetmask (see \ref parameterType) the netmask of the network
|
|
* @param integer $version of IP : only usefull for binary array as input (default 0)
|
|
*
|
|
* @return true if the network owns the IP address
|
|
**/
|
|
static function checkIPFromNetwork($address, $networkAddress, $networkNetmask, $version = 0) {
|
|
|
|
$IPNetmask = [0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff];
|
|
$relativity = self::checkNetworkRelativity($address, $IPNetmask, $networkAddress,
|
|
$networkNetmask, $version);
|
|
|
|
return ($relativity == "equals") || ($relativity == "second contains first");
|
|
}
|
|
|
|
|
|
/**
|
|
* \brief Check network relativity
|
|
* Check how networks are relative (fully different, equals, first contains second, ...)
|
|
*
|
|
* @param IPAddress|integer[] $firstAddress (see \ref parameterType) address of the first network
|
|
* @param IPAddress|integer[] $firstNetmask (see \ref parameterType) netmask of the first network
|
|
* @param IPAddress|integer[] $secondAddress (see \ref parameterType) address of the second network
|
|
* @param IPAddress|integer[] $secondNetmask (see \ref parameterType) netmask of the second network
|
|
* @param integer $version of IP : only usefull for binary array as input (default 0)
|
|
*
|
|
* @return string :
|
|
* - "different version" : there is different versions between elements
|
|
* - "?" : There is holes inside the netmask and both networks can partially intersect
|
|
* - "different" : the networks are fully different;
|
|
* - "equals" : both networks are equals
|
|
* - "first contains second" "second contains first" : one include the other
|
|
*/
|
|
static function checkNetworkRelativity($firstAddress, $firstNetmask, $secondAddress,
|
|
$secondNetmask, $version = 0) {
|
|
|
|
if ($firstAddress instanceof IPAddress) {
|
|
if ($version == 0) {
|
|
$version = $firstAddress->getVersion();
|
|
}
|
|
if ($version != $firstAddress->getVersion()) {
|
|
return "different version";
|
|
}
|
|
$firstAddress = $firstAddress->getBinary();
|
|
}
|
|
|
|
if ($firstNetmask instanceof IPAddress) {
|
|
if ($version != $firstNetmask->getVersion()) {
|
|
return "different version";
|
|
}
|
|
$firstNetmask = $firstNetmask->getBinary();
|
|
}
|
|
|
|
if ($secondAddress instanceof IPAddress) {
|
|
if ($version != $secondAddress->getVersion()) {
|
|
return "different version";
|
|
}
|
|
$secondAddress = $secondAddress->getBinary();
|
|
}
|
|
|
|
if ($secondNetmask instanceof IPAddress) {
|
|
if ($version != $secondNetmask->getVersion()) {
|
|
return "different version";
|
|
}
|
|
$secondNetmask = $secondNetmask->getBinary();
|
|
}
|
|
|
|
$startIndex = (($version == 4) ? 3 : 0);
|
|
$first = true;
|
|
$second = true;
|
|
for ($i = $startIndex; $i < 4; ++$i) {
|
|
$and = ($firstNetmask[$i] & $secondNetmask[$i]);
|
|
// Be carefull : php integers are 32 bits SIGNED.
|
|
// Thus, checking equality must be done by XOR ...
|
|
$first &= (($and ^ $firstNetmask[$i]) == 0);
|
|
$second &= (($and ^ $secondNetmask[$i]) == 0);
|
|
}
|
|
|
|
if (!$first && !$second) {
|
|
return "?";
|
|
}
|
|
|
|
if ($first && $second) {
|
|
$result = "equals";
|
|
$mask = &$firstNetmask;
|
|
} else if ($first) {
|
|
$result = "first contains second";
|
|
$mask = &$firstNetmask;
|
|
} else { // $second == true
|
|
$result = "second contains first";
|
|
$mask = &$secondNetmask;
|
|
}
|
|
|
|
for ($i = $startIndex; $i < 4; ++$i) {
|
|
if ((($firstAddress[$i] & $mask[$i]) ^ ($secondAddress[$i] & $mask[$i])) != 0) {
|
|
return "different";
|
|
}
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
|
|
/**
|
|
* Compute the first and the last address of $this
|
|
* \see computeNetworkRangeFromAdressAndNetmask()
|
|
*
|
|
* @param $start
|
|
* @param $end (default NULL)
|
|
* @param $excludeBroadcastAndNetwork Don't provide extremties addresses
|
|
* ($this->fields['addressable'] by default)
|
|
* (default '')
|
|
**/
|
|
function computeNetworkRange(&$start, &$end = null, $excludeBroadcastAndNetwork = '') {
|
|
|
|
if (!is_bool($excludeBroadcastAndNetwork)) {
|
|
if (isset($this->fields['addressable'])) {
|
|
$excludeBroadcastAndNetwork = ($this->fields['addressable'] == 1);
|
|
} else {
|
|
$excludeBroadcastAndNetwork = false;
|
|
}
|
|
}
|
|
|
|
self::computeNetworkRangeFromAdressAndNetmask($this->getAddress(), $this->getNetmask(),
|
|
$start, $end, $excludeBroadcastAndNetwork);
|
|
}
|
|
|
|
|
|
/**
|
|
* \brief Compute the first and the last address of a network.
|
|
* That is usefull, for instance, to compute the "real" network address (the first address)
|
|
* or the broadcast address of the network
|
|
*
|
|
* @param $address (see \ref parameterType) the address of the network
|
|
* @param $netmask (see \ref parameterType) its netmask
|
|
* @param $firstAddress (see \ref parameterType - in/out)
|
|
* the first address (ie real address of the network)
|
|
* @param $lastAddress (see \ref parameterType - in/out)
|
|
* the lastAddress of the network
|
|
* (ie. : the broadcast address) (default NULL)
|
|
* @param $excludeBroadcastAndNetwork boolean exclude broadcast and network address from the
|
|
* result (false by default)
|
|
**/
|
|
static function computeNetworkRangeFromAdressAndNetmask($address, $netmask, &$firstAddress,
|
|
&$lastAddress = null,
|
|
$excludeBroadcastAndNetwork = false) {
|
|
if ($address instanceof IPAddress) {
|
|
$address = $address->getBinary();
|
|
}
|
|
if ($netmask instanceof IPNetmask) {
|
|
$netmask = $netmask->getBinary();
|
|
}
|
|
$start = [];
|
|
$end = [];
|
|
for ($i = 0; $i < 4; ++$i) {
|
|
$start[$i] = IPAddress::convertNegativeIntegerToPositiveFloat($address[$i] & $netmask[$i]);
|
|
$end[$i] = IPAddress::convertNegativeIntegerToPositiveFloat($address[$i] | ~$netmask[$i]);
|
|
}
|
|
|
|
if ($excludeBroadcastAndNetwork) {
|
|
IPAddress::addValueToAddress($start, 1);
|
|
IPAddress::addValueToAddress($end, -1);
|
|
}
|
|
|
|
if ($firstAddress instanceof IPAddress) {
|
|
$firstAddress->setAddressFromBinary($start);
|
|
} else {
|
|
$firstAddress = $start;
|
|
}
|
|
|
|
if ($lastAddress instanceof IPAddress) {
|
|
$lastAddress->setAddressFromBinary($end);
|
|
} else {
|
|
$lastAddress = $end;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* \brief Recreate network tree
|
|
* Among others, the migration create plan tree network. This method allows to recreate the tree.
|
|
* You can also use it if you suspect the network tree to be corrupted.
|
|
*
|
|
* First, reset the tree, then, update each network by its own field, letting
|
|
* CommonImplicitTreeDropdown working such as it would in case of standard update
|
|
*
|
|
* @return void
|
|
**/
|
|
static function recreateTree() {
|
|
global $DB;
|
|
|
|
// Reset the tree
|
|
$DB->update(
|
|
'glpi_ipnetworks', [
|
|
'ipnetworks_id' => 0,
|
|
'level' => 1,
|
|
'completename' => new \QueryExpression($DB->quoteName('name'))
|
|
], [true]
|
|
);
|
|
|
|
// Foreach IPNetwork ...
|
|
$iterator = $DB->request([
|
|
'SELECT' => 'id',
|
|
'FROM' => self::getTable()
|
|
]);
|
|
|
|
$network = new self();
|
|
|
|
while ($network_entry = $iterator->next()) {
|
|
if ($network->getFromDB($network_entry['id'])) {
|
|
$input = $network->fields;
|
|
// ... update it by its own entries
|
|
$network->update($input);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @since 0.84
|
|
*
|
|
* @param $itemtype
|
|
* @param $base HTMLTableBase object
|
|
* @param $super HTMLTableSuperHeader object (default NULL)
|
|
* @param $father HTMLTableHeader object (default NULL)
|
|
* @param $options array
|
|
**/
|
|
static function getHTMLTableHeader($itemtype, HTMLTableBase $base,
|
|
HTMLTableSuperHeader $super = null,
|
|
HTMLTableHeader $father = null, array $options = []) {
|
|
|
|
if ($itemtype != 'IPAddress') {
|
|
return;
|
|
}
|
|
|
|
$column_name = __CLASS__;
|
|
if (isset($options['dont_display'][$column_name])) {
|
|
return;
|
|
}
|
|
|
|
$content = self::getTypeName();
|
|
$this_header = $base->addHeader($column_name, $content, $super, $father);
|
|
$this_header->setItemType(__CLASS__);
|
|
}
|
|
|
|
|
|
/**
|
|
* @since 0.84
|
|
*
|
|
* @param $row HTMLTableRow object (default NULL)
|
|
* @param $item CommonDBTM object (default NULL)
|
|
* @param $father HTMLTableCell object (default NULL)
|
|
* @param $options array
|
|
**/
|
|
static function getHTMLTableCellsForItem(HTMLTableRow $row = null, CommonDBTM $item = null,
|
|
HTMLTableCell $father = null, array $options = []) {
|
|
if (empty($item)) {
|
|
if (empty($father)) {
|
|
return;
|
|
}
|
|
$item = $father->getItem();
|
|
}
|
|
|
|
if ($item->getType() != 'IPAddress') {
|
|
return;
|
|
}
|
|
|
|
$column_name = __CLASS__;
|
|
if (isset($options['dont_display'][$column_name])) {
|
|
return;
|
|
}
|
|
|
|
$header= $row->getGroup()->getHeaderByName('Internet', __CLASS__);
|
|
if (!$header) {
|
|
return;
|
|
}
|
|
|
|
$createRow = (isset($options['createRow']) && $options['createRow']);
|
|
$options['createRow'] = false;
|
|
$network = new self();
|
|
|
|
foreach (self::searchNetworksContainingIP($item) as $networks_id) {
|
|
if ($network->getFromDB($networks_id)) {
|
|
$address = $network->getAddress();
|
|
$netmask = $network->getNetmask();
|
|
|
|
// Stop if we failed to retrieve address or netmask
|
|
if (!$address || !$netmask) {
|
|
continue;
|
|
}
|
|
|
|
if ($createRow) {
|
|
$row = $row->createRow();
|
|
}
|
|
|
|
//TRANS: %1$s is address, %2$s is netmask
|
|
$content = sprintf(
|
|
__('%1$s / %2$s'),
|
|
$address->getTextual(),
|
|
$netmask->getTextual()
|
|
);
|
|
|
|
if ($network->fields['addressable'] == 1) {
|
|
$content = "<span class='b'>".$content."</span>";
|
|
}
|
|
$content = sprintf(__('%1$s - %2$s'), $content, $network->getLink());
|
|
$row->addCell($header, $content, $father, $network);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Show all available IPNetwork for a given entity
|
|
*
|
|
* @param $entities_id entity of the IPNetworks (-1 for all entities)
|
|
* (default -1)
|
|
**/
|
|
static function showIPNetworkProperties($entities_id = -1) {
|
|
global $CFG_GLPI;
|
|
|
|
$rand = mt_rand();
|
|
self::dropdown(['entity' => $entities_id,
|
|
'rand' => $rand]);
|
|
|
|
$params = ['ipnetworks_id' => '__VALUE__'];
|
|
|
|
Ajax::updateItemOnSelectEvent("dropdown_ipnetworks_id$rand", "show_ipnetwork_$rand",
|
|
$CFG_GLPI["root_doc"]. "/ajax/dropdownShowIPNetwork.php",
|
|
$params);
|
|
|
|
echo "<span id='show_ipnetwork_$rand'> </span>\n";
|
|
}
|
|
|
|
|
|
/**
|
|
* Override title function to display the link to reinitialisation of the network tree
|
|
**/
|
|
function title() {
|
|
parent::title();
|
|
|
|
if (Session::haveRight('internet', UPDATE)
|
|
&& Session::canViewAllEntities()) {
|
|
|
|
echo "<div class='spaced' id='tabsbody'>";
|
|
echo "<table class='tab_cadre_fixe'>";
|
|
|
|
echo "<tr><td class='center'>";
|
|
Html::showSimpleForm(IPNetwork::getFormURL(), 'reinit_network',
|
|
__('Reinit the network topology'));
|
|
|
|
echo "</td></tr>";
|
|
|
|
echo "</table>";
|
|
echo "</div>";
|
|
}
|
|
}
|
|
}
|