# IPv4 - IPv4 Header
[TOC]
## 課程影片
### 第 7C 講 路由器運作原理以及網路互連技術 L07 3
{%youtube YDc9bV_PfFQ %}
### 第 7F 講 路由器運作原理以及網路互連技術 L07 6
{%youtube zH4Ep41EgfI %}
### [Python Network Packet Sniffer Tutorial - 4 - Unpacking IP Packet Headers](https://youtu.be/oKUkbMz5q7Y)
Note: this series starts from Ethernet packet sniffing.
{%youtube oKUkbMz5q7Y %}
## IPv4 Header Format
參考 [RFC791](https://datatracker.ietf.org/doc/html/rfc791):
```c
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Version| IHL |Type of Service| Total Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Identification |Flags| Fragment Offset |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Time to Live | Protocol | Header Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Destination Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Options | Padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
```
Linux 核心則定義在 [`include/uapi/linux/ip.h`](https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/ip.h#L86) 中:
```c
struct iphdr {
__u8 version:4,
ihl:4;
__u8 tos;
__be16 tot_len;
__be16 id;
__be16 frag_off;
__u8 ttl;
__u8 protocol;
__sum16 check;
__be32 saddr;
__be32 daddr;
/*The options start here. */
};
```
## `saddr` 與 `daddr` --- 發送者與接收者的 IP
`saddr` 為發送方的 IPv4 位址,而 `daddr` 則為接送方的 IPv4 位址。
## `id` 與 `frag_off` --- 屬於哪一份資料的哪一個部分?
使用 IP 協定傳輸資料時,一個完整的資料有可能被切分成多個部分,稱為 *fragment*。除此之外,當路由器在轉送封包時,因為不同傳遞媒介的 MTU (*maxima transmision unit*) 有可能不同。如果從一個網路轉送給另外一個網路時,發現單一封包擠不進另外一個網路的 MTU 時,就有可能在轉送的過程中切割從這些封包。比如說:乙太網路單一訊框能承載的資料大小與 WiFi 不同,因此若路由器要將來自 WiFi 的封包轉送給乙太網路,就可能需要切割封包。這些切割的小片段稱為 *fragement*。
這些切割的片段最終需要組合起來,而組合的依據就是 `id` 與 `frag_offset`。 `id` 來確認這些封包的內容是否同屬於一個完整的資料,而 `frag_offset` 則表示這個封包裝得是原先封包哪一個部分的資料。他的單位並不是位元組,而是以 8 位元組為一個單位。也就是當 `frag_offset` 為 $N$ 時,指得是「這個封包裝的 `data`,裝的是原先封包的資料中第 $8N$ 位元組開始」的資料。
## `protocol` --- 上層協定是什麼?
`protocol` 欄位用來表示這一個 IP 封包是拿來裝「L4 中哪個協定」的資料(按:目前所在的 IP 協定是 L3)。
## `ttl` --- 還可以被轉送幾次?
雖然英文用語是 *time to live*,但實際上是一個用來表示「這個封包還可以被路由器轉送幾次」的計數器。這個欄位一開始會是個非零的數值,而每當封包被路由器轉送一次,這個數值就會減一。當一個路由器發現一個封包的 `ttl` 欄位歸零時,就會捨棄這個封包。
會有這個理由是:如果大量迷路的封包累積在網路中不斷地被轉送,會佔據大量的流量。為了不讓他們持續遺留在網路中於是設計這個機制。
## `flag` --- 封包的屬性
```c
Bit 0: reserved, must be zero
Bit 1: (DF) 0 = May Fragment, 1 = Don't Fragment.
Bit 2: (MF) 0 = Last Fragment, 1 = More Fragments.
0 1 2
+---+---+---+
| | D | M |
| 0 | F | F |
+---+---+---+
```
### `DF` --- 封包能不能被切割?
指定封包的屬性。比如說 `DF` 欄位表示這個封包可不可以被切割?如果 `flag` 中被設定了這個值,就表示這個封包不可以被切割。那麼要嘛就幫他找一條都不用分割的路徑,或是如果這樣的路徑找不到,就只能丟棄它。
### `MF` --- 是不是某個封包的片段?
而 `MF` 位元表示「這個封包是某個被切割的封包的其中一個片段」。如果設為 `1`,表示「目前這個封包不是最後一個片段」; 反之就表示「它是最後一個片段」:

## `tos` --- 封包的種類
這 8 個位元又細分為幾個欄位。包含:
```c
Bits 0-2: Precedence.
Bit 3: 0 = Normal Delay, 1 = Low Delay.
Bits 4: 0 = Normal Throughput, 1 = High Throughput.
Bits 5: 0 = Normal Relibility, 1 = High Relibility.
Bit 6-7: Reserved for Future Use.
0 1 2 3 4 5 6 7
+-----+-----+-----+-----+-----+-----+-----+-----+
| | | | | | |
| PRECEDENCE | D | T | R | 0 | 0 |
| | | | | | |
+-----+-----+-----+-----+-----+-----+-----+-----+
```
### Precedence
這個欄位的前 3 個位元表示這個封包是一般資料的封包?或是網路的控制封包?
```c
Precedence
111 - Network Control
110 - Internetwork Control
101 - CRITIC/ECP
100 - Flash Override
011 - Flash
010 - Immediate
001 - Priority
000 - Routine
```
相關常數在 [`include/uapi/linux/ip.h`](https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/ip.h#L30) 中有定義:
```c
#define IPTOS_PREC_MASK 0xE0
#define IPTOS_PREC(tos) ((tos)&IPTOS_PREC_MASK)
#define IPTOS_PREC_NETCONTROL 0xe0
#define IPTOS_PREC_INTERNETCONTROL 0xc0
#define IPTOS_PREC_CRITIC_ECP 0xa0
#define IPTOS_PREC_FLASHOVERRIDE 0x80
#define IPTOS_PREC_FLASH 0x60
#define IPTOS_PREC_IMMEDIATE 0x40
#define IPTOS_PREC_PRIORITY 0x20
#define IPTOS_PREC_ROUTINE 0x00
```
### `D`、`T`、`R`
`D` 設為 `1` 表示 *low delay*,而 `T` 設為 `1` 表示 *high throughput*、`R` 被設為 `1` 則表示 *high reliability*:
```c
Bit 3: 0 = Normal Delay, 1 = Low Delay.
Bits 4: 0 = Normal Throughput, 1 = High Throughput.
Bits 5: 0 = Normal Relibility, 1 = High Relibility.
Bit 6-7: Reserved for Future Use.
```
在 [`include/uapi/linux/ip.h`](https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/ip.h#L23) 中有定義相關常數:
```c
#define IPTOS_TOS_MASK 0x1E
#define IPTOS_TOS(tos) ((tos)&IPTOS_TOS_MASK)
#define IPTOS_LOWDELAY 0x10
#define IPTOS_THROUGHPUT 0x08
#define IPTOS_RELIABILITY 0x04
#define IPTOS_MINCOST 0x02
```
這些欄位用來表示一個封包處理的優先程度。
## 其他欄位
### `tot_len` --- 封包的長度
封包的總長度。
### `check` --- 校驗碼
這整個 *header* 的 *checksum*(包含 *option*)
### `version` --- IP 協定的版本
這個封包使用 IPv4 或是 IPv6。