Existe maneira de combinar IP com IP + CIDR diretamente da consulta SELECT?
Pergunta
Algo como
SELECT COUNT(*) AS c FROM BANS WHERE typeid=6 AND (SELECT ipaddr,cidr FROM BANS) MATCH AGAINST 'this_ip';
Assim você não primeiro buscar todos os registros de banco de dados e, em seguida, combiná-los um por um.
Se c> 0, em seguida, foram pareados.
PROIBIÇÕES tabela:
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 ou 6) é conhecida, quando a consulta.
IP é, por exemplo :: 1 no formato binário
PROIBIDOS IP é, por exemplo :: 1/64
Solução
Lembre-se que IPs não são um endereço textual, mas uma identificação numérica. Eu tenho uma situação semelhante (que estamos fazendo pesquisas geo-IP), e se você armazenar todos os seus endereços IP como inteiros (por exemplo, o meu endereço IP é 192.115.22.33 por isso é armazenado como 3228767777), então você pode consultar IPs facilmente usando operadores de deslocamento para a direita.
A desvantagem de todos estes tipos de pesquisas é que você não pode beneficiar de índices e você tem que fazer uma varredura completa da tabela sempre que você fizer uma pesquisa. O esquema acima pode ser melhorada armazenando o endereço IP de rede da rede CIDR (o início do intervalo) e o endereço de broadcast (o fim da faixa), de modo que, por exemplo, para armazenar 192.168.1.0/24 você pode armazenar dois colunas:
network broadcast
3232235776, 3232236031
E então você pode combiná-lo você simplesmente fazer
SELECT count(*) FROM bans WHERE 3232235876 >= network AND 3232235876 <= broadcast
Isto permitem armazenar redes CIDR no banco de dados e combiná-los contra endereços IP de forma rápida e eficiente, tirando partido de índices numéricos rápidos.
Nota do debate abaixo :
MySQL 5.0 inclui um variado consulta otimização chamado " índice de fusão cruzam ", que permite acelerar essas consultas (e evitar varreduras de tabela cheia), desde que:
- Há um índice multi-coluna que corresponde exatamente as colunas na consulta, em ordem. Então -. Para a consulta de exemplo acima, o índice teria de ser
(network, broadcast)
- Todos os dados podem ser recuperados a partir do índice. Isto é verdade para
COUNT(*)
, mas não é verdade paraSELECT * ... LIMIT 1
.
MySQL 5.6 inclui uma otimização chamado MRR que também acelerar a recuperação de linha inteira, mas isso está fora do escopo desta resposta.
Outras dicas
endereços IPv4, endereços de rede e máscaras de rede são todos os números UINT32 e estão apresentadas de forma legível como "pontilhado quads". O código de tabela de roteamento no executa de kernel um muito rápido bit a bit E comparação ao verificar se um endereço está em um determinado espaço de rede (network / máscara de rede). O truque aqui é para armazenar os endereços IP pontilhada-quad, endereços de rede e máscaras de rede em suas tabelas como UINT32, e em seguida, executar o mesmo 32-bit bit-wise E para sua correspondência. eg
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 |
+----------------+
Para IPv4
, você pode usar:
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
Hmmm. Você poderia construir uma tabela das máscaras CIDR, juntá-lo, e então comparar o ip anded (&
em MySQL) com a máscara com o ipaddress bloco proibição. Que teria que fazer o que você quer?
Se você não quiser construir uma tabela de máscara, você pode calcular a máscara como -1 << (x-cidr)
com x = 64
ou 32
dependendo.
intervalos de endereços IP Generating como inteiros
Se o seu banco de dados não suporta operações de fantasia bit a bit, você pode usar uma simplificado inteiro abordagem.
O exemplo a seguir está usando PostgreSQL:
select (cast(split_part(split_part('4.0.0.0/8', '/', 1), '.', 1) as bigint) * (256 * 256 * 256) +
cast(split_part(split_part('4.0.0.0/8', '/', 1), '.', 2) as bigint) * (256 * 256 ) +
cast(split_part(split_part('4.0.0.0/8', '/', 1), '.', 3) as bigint) * (256 ) +
cast(split_part(split_part('4.0.0.0/8', '/', 1), '.', 4) as bigint))
as network,
(cast(split_part(split_part('4.0.0.0/8', '/', 1), '.', 1) as bigint) * (256 * 256 * 256) +
cast(split_part(split_part('4.0.0.0/8', '/', 1), '.', 2) as bigint) * (256 * 256 ) +
cast(split_part(split_part('4.0.0.0/8', '/', 1), '.', 3) as bigint) * (256 ) +
cast(split_part(split_part('4.0.0.0/8', '/', 1), '.', 4) as bigint)) + cast(
pow(256, (32 - cast(split_part('4.0.0.0/8', '/', 2) as bigint)) / 8) - 1 as bigint
) as broadcast;