2012-04-03 10 views
5

Ho una tabella A con indirizzi IP (ipNumeric) memorizzati come unsigned int e un tavolo B con sottoreti (subnetNumeric):Controllare se IP è in sottorete

INET_NTOA(ipNumeric) = 192.168.0.1 
INET_NTOA(subnetNumeric) = 192.168.0.0 

mi piacerebbe verificare se questo IP è un membro di una sottorete.

Le sottoreti sono di classe A, B e C.

Questo è possibile fare in un tempo ragionevole in MySQL al volo o dovrebbe gamme di sottorete essere precalcolate?

risposta

11

Certo, è fattibile. L'idea è che calcoliamo la subnet mask impostando i bit più significativi su 1, tanti quanti sono dettati dalla classe di sottorete. Per una classe C, sarebbe

SELECT -1 << 8; 

Quindi, E la subnet mask con l'indirizzo IP che si ha; se l'IP si trova all'interno della sottorete, il risultato dovrebbe essere uguale all'indirizzo della subnet - routing di rete standard. Così si finisce con:

SELECT (-1 << 8) & INET_ATON("192.168.0.1") = INET_ATON("192.168.0.0"); 

Aggiornamento: Sì, è necessario conoscere la classe di rete o la maschera di sottorete (che è informazioni equivalenti). Considerare come si può gestire il caso in cui la subnet è X.Y.0.0 se non si dispone di queste informazioni. È questo X.Y.0.0/16 o X.Y.0.0/8 dove il terzo ottetto succede semplicemente da 0? Non c'è modo di sapere.

Se si conosce la subnet mask, quindi la query può essere scritta come

SELECT (-1 << (33 - INSTR(BIN(INET_ATON("255.255.255.0")), "0"))) & 
     INET_ATON("192.168.0.1") = INET_ATON("192.168.0.0"); 
+0

Grazie, ma è questo dipende conoscendo la classe sottorete? – matiasf

+0

@matiasf: lo è. Dai un'occhiata all'aggiornamento. – Jon

+0

Ottima risposta - ma che dire di IPv6? È possibile? – TSG

0

Innanzitutto creare tabelle per sostenere il test.

CREATE TABLE a (ipNumeric INT UNSIGNED); 
INSERT INTO a (ipNumeric) VALUES(INET_ATON("172.16.40.101")); 
INSERT INTO a (ipNumeric) VALUES(INET_ATON("192.168.1.12")); 
INSERT INTO a (ipNumeric) VALUES(INET_ATON("10.1.5.51")); 
INSERT INTO a (ipNumeric) VALUES(INET_ATON("10.7.1.78"));  
CREATE TABLE b (subnetNumeric INT UNSIGNED); 
INSERT INTO b (subnetNumeric) VALUES (INET_ATON("192.168.0.0")); 
INSERT INTO b (subnetNumeric) VALUES (INET_ATON("10.1.0.0")); 
INSERT INTO b (subnetNumeric) VALUES (INET_ATON("172.16.0.0")); 

Ora fare un prescelto ritrovamento corrisponde tra le tabelle in base all'indirizzo & maschera = sottorete. Qui assumiamo sottoreti di classe B (255.255.0.0). Non è necessario spostare i bit in quanto possiamo utilizzare la funzione INET_ATON().

SELECT INET_NTOA(a.ipNumeric),INET_NTOA(b.subnetNumeric) FROM a,b 
     WHERE (a.ipNumeric & INET_ATON("255.255.0.0")) = b.subnetNumeric; 

+------------------------+----------------------------+ 
| INET_NTOA(a.ipNumeric) | INET_NTOA(b.subnetNumeric) | 
+------------------------+----------------------------+ 
| 192.168.1.12   | 192.168.0.0    | 
| 10.1.5.51    | 10.1.0.0     | 
| 172.16.40.101   | 172.16.0.0     | 
+------------------------+----------------------------+ 
0

Questa è la funzione MySQL che usiamo.

DELIMITER $$ 
DROP FUNCTION IF EXISTS `ip_in_subnet_mask`$$ 
CREATE DEFINER=`root`@`%` FUNCTION `ip_in_subnet_mask`(ip VARCHAR(20), subnet VARCHAR(20), netmask VARCHAR(20)) RETURNS TINYINT(1) 
    DETERMINISTIC 
BEGIN 
    RETURN (INET_ATON(ip) & INET_ATON(netmask)) = INET_ATON(subnet); 
END$$ 
DELIMITER ; 

ad es. Trova tutte le righe in cui t.ip è nella gamma 192.168.0.x dove 0 = x < < = 255

SELECT * 
FROM t 
WHERE 
    ip_in_subnet_mask(t.ip, "192.168.0.0", "255.255.255.0") 
0

ho una soluzione per quando gli indirizzi sono memorizzati come stringhe. Quindi se vuoi fare quella parte del tuo algoritmo, puoi usare la mia soluzione qui.

È un po 'complicato, specialmente per IPv6. In IPv6 possono essere opzionalmente dei segmenti compressi, come 1 :: 1 che è equivalente a 1: 0: 0: 0: 0: 0: 0: 1, che è la ragione principale per cui è complicato.

The IPAddress Java library produrrà mysql SQL per cercare indirizzi in una determinata subnet. Il collegamento descrive questo problema in modo più dettagliato. Disclaimer: io sono il project manager.

L'algoritmo di base consiste nel prendere la sezione di rete dell'indirizzo di sottorete, quindi prendere ogni variante di quella sezione (ad esempio le due stringhe sopra riportate sono varianti dell'indirizzo completo 1 :: 1), quindi contare il numero di segmenti separatori, quindi eseguire una sottostringa mysql sull'indirizzo corrispondente, ma anche contare i separatori totali nell'indirizzo che corrisponde.

Ecco il codice di esempio:

public static void main(String[] args) throws IPAddressStringException { 
    System.out.println(getSearchSQL("columnX", "1.2.3.4/16")); 
    System.out.println(getSearchSQL("columnX", "1:2:3:4:5:6:7:8/64")); 
    System.out.println(getSearchSQL("columnX", "1::8/64")); 
} 

static String getSearchSQL(String expression, String ipAddressStr) throws IPAddressStringException { 
    IPAddressString ipAddressString = new IPAddressString(ipAddressStr); 
    IPAddress ipAddress = ipAddressString.toAddress(); 
    IPAddressSection networkPrefix = ipAddress.getNetworkSection(ipAddress.getNetworkPrefixLength(), false); 
    StringBuilder sql = new StringBuilder("Select rows from table where "); 
    networkPrefix.getStartsWithSQLClause(sql, expression); 
    return sql.toString(); 
} 

Ouptut:

Select rows from table where (substring_index(columnX,'.',2) = '1.2') 
Select rows from table where (substring_index(columnX,':',4) = '1:2:3:4') 
Select rows from table where ((substring_index(columnX,':',4) = '1:0:0:0') OR ((substring_index(columnX,':',2) = '1:') AND (LENGTH (columnX) - LENGTH(REPLACE(columnX, ':', '')) <= 5)))