PyTCP
สแต็ก TCP/IP ที่เขียนด้วยภาษา Python
PyTCP เป็นสแต็ก TCP/IP ที่ทำงานได้อย่างสมบูรณ์ซึ่งเขียนด้วย Python รองรับการขนส่งแบบสตรีม TCP พร้อมการจัดส่งแพ็คเก็ตที่เชื่อถือได้ตามกลไกหน้าต่างบานเลื่อนและการควบคุมความแออัดขั้นพื้นฐาน นอกจากนี้ยังรองรับโปรโตคอล IPv6/ICMPv6 พร้อมการกำหนดค่าที่อยู่ SLAAC มันทำงานเป็นโปรแกรมพื้นที่ผู้ใช้ที่แนบมากับอินเทอร์เฟซ Linux TAP ได้ใช้การกำหนดเส้นทางแบบง่ายๆ และสามารถส่งและรับการรับส่งข้อมูลผ่านเครือข่ายท้องถิ่นและอินเทอร์เน็ต
เวอร์ชัน 2.7 ต่างจากรุ่นก่อนตรงที่มีโค้ดสแต็ก PyTCP ในรูปแบบของไลบรารี เพื่อให้สามารถนำเข้าและใช้งานโดยโค้ดภายนอกได้อย่างง่ายดาย สิ่งนี้ควรทำให้ประสบการณ์ผู้ใช้ราบรื่นขึ้น และในที่สุดก็มอบความสามารถเต็มรูปแบบในการแทนที่การเรียกสแต็ก Linux มาตรฐาน (เช่น ไลบรารีซ็อกเก็ต) ด้วยการเรียก PyTCP ในแอปพลิเคชันบุคคลที่สาม
โปรเจ็กต์นี้เริ่มแรกเริ่มต้นจากความพยายามด้านการศึกษาเพียงอย่างเดียวโดยมีเป้าหมายเพื่อพัฒนาทักษะ Python ของฉันและฟื้นฟูความรู้ด้านเครือข่ายของฉัน โดยเป็นส่วนหนึ่งของการเตรียมการสำหรับบทบาทวิศวกรเครือข่ายที่ Facebook ตั้งแต่นั้นมา มันก็กลายเป็นเหมือน 'โปรเจ็กต์สัตว์เลี้ยง' มากขึ้น ซึ่งฉันอุทิศเวลาบางส่วนอย่างไม่สม่ำเสมอ อย่างไรก็ตาม โดยปกติแล้วจะมีการเพิ่มการอัปเดตสองสามรายการทุกๆ เดือนหรือสองเดือน
ฉันยินดีรับความช่วยเหลือและความช่วยเหลือจากทุกคนที่สนใจในการเขียนโปรแกรมเครือข่าย การป้อนข้อมูลใด ๆ ที่ชื่นชม นอกจากนี้ โปรดจำไว้ว่าฟีเจอร์สแต็กบางอย่างอาจมีการใช้งานเพียงบางส่วนเท่านั้น (ตามที่จำเป็นสำหรับการดำเนินการสแต็ก) อาจมีการใช้งานในรูปแบบที่ไม่เหมาะที่สุดหรือไม่สอดคล้องกับ RFC 100% (เนื่องจากไม่มีเวลา) หรืออาจมีข้อบกพร่องที่ฉันยังต้องแก้ไข
โปรดตรวจสอบอีกสองโครงการที่เกี่ยวข้องของฉัน:
- RusTCP - พยายามเขียนฟังก์ชัน PyTCP บางส่วนใหม่ใน Rust และใช้เพื่อสร้างเราเตอร์ห้องปฏิบัติการ IPv6/SRv6
- SeaTCP - พยายามสร้างสแต็กเวลาแฝงต่ำโดยใช้ภาษา C และ Assembly
หลักการทำงานและการตั้งค่าการทดสอบ
สแต็ก PyTCP ขึ้นอยู่กับอินเทอร์เฟซ Linux TAP อินเทอร์เฟซ TAP เป็นอินเทอร์เฟซเสมือนที่สามารถ 'เสียบ' เข้ากับโครงสร้างพื้นฐานเครือข่ายเสมือนที่มีอยู่ที่ปลายเครือข่ายผ่านทาง Linux Bridge หรือ Open vSwitch ที่ส่วนภายใน สามารถใช้อินเทอร์เฟซ TAP ได้เหมือนกับ NIC อื่นๆ โดยการส่งและรับแพ็กเก็ตไปยัง/จากโดยทางโปรแกรม
หากคุณต้องการทดสอบสแต็ค PyTCP ในเครือข่ายท้องถิ่นของคุณ ฉันขอแนะนำให้สร้างการตั้งค่าเครือข่ายต่อไปนี้ซึ่งจะช่วยให้คุณสามารถเชื่อมต่อทั้งเคอร์เนล Linux (ซึ่งก็คือระบบปฏิบัติการ Linux ของคุณ) และสแต็ค PyTCP กับเครือข่ายท้องถิ่นของคุณในเวลาเดียวกัน .
<INTERNET> <---> [ROUTER] <---> (eth0)-[Linux bridge]-(br0) <---> [Linux TCP/IP stack]
|
|--(tap7) <---> [PyTCP TCP/IP stack]
หลังจากที่โปรแกรมตัวอย่าง (ไคลเอ็นต์หรือบริการ) เริ่มต้นสแต็กแล้ว โปรแกรมจะสามารถสื่อสารกับสแต็กผ่านซ็อกเก็ต BSD แบบง่าย เช่น อินเทอร์เฟซ API นอกจากนี้ยังมีความเป็นไปได้ของการส่งแพ็กเก็ตโดยตรงโดยการเรียกหนึ่งในเมธอด _*_phtx()
จากคลาส PacketHandler
การโคลน PyTCP จากที่เก็บ GitHub
ในกรณีส่วนใหญ่ PyTCP ควรถูกโคลนโดยตรงจากพื้นที่เก็บข้อมูล GitHub เนื่องจากการติดตั้งประเภทนี้จะให้สภาพแวดล้อมการพัฒนาและการทดสอบเต็มรูปแบบ
git clone http://github.com/ccie18643/PyTCP
หลังจากการโคลนนิ่ง เราสามารถรันหนึ่งในตัวอย่างที่รวมไว้:
- ไปที่ไดเรกทอรีรากของสแต็ก (เรียกว่า 'PyTCP')
- เรียกใช้คำสั่ง
sudo make bridge
เพื่อสร้างบริดจ์ 'br0' หากจำเป็น - เรียกใช้คำสั่ง
sudo make tap
เพื่อสร้างอินเทอร์เฟซ tap7 และกำหนดให้กับบริดจ์ 'br0' - รันคำสั่ง
make
เพื่อสร้างสภาพแวดล้อมเสมือนที่เหมาะสมสำหรับการพัฒนาและการทดสอบ - วิ่ง
. venv/bin/activate
คำสั่ง . venv/bin/activate
เพื่อเปิดใช้งานสภาพแวดล้อมเสมือน - ดำเนินการกับตัวอย่างใดๆ เช่น
example/run_stack.py
- กด Ctrl-C เพื่อหยุดมัน
หากต้องการปรับแต่งพารามิเตอร์การทำงานของสแต็กต่างๆ อย่างละเอียด โปรดแก้ไขไฟล์ pytcp/config.py
ตามนั้น
การติดตั้ง PyTCP จากที่เก็บ PyPi
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 และเริ่มใช้โค้ดต่อไปนี้ มันเริ่มต้นระบบย่อยสแต็กและกำหนดค่าอัตโนมัติทั้งที่อยู่โปรโตคอล IPv4 และ IPv6 โดยใช้ DHCPv4 และ IPv6 SLAAC ตามลำดับ
from pytcp import TcpIpStack
stack = TcpIpStack ( interface = "tap7" )
stack . start ()
ระบบย่อยสแต็กรันในเธรดของตัวเอง หลังจากเริ่มต้น สแต็กจะให้การควบคุมกลับไปยังโค้ดผู้ใช้และสามารถหยุดได้โดยใช้การเรียกต่อไปนี้
คุณสมบัติ
ดำเนินการแล้ว:
- Stack - Fast Packet Parser โดยใช้วิธี 'zero copy'
- Stack - Fast Packet Assembler โดยใช้วิธี 'zero copy'
- สแต็ก - ไลบรารีการจัดการที่อยู่ MAC - เข้ากันได้กับโปรโตคอลบัฟเฟอร์ (Memoryview)
- สแต็ก - ไลบรารีการจัดการที่อยู่ IPv4 - เข้ากันได้กับโปรโตคอลบัฟเฟอร์ (Memoryview) และไม่ขึ้นอยู่กับไลบรารีมาตรฐาน Python
- สแต็ก - ไลบรารีการจัดการที่อยู่ IPv6 - เข้ากันได้กับโปรโตคอลบัฟเฟอร์ (Memoryview) และไม่ขึ้นอยู่กับไลบรารีมาตรฐาน Python
- รหัส - การทดสอบหน่วยสำหรับไลบรารีและโมดูลบางส่วน (ขึ้นอยู่กับเฟรมเวิร์ก Testslide ของ Facebook)
- โปรโตคอลอีเธอร์เน็ต - รองรับเฟรมมาตรฐาน Ethernet II
- โปรโตคอลอีเธอร์เน็ต - Unicast, มัลติคาสต์ IPv4, มัลติคาสต์ IPv6 และการกำหนดที่อยู่การออกอากาศ
- โปรโตคอล ARP - การตอบกลับ การสืบค้น กลไกแคช ARP
- โปรโตคอล ARP - กลไกการตรวจจับข้อขัดแย้ง IP (ACD) ของ ARP Probe/การประกาศ
- โปรโตคอล IPv4 - การกำหนดเส้นทางเริ่มต้น สแต็กสามารถพูดคุยกับโฮสต์ผ่านทางอินเทอร์เน็ตโดยใช้โปรโตคอล IPv4
- โปรโตคอล IPv4 - การกำหนดค่าที่อยู่ IPv4 อัตโนมัติโดยใช้โปรโตคอล DHCPv4
- โปรโตคอล IPv4 - การจัดเรียงแพ็กเก็ตขาเข้า กลไกที่แข็งแกร่งสามารถจัดการส่วนข้อมูลที่ไม่อยู่ในลำดับและทับซ้อนกันได้
- โปรโตคอล IPv4 - การกระจายตัวของแพ็คเก็ตขาออก
- โปรโตคอล IPv4 - ยอมรับตัวเลือก IPv4 แต่ไม่รองรับ
- โปรโตคอล IPv4 - รองรับที่อยู่ IPv4 ของสแต็กหลายรายการ โดยแต่ละที่อยู่ทำหน้าที่ตามที่ถูกกำหนดให้แยก VRF
- โปรโตคอล ICMPv4 - ข้อความคำขอ echo, echo-reply และพอร์ตที่ไม่สามารถเข้าถึงได้
- โปรโตคอล IPv6 - การกำหนดเส้นทางเริ่มต้น สแตกสามารถพูดคุยกับโฮสต์ผ่านทางอินเทอร์เน็ตโดยใช้โปรโตคอล IPv6
- โปรโตคอล IPv6 - การกำหนดค่าที่อยู่ลิงก์ภายในอัตโนมัติโดยใช้ EUI64 และการตรวจจับที่อยู่ซ้ำ
- โปรโตคอล IPv6 - การกำหนดค่าที่อยู่ GUA อัตโนมัติโดยใช้ Router Advertisement / EUI64
- โปรโตคอล IPv6 - การกำหนดที่อยู่ Solicited Node Multicast โดยอัตโนมัติ
- โปรโตคอล IPv6 - การกำหนดที่อยู่ MAC มัลติคาสต์ IPv6 โดยอัตโนมัติ
- โปรโตคอล IPv6 - การจัดเรียงข้อมูลแพ็กเก็ตขาเข้า กลไกที่แข็งแกร่งสามารถจัดการส่วนข้อมูลที่ไม่อยู่ในลำดับและทับซ้อนกันได้
- โปรโตคอล IPv6 - การกระจายตัวของแพ็กเก็ตขาออก
- โปรโตคอล ICMPv6 - ข้อความ echo-request, echo-reply และ port-unreachable
- โปรโตคอล ICMPv6 - การค้นพบเพื่อนบ้าน, การตรวจจับที่อยู่ซ้ำ
- โปรโตคอล ICMPv6 - กลไกแคช Neighbor Discovery
- โปรโตคอล ICMPv6 - การใช้งานโปรโตคอล Multicast Listener Discovery v2 (MLDv2) (เฉพาะข้อความที่ต้องการโดยสแต็ก)
- โปรโตคอล UDP - รองรับอย่างเต็มที่ สแต็กสามารถแลกเปลี่ยนข้อมูลกับโฮสต์อื่นโดยใช้โปรโตคอล UDP
- ซ็อกเก็ต UDP - รองรับ API 'ผู้ใช้ปลายทาง' เต็มรูปแบบซึ่งคล้ายกับซ็อกเก็ต Berkeley
- บริการ UDP - บริการ Echo, Discard และ Daytime ที่นำไปใช้เพื่อการทดสอบ (ใน 'ตัวอย่าง')
- โปรโตคอล TCP - การใช้งาน TCP Finite State Machine อย่างเต็มรูปแบบ ณ จุดนี้ สแตกสามารถแลกเปลี่ยนข้อมูลจำนวนมากกับโฮสต์อื่นผ่านโปรโตคอล TCP
- โปรโตคอล TCP - ตัวเลือก TCP รองรับ: MSS, WSCALE, SACKPERM, TIMESTAMP
- โปรโตคอล TCP - กลไกหน้าต่างบานเลื่อน TCP พร้อมด้วยการส่งข้อมูลซ้ำ (การส่งข้อมูลซ้ำอย่างรวดเร็วและสถานการณ์ตามเวลา)
- โปรโตคอล TCP - กลไกการถอยกลับของ TCP / การควบคุมความแออัดขั้นพื้นฐาน
- โปรโตคอล TCP - การส่งข้อมูลแพ็กเก็ต TCP SYN/FIN ใหม่
- ซ็อกเก็ต TCP - รองรับ API 'ผู้ใช้ปลายทาง' ของสแต็กเต็มรูปแบบซึ่งคล้ายกับซ็อกเก็ต Berkeley
ที่จะนำไปใช้:
ตัวอย่าง
แพ็กเก็ต Ping หลายตัวและลิงสองตัวถูกส่งผ่าน TCP ผ่านโปรโตคอล IPv6
การค้นพบเพื่อนบ้าน IPv6 / การตรวจจับที่อยู่ซ้ำ / การกำหนดค่าอัตโนมัติของที่อยู่
- สแต็กพยายามกำหนดค่าที่อยู่ลิงก์ในเครื่องโดยอัตโนมัติ มันสร้างเป็นที่อยู่ EUI64 เป็นส่วนหนึ่งของกระบวนการ DAD โดยจะเข้าร่วมกลุ่มมัลติคาสต์ที่ร้องขอโหนดที่เหมาะสม และส่งการชักชวนเพื่อนบ้านสำหรับที่อยู่ที่สร้างขึ้น
- Stack ไม่ได้รับโฆษณา Neighbor Advertisement สำหรับที่อยู่ที่สร้างขึ้น ดังนั้นจึงกำหนดที่อยู่ดังกล่าวให้กับอินเทอร์เฟซของมัน
- สแต็กพยายามกำหนดที่อยู่คงที่ที่กำหนดค่าไว้ล่วงหน้า ในฐานะที่เป็นส่วนหนึ่งของกระบวนการ DAD จะเข้าร่วมกลุ่มมัลติคาสต์ที่ร้องขอโหนดที่เหมาะสม และส่งการชักชวนเพื่อนบ้านสำหรับที่อยู่แบบคงที่
- โฮสต์อื่นที่มีที่อยู่เดียวกันได้รับการตอบกลับพร้อมข้อความโฆษณาเพื่อนบ้านแล้ว สิ่งนี้จะบอกสแต็กว่าโฮสต์อื่นได้กำหนดที่อยู่ที่พยายามกำหนดไว้แล้ว ดังนั้นสแต็กจึงไม่สามารถใช้งานได้
- Stack ส่งข้อความ Router Solicitation เพื่อตรวจสอบว่ามีคำนำหน้าส่วนกลางที่ควรใช้หรือไม่
- เราเตอร์ตอบสนองด้วยโฆษณาเราเตอร์ที่มีคำนำหน้าเพิ่มเติม
- สแต็กพยายามกำหนดที่อยู่ที่สร้างขึ้นตามคำนำหน้าที่ได้รับและส่วนของโฮสต์ EUI64 ในฐานะที่เป็นส่วนหนึ่งของกระบวนการ DAD จะเข้าร่วมกลุ่มมัลติคาสต์ที่ร้องขอโหนดที่เหมาะสม และส่งการชักชวนเพื่อนบ้านสำหรับที่อยู่แบบคงที่
- Stack ไม่ได้รับโฆษณา Neighbor Advertisement สำหรับที่อยู่ที่สร้างขึ้น ดังนั้นจึงกำหนดให้กับอินเทอร์เฟซของมัน
- หลังจากกำหนดที่อยู่ทั้งหมดแล้ว สแตกจะส่งรายงาน Multicast Listener อีกหนึ่งรายงานโดยแสดงรายการที่อยู่ multicast ทั้งหมดที่ต้องการฟัง
TCP Fast Retransmit ดำเนินการหลังจากแพ็กเก็ต TX สูญหาย
- แพ็กเก็ตขาออก 'สูญหาย' เนื่องจากกลไกการสูญเสียแพ็กเก็ตจำลอง
- เพียร์สังเกตเห็นความไม่สอดคล้องกันในหมายเลข SEQ ของแพ็กเก็ต และส่ง 'คำขอส่งซ้ำอย่างรวดเร็ว' ออกไป
- Stack ได้รับการร้องขอและส่งแพ็กเก็ตที่สูญหายอีกครั้ง
คิวที่ไม่อยู่ในลำดับกำลังทำงานในระหว่างเหตุการณ์การสูญเสียแพ็กเก็ต RX
- แพ็กเก็ตที่เข้ามาจะ 'สูญหาย' เนื่องจากกลไกการสูญหายของแพ็กเก็ตจำลอง
- สแต็กสังเกตเห็นความไม่สอดคล้องกันในหมายเลข SEQ ของแพ็กเก็ตขาเข้า และส่งคำขอ 'ส่งซ้ำอย่างรวดเร็ว'
- ก่อนที่เพียร์จะได้รับคำขอ เพียร์จะส่งแพ็กเก็ตหลายชุดที่มี SEQ สูงกว่าที่สแต็กคาดไว้ สแต็กคิวแพ็กเก็ตเหล่านั้นทั้งหมด
- เพียร์จะส่งแพ็กเก็ตที่หายไปอีกครั้ง
- Stack รับแพ็กเก็ตที่สูญหาย ดึงแพ็กเก็ตทั้งหมดที่จัดเก็บไว้ในคิวที่ไม่อยู่ในลำดับ และประมวลผลแพ็กเก็ตเหล่านั้น
- Stacks จะส่งแพ็กเก็ต ACK ออกไปเพื่อรับทราบแพ็กเก็ตล่าสุดที่ถูกดึงออกจากคิว
TCP Finite State Machine - สแต็กกำลังเรียกใช้บริการ TCP Echo
- เพียร์เปิดการเชื่อมต่อ
- เพียร์ส่งข้อมูล
- Stack สะท้อนข้อมูลกลับ
- เพียร์ปิดการเชื่อมต่อ
TCP Finite State Machine - สแต็กกำลังเรียกใช้ไคลเอนต์ TCP Echo
- Stack เปิดการเชื่อมต่อ
- สแต็คส่งข้อมูล
- เพียร์สะท้อนข้อมูลกลับ
- Stack ปิดการเชื่อมต่อ
การตรวจสอบความสมบูรณ์ของแพ็คเก็ตก่อนแยกวิเคราะห์ในการดำเนินการ
- ภาพหน้าจอแรกแสดงสแต็กที่ปิดการตรวจสอบสภาพ แพ็กเก็ต ICMPv6 ที่มีรูปแบบไม่ถูกต้องอาจทำให้แพ็กเก็ตเสียหายได้
- ภาพหน้าจอที่สองแสดงสแต็กที่เปิดการตรวจสอบสภาพจิตใจไว้ แพ็กเก็ต ICMPv6 ที่มีรูปแบบไม่ถูกต้องจะถูกละทิ้งก่อนที่จะถูกส่งไปยังตัวแยกวิเคราะห์โปรโตคอล ICMPv6
- ภาพหน้าจอที่สามแสดงแพ็กเก็ตที่มีรูปแบบไม่ถูกต้อง จำนวนฟิลด์ระเบียน MA ได้รับการตั้งค่าเป็น 777 แม้ว่าแพ็คเก็ตจะมีเพียงระเบียนเดียวเท่านั้น
ARP Probe/กลไกการประกาศ
- Stack ใช้ ARP Probes เพื่อค้นหาข้อขัดแย้งที่เป็นไปได้สำหรับที่อยู่ IP ทุกรายการที่กำหนดค่า
- หนึ่งในที่อยู่ IP (192.168.9.102) ถูกใช้ไปแล้ว ดังนั้นสแต็กจะได้รับการแจ้งเตือนและข้ามไป
- ที่อยู่ IP ที่เหลือนั้นฟรี ดังนั้นสแต็กจึงอ้างสิทธิ์โดยการส่งประกาศ ARP สำหรับแต่ละที่อยู่
ความละเอียด ARP และการจัดการแพ็กเก็ต ping
- โฮสต์ 192.168.9.20 พยายามปิงสแต็ก เพื่อให้สามารถทำได้ ก่อนอื่นจะต้องส่งแพ็กเก็ต ARP Request เพื่อค้นหาที่อยู่ MAC ของสแต็ก
- สแต็กตอบสนองโดยการส่งแพ็กเก็ต ARP Reply (สแต็กไม่จำเป็นต้องส่งคำขอเนื่องจากสแต็กได้จดบันทึก MAC ของโฮสต์จากคำขอของโฮสต์แล้ว)
- โฮสต์ส่งแพ็กเก็ต ping และสแต็กตอบกลับ
การกระจายตัวของ IP
- โฮสต์ส่งดาตาแกรม UDP ขนาด 4Kb โดยใช้แพ็กเก็ต IP ที่กระจัดกระจายสามชุด (สามส่วน)
- Stack รับแพ็กเก็ตและประกอบเป็นชิ้นเดียว จากนั้นส่งต่อ (ผ่านตัวจัดการโปรโตคอล UDP และซ็อกเก็ต UDP) ไปยังบริการ UDO Echo
- บริการ UDP Echo จะรับข้อมูลและนำกลับเข้าไปในซ็อกเก็ต UDP
- ดาตาแกรม UDP ถูกส่งไปยังตัวจัดการโปรโตคอล IP ซึ่งจะสร้างแพ็กเก็ต IP และหลังจากตรวจสอบว่าเกินลิงก์แล้ว MTU จะแยกส่วนออกเป็นสามแพ็กเก็ต IP แยกกัน
- แพ็กเก็ต IP ถูกห่อหุ้มในเฟรมอีเทอร์เน็ตและสวมบนวงแหวน TX