PyTCP é uma pilha TCP/IP totalmente funcional escrita em Python. Ele suporta transporte baseado em fluxo TCP com entrega confiável de pacotes baseada em um mecanismo de janela deslizante e controle básico de congestionamento. Ele também suporta protocolos IPv6/ICMPv6 com configuração de endereço SLAAC. Ele opera como um programa de espaço do usuário conectado à interface Linux TAP. Ele implementou roteamento simples e pode enviar e receber tráfego através de uma rede local e da Internet.
A versão 2.7, ao contrário de seus antecessores, contém o código da pilha PyTCP na forma de uma biblioteca para que possa ser facilmente importado e usado por código externo. Isso deve tornar a experiência do usuário mais suave e, eventualmente, fornecer a capacidade total de substituir as chamadas de pilha padrão do Linux (por exemplo, biblioteca de soquetes) pelas chamadas PyTCP em qualquer aplicativo de terceiros.
Este projeto começou inicialmente como um esforço puramente educacional com o objetivo de melhorar minhas habilidades em Python e atualizar meu conhecimento de rede como parte da preparação para a função de Engenheiro de Rede no Facebook. Desde então, tornou-se mais como um 'projeto favorito' ao qual dedico parte do meu tempo de forma um tanto irregular. No entanto, algumas atualizações geralmente são adicionadas a cada um ou dois meses.
Agradeço quaisquer contribuições e ajuda de qualquer pessoa interessada em programação de rede. Qualquer contribuição é apreciada. Além disso, lembre-se de que alguns recursos da pilha podem ser implementados apenas parcialmente (conforme necessário para a operação da pilha). Eles podem ser implementados de maneira abaixo do ideal ou não 100% compatíveis com RFC (devido à falta de tempo), ou podem conter bugs que ainda preciso corrigir.
Fique à vontade para verificar meus outros dois projetos relacionados:
RusTCP - Tentativa de reescrever algumas funcionalidades do PyTCP em Rust e usá-las para criar um roteador de laboratório IPv6/SRv6.
SeaTCP - Tentativa de criar pilha de baixa latência usando linguagens C e Assembly.
Princípio de operação e configuração de teste
A pilha PyTCP depende da interface Linux TAP. A interface TAP é uma interface virtual que, no final da rede, pode ser “conectada” à infraestrutura de rede virtual existente por meio de ponte Linux ou Open vSwitch. Na extremidade interna, a interface TAP pode ser usada como qualquer outra NIC, enviando e recebendo pacotes programaticamente de/para ela.
Se você deseja testar a pilha PyTCP em sua rede local, sugiro criar a seguinte configuração de rede que permitirá conectar o kernel Linux (essencialmente seu sistema operacional Linux) e a pilha PyTCP à sua rede local ao mesmo tempo .
Depois que o programa de exemplo (cliente ou serviço) inicia a pilha, ele pode se comunicar com ela através de soquetes BSD simplificados como interface API. Também existe a possibilidade de enviar pacotes diretamente chamando um dos métodos _*_phtx() da classe PacketHandler .
Clonando PyTCP do repositório GitHub
Na maioria dos casos, o PyTCP deve ser clonado diretamente do repositório GitHub, pois este tipo de instalação fornece um ambiente completo de desenvolvimento e teste.
git clone http://github.com/ccie18643/PyTCP
Após a clonagem, podemos executar um dos exemplos incluídos:
Vá para o diretório raiz da pilha (chamado 'PyTCP').
Execute o comando sudo make bridge para criar a ponte ‘br0’, se necessário.
Execute o comando sudo make tap para criar a interface tap7 e atribuí-la à ponte ‘br0’.
Execute o comando make para criar o ambiente virtual adequado para desenvolvimento e teste.
Correr . venv/bin/activate comando . venv/bin/activate para ativar o ambiente virtual.
Execute qualquer exemplo, por exemplo, example/run_stack.py .
Pressione Ctrl-C para pará-lo.
Para ajustar vários parâmetros operacionais da pilha, edite o arquivo pytcp/config.py de acordo.
Instalando PyTCP do repositório PyPi
PyTCP também pode ser instalado como um módulo regular do repositório PyPi.
python -m pip install PyTCP
Após a instalação, certifique-se de que a interface TAP esteja operacional e adicionada à ponte.
sudo ip tuntap add name tap7 mode tapsudo ip link set dev tap7 upsudo brctl addbr br0sudo brctl addif br0 tap7
A pilha PyTCP pode ser importada e iniciada usando o código a seguir. Ele inicia os subsistemas de pilha e configura automaticamente os endereços dos protocolos IPv4 e IPv6 usando DHCPv4 e IPv6 SLAAC, respectivamente.
Os subsistemas de pilha são executados em seus próprios threads. Após iniciar, a pilha devolve o controle ao código do usuário e pode ser interrompida usando a seguinte chamada.
stack . stop ()
Características
Já implementado:
Stack - Fast Packet Parser usando abordagem de 'cópia zero'.
Stack - Fast Packet Assembler usando abordagem de 'cópia zero'.
Stack - Biblioteca de manipulação de endereços MAC - Compatível com protocolo buffer (Memoryview).
Stack - biblioteca de manipulação de endereços IPv4 - Compatível com protocolo buffer (Memoryview) e não dependente da biblioteca padrão Python.
Stack - Biblioteca de manipulação de endereços IPv6 - Compatível com protocolo buffer (Memoryview) e não dependente da biblioteca padrão Python.
Código - Teste de unidade para algumas bibliotecas e módulos (baseado na estrutura Testslide do Facebook)
Protocolo Ethernet - Suporte a quadros padrão Ethernet II.
Protocolo Ethernet - Unicast, multicast IPv4, multicast IPv6 e endereçamento de broadcast.
Protocolo ARP - Respostas, consultas, mecanismo de cache ARP.
Protocolo ARP - mecanismo de detecção de conflitos de IP (ACD) de sonda/anúncio ARP.
Protocolo IPv4 - Roteamento padrão, a pilha pode se comunicar com hosts pela Internet usando o protocolo IPv4.
Protocolo IPv4 - Configuração automática de endereço IPv4 usando o protocolo DHCPv4.
Protocolo IPv4 - Desfragmentação de pacotes de entrada, mecanismo robusto capaz de lidar com fragmentos de dados fora de ordem e sobrepostos.
Protocolo IPv4 - Fragmentação de pacotes de saída.
Protocolo IPv4 – opções IPv4 aceitas, mas não suportadas.
Protocolo IPv4 - Vários endereços IPv4 de pilha são suportados, cada um deles atua como foi atribuído a um VRF separado
Protocolo ICMPv4 - As mensagens de solicitação de eco, resposta de eco e porta inacessível.
Protocolo IPv6 - Roteamento padrão, a pilha pode se comunicar com hosts pela Internet usando o protocolo IPv6.
Protocolo IPv6 - Configuração automática de endereço local de link usando EUI64 e detecção de endereço duplicado.
Protocolo IPv6 - Atribuição automática de endereços Multicast de nós solicitados.
Protocolo IPv6 - Atribuição automática de endereços MAC multicast IPv6.
Protocolo IPv6 - Desfragmentação de pacotes de entrada, mecanismo robusto capaz de lidar com fragmentos de dados fora de ordem e sobrepostos.
Protocolo IPv6 - Fragmentação de pacotes de saída.
Protocolo ICMPv6 - As mensagens de solicitação de eco, resposta de eco e porta inacessível.
Protocolo ICMPv6 - descoberta de vizinho, detecção de endereço duplicado.
Protocolo ICMPv6 - Mecanismo de cache de descoberta de vizinho.
Protocolo ICMPv6 - implementação do protocolo Multicast Listener Discovery v2 (MLDv2) (apenas mensagens necessárias por pilha).
Protocolo UDP - Suporte total. A pilha pode trocar dados com outros hosts usando o protocolo UDP.
Soquetes UDP - Suporte completo, API de 'usuário final' da pilha semelhante aos soquetes Berkeley.
Serviços UDP - Os serviços Echo, Discard e Daytime implementados para fins de teste (em 'exemplos').
Protocolo TCP - Implementação completa da Máquina de Estados Finitos TCP. Neste ponto, a pilha pode trocar dados em massa com outros hosts através do protocolo TCP.
Protocolo TCP - Mecanismo de janela deslizante TCP com retransmissão de dados (retransmissão rápida e cenários baseados em tempo).
Protocolo TCP - Mecanismo de backoff TCP/controle básico de congestionamento.
Protocolo TCP - retransmissão de pacotes TCP SYN/FIN.
Soquetes TCP - Suporte completo, API de 'usuário final' da pilha semelhante aos soquetes Berkeley
A ser implementado:
ICMPv6 - O suporte a MLDv2 está uma bagunça agora. Precisa terminar.
Teste - É necessário refatorar os testes de fluxo de pacotes (tests/packet_flow_ .py) para usar o mesmo formato e diretório dos testes FPA baseados em test_frames.*
Teste - Crie testes de unidade FPA para relatório MLDv2 (len, str, assemble).
IPv4 - Reimplemente a desfragmentação de pacotes para armazenar pacotes inteiros no banco de dados de fluxo em vez de fazer cópias do cabeçalho IP e dos dados.
Stack - Implementa suporte a soquete RAW - para ser usado por exemplo, cliente ICMP-Echo.
Código - Teste unitário para bibliotecas e módulos restantes (baseado na biblioteca Testslide do Facebook).
Código - Reescreva o suporte ao protocolo DHCPv4 para usar a abordagem FPA/FPP padrão em vez do código legado.
Pilha - Volte à implementação do console de depuração de pilha para que certas informações sobre os componentes da pilha possam ser exibidas sob demanda, enviando comandos. por exemplo, 'show icmpv6 nd cache', 'show ipv6 route', etc... também deve permitir que você execute comandos interativos como ping ou clientes de eco UDP/TCP da pilha.
Protocolo QUIC - Pesquisa e plano de implementação. Isso depende da capacidade de criar um ambiente de laboratório para isso.
Protocolo IPv6 - Redesenhe o tratamento da opção RA PI e a configuração automática do prefixo ND para usar os sinalizadores A e L corretamente. Também é necessária alguma pesquisa quando um prefixo diferente de /64 é anunciado.
Protocolo IPv6 - Implemente os cabeçalhos de extensão restantes.
Protocolo IPv6 - Valide e possivelmente reimplemente certos mecanismos/processos IPv6 de acordo com as regras RFC.
Protocolo IPv6 - Investigue o cabeçalho de opções Hop-by-Hop e sua relação com a mensagem do relatório MLD2, implemente se necessário para que o MLD2 funcione corretamente.
Protocolo ICMPv6 - Implemente mensagem de redirecionamento ND.
Protocolo ICMPv6 - implementação completa do Multicast Listener Discovery v2 (MLDv2) <- pode ser exigido pela pilha para responder a consultas MLD.
Protocolo TCP - Manipulação adequada de pacotes RST em vários estados. Precisa pesquisar isso. Há um relatório de bug enviado sobre isso.
Protocolo TCP - É necessário retrabalhar o mecanismo de syscall CLOSE para que o sinalizador FIN possa ser definido no último pacote de dados em vez de ser transportado em um pacote separado.
Protocolo TCP - retransmissão de pacotes ACK caso obtivemos retransmissão FIN no estado TIME_WAIT. Precisa investigar isso.
Protocolos IPv6/IPv4 - mecanismo de roteamento adequado, tabelas de rotas, etc...
Protocolos IPv6/IPv4 - capacidade da pilha de atuar como roteador
Cache ARP - implemente FSM adequado
Cache ICMPv6 ND - implemente FSM adequado
Logging - Substitua o Loguru por um logger caseiro para melhorar o desempenho e a flexibilidade.
Pilha - Converta a pilha PyTCP em uma biblioteca para que aplicativos externos possam importá-la facilmente.
Pilha - Contadores de fluxo de pacotes para ajudar a coletar estatísticas de pacotes e permitir o rastreamento do fluxo de pacotes para testes de unidade.
Pilha - Implemente mecanismo de feedback para caminho TX para que falhas no envio de pacotes possam ser comunicadas aos soquetes.
Protocolo IPv6 – Capacidade de rotear tráfego para destinos externos por meio de gateway padrão.
Protocolo TCP - Certifique-se de que a comunicação de eventos da sessão TCP para o soquete funcione corretamente (por exemplo, redefinição da conexão pelo peer).
Protocolo IPv4 - É necessária melhoria do mecanismo de desfragmentação IP, manipulação de fragmentos fora de ordem e eliminação de fragmentos órfãos.
Protocolo UDP - Precisa do cliente UDP Echo e de um mecanismo para relatar o recebimento de mensagem de porta ICMP inacessível para o soquete UDP.
Soquetes UDP - É necessária uma revisão para fazer com que a interface do 'usuário final' corresponda mais aos soquetes Berkeley, para que aplicativos de terceiros possam usá-la sem portabilidade.
Soquetes TCP - A revisão é necessária para fazer com que a interface do 'usuário final' corresponda mais aos soquetes Berkeley, para que aplicativos de terceiros possam usá-la sem portabilidade.
Exemplos
Vários pacotes de ping e dois macacos foram entregues via TCP através do protocolo IPv6.
Descoberta de vizinho IPv6/detecção de endereço duplicado/configuração automática de endereço.
Stack tenta configurar automaticamente seu endereço local de link. Ele o gera como um endereço EUI64. Como parte do processo DAD, ele se junta ao grupo multicast de nó solicitado apropriado e envia solicitação de vizinho para seu endereço gerado.
Stack não recebe nenhum anúncio de vizinho para o endereço que gerou, então o atribui à sua interface.
Stack tenta atribuir um endereço estático pré-configurado. Como parte do processo DAD, ele se junta ao grupo multicast de nó solicitado apropriado e envia solicitação de vizinho para o endereço estático.
Outro host com o mesmo endereço já atribuído responde com uma mensagem de anúncio de vizinho. Isso informa à pilha que outro host já atribuiu o endereço que está tentando atribuir, portanto a pilha não pode usá-lo.
Stack envia uma mensagem de solicitação de roteador para verificar se há algum prefixo global que deva ser usado.
O roteador responde com anúncio de roteador contendo prefixo adicional.
Stack tenta atribuir um endereço gerado com base no prefixo recebido e na parte do host EUI64. Como parte do processo DAD, ele se junta ao grupo multicast de nó solicitado apropriado e envia solicitação de vizinho para o endereço estático.
Stack não recebe nenhum anúncio de vizinho para o endereço que gerou, então o atribui à sua interface.
Depois que todos os endereços são atribuídos, a pilha envia mais um relatório Multicast Listener listando todos os endereços multicast que deseja ouvir.
TCP Fast Retransmit em ação após perda de pacote TX.
O pacote de saída é 'perdido' devido ao mecanismo simulado de perda de pacotes.
O peer percebe a inconsistência nos números SEQ dos pacotes e envia uma 'solicitação de retransmissão rápida'.
Stack recebe a solicitação e retransmite o pacote perdido.
Fila fora de ordem em ação durante evento de perda de pacote RX
O pacote recebido é 'perdido' devido ao mecanismo simulado de perda de pacotes.
Stack percebe uma inconsistência no número SEQ do pacote de entrada e envia uma solicitação de 'retransmissão rápida'.
Antes de o peer receber a solicitação, ele envia vários pacotes com SEQ maior do que o esperado pela pilha. Stack enfileira todos esses pacotes.
O peer retransmite o pacote perdido.
Stack recebe o pacote perdido, extrai todos os pacotes armazenados na fila fora de ordem e os processa.
Stacks envia pacotes ACK para reconhecer os pacotes mais recentes retirados da fila.
Máquina de estado finito TCP - a pilha está executando o serviço TCP Echo.
O peer abre a conexão.
O peer envia dados.
Stack ecoa os dados de volta.
O peer fecha a conexão.
Máquina de estado finito TCP - a pilha está executando o cliente TCP Echo.
Stack abre a conexão.
Stack envia dados.
Peer ecoa os dados de volta.
Stack fecha a conexão.
Verificações de integridade de pacotes pré-analisadas em ação.
A primeira captura de tela mostra a pilha com a verificação de integridade desativada. Um pacote ICMPv6 malformado pode travá-lo.
A segunda captura de tela mostra a pilha com a verificação de integridade ativada. Um pacote ICMPv6 malformado é descartado antes de ser passado para o analisador de protocolo ICMPv6.
A terceira captura de tela mostra o pacote malformado. O campo de número de registros MA foi definido como 777, embora o pacote contenha apenas um registro.
Mecanismo de sondagem/anúncio ARP.
Stack usa sondas ARP para encontrar possíveis conflitos para cada endereço IP configurado.
Um dos endereços IP (192.168.9.102) já está em uso, então a pilha é notificada sobre isso e o ignora.
O restante dos endereços IP são gratuitos, portanto, a pilha os reivindica enviando um anúncio ARP para cada um deles.
Resolução ARP e manipulação de pacotes de ping.
O host 192.168.9.20 tenta fazer ping na pilha. Para fazer isso, primeiro ele envia um pacote de solicitação ARP para descobrir o endereço MAC da pilha.
A pilha responde enviando um pacote de resposta ARP (a pilha não precisa enviar sua solicitação, pois já anotou o MAC do host a partir da solicitação do host).
O host envia pacotes de ping e a pilha responde a eles.
Fragmentação de IP.
O host envia datagrama UDP de 4Kb usando três pacotes IP fragmentados (três fragmentos).
Stack recebe pacotes e os reúne em uma única peça, depois os passa (via manipulador de protocolo UDP e soquete UDP) para o serviço UDO Echo.
O serviço UDP Echo coleta dados e os coloca de volta no soquete UDP.
O datagrama UDP é passado para o manipulador do protocolo IP, que cria um pacote IP e, após verificar se ele excede o link, o MTU o fragmenta em três pacotes IP separados.
Os pacotes IP são encapsulados em frames Ethernet e colocados em um anel TX.