This commit is contained in:
Adrien MALINGREY 2025-05-11 23:31:27 +02:00
commit d6da7a6a2e
5 changed files with 208 additions and 0 deletions

73
Network.php Normal file
View File

@ -0,0 +1,73 @@
<?php
function binary_mask(int $cidr_mask) {
return bindec(str_pad(str_repeat('1', $cidr_mask), 32, '0'));
}
function cidr_mask(int $binary_mask) {
return substr_count(decbin($binary_mask), '1');
}
Class Network {
public int $address;
public int $mask;
public string $name = "";
public function __construct(int $address, int $cidr_mask, string $name = "", string $description = "") {
$this->mask = binary_mask($cidr_mask);
$this->address = $address & $this->mask;
$this->name = $name;
}
public function __toString(): string {
return long2ip($this->address) . " / " . cidr_mask($this->mask);
}
public function __debugInfo(): array {
return [
"name" => $this->name,
"ip_adress" => long2ip($this->address),
"cidr_mask" => cidr_mask($this->mask),
];
}
public function contains(Network $other) {
return ($other->address & $this->mask) == $this->address;
}
}
$networks = [
new Network(ip2long("10.75.0.0"), 16, "Paris"),
new Network(ip2long("10.94.0.0"), 16, "Val-de-Marne"),
new Network(ip2long("10.232.0.0"), 16, "DiRIF"),
new Network(ip2long("10.75.128.0"), 22, "Miollis"),
new Network(ip2long("10.75.128.0"), 24, "Serveurs"),
new Network(ip2long("10.75.129.0"), 24, "Data"),
new Network(ip2long("10.75.8.0"), 22, "Crillon"),
new Network(ip2long("10.94.8.0"), 22, "Créteil Archives"),
new Network(ip2long("10.232.160.0"), 22, "Créteil l'Echat"),
];
$subnetworks = [];
foreach ($networks as $network) {
$subnetworks[] = ["network" => $network, "subnetworks" => []];
}
usort($subnetworks, function($a, $b) {
return $b["network"]->mask <=> $a["network"]->mask;
});
$networks_tree = [];
while (count($subnetworks)) {
["network" => $network1, "subnetworks" => $subnetworks1] = array_shift($subnetworks);
foreach($subnetworks as ["network" => $network2, "subnetworks" => &$subnetworks2]) {
if ($network2->contains($network1)) {
$subnetworks2[] = ["network" => $network1, "subnetworks" => $subnetworks1];
continue 2;
}
}
$networks_tree[] = ["network" => $network1, "subnetworks" => $subnetworks1];
}
var_dump($networks_tree);

17
add-network.php Normal file
View File

@ -0,0 +1,17 @@
<?php
$address = filter_input(INPUT_POST, 'address', FILTER_VALIDATE_INT, [
"options" => [
"min_range" => 0,
"max_range" => 4294967295
]
]);
$mask = filter_input(INPUT_POST, 'mask', FILTER_VALIDATE_INT, [
"options" => [
"min_range" => 0,
"max_range" => 32
]
]);
echo long2ip($address), "/", $mask;

18
db.php Normal file
View File

@ -0,0 +1,18 @@
<?php
try {
$db = new SQLite3('ipam.db');
} catch (Exception $e) {
echo $e->getMessage();
exit();
}
$db->exec(<<<SQL
CREATE TABLE IF NOT EXISTS networks (
adress INTEGER NOT NULL,
mask INTEGER NOT NULL,
nom TEXT,
description TEXT,
PRIMARY KEY (adress, masque)
);
SQL);

100
index.php Normal file
View File

@ -0,0 +1,100 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>IPAM</title>
<script src="https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/fomantic-ui@2.9.4/dist/semantic.min.css">
<script src="https://cdn.jsdelivr.net/npm/fomantic-ui@2.9.4/dist/semantic.min.js"></script>
<style>
.ui.form input[type="number"].textfield {
appearance: textfield;
}
.ui.form input[type="number"].textfield ::-webkit-inner-spin-button,
.ui.form input[type="number"].textfield ::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;
}
.ui.input.address input {
text-align: right;
}
.address :not(:first-child):not(:last-child) {
border-radius: 0 !important;
}
.address :first-child {
border-top-right-radius: 0 !important;
border-bottom-right-radius: 0 !important;
}
.address :last-child {
border-top-left-radius: 0 !important;
border-bottom-left-radius: 0 !important;
}
</style>
</head>
<body>
<main class="ui container">
<div class="ui segment">
<form action="add-network.php" method="post" class="ui form">
<div class="field">
<label for="name">Nom</label>
<input type="text" id="name" name="name">
</div>
<div class="fields">
<div class="field">
<label for="address">Adresse</label>
<div class="ui labeled address input">
<input type="number" id="octet1Input" min="0" max="255" step="1" required class="textfield">
<div class="ui label">.</div>
<input type="number" id="octet2Input" min="0" max="255" step="1" required class="textfield">
<div class="ui label">.</div>
<input type="number" id="octet3Input" min="0" max="255" step="1" required class="textfield">
<div class="ui label">.</div>
<input type="number" id="octet4Input" min="0" max="255" step="1" required class="textfield">
</div>
</div>
<input type="hidden" id="addressInput" name="address">
<div class="field">
<label>Masque</label>
<div class="ui labeled input">
<div class="ui label">/</div>
<input type="number" id="maskInput" name="mask" min="0" max="32" step="1" required class="textfield" title="Masque au format CIDR">
</div>
</div>
<script>
let otctetInputs = [octet1Input, octet2Input, octet3Input, octet4Input]
console.log(otctetInputs.forEach)
otctetInputs.forEach(input => {
input.oninput = () => {
o1 = octet1Input.valueAsNumber || 0;
o2 = octet2Input.valueAsNumber || 0;
o3 = octet3Input.valueAsNumber || 0;
o4 = octet4Input.valueAsNumber || 0;
addressInput.value = ((o1 << 24) | (o2 << 16) | (o3 << 8) | o4) >>> 0
}
input.onfocus = () => {
input.select();
}
});
[octet1Input, octet2Input, octet3Input].forEach((input, index) => {
input.onkeydown = function (event) {
if (event.key == ".") {
event.preventDefault();
otctetInputs[index + 1].focus()
}
}
})
octet4Input.onkeydown = function (event) {
if (event.key == "/") {
event.preventDefault();
maskInput.focus()
}
}
</script>
</div>
<button type="submit" class="ui button">Ajouter</button>
</form>
</div>
</main>
</body>
</html>

0
ipam.db Normal file
View File