# packet、ip & htons() ###### tags: `EOS` 1. Ethernet II 封包(Frame) 2. IP封包(Datagram) 3. Transport Layer: tcp、udp、raw >reference: >* https://dotblogs.com.tw/Leo_CodeSpace/2019/03/29/203853 >* https://bryceknowhow.blogspot.com/2013/12/mac-frame-format.html >* [packet data structure結構](https://linux-kernel-labs.github.io/refs/heads/master/labs/networking.html#the-struct-socket-fields) >* https://linux.vbird.org/linux_server/redhat9/0110network_basic.php ![](https://hackmd.io/_uploads/S1ul8bFNh.png) ## Ethernet frame (L2 data link layer) * Preamble:一連串的1010…10,用來同步 * SFD(start of frame delimiter):為10101011,用來表示經同步之後,資料的起始 * DA(Destination Address):目標的MAC位址,MAC為6bytes的硬體碼,從00:00:00:00:00:00到FF:FF:FF:FF:FF:FF,前3碼為製造廠商碼,後3碼為廠商自訂的流水號。 * SA(Source Address):來源的MAC位址。 * Ether type:長度或者類別,IP封包為0x0800;ARP封包為0x0806。 * Payload: MAC封包所要傳送的資料內容,也就是IP封包或者ARP封包等等,如底下第2點所介紹。 * PAD(Padding):Ethernet封包長度介於46~1500bytes之間,因此假設IP封包長度沒有符合就必須做補滿的動作。 * FCS(Frame check sequence, CRC32):Checksum,用來確認傳送資料是否有錯誤。 ![](https://hackmd.io/_uploads/SJAwalYE3.png) 一個影格以7個位元組的前導碼和1個位元組的影格開始符作為影格的開始。快速乙太網路之前,線上路上影格的這部分的位元型樣是10101010 10101010 10101010 10101010 10101010 10101010 10101010 10101011。由於在傳輸一個位元組時最低位最先傳輸(LSB),因此其相應的16進位表示為0x55 0x55 0x55 0x55 0x55 0x55 0x55 0xD5。 ## Internet Protocol version 4 (L3 network layer) IP封包包含IP首部和資料部分 首部 * Version: IPv4(4) or IPv6(6)。 * Identification: IP封包最大可達65535Bytes,為了能夠放到MAC frame裡面,所以IP封包會分割,然後到了目的地電腦之後,再利用Identification來重組。 * Fragment offset: 被切割的IP封包在原本封包裡面的offset,用來重組。 * Time to Live: 為了避免無用的封包佔用網路資源,因此設計了一個TTL值,預設通常都是128 or 64,此參數的意義是封包每經過一個網路設備,TTL值就會減1,直到TTL值為0,封包就會被discard,才不會造成找不到目的地的IP封包會一直在網路上傳輸,佔用了網路的資源。 * Protocol: 常用的為TCP(0x06)或者UDP(0x17)封包協定。 * Header Checksum: 用來確認傳輸資料的正確與否。 * Source Address: 來源IP位址。 * Destination Address: 目的IP位址。 * Data: TCP or UDP封包的內容。 ![](https://hackmd.io/_uploads/B1AZRxYEn.png) ```c struct iphdr { #if defined(__LITTLE_ENDIAN_BITFIELD) __u8 ihl:4, version:4; #elif defined (__BIG_ENDIAN_BITFIELD) __u8 version:4, ihl:4; #else #error "Please fix <asm/byteorder.h>" #endif __u8 tos; __be16 tot_len; __be16 id; __be16 frag_off; __u8 ttl; __u8 protocol; __sum16 check; __be32 saddr; __be32 daddr; /*The options start here. */ }; ``` ## L4 Transport Layer > reference: >* [inet socket & packet socket](https://blog.csdn.net/chenmo187J3X1/article/details/101164024) >* [tcphdr](https://blog.csdn.net/weixin_42522912/article/details/116636402) - inet socket * SOCK_STREAM * SOCK_DGRAM - packet socket * SOCK_RAW ```c enum sock_type { SOCK_STREAM = 1, /* stream (connection) socket */ SOCK_DGRAM = 2, /* datagram (conn.less) socket */ SOCK_RAW = 3, /* raw socket */ }; ``` ![](https://hackmd.io/_uploads/H1Hs7ZY4n.png) ![](https://hackmd.io/_uploads/SkYJV-t43.png) AF_INET 與 AF_PACKET 的區別在於使用前者只能看到 IP 層以上的東西,而後者可以看到鏈路層的信息。 raw packet socket 對於 AF_PACKET 來說,一個報文可以這樣分解: packet = Ethernet Header + Payload 而 SOCK_DGRAM 和 SOCK_RAW 的區別就在於,在接收方向, 使用 SOCK_DGRAM 套接字的應用程序收到的報文已經去除了 Ethernet Header , 而 SOCK_RAW 套接字則會保留。 ### TCP * Source Port: 來源位址所使用的Port。 * Destination Port: 目的位址所使用的Port。 * Sequence Number, Acknowledgment Number: 要建立TCP連線為一個三向交握機制,為了確保兩者之間連線溝通的同步,就用這兩個參數來記錄。簡單來說Sequence Number為已送出的data量;而Acknowledgment Number為以接收的data量。假設A與B之間建立TCP連線,一開始A會送(SEQA=0, ACKA=0)的封包給B,請求與B建立連線,然後B收到之後會回覆一個(SEQB=0, ACKB=1)給A,確認A可以連線,因為已經收到一個封包資訊,所以ACK為1,A接收到之後最後會在回覆一個確認封包(SEQA=1, ACKA=1)給B,確認建立TCP連線,這也就是三向交握的機制。 * 這兩個參數的設計,可以確認A會在確認B送過來的封包之中的(SEQB/ACKB)得知B是否有正確收到上一個傳送過去的封包,然後才會再送下一個封包。 * 相較於UDP的設計,TCP可以確保每次封包都會正確傳送到目的地,是一個可靠的傳輸,但相對的缺乏效率。 * Window: TCP會確保封包是否正確送達目的地,不過每次都送出等待目的的回覆確認的話,會造成太多時間的在等待上,因此設計了Window參數,用來告訴對方一次可以接受的視窗大小,讓對方一次送N個TCP封包,然後再回覆確認,,若有沒收到的要求對方再重傳,確認N個封包都收到之後,再送下一個N個封包,如此可以增加傳送的效率。 * Checksum: 錯誤檢查碼,確認資料傳送的正確性。 ![](https://hackmd.io/_uploads/Hy3KbbFVn.png) ```c struct tcphdr { __be16 source; __be16 dest; __be32 seq; __be32 ack_seq; #if defined(__LITTLE_ENDIAN_BITFIELD) __u16 res1:4, doff:4, fin:1, syn:1, rst:1, psh:1, ack:1, urg:1, ece:1, cwr:1; #elif defined(__BIG_ENDIAN_BITFIELD) __u16 doff:4, res1:4, cwr:1, ece:1, urg:1, ack:1, psh:1, rst:1, syn:1, fin:1; #else #error "Adjust your <asm/byteorder.h> defines" #endif __be16 window; __sum16 check; __be16 urg_ptr; }; ``` ### UDP ![](https://hackmd.io/_uploads/ryWYGWKN3.png) ```c struct udphdr { __be16 source; //port __be16 dest; //port __be16 len; __sum16 check; }; ``` ## htons >https://www.cs.cmu.edu/~srini/15-441/S10/lectures/r01-sockets.pdf Binding contd - addrlen - size of the `sockaddr_in` ```c struct sockaddr_in saddr; int sockfd; unsigned short port = 4444; if((sockfd=socket(AF_INET, SOCK_STREAM, 0) < 0) { // from back a couple slides printf(“Error creating socket\n”); ... } memset(&saddr, '\0', sizeof(saddr)); // zero structure out saddr.sin_family = AF_INET; // match the socket() call saddr.sin_addr.s_addr = htonl(INADDR_ANY); // bind to any local address saddr.sin_port = htons(port); // specify port to listen on if((bind(sockfd, (struct sockaddr *) &saddr, sizeof(saddr)) < 0) { // bind! printf(“Error binding\n”); ... } ``` What'is htonl(), htons()? - Byte ordering - Network order is big-endian - Host order can be big- or little-endian - x86 is little-endian - SPARC is big-endian - Conversion - htons(), htonl(): host to network short/long - ntohs(), ntohl(): network order to host short/long - What need to be converted ? - Addresses - Port - etc.