2009-02-27 12 views
17

Qualcosa comeC'è un modo per abbinare IP con IP + CIDR direttamente dalla query SELECT?

SELECT COUNT(*) AS c FROM BANS WHERE typeid=6 AND (SELECT ipaddr,cidr FROM BANS) MATCH AGAINST 'this_ip';

in modo da non prima recuperare tutti i record da DB e poi abbinare uno per uno.

Se c> 0 sono stati abbinati. tavolo

BANS:

id int auto incr PK 
typeid TINYINT (1=hostname, 4=ipv4, 6=ipv6) 
ipaddr BINARY(128) 
cidr INT 
host VARCHAR(255) 

DB: MySQL 5

IP e tipo IPv (4 o 6) è noto quando l'interrogazione.

IP è per esempio :: 1 in formato binario

VIETATO IP è per esempio :: 1/64

+0

Raspi, So che questa domanda è vecchio, ma ... è la vostra colonna cidr il numero di one-bit nella maschera di rete, quindi per IPv6 è sempre 64 e per IPv4 è un numero compreso tra 0 e 32? Anche se suppongo che 0 vieterebbe tutti gli indirizzi ... :) – ErikE

risposta

22

Ricordate che l'IPS non sono un indirizzo testuale, ma un ID numerico. Ho una situazione simile (stiamo facendo ricerche geo-ip) e se memorizzi tutti i tuoi indirizzi IP come numeri interi (ad esempio, il mio indirizzo IP è 192.115.22.33 quindi è archiviato come 3228767777), allora puoi cercare gli IP facilmente utilizzando gli operatori di spostamento a destra.

Lo svantaggio di tutti questi tipi di ricerche è che non è possibile trarre vantaggio dagli indici e si deve eseguire una scansione completa della tabella ogni volta che si effettua una ricerca. Lo schema sopra può essere migliorato memorizzando sia l'indirizzo IP di rete della rete CIDR (l'inizio dell'intervallo) sia l'indirizzo di trasmissione (la fine dell'intervallo), quindi per esempio per memorizzare 192.168.1.0/24 è possibile memorizzare due colonne:

network  broadcast 
3232235776, 3232236031 

e quindi si può a partita che è sufficiente fare

SELECT count(*) FROM bans WHERE 3232235876 >= network AND 3232235876 <= broadcast 

Ciò permetterà di memorizzare reti CIDR nel database e abbinarli contro gli indirizzi IP in modo rapido ed efficiente sfruttando rapido indici numerici.

Nota della discussione sotto:

MySQL 5.0 include un'ottimizzazione delle query a distanza denominata "index merge intersect", che permette di velocizzare tali query (ed evitare di scansione completa della tabella), a condizione che:

  • C'è un indice a più colonne che corrisponde esattamente alle colonne nella query, in ordine. Quindi, per l'esempio di query sopra riportato, l'indice dovrebbe essere (network, broadcast).
  • Tutti i dati possono essere recuperati dall'indice. Questo è vero per COUNT(*), ma non è vero per SELECT * ... LIMIT 1.

MySQL 5.6 include un'ottimizzazione denominata MRR che velocizza anche il recupero completo delle righe, ma non rientra nell'ambito di questa risposta.

+0

Sfortunatamente, MySQL non può combinare due indici insieme. Ovviamente proverà a utilizzare l'indice su una rete o su una trasmissione, ma poiché gli indirizzi IP sono distribuiti in modo uniforme, la scansione di una tabella completa sarà molto più efficiente in questo caso. – Quassnoi

+0

Questo mito è un po 'obsoleto :-) a partire da MySQL 5.0, il server può unire più indici (http://dev.mysql.com/doc/refman/5.1/en/index-merge-optimization.html) . Indipendentemente da ciò, non riesco a vedere come una scansione di una tabella completa sia migliore di un indice, anche se è solo un indice. – Guss

+0

Se il filtro restituisce più di circa il 10% o le righe, la scansione completa della tabella è migliore. Provalo :) – Quassnoi

0

Hmmm. È possibile creare una tabella delle maschere cidr, aggiungerla e quindi confrontare l'indirizzo IP (& in MySQL) con la maschera con il blocco indirizzi IP. Farebbe quello che vuoi?

Se non si desidera creare una tabella maschera, è possibile calcolare la maschera come -1 << (x-cidr) con x = 64 o 32 in base.

3

Per IPv4, è possibile utilizzare:

SET @length = 4; 

SELECT INET_NTOA(ipaddr), INET_NTOA(searchaddr), INET_NTOA(mask) 
FROM (
    SELECT 
     (1 << (@length * 8)) - 1 & ~((1 << (@length * 8 - cidr)) - 1) AS mask, 
     CAST(CONV(SUBSTR(HEX(ipaddr), 1, @length * 2), 16, 10) AS DECIMAL(20)) AS ipaddr, 
     CAST(CONV(SUBSTR(HEX(@myaddr), 1, @length * 2), 16, 10) AS DECIMAL(20)) AS searchaddr 
    FROM ip 
) ipo 
WHERE ipaddr & mask = searchaddr & mask 
+0

Grazie. Ero abbastanza sicuro di non aver bisogno di un tavolo per fare CIDR, ma potevo calcolare/creare le maschere al volo in questo modo. Credo che la maschera possa essere semplificata in ~ ((1 << cidr) -1), dove ((1 << cidr) -1) imposta i bit cidr più a destra veri e NOT, IE: the ~ inverte tutti i bit nel lungo non firmato in modo che i bit cidr più a destra siano falsi e tutti quelli a sinistra siano veri. Il calc Win7 +, utilizzando l'interfaccia del programmatore, è molto utile in questi casi. – RocketRoy

-2

MySQL: Convertire Intervallo IP per CIDR

Ho trascorso diverse ore alla ricerca di un modo per utilizzare MySQL per prendere un intervallo di indirizzi IP e di uscita di un indirizzo formattato CIDR che copre l'intervallo IP adatto per il mantenimento di un blacklist IP .

Specifiche del mio ambiente e requisiti Utilizzo OpenWeb Analytics per registrare il traffico sul mio sito Web e diversi che gestisco. Ho sviluppato un processo per estrarre indirizzi IP distinti registrati da OWA e quindi unire elementi di dati geografici sui record che vengono quindi mantenuti in una tabella personalizzata. Dotata di questa tabella di dati IP-to-Location offre la possibilità di generare report su hit provenienti da fonti indesiderate o fonti irrilevanti per la mia attività locale - Cina, Giappone, Corea, Russia, ecc. Ciò ha portato a un numero crescente di IP indirizzi; molti dei quali rientrano nella stessa rete. Per facilitare i requisiti di manutenzione per il file .htaccess del mio server, diventa vantaggioso poter registrare indirizzi IP in blacklist in formato CIDR. Ciò porta quindi alla necessità di essere in grado e di produrre il CIDR dagli indirizzi IP già registrati.

L'approccio MySQL La maggior parte degli host Web fornisce accesso ai database MySQL. Tuttavia, pochi o nessuno consentono la possibilità di creare funzioni di database. Questo ha complicato un po 'la codifica. SQL

campione disponibile presso http://blog.watsoninfotech.com/2012/12/mysql-convert-ip-range-to-cidr.html

+1

Ho difficoltà a vedere come questo * direttamente * è correlato alla domanda posta. –

2

indirizzi IPv4, indirizzi di rete e maschere di rete sono tutti UINT32 i numeri e sono presentati in forma leggibile come "tratteggiate-quad". Il codice della tabella di routing nel kernel esegue un confronto AND bit-speed molto veloce quando si controlla se un indirizzo si trova in un dato spazio di rete (rete/maschera di rete). Il trucco qui è di memorizzare gli indirizzi IP a punti tratteggiati, gli indirizzi di rete e le maschere di rete nelle tue tabelle come UINT32, e quindi eseguire lo stesso E bit a 32 bit per la tua corrispondenza. ad esempio

SET @test_addr = inet_aton('1.2.3.4'); 
SET @network_one = inet_aton('1.2.3.0'); 
SET @network_two = inet_aton('4.5.6.0'); 
SET @network_netmask = inet_aton('255.255.255.0'); 

SELECT (@test_addr & @network_netmask) = @network_one AS IS_MATCHED; 
+------------+ 
| IS_MATCHED | 
+------------+ 
|   1 | 
+------------+ 

SELECT (@test_addr & @network_netmask) = @network_two AS IS_NOT_MATCHED; 
+----------------+ 
| IS_NOT_MATCHED | 
+----------------+ 
|    0 | 
+----------------+