Trabalhar com IPv6 em PHP

Recentemente, recebemos o status LIR e / 29 IPv6 block. E havia a necessidade de acompanhar as sub-redes designadas. E como nosso faturamento foi escrito em PHP, tive que me inspirar um pouco no problema e perceber que essa linguagem não é a mais amigável em termos de trabalho com o IPv6. Sob o corte - nossa solução para os problemas de trabalhar com endereços e intervalos. Talvez não seja o mais elegante, mas executa as tarefas.



Pouco de teoria


Isenção de responsabilidade. Se você estiver familiarizado com o que é IPv6 e comê-lo, essa parte pode ser chata para você. Pode não ser.

As pessoas que vêem a anotação IPv6 pela primeira vez podem ficar desanimadas. Após o elegante 64.233.177.101, de repente encontramos 2607: f8b0: 4002: c08 :: 8b e podemos ficar confusos. Tanto isso quanto outro - apenas representação legível por humanos de 32 e 128 bits, respectivamente. Qualquer pacote IP contém um cabeçalho com uma atribuição estritamente padronizada de cada bit. Sem aprofundar ainda mais a estrutura dos cabeçalhos, precisamos tirar uma coisa daqui: para operações com endereços IP e intervalos, geralmente é conveniente usar operações binárias de matemática e bit a bit. Também é mais conveniente armazená-los no banco de dados como BINARY (4) para IPv4 e BINARY (16) para IPv6.

Outro aspecto importante que deve ser tratado são as máscaras de rede e a notação CIDR. CIDR é um acrônimo para Roteamento sem Domínio Classless. Esse conceito substituiu a classe 1 na questão de determinar qual parte do endereço IP é o prefixo da rede e qual parte é o endereço da interface de rede nessa rede. Na prática, os primeiros n bits correspondentes ao prefixo serão configurados para 1 e os demais para 0.

Na forma legível por humanos, isso é escrito como ip.add.re.ss. / cidr . Por exemplo, 64.233.177.0/24 significa que os primeiros 24 bits se referem ao prefixo. Os últimos 8 bits, eles são o último número em uma entrada legível por humanos, referem-se ao endereço dentro da sub-rede. Mais alguns exercícios. 64.233.177.101/32 e 2607: f8b0: 4002: c08 :: 8b / 128 - um endereço específico. 2607: f8b0: 4002: c08 :: / 64 - os primeiros 64 bits (os 4 primeiros grupos) - o prefixo, os 64 bits restantes - a parte local. A propósito, se alguém ficar embaraçado com o "::" na entrada, dois pontos e dois pontos substituirão um número arbitrário de seções contendo 0. Ele pode ocorrer na anotação apenas 1 vez. Em outras palavras, 2607: f8b0: 4002: c08 :: 8b = 2607: f8b0: 4002: c08: 0: 0: 0: 8b .

O que precisamos aprender com tudo isso? Primeiramente, o primeiro e o último endereço de sub-rede podem ser obtidos usando AND e OR binários, conhecendo a máscara na forma binária. Em segundo lugar, a próxima sub-rede de tamanho (isto é, com CIDR) n pode ser calculada adicionando 1 à n- ésima posição na representação binária. Por visão binária, quero dizer o resultado do uso das funções pack () e inet_pton () e o uso adicional de operadores bit a bit , por binary - uma representação no sistema binário, que pode ser obtida, digamos, usando base_convert () .

Antecedentes históricos
A segregação sem classes de endereçamento precedeu a sem classe. Naqueles anos distantes, ninguém esperava que houvesse tantas sub-redes; elas foram distribuídas à direita e à esquerda em grandes blocos: classe A - os primeiros 8 bits (ou seja, o primeiro número) foram prefixados, com o bit inicial 0; classe B - os 16 primeiros (dois primeiros números), os bits iniciais de 10; classe C - os primeiros 24 bits, os bits iniciais de 110. Esses mesmos bits iniciais definem os intervalos nos quais o endereço de uma classe foi emitido: 0.0.0.0 - 127.255.255.255 para a classe A, 128.0.0.0 - 191.255.255.255 - classe B, 192.0 .0.0 - 223.255.255.255 - classe C. À medida que a Internet se espalhava por todo o planeta, os reguladores perceberam que estavam errados e, no início dos anos 90, desenvolveram um conceito sem classe, que lhes permitia não se apegar aos bits principais. Um pouco mais de detalhes podem ser encontrados, digamos, no grande e onisciente .


Vamos seguir praticando


Na prática, implementamos as três tarefas mais prováveis, como me pareceu:

  1. obtendo o primeiro e o último endereço do intervalo;
  2. obter o próximo intervalo de um determinado tamanho (CIDR);
  3. verificando se o endereço pertence a um intervalo.

A implementação será para IPv6, mas se necessário, a lógica pode ser facilmente adaptada. Eu tenho algumas idéias daqui , mas implementei um pouco diferente. Também nos exemplos, não há verificação de erros de entrada. Então vamos lá.

Como já mencionei, o primeiro e o último endereço de um intervalo podem ser determinados usando operações bit a bit, conhecendo o início do intervalo e a máscara de sub-rede binária. Portanto, a primeira coisa que precisamos fazer é transformar o CIDR em uma máscara binária. Para fazer isso, colete sua representação hexadecimal e empacote-a em binário.

function cidrToMask ($cidr) { $mask = str_repeat('f', ceil($cidr / 4)); $mask .= dechex(4 * ($cidr % 4)); $mask = str_pad($mask, 32, '0'); return pack('H*', $mask); } 

O pacote de chamadas ('H *', $ mask) compacta a representação hexadecimal da mesma maneira que inet_pton () . A única diferença é que, quando você chama pack (), todos os 0 devem estar no lugar e não deve haver dois pontos na entrada, em contraste com a entrada legível por humanos.

O próximo passo é calcular o início e o fim do intervalo. E aqui há nuances. As operações bit a bit são limitadas pela capacidade do processador. Assim, no meu CubieTruck de 32 bits, que às vezes uso para qualquer mimo de teste, todos os 128 bits do endereço não podem ser processados ​​em uma operação. No entanto, nada nos impede de dividi-lo em grupos de 32 bits (por precaução, quem sabe em quais processadores estaremos rodando).

 function getRangeBoundary ($ip, $cidr, $which, $ipIsBin = false, $returnBin = false) { $mask = cidrToMask($cidr); if (!$ipIsBin) { $ip = inet_pton($ip); } $ipParts = str_split($ip, 4); $maskParts = str_split($mask, 4); $rangeParts = []; for ($i = 0; $i < count($ipParts); $i++) { if ($which == 'start') { /*  &       . */ $rangeParts[$i] = $ipParts[$i] & $maskParts[$i]; } else { /*  |    (~)           1. */ $rangeParts[$i] = $ipParts[$i] | ~$maskParts[$i]; } } $rangeBoundary = implode($rangeParts); if ($returnBin) { return $rangeBoundary; } else { return inet_ntop($rangeBoundary); } } 

Para uso futuro, forneceremos a capacidade de transmitir IP e obter o resultado tanto na forma binária quanto na legível por humanos. O parâmetro $ which aqui define se queremos obter o início ou o fim do intervalo (os valores são 'start' ou 'end', respectivamente).

A próxima tarefa (além da mais prática para a nossa empresa) é calcular o próximo intervalo. Para esta tarefa, nada melhor veio à mente, exceto como decompor o endereço em uma cadeia binária e adicionar 1 na posição desejada, e recolher tudo de volta. Para impedir que artefatos apareçam em qualquer lugar, decidi dividir o endereço por byte durante a decomposição e montagem.

 function getNextBlock ($ipStart, $cidr, $ipIsBin = false, $returnBin = false) { if (!$ipIsBin) { $ipStart = inet_pton($ipStart); } $ipParts = str_split($ipStart, 1); $ipBin = ''; foreach ($ipParts as $ipPart) { $ipBin .= str_pad(base_convert(unpack('H*', $ipPart)[1], 16, 2), 8, '0', STR_PAD_LEFT); } /*  1       "" :) */ $i = $cidr - 1; while ($i >= 0) { if ($ipBin[$i] == '0') { $ipBin[$i] = '1'; break; } else { $ipBin[$i] = '0'; } $i--; } $ipBinParts = str_split($ipBin, 8); foreach ($ipBinParts as $key => $ipBinPart) { $ipParts[$key] = pack('H*', str_pad(base_convert($ipBinPart, 2, 16), 2, '0', STR_PAD_LEFT)); } $nextIp = implode($ipParts); if ($returnBin) { return $nextIp; } else { return inet_ntop($nextIp); } } 

Na saída, obtemos o prefixo do próximo intervalo de tamanho especificado em $ cidr . Com essa função, alocamos blocos de endereços para nossos clientes.

Por fim, verifique se o endereço pertence ao intervalo. Por exemplo, alocamos um bloco / 48 para distribuição de / 64 blocos aos clientes e precisamos garantir que, durante o compromisso, não excedamos o bloco alocado (na prática, isso acontecerá em breve, mas ainda há uma chance). Tudo é simples aqui. Obtemos o início e o fim do intervalo em formato binário e verificamos se o endereço está dentro.

 function ipInRange ($ip, $rangeStart, $cidr) { $start = getRangeBoundary($rangeStart, $cidr, 'start',false, true); $end = getRangeBoundary($rangeStart, $cidr, 'end',false, true); $ipBin = inet_pton($ip); return ($ipBin >= $start && $ipBin <= $end); } 

Espero que tenha sido útil. Quais outros recursos de endereçamento você pode achar úteis? Quaisquer adições, comentários e revisões de código são muito bem vindos nos comentários.

Se você já é nosso cliente ou está apenas pensando em se tornar um, por ocasião do lançamento deste artigo, sugerimos que você obtenha o bloco / 64 gratuitamente para todos os serviços vps ou um servidor dedicado no data center Equinix Tier IV, na Holanda, mediante solicitação ao departamento de vendas, fornecendo um link para este artigo no ticket. A oferta é válida até março de 2020.

Um pouco de publicidade :)


Obrigado por ficar conosco. Você gosta dos nossos artigos? Deseja ver materiais mais interessantes? Ajude-nos fazendo um pedido ou recomendando aos seus amigos VPS baseado em nuvem para desenvolvedores a partir de US $ 4,99 , um analógico exclusivo de servidores básicos que foi inventado por nós para você: Toda a verdade sobre o VPS (KVM) E5-2697 v3 (6 núcleos) 10GB DDR4 480GB SSD 1Gbps de 10GB de US $ 19 ou como dividir o servidor? (as opções estão disponíveis com RAID1 e RAID10, até 24 núcleos e até 40GB DDR4).

Dell R730xd 2 vezes mais barato no data center Equinix Tier IV em Amsterdã? Somente temos 2 TVs Intel TetraDeca-Core Xeon 2x E5-2697v3 2.6GHz 14C 64GB DDR4 4x960GB SSD 1Gbps 100 TV a partir de US $ 199 na Holanda! Dell R420 - 2x E5-2430 2.2Ghz 6C 128GB DDR3 2x960GB SSD 1Gbps 100TB - a partir de US $ 99! Leia sobre Como criar um prédio de infraestrutura. classe usando servidores Dell R730xd E5-2650 v4 custando 9.000 euros por um centavo?

Source: https://habr.com/ru/post/pt484586/


All Articles