# 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

## 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,用來確認傳送資料是否有錯誤。

一個影格以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封包的內容。

```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 */
};
```


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: 錯誤檢查碼,確認資料傳送的正確性。

```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

```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.