Come convertire IPv6 da binario per la memorizzazione in MySQL
Domanda
Sto cercando di memorizzare indirizzi IPv6 in MySQL 5.0 in modo efficiente. Ho letto le altre domande relative a questo, come questo . L'autore di questa domanda alla fine ha scelto per due campi BIGINT. Le mie ricerche hanno anche alzato un altro meccanismo usato spesso: Utilizzo di un DECIMAL (39,0) per memorizzare l'indirizzo IPv6. Ho due domande al riguardo.
- Quali sono i vantaggi e gli svantaggi di usare DECIMAL (39,0) rispetto agli altri metodi, come 2 * BIGINT?
- Come faccio a convertire (in PHP) dal formato binario come restituito da inet_pton () per un formato stringa decimale utilizzabile da MySQL, e come faccio a convertire indietro in modo che posso pretty-stampa con inet_ntop ()?
Soluzione 2
Ecco le funzioni che ora utilizzare per convertire gli indirizzi IP da e per formato decimale (39,0). Essi sono chiamati inet_ptod e inet_dtop per "presentazione a decimale" e "decimale a presentazione". Ha bisogno di supporto IPv6 e bcmath in PHP.
/**
* Convert an IP address from presentation to decimal(39,0) format suitable for storage in MySQL
*
* @param string $ip_address An IP address in IPv4, IPv6 or decimal notation
* @return string The IP address in decimal notation
*/
function inet_ptod($ip_address)
{
// IPv4 address
if (strpos($ip_address, ':') === false && strpos($ip_address, '.') !== false) {
$ip_address = '::' . $ip_address;
}
// IPv6 address
if (strpos($ip_address, ':') !== false) {
$network = inet_pton($ip_address);
$parts = unpack('N*', $network);
foreach ($parts as &$part) {
if ($part < 0) {
$part = bcadd((string) $part, '4294967296');
}
if (!is_string($part)) {
$part = (string) $part;
}
}
$decimal = $parts[4];
$decimal = bcadd($decimal, bcmul($parts[3], '4294967296'));
$decimal = bcadd($decimal, bcmul($parts[2], '18446744073709551616'));
$decimal = bcadd($decimal, bcmul($parts[1], '79228162514264337593543950336'));
return $decimal;
}
// Decimal address
return $ip_address;
}
/**
* Convert an IP address from decimal format to presentation format
*
* @param string $decimal An IP address in IPv4, IPv6 or decimal notation
* @return string The IP address in presentation format
*/
function inet_dtop($decimal)
{
// IPv4 or IPv6 format
if (strpos($decimal, ':') !== false || strpos($decimal, '.') !== false) {
return $decimal;
}
// Decimal format
$parts = array();
$parts[1] = bcdiv($decimal, '79228162514264337593543950336', 0);
$decimal = bcsub($decimal, bcmul($parts[1], '79228162514264337593543950336'));
$parts[2] = bcdiv($decimal, '18446744073709551616', 0);
$decimal = bcsub($decimal, bcmul($parts[2], '18446744073709551616'));
$parts[3] = bcdiv($decimal, '4294967296', 0);
$decimal = bcsub($decimal, bcmul($parts[3], '4294967296'));
$parts[4] = $decimal;
foreach ($parts as &$part) {
if (bccomp($part, '2147483647') == 1) {
$part = bcsub($part, '4294967296');
}
$part = (int) $part;
}
$network = pack('N4', $parts[1], $parts[2], $parts[3], $parts[4]);
$ip_address = inet_ntop($network);
// Turn IPv6 to IPv4 if it's IPv4
if (preg_match('/^::\d+.\d+.\d+.\d+$/', $ip_address)) {
return substr($ip_address, 2);
}
return $ip_address;
}
Altri suggerimenti
Siamo andati per una colonna VARBINARY(16)
invece e utilizzare inet_pton()
e inet_ntop()
a fare le conversioni:
https://github.com/skion/mysql-udf-ipv6
Le funzioni possono essere caricati in un server MySQL in esecuzione e vi darà INET6_NTOP
e INET6_PTON
in SQL, proprio come le note funzioni INET_NTOA
e INET_ATON
per IPv4.
Edit: Ci sono funzioni compatibili in MySQL ora, solo con href="http://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html#function_inet6-ntoa" rel="nofollow noreferrer"> nomi . Utilizzare solo il sopra, se si è in pre-5.6 di MySQL e sono alla ricerca di un futuro comodo percorso di aggiornamento.
DECIMAL (39)
Pro:
- Funziona con operatori aritmetici di base (come + e -).
- Opere con indicizzazione di base (esatto o intervallo).
- Il formato è visualizzare amichevole.
Contro:
- può accettare di valori gamma per IPv6.
- Non è un meccanismo di archiviazione molto efficiente.
- Può causare confusione su quale operatori matematici o funzioni funzionano e quali no.
BINARIO (16) ...
Pro:
- formato più efficiente per la rappresentazione esatta.
- Opere con indicizzazione di base (esatto e la gamma).
- Opere con prefisso indicizzazione per i prefissi che sono multipli di 8 bit.
- Negozi solo valori IPv6 validi (anche se non garantisce la validità di indirizzamento).
- MySQL nelle versioni successive ha funzioni che supportano le conversioni per questo formato da e per rappresentazioni IPv6 (ma non 4in6).
Contro:
- Non adatto per la visualizzazione.
- non è amichevole con gli operatori o funzioni significato per i numeri.
BINARIO (39) ...
Questo è per gli indirizzi completi (utilizzando hexdec anche per 4in6). Può anche essere ASCII piuttosto che binario.
Pro:
- leggibile (se si può chiamare IPv6).
- supporta l'indicizzazione di base (esatto e la gamma).
- Supporti indicizzazione prefisso per multiplo di 4 bit.
- standard IPv6 Direttamente compatibile. Nessuna conversione necessaria.
Contro:
- Non funziona bene con qualsiasi funzioni o operatori matematici.
- storage più inefficiente.
- può permettere rappresentazioni non validi.
Curiosità:
- Ottiene complesso se si vuole le cose come case insensitive.
- IPv6 ha altri formati di visualizzazione, anche se l'utilizzo di tali marche per ulteriori complessità, come si possono avere due rappresentazioni dello stesso indirizzo o si perde ricerche gamma. Può anche finire per dover renderlo 45 byte lungo o utilizzando varchar / varbinary.
- varianze di questo può supportare preservare l'indirizzo di origine ricevuto. Che può raramente desiderare, ma quando si perde un sacco di vantaggi.
- Rimuovere i separatori con il formato pieno e proprio negozio è come stringa esadecimale per meno fastidi e un po 'di più l'efficienza. Si può prendere questo una lunga strada se il prefisso indicizzazione è importante (BINARY (128)).
BIGINT UNSIGNED * 2
Pro:
- Funziona con operatori matematici e funzioni con l'avvertenza di dover fare cose in più intorno ad esso essendo due colonne.
- efficiente ma ancora una volta con l'avvertenza che essendo due colonne aggiungere un po 'sopra la testa.
- Opere con gli indici di base (esatte, range).
- Opere con indice prefisso durante prefisso è 64 bit.
- Display disposizione amichevole.
Contro:
- Due colonne rende non-atomico e significa il raddoppio di un sacco di operazioni su di esso.
Curiosità:
- Molti linguaggi e sistemi moderni danno a 64 bit interi, ma non senza segno. Firmato è problematica. I numeri negativi presenti come inferiore positivo ma le loro sequenze di bit sono più elevati. Per questo motivo è comune, invece di utilizzare 4 * unsigned int.
- Allo stesso modo la gente potrebbe disgregare per il prefisso l'indicizzazione e si può andare almeno fino a 8 bit (TINYINT senza segno). Alcune persone potrebbero anche fare uso della BIT (1) tipo per l'indicizzazione full prefisso, assumendo co indici postulare MySQL sui tipi di bit in modo corretto.
- Sempre analogamente, con quattro colonne alcune operazioni che richiedono cose come portare dall'alto all'altro sono paradossalmente più facile a causa slack bit durante calcoli (valori intermedi in calcoli possono essere ancora 64 bit).
Riepilogo
La gente userà formati differenti per diversi motivi. Compatibilità può essere un motivo e che dipende da ciò che era stato fatto per IPv4. Altri dipendono da come gli indirizzi vengono utilizzati e optimisations intorno a quello. Si può visualizzare più di un approccio in uso.
B16 è un buon approccio di default dal momento che è il più efficiente e senza problemi.
Per le conversioni in PHP si può fare a mano se si ricerca:
- GMP o bcmath operatori
- PHP movimentazione numero e bit a bit, essere particolarmente sensibile limitazioni int o float nonché funzioni che dipendono da loro che altrimenti potrebbero sembrare utile
- I formati IPv6
- pacchetto / decomprimere, BIN2HEX / HEX2BIN.
Suggerirei però utilizzando una libreria comune per affrontare i vari formati di visualizzazione di IPv6.