PyTCP
Python으로 작성된 TCP/IP 스택
PyTCP는 Python으로 작성된 완전한 기능을 갖춘 TCP/IP 스택입니다. 슬라이딩 윈도우 메커니즘과 기본 정체 제어를 기반으로 하는 안정적인 패킷 전달을 통해 TCP 스트림 기반 전송을 지원합니다. 또한 SLAAC 주소 구성으로 IPv6/ICMPv6 프로토콜을 지원합니다. Linux TAP 인터페이스에 연결된 사용자 공간 프로그램으로 작동합니다. 간단한 라우팅을 구현했으며 로컬 네트워크와 인터넷을 통해 트래픽을 주고받을 수 있습니다.
버전 2.7은 이전 버전과 달리 PyTCP 스택 코드를 라이브러리 형태로 포함하므로 외부 코드에서 쉽게 가져와 사용할 수 있습니다. 이를 통해 사용자 경험이 더 부드러워지고 결국 표준 Linux 스택 호출(예: 소켓 라이브러리)을 타사 애플리케이션의 PyTCP 호출로 대체할 수 있는 완전한 기능을 제공하게 됩니다.
이 프로젝트는 처음에는 Facebook에서 네트워크 엔지니어 역할을 준비하는 과정의 일환으로 Python 기술을 향상하고 네트워크 지식을 새롭게 하기 위한 순수한 교육 노력으로 시작되었습니다. 그 이후로 그것은 다소 불규칙적으로 시간을 투자하는 '애완동물 프로젝트'에 가까워졌습니다. 그러나 일반적으로 한두 달에 한 번씩 몇 가지 업데이트가 추가됩니다.
네트워크 프로그래밍에 관심이 있는 모든 분의 기여와 도움을 환영합니다. 모든 의견을 보내 주시면 감사하겠습니다. 또한 일부 스택 기능은 (스택 작업에 필요한 경우) 부분적으로만 구현될 수 있다는 점을 기억하세요. 최적이 아닌 방식으로 구현되거나 RFC와 100% 호환되지 않는 방식(시간 부족으로 인해)으로 구현될 수도 있고, 아직 수정해야 할 버그가 포함될 수도 있습니다.
다른 두 가지 관련 프로젝트를 자유롭게 확인해 보세요.
- RusTCP - Rust에서 PyTCP 기능 중 일부를 다시 작성하고 이를 사용하여 IPv6/SRv6 랩 라우터를 생성해 봅니다.
- SeaTCP - C 및 어셈블리 언어를 사용하여 대기 시간이 짧은 스택을 생성하려고 시도합니다.
작동 원리 및 테스트 설정
PyTCP 스택은 Linux TAP 인터페이스에 따라 다릅니다. TAP 인터페이스는 네트워크 끝에서 Linux 브리지 또는 Open vSwitch를 통해 기존 가상 네트워크 인프라에 '플러그인'될 수 있는 가상 인터페이스입니다. 내부 끝에서 TAP 인터페이스는 프로그래밍 방식으로 패킷을 보내고 받음으로써 다른 NIC처럼 사용할 수 있습니다.
로컬 네트워크에서 PyTCP 스택을 테스트하려면 Linux 커널(기본적으로 Linux OS)과 PyTCP 스택을 로컬 네트워크에 동시에 연결할 수 있는 다음 네트워크 설정을 만드는 것이 좋습니다. .
<INTERNET> <---> [ROUTER] <---> (eth0)-[Linux bridge]-(br0) <---> [Linux TCP/IP stack]
|
|--(tap7) <---> [PyTCP TCP/IP stack]
예제 프로그램(클라이언트 또는 서비스)이 스택을 시작한 후 API 인터페이스와 같은 단순화된 BSD 소켓을 통해 스택과 통신할 수 있습니다. PacketHandler
클래스에서 _*_phtx()
메서드 중 하나를 호출하여 직접 패킷을 보낼 수도 있습니다.
GitHub 저장소에서 PyTCP 복제
대부분의 경우 PyTCP는 GitHub 저장소에서 직접 복제해야 합니다. 이러한 유형의 설치는 전체 개발 및 테스트 환경을 제공하기 때문입니다.
git clone http://github.com/ccie18643/PyTCP
복제 후 포함된 예제 중 하나를 실행할 수 있습니다.
- 스택 루트 디렉터리('PyTCP'라고 함)로 이동합니다.
- 필요한 경우
sudo make bridge
명령을 실행하여 'br0' 브리지를 만듭니다. -
sudo make tap
명령을 실행하여 tap7 인터페이스를 생성하고 이를 'br0' 브리지에 할당합니다. -
make
명령을 실행하여 개발 및 테스트에 적합한 가상 환경을 만듭니다. - 달리다
. venv/bin/activate
명령을 사용하여 가상 환경을 활성화합니다. -
example/run_stack.py
와 같은 예제를 실행하세요. - 중지하려면 Ctrl-C를 누르세요.
다양한 스택 작동 매개변수를 미세 조정하려면 그에 따라 pytcp/config.py
파일을 편집하십시오.
PyPi 저장소에서 PyTCP 설치
PyTCP는 PyPi 저장소에서 일반 모듈로 설치할 수도 있습니다.
python -m pip install PyTCP
설치 후 TAP 인터페이스가 작동하고 브리지에 추가되었는지 확인하십시오.
sudo ip tuntap add name tap7 mode tap
sudo ip link set dev tap7 up
sudo brctl addbr br0
sudo brctl addif br0 tap7
다음 코드를 사용하여 PyTCP 스택을 가져오고 시작할 수 있습니다. 스택 하위 시스템을 시작하고 각각 DHCPv4 및 IPv6 SLAAC를 사용하여 IPv4 및 IPv6 프로토콜 주소를 자동 구성합니다.
from pytcp import TcpIpStack
stack = TcpIpStack ( interface = "tap7" )
stack . start ()
스택 하위 시스템은 자체 스레드에서 실행됩니다. 시작된 후 스택은 사용자 코드에 제어권을 다시 부여하고 다음 호출을 사용하여 중지할 수 있습니다.
특징
이미 구현됨:
- 스택 - '제로 복사' 접근 방식을 사용하는 빠른 패킷 파서.
- 스택 - '제로 복사' 접근 방식을 사용하는 고속 패킷 어셈블러입니다.
- 스택 - MAC 주소 조작 라이브러리 - 버퍼 프로토콜(Memoryview)과 호환됩니다.
- 스택 - IPv4 주소 조작 라이브러리 - 버퍼 프로토콜(Memoryview)과 호환되며 Python 표준 라이브러리에 종속되지 않습니다.
- 스택 - IPv6 주소 조작 라이브러리 - 버퍼 프로토콜(Memoryview)과 호환되며 Python 표준 라이브러리에 종속되지 않습니다.
- 코드 - 일부 라이브러리 및 모듈에 대한 단위 테스트(Facebook의 Testslide 프레임워크 기반)
- 이더넷 프로토콜 - 이더넷 II 표준 프레임을 지원합니다.
- 이더넷 프로토콜 - 유니캐스트, IPv4 멀티캐스트, IPv6 멀티캐스트 및 브로드캐스트 주소 지정.
- ARP 프로토콜 - 응답, 쿼리, ARP 캐시 메커니즘.
- ARP 프로토콜 - ARP 프로브/공지 IP 충돌 감지(ACD) 메커니즘입니다.
- IPv4 프로토콜 - 기본 라우팅, 스택은 IPv4 프로토콜을 사용하여 인터넷을 통해 호스트와 통신할 수 있습니다.
- IPv4 프로토콜 - DHCPv4 프로토콜을 사용하여 자동 IPv4 주소 구성입니다.
- IPv4 프로토콜 - 인바운드 패킷 조각 모음, 순서가 잘못되고 겹치는 데이터 조각을 처리할 수 있는 강력한 메커니즘입니다.
- IPv4 프로토콜 - 아웃바운드 패킷 조각화.
- IPv4 프로토콜 - IPv4 옵션이 허용되지만 지원되지 않습니다.
- IPv4 프로토콜 - 다중 스택의 IPv4 주소가 지원되며, 각각은 별도의 VRF에 할당된 것처럼 작동합니다.
- ICMPv4 프로토콜 - 에코 요청, 에코 응답 및 포트에 연결할 수 없는 메시지입니다.
- IPv6 프로토콜 - 기본 라우팅, 스택은 IPv6 프로토콜을 사용하여 인터넷을 통해 호스트와 통신할 수 있습니다.
- IPv6 프로토콜 - EUI64 및 중복 주소 감지를 사용한 자동 링크-로컬 주소 구성입니다.
- IPv6 프로토콜 - 라우터 광고/EUI64를 사용한 자동 GUA 주소 구성.
- IPv6 프로토콜 - 요청된 노드 멀티캐스트 주소의 자동 할당입니다.
- IPv6 프로토콜 - IPv6 멀티캐스트 MAC 주소 자동 할당.
- IPv6 프로토콜 - 인바운드 패킷 조각 모음, 순서가 잘못되고 겹치는 데이터 조각을 처리할 수 있는 강력한 메커니즘입니다.
- IPv6 프로토콜 - 아웃바운드 패킷 조각화.
- ICMPv6 프로토콜 - 에코 요청, 에코 응답 및 포트에 연결할 수 없는 메시지입니다.
- ICMPv6 프로토콜 - 이웃 검색, 중복 주소 감지.
- ICMPv6 프로토콜 - Neighbor Discovery 캐시 메커니즘.
- ICMPv6 프로토콜 - MLDv2(Multicast Listener Discovery v2) 프로토콜 구현(스택에 필요한 메시지만)
- UDP 프로토콜 - 전체 지원. 스택은 UDP 프로토콜을 사용하여 다른 호스트와 데이터를 교환할 수 있습니다.
- UDP 소켓 - Berkeley 소켓과 유사한 스택의 '최종 사용자' API를 완벽하게 지원합니다.
- UDP 서비스 - 테스트 목적으로 구현된 Echo, Discard 및 Daytime 서비스('예제').
- TCP 프로토콜 - TCP 유한 상태 머신의 전체 구현입니다. 이 시점에서 스택은 TCP 프로토콜을 통해 다른 호스트와 대량 데이터를 교환할 수 있습니다.
- TCP 프로토콜 - MSS, WSCALE, SACKPERM, TIMESTAMP에 대한 TCP 옵션 지원.
- TCP 프로토콜 - 데이터 재전송이 포함된 TCP 슬라이딩 윈도우 메커니즘(빠른 재전송 및 시간 기반 시나리오)입니다.
- TCP 프로토콜 - TCP 백오프 메커니즘/기본 혼잡 제어.
- TCP 프로토콜 - TCP SYN/FIN 패킷 재전송.
- TCP 소켓 - 버클리 소켓과 유사한 스택의 '최종 사용자' API를 완벽하게 지원합니다.
구현 예정:
예
여러 개의 핑 패킷과 두 마리의 원숭이가 IPv6 프로토콜을 통해 TCP를 통해 전달되었습니다.
IPv6 이웃 검색/중복 주소 감지/주소 자동 구성.
- 스택은 링크 로컬 주소를 자동 구성하려고 시도합니다. EUI64 주소로 생성됩니다. DAD 프로세스의 일부로 적절한 요청 노드 멀티캐스트 그룹에 가입하고 생성된 주소에 대한 이웃 요청을 보냅니다.
- 스택은 자신이 생성한 주소에 대한 이웃 광고를 수신하지 않으므로 해당 주소를 인터페이스에 할당합니다.
- 스택은 미리 구성된 고정 주소를 할당하려고 시도합니다. DAD 프로세스의 일부로 적절한 요청 노드 멀티캐스트 그룹에 가입하고 정적 주소에 대한 이웃 요청을 보냅니다.
- 이미 할당된 동일한 주소를 가진 다른 호스트는 Neighbor Advertising 메시지로 응답합니다. 이는 다른 호스트가 할당하려는 주소를 이미 할당했기 때문에 스택이 해당 주소를 사용할 수 없음을 스택에 알려줍니다.
- 스택은 사용해야 하는 전역 접두사가 있는지 확인하기 위해 라우터 요청 메시지를 보냅니다.
- 라우터는 추가 접두사가 포함된 라우터 광고로 응답합니다.
- 스택은 수신된 접두사와 EUI64 호스트 부분을 기반으로 생성된 주소를 할당하려고 시도합니다. DAD 프로세스의 일부로 적절한 요청 노드 멀티캐스트 그룹에 가입하고 정적 주소에 대한 이웃 요청을 보냅니다.
- 스택은 자신이 생성한 주소에 대한 이웃 광고를 수신하지 않으므로 해당 주소를 인터페이스에 할당합니다.
- 모든 주소가 할당된 후 스택은 수신하려는 모든 멀티캐스트 주소를 나열하는 멀티캐스트 수신기 보고서를 하나 더 보냅니다.
TX 패킷 손실 후 TCP 빠른 재전송이 작동 중입니다.
- 시뮬레이션된 패킷 손실 메커니즘으로 인해 나가는 패킷이 '손실'됩니다.
- 피어는 패킷 SEQ 번호의 불일치를 발견하고 '빠른 재전송 요청'을 보냅니다.
- 스택은 요청을 수신하고 손실된 패킷을 재전송합니다.
RX 패킷 손실 이벤트 중에 작동하는 순서가 잘못된 큐
- 시뮬레이션된 패킷 손실 메커니즘으로 인해 들어오는 패킷이 '손실'됩니다.
- 스택은 인바운드 패킷의 SEQ 번호에서 불일치를 발견하고 '빠른 재전송' 요청을 보냅니다.
- 피어는 요청을 받기 전에 스택이 예상하는 것보다 높은 SEQ로 여러 패킷을 보냅니다. 스택은 모든 패킷을 대기열에 넣습니다.
- 피어는 손실된 패킷을 재전송합니다.
- 스택은 손실된 패킷을 수신하고, 비순차적 큐에 저장된 모든 패킷을 가져와서 처리합니다.
- 스택은 대기열에서 가져온 최신 패킷을 확인하기 위해 ACK 패킷을 보냅니다.
TCP 유한 상태 머신 - 스택이 TCP Echo 서비스를 실행 중입니다.
- 피어가 연결을 엽니다.
- 피어가 데이터를 보냅니다.
- 스택은 데이터를 다시 에코합니다.
- 피어가 연결을 닫습니다.
TCP 유한 상태 머신 - 스택이 TCP 에코 클라이언트를 실행 중입니다.
- 스택이 연결을 엽니다.
- 스택이 데이터를 보냅니다.
- 피어는 데이터를 다시 에코합니다.
- 스택이 연결을 닫습니다.
사전 구문 분석 패킷 온전성 검사가 실행 중입니다.
- 첫 번째 스크린샷은 온전성 검사가 꺼진 스택을 보여줍니다. 잘못된 ICMPv6 패킷으로 인해 충돌이 발생할 수 있습니다.
- 두 번째 스크린샷은 온전성 검사가 켜진 스택을 보여줍니다. 잘못된 ICMPv6 패킷은 ICMPv6 프로토콜 파서에 전달되기 전에 삭제됩니다.
- 세 번째 스크린샷은 잘못된 패킷을 보여줍니다. 패킷에 레코드가 하나만 포함되어 있어도 MA 레코드 수 필드는 777로 설정되었습니다.
ARP 프로브/공지 메커니즘.
- 스택은 ARP 프로브를 사용하여 구성된 모든 IP 주소에 대해 가능한 충돌을 찾습니다.
- IP 주소 중 하나(192.168.9.102)가 이미 사용 중이므로 스택은 이에 대한 알림을 받고 건너뜁니다.
- 나머지 IP 주소는 무료이므로 스택은 각각에 대해 ARP 공지를 전송하여 이를 요청합니다.
ARP 확인 및 핑 패킷 처리.
- 호스트 192.168.9.20은 스택에 대한 ping을 시도합니다. 이를 수행하려면 먼저 ARP 요청 패킷을 보내 스택의 MAC 주소를 알아냅니다.
- 스택은 ARP 응답 패킷을 보내 응답합니다(스택은 이미 호스트의 요청에서 호스트의 MAC을 기록해 두었으므로 요청을 보낼 필요가 없습니다).
- 호스트는 ping 패킷을 보내고 스택은 이에 응답합니다.
IP 조각화.
- 호스트는 3개의 조각화된 IP 패킷(3개의 조각)을 사용하여 4Kb UDP 데이터그램을 보냅니다.
- 스택은 패킷을 수신하고 이를 단일 조각으로 조립한 다음 이를 (UDP 프로토콜 핸들러 및 UDP 소켓을 통해) UDO Echo 서비스에 전달합니다.
- UDP Echo 서비스는 데이터를 수집하여 UDP 소켓에 다시 넣습니다.
- UDP 데이터그램은 IP 패킷을 생성하는 IP 프로토콜 핸들러로 전달되고, 그것이 링크를 초과하는지 확인한 후 MTU는 이를 3개의 개별 IP 패킷으로 분할합니다.
- IP 패킷은 이더넷 프레임에 캡슐화되어 TX 링에 배치됩니다.