Зачем нужна генерация ipv6 адреса ?
Так как я администрирую сервера и популярность ipv6 возрастает, у меня встала задача по генерации уникального ipv6 для каждого домена.
Провайдер выдал мне подсеть /64 и каждому домену нужно будет присвоить уникальный ipv6.
Формировать их тупо цифрами из ipv4 я не хотел.
Так как доменов на хостинге у меня не мало, генерировать ручками был не вариант, решено было сделать это скриптом (подобных скриптов в рунете не нашел).
Идея
Задача сводилась к тому, чтобы скрипт генерировал уникальный hex для каждого домена, максимум 64 бита и склеивался с нашей частью подсети.
Например у нас есть подсеть aaaa:bbbb:cccc:dddd::/64
Первая часть ipv6 адреса будет
aaaa:bbbb:cccc:dddd
Вторая часть должна генерироваться скриптом по названию домена, например для домена anton-slim.com это будет
00f6:3cb8:0975:6b24
Склеиваем части, и получаем ipv6:
aaaa:bbbb:cccc:dddd:00f6:3cb8:0975:6b24
Так же помимо простой генерации, мне понадобился файлик для настройки сети в debian, и ptr записи обратных днс для провайдера. Кроме того, помимо ipv6, мне понадобилось сформировать такую же базу по ipv4, поэтому я решил сделать это все в одном скрипте.
Я решил сделать это скриптом на PHP и использовать в bash (через консоль).
Скрипт назвал domain_to_ipv6.php.
Пример использования
Скрипт на вход принимает stdIn поток со списком доменов (или с одним доменом), где каждый домен отдельной строкой.
1 |
echo 'ipv4 domain' | php domain_to_ipv6.php 'ipv6_network/ipv6_subnet' 'ipv4_network/ipv4_subnet' --type=type |
или
1 |
cat 'domain_list.txt' | php domain_to_ipv6.php 'ipv6_network/ipv6_subnet' 'ipv4_network/ipv4_subnet' --type=type |
или
1 |
php domain_to_ipv6.php 'ipv6_network/ipv6_subnet' 'ipv4_network/ipv4_subnet' --type=type < domain_list.txt |
(domain_list.txt — список доменов на каждой строке, если указать пары ipv4 и домен то дополнительно будет выведен блок с ipv4 для домена ).
Где:
stdIn: домен или ipv4 и домен (можно несколько на каждой строке)
ipv6_network/ipv6_subnet — ipv6 подсеть используемая для доменов
ipv4_network/ipv4_subnet — ipv4 подсеть используемая для доменов
—type=csv (генерация CSV файла со всеми данными)
—type=ptr (генерация PTR для отправки провайдеру)
—type=debian-net (конфиг для сети в debian /etc/network/interfaces)
stdOut: текстовый блок в соответствии с выбранным форматом —type
Пример генерации CSV:
1 |
php domain_to_ipv6.php '2a01:4f8:120:8183::/64' 78.46.95.226/255.255.255.254 --csv < domain_list.txt > domain_list_ipv4_ipv6.csv |
будет сформирован csv файл domain_list_ipv4_ipv6.csv:
Генерация куска /etc/network/interfaces для Debian:
1 |
echo '78.46.95.227 anton-slim.com' | php domain_to_ipv6.php '2a01:4f8:120:8183::/64' 78.46.95.226/255.255.255.254 --type=debian-net |
получим
### subnet ipv4: 78.46.95.226 / 255.255.255.254
# anton-slim.com
iface eth0 inet static
address 78.46.95.227
netmask 255.255.255.254
### subnet ipv6: 2a01:4f8:120:8183::::/64
# anton-slim.com
iface eth0 inet6 static
address 2a01:4f8:120:8183:f6:3cb8:975:6b24
netmask 128
Генерация PTR для провайдера:
1 |
echo '78.46.95.227 anton-slim.com' | php domain_to_ipv6.php '2a01:4f8:120:8183::/64' 78.46.95.226/255.255.255.254 --type=ptr |
получим
### ipv4 ptr
anton-slim.com (78.46.95.227)
227.95.46.78.in-addr.arpa IN PTR anton-slim.com.
### ipv6 ptr
anton-slim.com (2a01:4f8:120:8183:f6:3cb8:975:6b24)
4.2.b.6.5.7.9.0.8.b.c.3.6.f.0.0.3.8.1.8.0.2.1.0.8.f.4.0.1.0.a.2.ip6.arpa IN PTR anton-slim.com.
Исходный код скрипта domain_to_ipv6.php
Внимание ! Данный скрипт работает только на 64 битных системах.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 |
<?php $subnet = $_SERVER['argv'][1]; # subnet ipv6 $subnet = explode('::', $subnet); $netmask = array_pop($subnet); $netmask = explode('/', $netmask); $netmask = array_pop($netmask); $subnet = explode(':', $subnet[0]); $subnet = array_filter($subnet); $subnet_ipv4 = isset($_SERVER['argv'][2]) ? $_SERVER['argv'][2] : '0.0.0.0/255.255.255.0'; $subnet_ipv4 = explode('/', $subnet_ipv4); $netmask_ipv4 = array_pop($subnet_ipv4); $subnet_ipv4 = $subnet_ipv4[0]; $cur_type = false; if (isset($_SERVER['argv'][3])) { $type = explode('=', $_SERVER['argv'][3]); if (isset($type[1]) && in_array($type[1], array('ptr', 'csv', 'debian-net'))) { $cur_type = $type[1]; } } if (count($subnet) < 8) { $subnet += array_fill(count($subnet), 8 - count($subnet), ''); } $buf = file_get_contents('php://stdin'); if ($buf == '') die('empty list of domains'); $domains = explode(PHP_EOL, trim($buf)); if (!$domains) die('empty list of domains'); $domain_list = array(); foreach ($domains as $d) { $d = explode(' ', $d); $ipv4 = null; if (count($d) >= 2) { $ipv4 = array_shift($d); } $d = $d[0]; $ipv6 = getDomainIpv6($d, $subnet); $ipv6_full = getDomainIpv6($d, $subnet, true); $domain_list[] = array('d' => $d, 'ipv4' => $ipv4, 'ipv6' => $ipv6, 'ipv6_full' => $ipv6_full, 'ptrv4' => getIpv4Ptr($ipv4), 'ptrv6' => getIpv6Ptr($ipv6)); } switch ($cur_type) { case 'csv': echo '#domain#;#ipv4#;#ptr v4#;#ipv6 short#;#ipv6 full#;#ptr v6#' . PHP_EOL . PHP_EOL; foreach ($domain_list as $line) { echo implode(";", array($line['d'], $line['ipv4'], $line['ptrv4'], $line['ipv6'], $line['ipv6_full'], $line['ptrv6'])); echo PHP_EOL . PHP_EOL; } break; case 'ptr': $head_printed = false; foreach ($domain_list as $line) { if ($line['ipv4'] == null) { continue; } if (!$head_printed) { echo '### ipv4 ptr' . PHP_EOL; $head_printed = true; } echo PHP_EOL . "{$line['d']} ({$line['ipv4']})"; echo PHP_EOL . "{$line['ptrv4']} IN PTR {$line['d']}." . PHP_EOL; echo PHP_EOL; } // 123.45.67.89.in-addr.arpa. IN PTR mail.mydomain.ru. if ($head_printed) { echo PHP_EOL; } echo '### ipv6 ptr' . PHP_EOL; foreach ($domain_list as $line) { echo PHP_EOL . "{$line['d']} ({$line['ipv6']})"; echo PHP_EOL . "{$line['ptrv6']} IN PTR {$line['d']}." . PHP_EOL; echo PHP_EOL; } break; case 'debian-net': $head_printed = false; foreach ($domain_list as $line) { if ($line['ipv4'] == null) { continue; } if (!$head_printed) { echo '### subnet ipv4: ' . $subnet_ipv4 . ' / ' . $netmask_ipv4 . PHP_EOL . PHP_EOL; $head_printed = true; } echo '# ' . $line['d']; echo PHP_EOL . "iface eth0 inet static" . PHP_EOL . " address {$line['ipv4']}" . PHP_EOL . " netmask {$netmask_ipv4}" . PHP_EOL; echo PHP_EOL; } if ($head_printed) { echo PHP_EOL . PHP_EOL; } echo '### subnet ipv6: ' . implode(':', $subnet) . '/' . $netmask . PHP_EOL . PHP_EOL; foreach ($domain_list as $line) { echo '# ' . $line['d']; echo PHP_EOL . "iface eth0 inet6 static" . PHP_EOL . " address {$line['ipv6']}" . PHP_EOL . " netmask 128" . PHP_EOL; echo PHP_EOL; } break; default: echo 'unknown type, the right type is: --type=csv --type=ptr or --type=debian-net' . PHP_EOL; } die; function getDomainHex($s) { $val = '0'; $ret = ''; for ($i=0; $i<strlen($s); $i++) { $val = bcmul($val, 10); $val = bcadd($val, strval(ord($s[$i]) & 0x0F)); } // От -9 223 372 036 854 775 808 до 9 223 372 036 854 775 807 // 24565343044463245653430444632456534304446324565343044463 == 4b7007961c97a329 $f = floatval($val); $domainHex = float2hex($f); return $domainHex; } function getDomainIpv6($domain, $subnet, $full = false) { $domainHex = getDomainHex($domain); $ipv6_parts = str_split(strrev($domainHex), 4); if (count($ipv6_parts) > 4) { $ipv6_parts = array_slice($ipv6_parts, 0, 4); } $ipv6 = $subnet; array_splice($ipv6, -1*count($ipv6_parts), count($ipv6_parts), $ipv6_parts); if ($full) { $ipv6 = array_map(function($v) { $v = str_pad(strval($v), 4, '0', STR_PAD_LEFT); return $v; }, $ipv6); } else { $ipv6 = array_map(function($v) { $v = ltrim(strval($v), '0'); return $v; }, $ipv6); } $ipv6_str = implode(':', $ipv6); $ipv6_str = preg_replace('![:]{2,}!', '::', $ipv6_str); return $ipv6_str; } function getIpv6Ptr($ip) { $addr = inet_pton($ip); $unpack = unpack('H*hex', $addr); $hex = $unpack['hex']; $arpa = implode('.', array_reverse(str_split($hex))) . '.ip6.arpa'; return $arpa; } function getIpv4Ptr($ip) { if ($ip === null) { return null; } $arpa = implode('.', array_reverse(explode('.', $ip))) . '.in-addr.arpa'; return $arpa; } function strToHex($string){ $hex = ''; for ($i=0; $i<strlen($string); $i++){ $ord = ord($string[$i]); $hexCode = dechex($ord); $hex .= substr('0'.$hexCode, -2); } return strToUpper($hex); } function hexToStr($hex){ $string=''; for ($i=0; $i < strlen($hex)-1; $i+=2){ $string .= chr(hexdec($hex[$i].$hex[$i+1])); } return $string; } function hex2float($strHex) { $hex = sscanf($strHex, "%02x%02x%02x%02x%02x%02x%02x%02x"); $hex = array_reverse($hex); $bin = implode('', array_map('chr', $hex)); $array = unpack("dnum", $bin); return $array['num']; } function float2hex($num) { $bin = pack("d", $num); $hex = array_map('ord', str_split($bin)); $hex = array_reverse($hex); $strHex = vsprintf("%02x%02x%02x%02x%02x%02x%02x%02x", $hex); return $strHex; } |
Скрипт из строки с названием домена формирует число (24565343044463). Вероятности совпадения нет, так как каждый символ домена суммируется с общим числом и возводится в степень 10. Далее из этого числа формируется 64 битное бинарное число 11110110001111001011100000001001011101010110101100100100, затем оно преобразуется в HEX длинною 16 символов (006fc38b9057b642), затем оно переворачивается (00f63cb809756b24). Далее этот HEX преобразуется в строку с разбивкой по 4 октета (00f6:3cb8:0975:6b24), это и есть наша 2-я часть ipv6 адреса.
Диапазона 64 битного числа должно хватить с запасном. Длина домена по RFC не может превышать 255 символов, например для домена zzzzz…z (255 символов), hex сумма его символов = 74e2 f1a8 714a 7119, то есть даже еще остается диапазон.
Пользуйтесь на здоровье :=)
Добавить комментарий