คุยเรื่อง TCP Protocol แบบ ยาวไป ยาวไป !! [ตอนที่ 1]

Panupong Simto
4 min readMar 13, 2020

--

สวัสดีครับไม่ได้เขียน Blog นาน ช่วงนี้ผมค่อนข้างห่างจาก Linux แล้ว เนื่องจากภาระงานที่ได้รับมอบหมายมีมากขึ้น และกลับมาบ้านต้องเลี้ยงลูกด้วย เลยไม่ค่อยมีเวลาหมกมุ่นกับ Linux เท่าไหร่ และต้องทำอย่างอื่นแทน นั่นก็คือ Wireshark

เร็วๆนี้ ผมคงจะคลุกคลีกับ Wireshark มากขึ้น อาจจะได้พบกัน ทั้งโลก Online และ Offline รอติดตามกันนะครับ

สำหรับวันนี้เราจะมาพูดถึง TCP Protocol ชื่อเต็มๆคือ Transmission Control Protocol ที่สามารถหาอ่านได้เป็นร้อยๆพันๆหน้า ทั้งในหนังสือและในเว็บไซต์บนอินเตอร์เน็ต

แต่ปัญหาที่มักจะพับกัน โดยเฉพาะหนังสือหรือเว็บคนไทย

  • อธิบายเรื่อง Three-way Handshake แล้วจบเลย
  • อธิบายว่า มันคือโปรโตคอลที่เป็น Connection Oriented หากข้อมูลส่งไม่ครบมันจะส่งใหม่ แล้วจบเลย
  • อธิบายเรื่อง Port ต้นทาง ปลายทาง แล้วจบเลย

ทั้งนี้ทั้งนั้น มีคนไทยอีกมากมายที่เก่งกว่าผมเยอะแยะ แต่อาจจะไม่มีโอกาสได้ Publish บทความที่เข้าใจที่มาที่ไปเป็นภาษาไทยได้ หรือไม่ผมก็หาไม่เจอ รวมถึง สาเหตุเดิมๆคือความหงุดหงิด และรำคาญของผมเอง ผมก็เลยจะเขียนมันขึ้นมาเอง

เท่าที่ลองค้นหา สิ่งที่ขาดหายไปของเว็บคนไทย หนังสือภาษาไทย ในเรื่องนี้คือ

  • TCP Segment และ Maximum Segment Size (MSS) คืออะไร ทำไมต้องเป็นเช่นนั้น
  • TCP Sequence Number คืออะไร ทำไมต้องเป็นเช่นนั้น
  • TCP Windows Scaling คืออะไร ทำไมต้องเป็นเช่นนั้น
  • TCP Flag ต่างๆมีอะไรบ้าง และแต่ละ Flag จะใช้ตอนไหน ทำไมต้องเป็นเช่นนั้น
  • TCP Option อื่นๆ เช่น Selective Acknowledgment (SACK) คืออะไรบ้าง ทำไมต้องเป็นเช่นนั้น
  • TCP Re-transmission กระบวนการที่ TCP มองว่าต้องส่งข้อมูลซ้ำเพื่อให้ครบถ้วน ตัดสินใจอย่างไร ทำไมต้องเป็นเช่นนั้น
  • TCP Socket , Port ต้นทาง , ปลายทาง , เรื่องพื้นฐานที่เราอาจจะมองข้ามอะไรไปหรือเปล่า ?

เหล่านี้ ถ้าเราเข้าใจที่มาที่ไปอย่างถ่องแท้ ก็จะใช้ Wireshark ได้อย่างคล่องแคล่ว ไม่ใช่แค่เปิด Capture แล้วปล่อยให้ packet มันไหลๆๆๆไปเฉยๆ

เริ่มต้นที่ องค์ประกอบโครงสร้างของ TCP นั่นคือ TCP Header

ภาพจาก : https://upload.wikimedia.org/wikipedia/commons/0/08/TCP_Header.png

ค่อยๆสนใจไปตามผมนะ สิ่งที่ผมจะสนใจอันแรกคือ Flags มองเห็นมั้ยครับมันอยู่ตรงไหน ? 8 Bits ครับ ประกอบด้วย CWR , ECE , URG , ACK , PSH , RST , SYN , FIN

ทำไมต้องสนใจ Flags ?

เพราะจุดเริ่มต้น ของการสื่อสารระหว่าง Host 2 เครื่องด้วย Protocol TCP จะเริ่มต้นด้วยกระบวนการ “เขย่ามือ 3 ทาง” ภาษาอังกฤษที่เราคุ้นเคยกันดีคือ Three-Ways Handshake

โดยไอ้ Flags นี้จะเป็นตัวบอกตอนเริ่มต้น — เอาแค่เริ่มต้นก่อนนะ เดี๋ยวธาตุไฟแตก

ว่า “ฉันจะเริ่มสื่อสารกับคุณแล้วนะ คุณพร้อมจะสื่อสารมั้ย ?”

Flag ที่ต้องส่งไปหาผู้รับ และผู้รับส่งกลับมาคือ Flags = SYN

เขย่ามือ 3 ครั้งคือ

ครั้งที่ 1 ผู้ส่งขอเริ่มติดต่อ > ส่ง Flags = SYN

ครั้งที่ 2 ผู้รับรับการติดต่อและพร้อมจะติดต่อ > ส่ง Flags = SYN และมันจะถูกมองว่าเป็น SYN/ACK หรือพูดอีกนัยหนึ่งคือ การ ACK จากการ SYN การเขย่ามือครั้งที่ 1 และจะขอ SYN กลับเพื่อให้ผู้ส่ง ACK กลับมาเช่นกัน มันเลยกลายเป็น SYN/ACK

ครั้งที่ 3 ผู้ส่งส่ง ACK กลับไปหาผู้รับว่า เราสองคนมี SOCKET , TCP option ต่างๆที่ตกลงไว้แล้วนะ จะเริ่มส่งละนะ

จบกระบวนการ Three-way Handshake = Connection Established

สไลด์เมาส์วนกลับไปดูรูป TCP Header ข้างบนจะพบว่า ส่วนของ Flags = 8 bits แล้วมันประมาณไหนละ 8 Bits ? ดูภาพนี้ใน Wireshark เลย

ตัวอย่างในรูปคือ Flags ตอนเขย่ามือครั้งที่ 2 หรือจังหวะ SYN/ACK ซึ่ง Wireshark จะแสดงให้เราเห็นว่าค่า Flags = 0x012 สังเกตเห็นมีเลข 1 โผล่มาอยู่ 2 ที่ คือ Acknowledgment (ACK) และ Syn ซึ่งเอามาเรียงกันตามบิตก็จะได้ค่า Flags SYN/ACK อย่างที่เห็น เมื่อเราดูใน Wireshark เราต้องรู้ว่า TCP Segment นี้คือการเขย่ามือครั้งที่ 2 นั่นเอง

ปัจจัยในการที่จะทำ Three-Way Handshake สำเร็จ

  • TCP จากเครื่องส่ง Generate TCP Segment ขึ้นมาโดยสุ่ม Source Port เพื่อแปะลงบน TCP Header Segment ของตัวเอง
  • TCP จากเครื่องส่ง แปะ Flags = SYN มา ใน TCP Segment ตัวเองถูกต้อง
  • TCP จากเครื่องส่ง ระบุ Destination port (พอร์ตปลายทาง ต่อไปผมจะเขียนย่อๆว่า dst.port ) จาก Application ใดๆของเครื่องส่งระบุได้ถูกต้อง เช่น เปิดเว็บไซต์ผ่าน http ด้วยโปรแกรม Firefox ซึ่ง Default จะระบุ dst.port = 80

จากนั้น

  • TCP ณ เครื่องรับ มี Port เปิดตั้งท่ารออยู่ Service ทำงานปกติ เช่น Apache port 80
  • TCP ณ เครื่องรับ ได้รับ SYN มา และพบว่า TCP Header ที่ผู้ส่งแปะมาเป็นข้อมูลที่ Match พอที่จะสื่อสารได้กับตัวเอง จึงต้องส่ง SYN/ACK กลับไป โดยระบุ src.prt = 80 และ dst.port ก็คือ port ที่เครื่องส่ง สุ่มขึ้นมาในขั้นตอนแรก
  • เมื่อเครื่องส่งได้รับ TCP SYN/ACK ก็จะส่ง ACK กลับไปเพื่อหาผู้รับอีกครั้ง “เพื่อขอจองอะไรบางอย่าง” หลังจากนั้นก็จะเริ่มสื่อสารแบบ Connection Oriented ได้

Step ถัดมาหลังจากอ่านมาถึงเรื่องนี้ คือเรื่อง Port , TCP Port นั่นเอง

สไลด์เมาส์วนกลับไปดูรูป TCP Header ข้างบนอีกครั้งจะพบว่า TCP Header จะกำหนดให้ src.port และ dst.port ต่างมีบิตให้ใช้ port ได้ 16 บิต หรือ 65535 port (มาจาก 2 ยกกำลัง 16 = 65536–1)

port หมายเลข 1–65535 มันมีอะไรบ้างละ ? ก็เยอะไง แต่เราหลายๆคนคงรู้จัก port หลายๆ port ดีอยู่แล้ว เช่น

22-SSH

80-http

3389-Remote Desktop

เป็นต้น

โดย “ทั่วไป , ที่เห็นได้ตามสากล” เช่น การติดต่อกับเครื่อง Client <> Server เครื่อง Server จะต้องเปิด port รอรับการเชื่อมต่อ นิ่งๆ เป็นเว็บก็เปิด 80 รอไว้ ตัวมันเองจึงมอง src.port = 80

ส่วน client ตรง src.port จำเป็นต้องสุ่มขึ้นมา ………… เพราะพอ server ได้อ่าน TCP Header จาก Client แล้ว ก็จะรู้ว่า ต้องส่งกลับไปหา Client ที่ port ไหน เช่น 15436 ขาไปสุ่มมาแบบนี้ , ขากลับ server ก็ต้อง dst.port =15436 เมื่อ client ได้รับข้อมูล tcp header จาก server ก็จะสามารถส่งต่อไปยัง layer สูงกว่า tcp ได้ นั่นก็คือ application layer ก็จะรู้ว่าจะเข้า application ตัวไหน ไปโผล่ข้อมูลออกช่องไหน เพราะ src.port ที่ถูกสุ่มมันจะต้องไม่ซ้ำกับ app อื่นๆ เพราะในคอมพิวเตอร์ของเราก็ยังมีอีกหลาย app ที่ต้องใช้สื่อสารไปที่อื่นๆ ในเวลาเดียวกันเช่นกัน หาก src.port ซ้ำกัน เวลาส่งขากลับมา protocol คง งง ว่าจะเข้า app ไหน เพราะฉะนั้นต้อง random และห้ามซ้ำ นั่นเอง

ตอนนี้เราได้อะไรไปแล้วบ้าง ?

Thee-way Handshake , Flags 2 อัน , TCP Ports ไปแล้วนะ

ต่อมา หลังจากที่ 3 way handshake สำเร็จจะเริ่มการส่งแลกเปลี่ยนข้อมูล

TCP Sequence Number

ในการส่งข้อมูลจนถึงปลายทาง เราจะส่งไฟล์ขนาดใหญ่ๆผ่าน Network ไปจนถึงเครื่องปลายทางเลยในครั้งเดียว ไม่ได้ เพราะมันมีข้อจำกัดของ MTU (Maximum Transmission Unit) ถูกกำหนดโดยทั่วไปไว้ว่า 1 MTU จะส่งผ่าน Ethernet ได้ 1500 Bytes

งานของ Network Stack คือต้องหั่นชิ้นส่วนของการรับส่งขอมูลเป็นชิ้นๆ 1500 Bytes นี้ส่งผ่าน สาน Lan , Wireless , Fiber Optic ให้ได้

ปัญหาคือมีหลาย Layer และแต่ละ Layer ก็มี Header ของตัวเอง TCP Header (เลื่อนเมาส์ไปดู) ขอไว้ 20 Bytes IP Header ก็ขอไว้ 20 Bytes รวมเป็น 40 Bytes มันเลยกลายเป็น Ethernet Frame ที่จะถูก TCP , IP ที่อยู่ข้างบน แปะหัวส่งต่อมาให้มัน ซึ่งเป็น Layer ล่างๆเพื่อสร้างเป็น Frame ที่สูงสุดได้ 1500 มันจะต้องกลายเป็น 1500–40 = 1460 Bytes

เพราะฉะนั้น จะแปะอะไรใน Application Layer , TCP Layer , IP Layer ต้องรวมกันไม่ให้เกิน 1460 Bytes ……….. ถ้าเกินทำยังไง ? ก็ต้องหั่นเป็นชิ้นๆ เรียกกระบวนการนี้ว่า Fragmentation พอไปถึงปลายทาง ก็เอาชิ้นที่หั่นๆ มาประกอบเข้ากันใหม่

แล้วทีนี้จะรู้ได้ไงว่าชิ้นไหนเป็นข้อมูลของอันไหน มันไม่ประกอบกันมั่วรึ ?

ข้อมูลชิ้นนั้นที่ถูกหั่นก็ต้องแปะ TCP Sequence Number ด้วยไงครับ พอถึงฝั่งผู้รับ ก็แกะ TCP Header มาดู Sequence Number ว่าเลขอะไร ถ้าเป็นเลขชุดเดียวกันก็ประกอบร่าง ส่งต่อให้ Application Layer ต่อไปได้

แต่นั่นยังไม่เพียงพอ คนส่งก็ต้องรู้ว่าตัวเองส่งอะไรไปบ้าง ขาดส่งอะไรบ้าง องค์ประกอบเพิ่มเติมที่ต้องใช้ตอนนี้คือ Acknowledgement Numbers (คนละอันกับ Flags : ACK นะครับ) ก็จะส่งไปส่งกลับกันแบบนี้ สำหรับ Three-way Handshake ก็มี Sequence Number เหมือนกันครับ แต่ถ้าเราดูใน Wireshark มันจะทำการ Analyze ให้เราแล้วเป็นค่า default ทำให้เราเห็นเลข SEQ / ACK ได้สั้นลง ก็คือมันจะกลายเป็น Relative Number นั่นเองครับค่า SEQ/ACK จะเริ่มต้นด้วย 0 ใน TCP แรกของ Packet นั้นๆ ทำให้เราอ่านได้ง่ายขึ้นครับ (option นี้สามารถปิดได้ แต่เราจะเห็นเลขจริงของ SEQ/ACK แต่มันจะยาวและอ่านยากครับ)

host a จะเริ่ม SYN จะมี SEQ = 0 ACK = 0

host b จะ SYN/ACK จะมี SEQ = 0 ACK = 1

host a จะ ACK จะมี SEQ = 1 ACK = 1

จริงๆเขียนลึกไปเรื่อยๆผมว่าแม่งจะกลายเป็น RFC ฉบับแปลไทยแน่นอน แต่ช่างมันเหอะ ผมรำคาญ จะเขียนต่อแม่งยังงี้แหละ แต่ตอนนี้มันก็ยาวมากแล้ว ไว้ต่อตอนหน้านะครับ

--

--