PyTCP es una pila TCP/IP completamente funcional escrita en Python. Admite transporte basado en flujos TCP con entrega confiable de paquetes basada en un mecanismo de ventana deslizante y control básico de congestión. También admite protocolos IPv6/ICMPv6 con configuración de dirección SLAAC. Opera como un programa de espacio de usuario adjunto a la interfaz TAP de Linux. Ha implementado un enrutamiento simple y puede enviar y recibir tráfico a través de una red local e Internet.
La versión 2.7, a diferencia de sus predecesoras, contiene el código de pila PyTCP en forma de biblioteca para que pueda importarse y utilizarse fácilmente mediante código externo. Esto debería hacer que la experiencia del usuario sea más fluida y, eventualmente, brindar la capacidad total de reemplazar las llamadas a la pila estándar de Linux (por ejemplo, la biblioteca de sockets) con las llamadas PyTCP en cualquier aplicación de terceros.
Inicialmente, este proyecto comenzó como un esfuerzo puramente educativo destinado a mejorar mis habilidades en Python y actualizar mis conocimientos sobre redes como parte de la preparación para el puesto de ingeniero de redes en Facebook. Desde entonces, se ha convertido más bien en un "proyecto favorito" al que dedico parte de mi tiempo de forma algo irregular. Sin embargo, normalmente se le añaden un par de actualizaciones cada uno o dos meses.
Doy la bienvenida a cualquier contribución y ayuda de cualquier persona interesada en la programación de redes. Se agradece cualquier aportación. Además, recuerde que algunas funciones de la pila pueden implementarse solo parcialmente (según sea necesario para el funcionamiento de la pila). Es posible que se hayan implementado de manera subóptima o que no cumplan al 100% con RFC (debido a falta de tiempo), o pueden contener errores que aún necesito corregir.
No dude en consultar mis otros dos proyectos relacionados:
RusTCP: intente reescribir algunas de las funciones de PyTCP en Rust y utilícelas para crear un enrutador de laboratorio IPv6/SRv6.
SeaTCP: intento de crear una pila de baja latencia utilizando lenguajes C y ensamblador.
Principio de funcionamiento y configuración de la prueba.
La pila PyTCP depende de la interfaz TAP de Linux. La interfaz TAP es una interfaz virtual que, en el extremo de la red, se puede "conectar" a la infraestructura de red virtual existente a través de un puente Linux o Open vSwitch. En el extremo interno, la interfaz TAP se puede utilizar como cualquier otra NIC enviando y recibiendo paquetes programáticamente hacia/desde ella.
Si desea probar la pila PyTCP en su red local, le sugiero que cree la siguiente configuración de red que le permitirá conectar tanto el kernel de Linux (esencialmente su sistema operativo Linux) como la pila PyTCP a su red local al mismo tiempo. .
Después de que el programa de ejemplo (ya sea cliente o servicio) inicia la pila, puede comunicarse con ella a través de sockets BSD simplificados como una interfaz API. También existe la posibilidad de enviar paquetes directamente llamando a uno de los métodos _*_phtx() de la clase PacketHandler .
Clonación de PyTCP desde el repositorio de GitHub
En la mayoría de los casos, PyTCP debe clonarse directamente desde el repositorio de GitHub, ya que este tipo de instalación proporciona un entorno de desarrollo y prueba completo.
git clone http://github.com/ccie18643/PyTCP
Después de la clonación, podemos ejecutar uno de los ejemplos incluidos:
Vaya al directorio raíz de la pila (se llama 'PyTCP').
Ejecute el comando sudo make bridge para crear el puente 'br0' si es necesario.
Ejecute el comando sudo make tap para crear la interfaz tap7 y asignarla al puente 'br0'.
Ejecute el comando make para crear el entorno virtual adecuado para el desarrollo y las pruebas.
Correr . venv/bin/activate comando . venv/bin/activate para activar el entorno virtual.
Ejecute cualquier ejemplo, por ejemplo, example/run_stack.py .
Presiona Ctrl-C para detenerlo.
Para ajustar varios parámetros operativos de la pila, edite el archivo pytcp/config.py en consecuencia.
Instalación de PyTCP desde el repositorio de PyPi
PyTCP también se puede instalar como un módulo normal desde el repositorio de PyPi.
python -m pip install PyTCP
Después de la instalación, asegúrese de que la interfaz TAP esté operativa y agregada al puente.
sudo ip tuntap add name tap7 mode tapsudo ip link set dev tap7 upsudo brctl addbr br0sudo brctl addif br0 tap7
La pila PyTCP se puede importar e iniciar usando el siguiente código. Inicia los subsistemas de pila y configura automáticamente las direcciones de protocolo IPv4 e IPv6 utilizando DHCPv4 e IPv6 SLAAC, respectivamente.
Los subsistemas de pila se ejecutan en sus propios subprocesos. Después de comenzar, la pila devuelve el control al código de usuario y se puede detener mediante la siguiente llamada.
stack . stop ()
Características
Ya implementado:
Pila: analizador rápido de paquetes que utiliza el enfoque de "copia cero".
Pila: ensamblador rápido de paquetes que utiliza el enfoque de "copia cero".
Pila: biblioteca de manipulación de direcciones MAC: compatible con el protocolo de búfer (Memoryview).
Pila: biblioteca de manipulación de direcciones IPv4: compatible con el protocolo de búfer (Memoryview) y no depende de la biblioteca estándar de Python.
Pila: biblioteca de manipulación de direcciones IPv6: compatible con el protocolo de búfer (Memoryview) y no depende de la biblioteca estándar de Python.
Código: pruebas unitarias para algunas de las bibliotecas y módulos (basado en el marco Testslide de Facebook)
Protocolo Ethernet: soporte de tramas estándar Ethernet II.
Protocolo Ethernet: unidifusión, multidifusión IPv4, multidifusión IPv6 y direccionamiento de difusión.
Protocolo ARP: respuestas, consultas, mecanismo de caché ARP.
Protocolo ARP: mecanismo de detección de conflictos de IP (ACD) de anuncio/sondeo ARP.
Protocolo IPv4: enrutamiento predeterminado, la pila puede comunicarse con los hosts a través de Internet utilizando el protocolo IPv4.
Protocolo IPv4: configuración automática de la dirección IPv4 mediante el protocolo DHCPv4.
Protocolo IPv4: desfragmentación de paquetes entrantes, mecanismo robusto capaz de manejar fragmentos de datos desordenados y superpuestos.
Protocolo IPv4: fragmentación de paquetes salientes.
Protocolo IPv4: se aceptan opciones IPv4, pero no se admiten.
Protocolo IPv4: se admiten varias direcciones IPv4 de pila, cada una de ellas actúa como fue asignada a un VRF separado
Protocolo ICMPv4: mensajes de solicitud de eco, respuesta de eco y puerto inalcanzable.
Protocolo IPv6: enrutamiento predeterminado, la pila puede comunicarse con los hosts a través de Internet utilizando el protocolo IPv6.
Protocolo IPv6: configuración automática de direcciones locales de enlace mediante EUI64 y detección de direcciones duplicadas.
Protocolo IPv6: configuración automática de direcciones GUA mediante anuncio de enrutador/EUI64.
Protocolo IPv6: asignación automática de direcciones de multidifusión de nodos solicitados.
Protocolo IPv6: asignación automática de direcciones MAC de multidifusión IPv6.
Protocolo IPv6: desfragmentación de paquetes entrantes, mecanismo robusto capaz de manejar fragmentos de datos desordenados y superpuestos.
Protocolo IPv6: fragmentación de paquetes salientes.
Protocolo ICMPv6: mensajes de solicitud de eco, respuesta de eco y puerto inalcanzable.
Protocolo ICMPv6: descubrimiento de vecinos, detección de direcciones duplicadas.
Protocolo ICMPv6: mecanismo de caché de descubrimiento de vecinos.
Protocolo ICMPv6: implementación del protocolo Multicast Listener Discovery v2 (MLDv2) (solo los mensajes son necesarios por pila).
Protocolo UDP: soporte completo. La pila puede intercambiar datos con otros hosts mediante el protocolo UDP.
Sockets UDP: soporte completo, API de pila de 'usuario final' similar a los sockets Berkeley.
Servicios UDP: los servicios Echo, Discard y Daytime implementados con fines de prueba (en 'ejemplos').
Protocolo TCP: implementación completa de la máquina de estados finitos TCP. En este punto, la pila puede intercambiar datos masivos con otros hosts a través del protocolo TCP.
Protocolo TCP: mecanismo de ventana deslizante TCP con retransmisión de datos (retransmisión rápida y escenarios basados en el tiempo).
Protocolo TCP: mecanismo de retroceso de TCP/control básico de congestión.
Protocolo TCP: retransmisión de paquetes TCP SYN/FIN.
Sockets TCP: soporte completo, API de 'usuario final' de pila similar a los sockets Berkeley
A implementar:
ICMPv6: la compatibilidad con MLDv2 es un desastre ahora. Necesito terminarlo.
Pruebas: es necesario refactorizar las pruebas de flujo de paquetes (tests/packet_flow_.py ) para usar el mismo formato y directorio que las pruebas FPA basadas en test_frames.*
Pruebas: cree pruebas unitarias de FPA para el informe MLDv2 (len, str, ensamblar).
IPv4: vuelva a implementar la desfragmentación de paquetes para almacenar paquetes completos en la base de datos de flujo en lugar de hacer copias del encabezado y los datos de IP.
Pila: implementar compatibilidad con sockets RAW; se utilizará como ejemplo, cliente ICMP-Echo.
Código: pruebas unitarias para las bibliotecas y módulos restantes (basados en la biblioteca Testslide de Facebook).
Código: reescriba la compatibilidad con el protocolo DHCPv4 para utilizar el enfoque FPA/FPP estándar en lugar del código heredado.
Pila: vuelva a implementar la consola de depuración de la pila para que cierta información sobre los componentes de la pila se pueda mostrar a pedido mediante el envío de comandos. por ejemplo, 'mostrar icmpv6 nd cache', 'mostrar ruta ipv6', etc... también debería permitirle ejecutar comandos interactivos como ping o apilar clientes de eco UDP/TCP.
Protocolo QUIC - Investigación y plan de implementación. Esto depende de la capacidad de crear un entorno de laboratorio para ello.
Protocolo IPv6: rediseñe el manejo de la opción RA PI y la configuración automática del prefijo ND para usar los indicadores A y L correctamente. También es necesario investigar un poco cuando se anuncia un prefijo diferente a /64.
Protocolo IPv6: implemente los encabezados de extensión restantes.
Protocolo IPv6: validar y posiblemente volver a implementar ciertos mecanismos/procesos IPv6 de acuerdo con las reglas RFC.
Protocolo IPv6: investigue el encabezado de opciones de salto a salto y su relación con el mensaje de informe de MLD2, impleméntelo si es necesario para que MLD2 funcione correctamente.
Protocolo ICMPv6: implementar el mensaje de redireccionamiento ND.
Protocolo ICMPv6: implementación completa de Multicast Listener Discovery v2 (MLDv2) <-- es posible que la pila lo requiera para responder a consultas MLD.
Protocolo TCP: manejo adecuado de paquetes RST en varios estados. Necesito investigar esto. Se ha enviado un informe de error al respecto.
Protocolo TCP: es necesario reelaborar el mecanismo de llamada al sistema CLOSE para que el indicador FIN se pueda configurar en el último paquete de datos en lugar de transportarse en uno separado.
Protocolo TCP: retransmisión de paquetes ACK en caso de que tengamos la retransmisión FIN en estado TIME_WAIT. Necesito investigar esto.
Protocolo TCP: implemente una respuesta adecuada a los paquetes que contienen números SEQ y/o ACK antiguos. Necesito investigar esto.
Protocolos IPv6/IPv4: mecanismo de enrutamiento adecuado, tablas de enrutamiento, etc.
Protocolos IPv6/IPv4: capacidad de la pila para actuar como enrutador
Caché ARP: implementar FSM adecuado
Caché ICMPv6 ND: implementar FSM adecuado
Registro: reemplace Loguru con un registrador local para mejorar el rendimiento y la flexibilidad.
Pila: convierta la pila PyTCP en una biblioteca para que las aplicaciones externas puedan importarla fácilmente.
Pila: contadores de flujo de paquetes para ayudar a recopilar estadísticas de paquetes y permitir el seguimiento del flujo de paquetes para pruebas unitarias.
Pila: implemente un mecanismo de retroalimentación para la ruta TX para que las fallas en el envío de paquetes se puedan comunicar a los sockets.
Protocolo IPv6: capacidad de enrutar el tráfico a destinos externos a través de la puerta de enlace predeterminada.
Protocolo TCP: asegúrese de que la comunicación de eventos desde la sesión TCP al socket funcione correctamente (por ejemplo, restablecimiento de la conexión por parte del par).
Protocolo IPv4: se necesita mejorar el mecanismo de desfragmentación de IP, manejo de fragmentos fuera de servicio y eliminación de fragmentos huérfanos.
Protocolo UDP: necesita un cliente UDP Echo y un mecanismo para informar la recepción de un mensaje de puerto ICMP inalcanzable al socket UDP.
Sockets UDP: se necesita una revisión para que la interfaz del 'usuario final' coincida más estrechamente con los sockets de Berkeley para que las aplicaciones de terceros puedan usarla sin necesidad de realizar la portabilidad.
Sockets TCP: se necesita una revisión para que la interfaz del 'usuario final' coincida más estrechamente con los sockets de Berkeley para que las aplicaciones de terceros puedan usarla sin necesidad de portarse.
Ejemplos
Se entregaron varios paquetes de ping y dos monos a través de TCP a través del protocolo IPv6.
Descubrimiento de vecinos IPv6 / Detección de direcciones duplicadas / Configuración automática de direcciones.
Stack intenta configurar automáticamente su dirección de enlace local. Lo genera como una dirección EUI64. Como parte del proceso DAD, se une al grupo de multidifusión de nodos solicitados apropiado y envía una solicitud al vecino para su dirección generada.
Stack no recibe ningún anuncio de vecino para la dirección que generó, por lo que lo asigna a su interfaz.
Stack intenta asignar una dirección estática preconfigurada. Como parte del proceso DAD, se une al grupo de multidifusión de nodos solicitados apropiado y envía una solicitud al vecino para la dirección estática.
Otro host con la misma dirección ya asignada responde con un mensaje de anuncio de vecino. Esto le dice a la pila que otro host ya ha asignado la dirección que está intentando asignar, por lo que la pila no puede usarla.
Stack envía un mensaje de solicitud de enrutador para verificar si hay algún prefijo global que deba usar.
El enrutador responde con un anuncio de enrutador que contiene un prefijo adicional.
Stack intenta asignar una dirección generada en función del prefijo recibido y la parte del host EUI64. Como parte del proceso DAD, se une al grupo de multidifusión de nodos solicitados apropiado y envía una solicitud al vecino para la dirección estática.
Stack no recibe ningún anuncio de vecino para la dirección que generó, por lo que lo asigna a su interfaz.
Después de asignar todas las direcciones, la pila envía un informe más de escucha de multidifusión que enumera todas las direcciones de multidifusión que desea escuchar.
TCP Fast Retransmit en acción después de la pérdida del paquete TX.
El paquete saliente se "pierde" debido al mecanismo de pérdida de paquetes simulado.
Peer nota la inconsistencia en los números SEQ de los paquetes y envía una "solicitud de retransmisión rápida".
Stack recibe la solicitud y retransmite el paquete perdido.
Cola fuera de servicio en acción durante un evento de pérdida de paquetes RX
El paquete entrante se "pierde" debido al mecanismo de pérdida de paquetes simulado.
Stack detecta una inconsistencia en el número SEQ del paquete entrante y envía una solicitud de "retransmisión rápida".
Antes de que el par reciba la solicitud, envía varios paquetes con un SEQ más alto de lo que espera la pila. Apila todos esos paquetes en cola.
El par retransmite el paquete perdido.
Stack recibe el paquete perdido, extrae todos los paquetes almacenados en la cola desordenada y los procesa.
Stacks envía un paquete ACK para reconocer los últimos paquetes extraídos de la cola.
Máquina de estados finitos TCP: la pila ejecuta el servicio TCP Echo.
Peer abre la conexión.
El par envía datos.
La pila repite los datos.
Peer cierra la conexión.
Máquina de estados finitos TCP: la pila ejecuta el cliente TCP Echo.
Stack abre la conexión.
La pila envía datos.
Peer repite los datos.
La pila cierra la conexión.
Comprobaciones de integridad de paquetes previas al análisis en acción.
La primera captura de pantalla muestra la pila con la verificación de cordura desactivada. Un paquete ICMPv6 con formato incorrecto puede bloquearlo.
La segunda captura de pantalla muestra la pila con la verificación de cordura activada. Un paquete ICMPv6 con formato incorrecto se descarta antes de pasarlo al analizador del protocolo ICMPv6.
La tercera captura de pantalla muestra el paquete con formato incorrecto. El campo de número de registros MA se ha establecido en 777 aunque el paquete contiene solo un registro.
Mecanismo de sonda/anuncio ARP.
Stack utiliza sondas ARP para encontrar posibles conflictos para cada dirección IP configurada.
Una de las direcciones IP (192.168.9.102) ya está en uso, por lo que la pila recibe una notificación y la omite.
El resto de las direcciones IP son gratuitas, por lo que Stack las reclama enviando un anuncio ARP para cada una de ellas.
Resolución ARP y manejo de paquetes ping.
El host 192.168.9.20 intenta hacer ping a la pila. Para poder hacerlo, primero envía un paquete de solicitud ARP para conocer la dirección MAC de la pila.
La pila responde enviando un paquete de respuesta ARP (la pila no necesita enviar su solicitud ya que ya tomó nota de la MAC del host a partir de la solicitud del host).
El host envía paquetes de ping y la pila responde a ellos.
Fragmentación de la propiedad intelectual.
El host envía un datagrama UDP de 4 Kb utilizando tres paquetes IP fragmentados (tres fragmentos).
Stack recibe paquetes y los ensambla en una sola pieza, luego los pasa (a través del controlador de protocolo UDP y el socket UDP) al servicio UDO Echo.
El servicio UDP Echo recoge datos y los devuelve al UDP Socket.
El datagrama UDP se pasa al controlador de protocolo IP, que crea un paquete IP y, después de verificar que excede el enlace, MTU lo fragmenta en tres paquetes IP separados.
Los paquetes IP se encapsulan en tramas Ethernet y se colocan en un anillo TX.