1. 路由

以太网交换机:

以太网交换机工作在数据链路层, 用于在同一网络中的设备转发以太网帧.

路由器:

负责不同网络之间的数据包传送.
路由器通过路由表来确定用于转发数据包的最佳路径.
当网络向不同IP网络中的设备发送数据包时, 数据包会先被转发到默认网关.

2. TCP/IP协议

协议 PDU 英文名 标识

物理层

比特

bit

链路层

frame

MAC地址

网络层

数据包

packet

IP地址

传输层

数据段

segment

端口号

应用层

报文

message

应用协议

3. 数据链路层

3.1. CSMA/CD

  1. 一个站点首先检测网络上正在发送的信号, 并在网络空闲时发送自己的帧.

  2. 如果其他站点碰巧同时发送, 则为一次碰撞.

  3. 每个站点等待一个随即时间, 然后再次尝试发送.

  4. 随后如果再次碰撞, 等待的时间翻倍.

  5. 最终, 每个站点会得到机会发送, 或者在尝试一定次数(16)后超时.

3.2. Ethernet帧格式

  1. 前导字段 8

  2. 目标MAC地址 6

  3. 源MAC地址 6

  4. 长度或协议类型 2

  5. P/Q标签 0/2

  6. 上层协议数据 46-1500字节

  7. FCS 4 发送和接收由硬件处理

协议类型:
  • IPv4: 0x0800

  • IPv6: 0x86dd

  • ARP: 0x0806

  • RARP: 0x80

  • Q标签帧: 0x8100

MTU: 以太网帧大小范围为64~1518字节, 首部占14字节, CRC校验和占4字节, 所以数据部分大小范围为46~1500字节, 最大传输限制称为 MTU .

3.3. ARP帧格式

ARP提供网络层地址到硬件地址的映射

  1. 目标地址(1.1.1.1)

  2. 源地址

  3. 类型(0x0806)

  4. 硬件类型(Ethernet(1))

  5. 协议类型(IPv4)

  6. 硬件地址大小

  7. 协议地址大小

  8. Opcode: ARP请求(1)/应答(2)

  9. 发送方MAC地址

  10. 发送方IP地址

  11. 接收方MAC地址

  12. 接收方IP地址

3.4. 物理设备

  • Repeater(中继器): 物理层设备, 用来放大信号.

  • Hub(集线器): 每次发送的信号都会被发送给连接Hub的其他所有机器.

  • Bridge(桥): 保存连接端口的所有设备的MAC地址.

  • Switch(交换机): 保存连接端口的所有设备的MAC地址, 全双工.

3.5. 交换机

交换机内部保存<Mac地址, 连接端口>表.

mac地址记录与转发流程
  1. 设备1向设备2发送数据包, 交换机收到请求, 根据以太网帧记录下发送方的Mac地址以及端口.

  2. 如果MAC表里有接收方的Mac地址, 则直接转发数据包到该条记录的端口.

  3. 如果没有找到记录, 则广播给其他所有端口, 此时连接Hub的所有设备都会收到该数据包, 但只有目标Mac地址与设备自身的Mac地址相同的设备才会受理该数据包.

  4. 此时总会有一个端口收到响应的数据, 交换机收到数据包后记录下发送方的Mac地址和端口.

4. IP

IP提供了一种尽力而为,无连接的数据包交付服务.

  1. 版本(0x0100/0x0110)

  2. 首部长度(一般为0101)

  3. Differentiated Service

  4. Explicit Congestion Notification

  5. 总长度

  6. 数据报标识

  7. 分片标记

  8. 分片偏移

  9. 生存时间

  10. 传输层协议类型

  11. 头部校验和

  12. 源IP地址

  13. 目的IP地址

  14. 选项

IP数据包最大为65535字节, 当一个IP数据包大于以太网的MTU时, IP协议会把数据包报文切分为多个小的片段.

4.1. IPv6格式

  • 长度为128位, 表现为16进制, 每16位为一块. 如 5f05:2000:80ad:5800:58:800:2023:1d71 .

  • a到f的16进制数必须小写.

  • 每一块中前导0必须省略不写, 如 2001:0db8::0022 必须要写成 2001:0db8::22 .

  • 最长的全0块必须简写成 :: , 如果有两个最长的全0块, 则第一个简写. 如 5f05:0000:0000:5800:58:0000:0000:1d71 简写成 5f05::5800:58:0000:0000:1d71 .

4.2. IPv4分类

类别 前导位

A

0

B

10

C

110

D

1110

E

1111

5. TCP

TCP是一种可靠地, 面向连接的, 基于字节流的, 全双工的传输层协议.

  • 面向连接的: 通信双方建立连接时要经过三次握手, 断开连接时要经过四次挥手, 四元组 <源地址, 源端口, 目标地址, 目标端口> 标识了一条TCP连接.

  • 可靠地:

    • 每个TCP首部都有两字节表示校验和, 如果收到一个校验和有差错的报文, TCP会直接丢弃该报文等待重传.

    • TCP的序列号保证了接收数据的顺序.

    • TCP在发送数据后会启动一个定时器, 等待对方确认收到这个数据包, 如果在指定时间内没有收到ACK确认包, 就会重传数据包.

    • TCP提供了拥塞控制机制.

    • 面向字节的: 字节写入内核后, 最终TCP以多少条报文发送出去是不确定的.

    • 全双工的: 通信的双方可以同时发送/接收数据.

5.1. TCP首部

  1. 源端口. 16

  2. 目的端口. 16

  3. 序列号: 标识了TCP发送端到TCP接收端的数据流的序号, 序列号用于保证包的顺序, 或者交换彼此的报文(SYN报文). 32

  4. 确认号: TCP使用确认号来告知对方下一个期望接受的序列号. 32

    • 确认号表示小于此确认号的字节都已经收到.

    • 不是所有包都需要确认.

    • 收到了数据包可以延迟一会儿再确认.

    • ACK包不需要确认.

  5. 首部长度. 4

  6. 保留位. 4

  7. 标志位. 8

    • Nonce

    • CWR(Congest Window reduced): 发送方降低它的发送速率.

    • ECN-Echo: 发送方接收到了一个更早的拥塞通告.

    • URG: 标识紧急指针字段有效

    • ACK: 标识确认数据包.

    • PSH: 告知对方这些数据包收到后应立即交给上层应用, 不能缓存起来.

    • RST: 标识强制断开连接.

    • SYN: 标识这个数据包用于发起连接时同步双方的初始序列号.

    • FIN: 告知对方自己发送完了所有数据, 后续不会再有数据发送了.

  8. 窗口大小: 窗口大小值*缩放因子. 16

  9. 校验和. 16

  10. 紧急指针. 16

  11. 可选项. 320max

    • MSS: 发送方允许的最大数据段大小, 默认为536个字节.

    • SACK: 选择确认选项, 发送方带上SACK选项来标识自己支持选择确认的功能.

    • WSCALE: 窗口缩放选项, 标识TCP连接的实际窗口大小为 \$"window"xx2^x\$ .

    • TSOPT: 时间戳选项, 双方通信时记录时间戳, 用来计算往返时间.

    • UTO: 用户超时选项, 标识TCP发送方愿意等待ACK确认的最大超时时间.

5.2. MSS

TCP数据段最大值 = MTU - IP头大小 - TCP头大小 (\$1500-20-20=1460\$)

5.3. 端口号

端口号被划分成以下 3 种类型:

  • 熟知端口号(0~1023)

  • 已登记的端口(1024~49151)

  • 临时端口号(49152~65535), 运行 cat /proc/sys/net/ipv4/ip_local_port_range 命令可以查看机器上可用的端口范围.

  • 查看使用端口号的进程id:

    • sudo netstat -ltpn | grep :<PORT>

    • sudo lsof -n -P -i:<PORT>

  • 查看进程使用的端口号:

    • sudo netstat -atpn | grep <PID>

    • sudo lsof -n -P -p <PID> | grep TCP

5.4. 三次握手

  1. [C]客户端发送SYN包. SYN-SENT

  2. [S]服务端接收到后加一作为ACK包, 然后自己生成一个SYN包一起发送. SYN-RECV

    1. 服务端此时会将这个连接信息放入 半连接队列 (SYN 队列) .

  3. [C]客户端接收到服务端的SYN包加一, 作为ACK包发送给服务端. ESTABLISHED

    1. 服务端收到客户端的ACK包后会将这个连接信息移动到 全连接队列 (Accept队列) . 此时socket处于 ESTABLISHED 状态,每次调用 accept 函数会移除队列头的连接. 如果队列为空, 则会阻塞 accept 函数.

  • 如果发送SYN包没有收到ACK, 则会经过 \$x\$ 秒后重传, 如果依然没有收到则会经过 \$2x\$ 秒后重传, 以此类推, 可以通过系统变量 net.ipv4.tcp_syn_retries 设置最大SYN包重传次数, net.ipv4.tcp_synack_retries 设置最大SYN+ACK包重传次数.

  • 当服务端收到SYN包后, 会检查系统参数 net.ipv4.tcp_max_syn_backlog , 如果处于 SYN_RCVD 状态的连接数超过了这一阈值, 会拒绝该连接.

5.5. 四次挥手

  1. [C]客户端发送FIN包, 以后客户端不能再发送数据给服务端了. FIN-WAIT-1

  2. [S]服务端接收到后回复ACK包. CLOSE-WAIT

  3. [C]客户端接收到ACK包. FIN-WAIT-2

  4. [S]服务端发送FIN包. LAST-ACK

  5. [C]客户端收到FIN包, 发送ACK包. TIME-WAIT

  6. [S]服务端收到ACK包断开连接. CLOSED

  7. [C]客户端经过两个MSL后断开连接. CLOSED

net.ipv4.tcp_fin_timeout 设置了 TIME-WAIT 状态需要等待的超时时间.
  1. TIME-WAIT 状态存在的意义?

    • 保证上一个连接的包不会因为网络慢发送到一个连接里.

    • 可以收到对方的第二个 FIN 包.

    • 如果主动断开方重用端口, 进行三次握手发送SYN包, 对方( LAST_ACK )立即会返回 RST 包导致三次握手失败.

  2. 为什么是两个MSL?

    • 1个MSL保证 ACK 包能发送到对方.

    • 1个MSL保证对方如果没有收到 ACK 包, 那么可以收到对方重传的 FIN 包.

  3. 发送方对一个ACK应该等待多长时间?

    TODO

  4. 如果ACK丢失该怎么办?

    TODO

  5. 如果分组被接收到了, 但是里面有错该怎么办?

    • 使用差错纠正码修复数据.

    • 重传.

5.6. TCP时间戳选项

TCP时间戳选项首部由四部分组成:

  • Kind: 时间戳类别固定为8

  • Length: 固定为10

  • TS value

  • TS echo reply

    1. 三次握手SYN包将时间戳写在 TS value 字段上.

    2. 服务端收到SYN包后, 将收到的 TS value 写到 TS echo reply 字段上, 然后生成自己的时间戳写到 TS value 字段上.

    3. 以此往复.

5.8. SO_REUSEADDR

TCP四次挥手后, 主动断开连接的一方会进入 TIME_WAIT 状态, 等待两个MSL后才最终释放这个连接, 此时进程虽然结束, 但是不能在 TIME_WAIT 状态下继续使用该端口.
SO_REUSEADDR 设置为1后即使在 TIME_WAIT 状态下也可以复用该端口.

5.9. SO_LINGER

struct linger {
    int l_onoff;    /* linger active */
    int l_linger;   /* how many seconds to linger for */
};
  • l_onoff 为0时表示禁用该特性, close函数会立即返回,操作系统负责把缓冲队列中的数据全部发送至对方.

  • l_onoff 为非0时表示启用该特性.

    • l_linger 为0, close函数会立即返回,不执行正常的四次挥手, 操作系统把缓冲区数据全部丢弃并立即发送RST包重置连接.

    • l_linger 为非0, 那么此时close函数在l_linger时间内发送数据, 之后操作系统把缓冲区数据全部丢弃并立即发送RST包重置连接.

6. HTTP

Cookie记录了当前会话的一些数据, 保存在客户端的磁盘或内存中.

Diagram
  • Set-Cookie头部只能传递一个key/value对, 但可以有多个Set-Cookie头.

  • Cookie属性:

    • Expires: 设置到指定日期后Cookie失效.

    • Max-Age: 设置经过多少秒后Cookie失效, 优先级大于Expires.

    • Domain: 设置该Cookie可以在哪些域名下访问.

    • Path: 设置该Cookie可以在哪些路径下访问.

    • Secure: 设置只有https协议下才能访问该Cookie.

    • HttpOnly: 设置不能用js访问到该Cookie.

跨站请求脚本攻击(CSRF): 第三方网站B带上用户本地存储的服务器A的Cookie, 来请求服务器A的接口.

CSRF防范策略:
  • 校验Referer头是否为本站域名.

  • 服务端返回表单中加上隐藏的CSRF Token字段, 表单提交的时候需要校验CSRF Token是否有效.

6.2. 重定向

6.2.1. 永久重定向

浏览器会缓存永久重定向的地址.

  • 301: 使用GET请求新的地址.

  • 308: 使用原请求的方法和请求体请求新的地址.

6.2.2. 临时重定向

  • 302: 使用GET请求新的地址.

  • 303: 使用GET请求新的地址(语义与300不同).

  • 307: 使用原请求的方法和请求体请求新的地址.

7. HTTP2

7.2. Stream结构

  1. 帧长度, 0~16MB. 24

  2. 消息内容类型 8

    • DATA

    • HEADERS

    • PRIORITY

    • RST_STREAM

    • SETTING

    • PUSH_PROMISE

    • PING

    • GOAWAY

    • WINDOW_UPDATE

    • CONTINUATION

  3. 标志 8

  4. 保留位 1

  5. StreamId: 标识同一条Stream消息, 由客户端建立的流是奇数, 由服务端建立的流是偶数. 31

  6. 消息内容

8. 网络查看命令行工具

8.1. netstat

netstat 能够查看所有已连接的TCP/UDP网络连接, 网络协议分析, 端口分析, 查看路由表等.

8.1.1. 安装

sudo apt install net-tools

8.1.2. 命令选项

  • -l 显示所有正在 listen 的socket

  • -a 显示所有的socket

  • -r 显示路由表

  • -i 显示所有接口

  • -g 显示所有广播组

  • -s 显示网络使用情况

  • -M 显示所有伪装的链接

  • -v 显示详细信息

  • -W 显示时不截断ip地址

  • -n 不解析主机名

  • -e 显示更多信息

  • -p 显示socket的进程id

  • -o 显示所有的定时器

  • -F 显示转发信息

  • -C 显示路由缓存

8.1.3. 命令示例

# 查看端口占用的进程
sudo netstat -lnp | grep 22| awk '{print $NF}'

# 查看IPv4监听的端口列表
sudo netstat -vutlnp --listening -4

# 查看tcp使用情况分析
sudo netstat -st

# 查看所有监听的unix socket
sudo netstat -lx

8.2. ss

ss相比于netstat还能够查看更多socket信息.

8.2.1. 安装

sudo apt install iproute2 iproute2-doc

8.2.2. 命令选项

  • -n 不解析服务名称

  • -r 解析主机

  • -l 显示所有监听中的socket

  • -o 显示所有的定时器

  • -e 显示socket详细信息

  • -m 显示socket内存使用

  • -p 显示socket所属的进程

  • -s 显示socket使用概况

  • -4 仅显示 IPv4 socket

  • -6 仅显示 IPv6 socket

  • –0 显示 PACKET sockets

  • -t 显示 TCP sockets

  • -S 显示 SCTP sockets

  • -u 显示 UDP sockets

  • -w 显示 RAW sockets

  • -x 显示 Unix domain sockets

  • -f 显示指定FAMILY_TYPE的sockets, 支持 unix, inet, inet6, link, netlink

  • -tun 不解析主机名

8.2.3. 命令示例

# 查看指定目标地址/端口的连接
ss dst 192.168.0.2

# 查看指定状态的socket
ss state ESTABLISHED

# 查看port小于1024的socket
ss -n sport \< 1024

9. Appendix

  1. 路由器的主要功能和特性是什么?

  2. 在小型路由网络中,如何将设备连接起来?

  3. 如何使用CLI配置路由器上的基本设置,以实现两个直连网络之间的路由?

  4. 如何检验直连到路由器的两个网络之间的连接?

  5. 在接口之间交换数据包时,路由器使用的封装和解封装的过程是什么?

  6. 什么是路由器的路径决定功能?

  7. 直连网络的路由表条目是什么?

  8. 路由器如何创建直连网络的路由表?

  9. 路由器如何使用静态路由创建路由表?

  10. 路由器如何使用动态路由协议创建路由表?