Zuerst einmal ein Entity um die IP-Definition zu speichern:
class NetworkAddress {Und hier die eigentlich Logik:
/**
* @var string
*/
private $ip;
/**
* @var integer
*/
private $asInteger;
function __construct($ip = null, $asInteger = null) {
$this->setIp($ip);
$this->setAsInteger($asInteger);
}
/**
* Get id
* @return integer
*/
public function getId() {
return $this->id;
}
/**
* Set ip
* @param string $ip
* @return NetworkAddress
*/
public function setIp($ip) {
$this->ip = $ip;
return $this;
}
/**
* Get ip
* @return string
*/
public function getIp() {
return $this->ip;
}
/**
* Set asInteger
* @param integer $asInteger
* @return NetworkAddress
*/
public function setAsInteger($asInteger) {
$this->asInteger = floor((float) $asInteger);
return $this;
}
/**
* Get asInteger
* @return integer
*/
public function getAsInteger() {
return (floor) $this->asInteger;
}
}
class Network {
const MAX_IP_AS_INT = 4294967295; //255.255.255.255
/**
* korrigiert die inegabe von CIDR
* @param int $cidr
* @return int
*/
static protected function fixCIDR($cidr) {
return max(0, min(32, (int) $cidr)); //cidr zwischen 0 - 32
}
/**
* @param integer $asInt
* @return \NetworkAddress
* @throws \Excpetion
*/
static public function generateNetworkAddressByInteger($asInt) {
$asInt = floor((float) $asInt);
if ($asInt >= 0 && $asInt <= self::MAX_IP_AS_INT) {
return new NetworkAddress(long2ip($asInt), $asInt);
} else {
throw new \Exception('Invalid integer range: ' . $asInt);
}
}
/**
* @param string $asString
* @return \NetworkAddress
* @throws \Excpetion
*/
static public function generateNetworkAddressByString($asString) {
if (filter_var($asString, FILTER_VALIDATE_IP)) {
return new NetworkAddress($asString, ip2long($asString));
} else {
throw new \Excpetion('Invalid IP: ' . $asString);
}
}
/**
* gibt die erste adresse eines netzwerkes zurück.
* @param \Network $network
* @return \NetworkAddress
*/
static public function getFirstHost(\SkyBurner\StoreBundle\Entity\Network $network) {
return self::generateNetworkAddressByInteger($network->getNetaddressNetworkAddress()->getAsInteger() + 1);
}
/**
*
* @param int $cidr
* @return \NetworkAddress
*/
public function subnetMask($cidr) {
$asInt = bindec(str_pad(str_repeat(1, self::fixCIDR($cidr)), 32, 0, STR_PAD_RIGHT));
return self::generateNetworkAddressByInteger($asInt);
}
/**
*
* @param \NetworkAddress $netmask
* @return int
*/
public function subnetMaskToCidr(NetworkAddress $netmask) {
return 32 - log(($netmask->getAsInteger() ^ self::MAX_IP_AS_INT) + 1, 2);
}
/**
*
* @param \NetworkAddress $ip
* @param \NetworkAddress $netmask
* @return \NetworkAddress
*/
public function netAddress(NetworkAddress $ip, NetworkAddress $netmask) {
$asInt = $ip->getAsInteger() & $netmask->getAsInteger();
return self::generateNetworkAddressByInteger($asInt);
}
/**
*
* @param \NetworkAddress $netAddress
* @param \NetworkAddress $netmask
* @return \NetworkAddress
*/
public function broadcast(NetworkAddress $netAddress, NetworkAddress $netmask) {
// $ipAsLong = $netAddress->getAsInteger() | (~ $netmask->getAsInteger());
$ipAsLong = $netAddress->getAsInteger() | $this->flipBin($netmask->getAsInteger());
return self::generateNetworkAddressByInteger($asInt);
}
protected function flipBin($number) {/**
$bin = str_pad(base_convert($number,10,2), 32, 0, STR_PAD_LEFT);
for($i = 0; $i < 32; $i++) {
$bin{$i} = $bin{$i} === '0' ? '1' : '0';
}
return bindec($bin);
}
*
* @param int $cidr
* @param \NetworkAddress $ip
* @return \NetworkAddress
*/
public function minIp($cidr, NetworkAddress $ip) {
$netmask = $this->subnetMask($cidr);
$netAddress = $this->netAddress($ip, $netmask);
return self::generateNetworkAddressByInteger($netAddress->getAsInteger() + 1);
}
/**
*
* @param int $cidr
* @param \NetworkAddress $ip
* @return \NetworkAddress
*/
public function maxIp($cidr, NetworkAddress $ip) {
$netmask = $this->subnetMask($cidr);
$netAddress = $this->netAddress($ip, $netmask);
$broadcast = $this->broadcast($netAddress, $netmask);
return self::generateNetworkAddressByInteger($broadcast->getAsInteger() - 1);
}
/**
* @param int $cidr
* @return int
*/
public function countByCidr($cidr) {
return max(0, pow(2, 32 - self::fixCIDR($cidr)) - 2);
}
}
Zu beachten ist hier die interne verwendung von "FLOAT", da es bei "INT" probleme geben kann dass wir ein Überlauf erzeugen. Also solltet ihr diese Daten in die Datenbank speichern wollen achtet darauf ein "UNSIGNED INT" zu benutzen! Aus gleichem Grund habe ich eine Reimplementierung vom "Bitweisem NOT (~)" erstellt -> "flipBin".
Ich denke das ganze noch in ein passenden Namespace packen und gut is. Wer hierzu noch ein paar vorschläge hat, immer her damit!
mach noch ne v6 Variante dazu und du kannst nen micolib draus machen ^^
AntwortenLöschendas artet ja in arbeit aus ;)
Löschen