---
# System prepended metadata

title: packet、ip & htons()
tags: [EOS]

---

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