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

Foi útil?

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 para SELECT * ... 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;
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top