粉絲不過W
MAC 頭部
CRC、 PAD 在組包時可以忽略
CRC,循環冗餘校驗碼: 數據通信領域中最常用的一種查錯校驗碼, 特徵:信息字段和校驗字段的長度可以任意選定
循環冗餘檢查:一種數據傳輸檢錯功能, 對數據進行h多項式計算, 並將得到的結果附在幀的後面, 接收設備也執行類似的算法 , 來保證數據傳輸的正確性和完整性
///usr/include/net/ethernet.h
/* 10Mb/s ethernet header */
struct ether_header
{
uint8_t ether_dhost[ETH_ALEN]; /* destination eth addr */
uint8_t ether_shost[ETH_ALEN]; /* source ether addr */
uint16_t ether_type; /* packet type ID field */
} __attribute__ ((__packed__));
// /usr/include/linux/if_ether.h
/*
* This is an Ethernet frame header.
*/
/* allow libcs like musl to deactivate this, glibc does not implement this. */
#ifndef __UAPI_DEF_ETHHDR
#define __UAPI_DEF_ETHHDR 1
#endif
#if __UAPI_DEF_ETHHDR
struct ethhdr
{
unsigned char h_dest[ETH_ALEN]; /* destination eth addr */
unsigned char h_source[ETH_ALEN]; /* source ether addr */
__be16 h_proto; /* packet type ID field */
} __attribute__((packed));
#endif
ARP 頭部
Dest MAC: 目的MAC地址
Src MAC: 源MAC地址
幀類型: 0x0806
硬件類型: 1( 以太網)
協議類型: 0x0800( IP地址)
硬件地址長度: 6
協議地址長度: 4
OP: 1( ARP請求) , 2( ARP應答) , 3( RARP請求) , 4( RARP應答)
// /usr/include/net/if_arp.h
/* See RFC 826 for protocol description. ARP packets are variable
in size; the arphdr structure defines the fixed-length portion.
Protocol type values are the same as those for 10 Mb/s Ethernet.
It is followed by the variable-sized fields ar_sha, arp_spa,
arp_tha and arp_tpa in that order, according to the lengths
specified. Field names used correspond to RFC 826. */
struct arphdr
{
unsigned short int ar_hrd; /* Format of hardware address. */
unsigned short int ar_pro; /* Format of protocol address. */
unsigned char ar_hln; /* Length of hardware address. */
unsigned char ar_pln; /* Length of protocol address. */
unsigned short int ar_op; /* ARP opcode (command). */
#if 0
/* Ethernet looks like this : This bit is variable sized
however... */
unsigned char __ar_sha[ETH_ALEN]; /* Sender hardware address. */
unsigned char __ar_sip[4]; /* Sender IP address. */
unsigned char __ar_tha[ETH_ALEN]; /* Target hardware address. */
unsigned char __ar_tip[4]; /* Target IP address. */
#endif
};
IP 頭部
.版本: IP協議的版本。 通信雙方使用過的IP協議的版本必須一致, 最廣泛使用的IP協議版本號爲4( 即IPv4 )
首部長度: 單位是32位( 4字節)
服務類型: 一般不適用, 取值爲0。 前3位: 優先級, 第4-7位: 延時, 吞吐量, 可靠性, 花費。 第8位保留
總長度: 指首部加上數據的總長度, 單位爲字節。 最大長度爲65535字節
標識( identification) : 標識主機發送的每一份數據報。 IP軟件在存儲器中維持一個計數器, 每產生一個數據報, 計數器就加1, 並將此值賦給標識字段
標誌( flag) : 目前只有兩位有意義
標誌字段中的最低位記爲MF。 MF=1:後面“還有分片”的數據報。 MF=0:已是若干數據報片中的最後一個
標誌字段中間的一位記爲DF, :“不能分片”, DF=0:允許分片
片偏移: 指出較長的分組在分片後, 某片在源分組中的相對位置, 也就是說, 相對於用戶數據段的起點, 該片從何處開始。 片偏移以8字節爲偏移單位
生存時間: TTL,數據報在網絡中的壽命, 也爲“ 跳數限制 ”, 由發出數據報的源點設置這個字段。 路由器在轉發數據之前就把TTL值減一, 當TTL值減爲零時, 就丟棄這個數據報。 通常設置爲32、64、 128
協議: 指出此數據報攜帶的數據時使用何種協議, 讓目的主機的IP層知道應將數據部分上交給哪個處理過程, 常用 ICMP(1), IGMP(2), TCP(6), UDP(17), IPv6( 41)
首部校驗和: 只校驗數據報的首部, 不包括數據部分
源地址: 發送方IP地址
目的地址: 接收方IP地址
選項: 用來定義一些任選項; 如記錄路徑、 時間戳等。 這些選項很少被使用, 不是所有主機和路由器都支持這些選項
// /usr/include/netinet/ip.h
struct iphdr
{
#if __BYTE_ORDER == __LITTLE_ENDIAN
unsigned int ihl:4; /* 首部長度 */
unsigned int version:4; /* 版本 */
#elif __BYTE_ORDER == __BIG_ENDIAN
unsigned int version:4; /* 版本 */
unsigned int ihl:4; /* 首部長度 */
#else
# error "Please fix <bits/endian.h>"
#endif
uint8_t tos; /* 服務類型 */
uint16_t tot_len; /* 總長度 */
uint16_t id; /* 標識 */
uint16_t frag_off; /* 標誌 偏移量 */
uint8_t ttl; /* 生存時間 */
uint8_t protocol; /* 協議 */
uint16_t check; /* 首部校驗和 */
uint32_t saddr; /* 源地址 */
uint32_t daddr; /* 目標地址 */
/*The options start here. */
};
UDP 頭部
源端口號: 發送方端口號
目的端口號: 接收方端口號
長度: UDP用戶數據報的長度, 最小值是8( 僅有首部)
校驗和: 檢測UDP用戶數據報在傳輸中是否有錯, 有錯就丟棄
// /usr/include/netinet/udp.h
/* UDP header as specified by RFC 768, August 1980. */
struct udphdr
{
__extension__ union
{
struct
{
uint16_t uh_sport; /* source port */
uint16_t uh_dport; /* destination port */
uint16_t uh_ulen; /* udp length */
uint16_t uh_sum; /* udp checksum */
};
struct
{
uint16_t source;
uint16_t dest;
uint16_t len;
uint16_t check;
};
};
};
TCP 頭部
源端口號: 發送方端口號
目的端口號: 接收方端口號
序列號: 本報文段的數據的第一個字節的序號
確認序號: 期望收到對方下一個報文段的第一個數據字節的序號
首部長度( 數據偏移): TCP報文段的數據起始處與 TCP報文段的起始處距離, 即首部長度。 單位: 32位, 計算單位:4字節
保留: 佔6位, 保留爲今後使用, 目前應置爲0
緊急URG: 1:緊急指針字段有效, 它告訴系統此報文段中有緊急數據, 應儘快傳送
確認ACK: ACK = 1:確認號字段 有效, TCP規定, 在連接建立後所有傳達的報文段都必須把ACK = 1
推送PSH: 當兩個應用進程進行交互式的通信時, 有時在一端的應用進程希望在鍵入一個命令後立即就能夠收到對方的響應。 在這種情況下, TCP就可以使用推送( push) 操作, 這時, 發送方TCP把PSH = 1, 並立即創建一個報文段發送出去, 接收方收到PSH=1的報文段, 就儘快地( 即“ 推送” 向前) 交付給接收應用進程, 而不再等到整個緩存都填滿後再向上交付
復位RST: 復位相應的TCP連接
同步SYN: 在三次握手建立TCP連接時有效。 當SYN=1而ACK=0:一個連接請求報文段,對方若同意建立連接, 則應在相應的報文段中使用SYN=1和ACK=1。因此, SYN=1:一個連接請求或連接接受報文
終止FIN: 用來釋放一個連接。 FIN=1:此報文段的發送方的數據已經發送完畢, 並要求釋放運輸連接
窗口: 指發送本報文段的一方的接收窗口( 而不是自己的發送窗口)
校驗和: 校驗和字段檢驗的範圍包括 首部和 數據兩部分, 在計算校驗和時需要加上12字節的僞頭部
緊急指針: URG=1:有用, 它指出本報文段中的緊急數據的字節數( 緊急數據結束後就是普通數據, 即指出了緊急數據的末尾在報文中的位置, 注意: 即使窗口 = 9,也可發送緊急數據
選項: 長度可變, 最長可達40字節, 當沒有使用選項時, TCP首部長度是20字節
// /usr/include/netinet/tcp.h
/*
* TCP header.
* Per RFC 793, September, 1981.
*/
struct tcphdr
{
__extension__ union
{
struct
{
uint16_t th_sport; /* source port */
uint16_t th_dport; /* destination port */
tcp_seq th_seq; /* sequence number */
tcp_seq th_ack; /* acknowledgement number */
# if __BYTE_ORDER == __LITTLE_ENDIAN
uint8_t th_x2:4; /* (unused) */
uint8_t th_off:4; /* data offset */
# endif
# if __BYTE_ORDER == __BIG_ENDIAN
uint8_t th_off:4; /* data offset */
uint8_t th_x2:4; /* (unused) */
# endif
uint8_t th_flags;
# define TH_FIN 0x01
# define TH_SYN 0x02
# define TH_RST 0x04
# define TH_PUSH 0x08
# define TH_ACK 0x10
# define TH_URG 0x20
uint16_t th_win; /* window */
uint16_t th_sum; /* checksum */
uint16_t th_urp; /* urgent pointer */
};
struct
{
uint16_t source; /*源端口號*/
uint16_t dest;
uint32_t seq;
uint32_t ack_seq;
# if __BYTE_ORDER == __LITTLE_ENDIAN
uint16_t res1:4;
uint16_t doff:4;
uint16_t fin:1;
uint16_t syn:1;
uint16_t rst:1;
uint16_t psh:1;
uint16_t ack:1;
uint16_t urg:1;
uint16_t res2:2;
# elif __BYTE_ORDER == __BIG_ENDIAN
uint16_t doff:4;
uint16_t res1:4;
uint16_t res2:2;
uint16_t urg:1;
uint16_t ack:1;
uint16_t psh:1; /*推送PSH*/
uint16_t rst:1; /* 復位RST */
uint16_t syn:1; /* 同步SYN */
uint16_t fin:1; /* 終止FIN */
# else
# error "Adjust your <bits/endian.h> defines"
# endif
uint16_t window; /* 窗口 */
uint16_t check; /* 校驗和 */
uint16_t urg_ptr; /* 緊急指針 */
};
};
};