CS144 Lecture Notes
Unit 1: Internet and IP
网络四层模型:application, transport, network, link
7层OSI, 拆app,trans,link为更细的两个
网络的基础和底层是IP, 只有IP model是不可替代的“细腰”,上层的application、transport和下层的link都是可以替换的
application 应用层,smtp, ftp, http, ssh
transport 传输层,tcp, udp, rdp
link 连接层,4G, WiFi, ...
网络连接的几种常见例子:
client-server, BitTorren 一服务器多client的集群, ......
网络 application上 :read/write
传输过程 app->...->link->(link->network->link)(router) ->... link->...->app
IP(Internet protocol) service model:
设计目标:简单甚至笨拙,端到端(end-to-end),意味着尽可能减少中间router的工作量,能放在end的计算机上的都放在end的计算机上。对上下做非常少的假设。
几个特点:
- Unreliable(不保证能传到) 由上层的协议,如tcp来实现重传等确保传到
- Best effort: 尽量传到,只有在必要时才丢弃数据包(什么是必要时?例如中间路由器满了无法处理新请求)
- Datagram:传输的每一个包(packets)都是自路由(individually routed),也就是保存了
Src_IP
和Dst_IP
不需要依赖其他包来传输,同时传输依靠路由器一跳一跳(hop-by-hop) - Connectionless: 无状态,包的顺序可以被打乱,发送和接受也没有要求“建立连接”
一些细节:
-
Tries to prevent packets looping: packet内简单对跳转次数计数,达到某个值时router会丢弃
-
大数据包会拆成多个 (fragment)
-
使用IP 标头校验是否发送错误和重新组合
-
允许加入新字段
IP 标头
大部分字段都是不言而喻的,flags和fragment offset用于拆分数据的重新组合,checksum是标头的,用于检测是否损坏,protocol ID根据ID指定协议,比如TCP, version指定IPv4, TTL用于跳转计数避免循环,type of service是一个服务器标识符,用于表达这个包的重要性
可以用traceroute
查看包在路由器中的路径
TCP Byte Stream
通过{IP, port}
来通信,中间过计算机或者router, 例如wifi第一跳是过wifi接入点路由器
路由器转发:转发表模式匹配(最大前缀匹配)
如果在地址匹配过程中,不能和路由表中任何条目所匹配,packet将被丢弃。
【一个名为 Destination Unreachable(目标不可达)的ICMP信息将发回给源地址】
TCP 三次握手(3-way handshake)
- client \-> server SYN 请求
- server \-> client SYN/ACK 确认请求
- client \-> server ACK 确认请求
packet switching:
Independently for each arriving packet, pick its outgoing link. If the link is free, send it. Else hold the packet for later.
早期的跳跃是这样的,每一个路由器将src_ip改成自己(这就是NAT,网络地址转换,将大量的私有IP隐藏在少量公有IP里面),然后继续往下走
安全网关处于私有网络和公有网络的连接处。当内部PC(10.1.1.2)向外部服务器(202.1.1.2)发送一个IP包1时,IP包将通过安全网关。安全网关查看包头内容,发现该IP包是发向公有网络的,然后它将IP包1的源地址10.1.1.2换成一个可以在Internet上选路的公有地址202.1.1.1,并将该IP包发送到外部服务器,与此同时,安全网关还在网络地址转换表中记录这一映射。外部服务器给内部PC发送IP包1的应答报文2(其初始目的地址为202.1.1.1),到达安全网关后,安全网关再次查看包头内容,然后查找当前网络地址转换表的记录,用内部PC的私有地址10.1.1.2替换目的地址。这个过程中,安全网关对PC和Server来说是透明的。对外部服务器来说,它认为内部PC的地址就是202.1.1.1,并不知道10.1.1.2这个地址。因此,NAT“隐藏”了企业的私有网络。
但安全问题比较大,路由器不希望目标知道自己的ip, 就改成全dst, 每次跳跃都将src改为下一跳的ip
结果
- 简单的packet传播
- 高效的链接共享
No per-flow state required:
两端不需要知道router的state, router不需要存储其他的state(因而不需要考虑清除错误的状态)
由于是以包作为最小单位,链接共享和流量分配很简单->根本不需要管谁传过来的,有包就发
Layering 分层:
seperation concerns
模块化,重用,明确的上下边界,关注点分离,Peer-to-peer communication
装包是灵活的
VPN example
从里到外HTTP data \-> TCP(to web) ->IP(to web) \-> TLS(to VPN)->TCP(to VPN)->IP(to VPN)\-> Ethernet(to next hop)
字节序,内存:网络字节序,大端序
IPv4
32位 a.b.c.d 192.168.0.1
netmask 子网掩码:apply this mask, if it matches, in te same network (if IP_A & mask == IP_B & mask,在一个子网里面)
例如 netmast 255.255.255.0 意味着前24bit match
第一跳时,如果Src_IP 和 Dst_IP在同一子网内,不走外面路由器 \-> 127.0.0.1 和 127.X.X.X
> ifconfig
...
inet 127.0.0.1 netmask 255.0.0.0
...
地址结构(historical):
问题:不够灵活
目前:CIDR Classless Inter-Domain Routing
171.64.0.0/16 /16意味着16位子网掩码,171.64.0.0 ~ 171.64.255.255都是
ICANN ip地址分配
Longest Prefix Match(最长前缀匹配)
转发表实际上是 a set of CIDR entries
dest | link |
---|---|
0.0.0.0/0 (x.x.x.x, default, 每个router都有) | 1 |
171.33.0.0/16 (171.33.x.x) | 5 |
Address Resolution Protocol(ARP) 地址解析协议
解决问题:我有IP, 那发送到哪个硬件?(Link层,描述特定网卡,有唯一ID)
eg 48bit Ethernet 00:13:72:4c:d9:6a
例如,有一个网关gateway交换机,他有不同的接口,对应不同的ip
从IP_A -> IP_B的内部请求,IP_A先由子网掩码把请求发到gateway接口A, gateway识 别packet的dst_IP之后通过接口B发出,再由子网掩码发送到IP_B
ARP link 和 network层之间,缓存 ip <-> link 地址的mapping
简单的 request-reply 结构
要发送消息给ip X, 先查缓存, 如果没有, sent request to link layer broadcast address “谁有IP X?”
当IP_X接收到数据的时候给Src发“我有IP X"(不走广播)
Unit 2: Transport
The TCP Service Model
Reliable byte delivery service
- Acknowledgments indicate correct delivery
- Checksums detect corrupted data
- Sequance numbers detect missing data
- Flow-control prevents overruning receiver
- Congestion Control 拥堵控制
TCP Segment Format
Unique TCP connection: IP DA, IP SA, Protocol ID="TCP", Source Port, Destation Port
通过sequence 和acknowledgment sequence可以确定前N个字节已经正确发送,sequence是TCP Data的首个字节在TCP试图发送的字节流中位置
FIN flag 标志关闭,关闭TCP连接的过程就是一方A先发FIN, 关闭A-\>B的管道, 之后B确定发送完数据后发送FIN到A, 关闭B到A管道。 PSH指示立刻发送新数据而不是等待更多老数据
window-based flow control
retransmission and timeouts
UDP Service Model
UDP 标头只有四个段:
Source Port, Destination Port, Checksum, Length
No connection established
Packets may show up in any order!!!
没有确认,没有检测丢包和乱序的机制,没有流量控制
ICMP Internet Control Message Protocal
Communicates network layer info between end hosts and routers
Reports error conditions
- Reporting Message
- Unreliable - no retries
典型的ICMP IP header | ICMP header | ICMP data
ICMP header里面主要就是 type 和 code, 两者合起来描述了一个message(通过对照表, 例如 )
0-Echo响应 | 0 | Echo响应报文 |
---|---|---|
3-目的不可达 | 0 | 目标网络不可达报文 |
用于 ping, traceroute等
traceroute怎么实现?很精巧的设计
clientA-\>B A发送一个TTL为1的UDP message,然后第一个router接受,TTL为0, 决定丢弃并回传一个ICMP消息
11-ICMP超时 | 0 | TTL超时报文 |
---|
clientA拿到这个报文就得到了第一个router节点的信息,之后就可以发送一个TTL=2的...以此类推
End-To-End Principle
Error Detection
- Checksum:简单将data 加和,弱但快
- CRC校验:计算多项式余数 强一些,慢一些,纠错一位,查错2位
- 消息验证码MAC -> TLS, 需要密钥 剩下的一点问题在与一个k位的码如果发生单位翻转,有2^-k的可能检测不出来,从这个角度上可能不如CRC
IP/UDP/TCP Checksum: 报头中所有其他16位字的反码和的16位反码
检验,整个IP标头的求和结果应该为0(修改标头比如TTL时需要重新计算)
快,但只保证查1位错
Link Layer广泛使用CRC
MAC
有限状态机 FSM
Flow Control
sender可以send 500000个packet/s
receiver 只能receive 200000个
原则是sender减 少发送的包数量,receiver give feedback
- stop & wait sender发一个就等一个ACK, 除非超时没有接受ACK重发,receiver接一个回一个ACK
有一个问题是ACK Delay
如果sender发了一个包A0,但ACK在timeout了,尝试重发A1后才收到,于是sender继续发下一个包B0,sender如何区分此时接受到的ACK是A1的ACK还是B0的ACK?
一种部分的解决方法是加入一些标志位来标识是这个packet是复制还是原始数据,但还是有一些问题,例如这样假设了网络传输的数据不是复制的,还有无法解决长延迟的问题(假设有k个标志位,最多记录第2^k^次重发,如果有超过2^k^次timeout的延迟,就还是会出现无法区分重发和新包的问题)
当然通过动态调节timeout和把ACK做成一个够长的标志(比如下面的序列号)是可以做的,比如下文的滑动窗口就是stop & wait的泛化,
linux 下这个timeout被动态地(通过测量发出到ACK的时间采样等方法)调节为略大于传输时间,为了处理网络的波动性,如果发现一个包重传之后还是丢,就把timeout设置为原来的2倍
对于重复的更优处理还会有SACK字段(选择性确认),receiver回传数据表明哪里是复制了,sender知道之后就继续发送了
更大的问题在于速度,假设两地有50ms延迟,来回100ms, 一个包比如1.5KB, 这样的网络最大只能有15KB的速率!
- sliding window
允许多个未确认的片段 (multiple un-acked segments)
将一些绑起来的片段叫做window,始终保持管道速率拉满
什么意思?原先只能有1个包在路上,现在可以有n个
Sender
每个片段都有一个序列号(SeqNo)
维护三个变量:Send window size(SWS),Last ack received(LAR), Last seg sent(LSS)
维持的要求:LSS-LAR <= SWS, 保证中间飞的包 <= SWS 个 (一个SWS大小的buffer
Receiver
还是维护三个变量:RWS, last acceptable seg(LAS), last seg received (LSR)
要求 LAS - LSR <= RWS, 也就是说如果窗口是5, 上一个接受的是3, 不会接受9以后的SeqNo的包,并且 LAS <= LSE + RWS都接受,也就是说9之前的都是可以的,比如2,会接受并回一个ACK = 下一个字节
也就是说 第一个包假如是 0-999字节 ACK 就是1000
下文中 ack 忽略掉这个值区别 包1,ACK1只表示逻辑上的第一个包和第一个ACK
(逻辑上下一个包,例如发送1,2丢,345,回的ACK是1 222)
如何知道包丢了
目前的方法就是等超时,例如2丢了一直等ACK 3,到timeout了重发2
有没有更好的方法呢?比如我在发12345,2丢了,回ACK1 222, 连续3个2表明确实是2丢了,那就可以不用等超时立刻触发重传(TCP快速重传)
那如果ACK丢了呢?
如果是前面的ACK丢了无所谓,最后一个ACK就表明这个之前的所有都已经接受,不然就重发呗
重传多少?
- Go-back-N 一个包loss,整个window重传 对网络情况悲观
- Selective repeat 只发loss的 对网络情况乐观
看实际情况,不一定哪个快,这是一个trade-off
RWS <= SWS
所以这里面的
window size就是滑动窗口的大小,Seq 和 ACK Seq就是上文所述的序列号
一个发送第4000-4999字节的包,seqNo会是4000, Ack会是4999+1=5000=下一个包应该的seqNo
RSVD: reserved
Hlen,也叫offset,表明data段从哪里开始
Flags:ACK表示ACK 是否有效(第一个发出包的ack就无意义,其他都有意义), SYN/FIN 位掌管TCP连接(第一个包, ack not set,syn set)
所以后面传回的就是syn/ack ,从第二个包开始就结束了syn且有合法ack,就是ack
RST: reset the connection
TCP Setup/Teardown
Setup
需要注意的是,seqNo通常不从0开始(安全原因),所以第一次握手
active 带上 {SYN, SA} (A means active) 我的序列开始是Sa
passive 回复{SYN, SP, ACK, Sa+1} (P means passive) 表明我的序列开始是Sp
active 回复 {Sa+1, ACK, SP+1}
以上为 0字节 data
Teardown
使用FIN bit
A->B FIN,seq Sa, ack Sb
B->A ack Sa+1
B->A FIN, seq Sb, ack Sa+1
A->B ack Sb+1
cleaning up safely
如果teardown的部分(final ack)丢包怎么办?-> 不管丢不丢,在发出FIN之后,active 都会到TIME_WAIT状态,大约会keep socket twice the maxium segment lifetime,之后关掉并重置
Unit 3: Packet Switching
Circuit Switching 电话
Problems
- Inefficient (for brusty req)
- Diverse Rates
- State Management
Packet Switching
-
通过路由表独立路由
-
所有的包共享相同的最大连接速率(router需要大buffer来收包)
-
router无连接状态
好处:
- efficient use of expensive links 共享速率使router不闲置
- resilience to failure of links & routers, no state -> 轻松切换线路
Delays
Propagation Delay 距离 l / 光速c
Packetization Delay 包长度p / 每秒能放到连接上的bit数r
Queueing Delay 在路由器的buffer queue之中排队等待发射时间
End-to-end delay 对所有经过的router ri
前两个是固定的,queueing delay是随机的,导致了端到端时间具有一定的波动性
Real-Time Apps 为了避免queueing delay带来的体验下降,会引入
Playback buffers 视频缓冲区
playback buffer -> video decoder -> screen
也不是固定值,会增大buffer来尽量不要停
queue model: FIFO
需要注意的是字节数随时间的变化是分段的(包一个个来),近似可以认为每一段是直线
Small packet help reduce end-to-end delay
小包的情况下 各个中间router的Queue delay + Packetization delay可以并行,如图,类似cpu流水线, 关键是一个包是最小传输单位
Statistical Multiplexing Gain
由于从一个link输入的packet的速率A具有波动性和随机性,设要使他不丢包需要R的输出速率
假设有另一个link 输入B, 也需要R,同时接受AB的路由器需要的输出速率R' < 2R, 因为AB的最大值时间点大概率不重叠
让我们在处理多路流量的时候反而更高效
Queues With Random Arrival Processes
-
Burstiness increase delay -> 包不重叠,平均delay低
-
Determinism minimizes delay -> 如果queue为空,就无法利用输出,不确定的到来时间加剧拥挤
-
Little's Result 平均到达率 平均排队者数量L,平均排队延迟d, L =
-
M/M/1 queue
将收包事件近似为泊松过程,相互独立且t时间内到达的包数n, E(n)=
(注意网络包实际很突发,不是泊松过程,但这个model work for new flows, 新连接是近似泊松的)
当入包速率接近出包速率时,等待时间会大幅度上涨
平均排队者数量就是
packet switch 交换机
一个包来之后 -> 查地址 -> 更新header -> 进入排队 -> 发送
Internet router
1, 如果DA 不等于自己,丢
2, 检查ip version和datagram长度
3, 减少TTL, 更新校验和
4, TTL == 0?
5, 查路由表, IP DA决定下一跳, 找到对应的Ethernet DA, 发送到电线
IP match -> binary TRIE 或者 三进制可寻址内存-> 01X暴力并行比较,很快
Ethernet match -> hash
Input Queued Packet Switch
一个有 N 个输入口的交换机,每发送一个packet耗时R,最坏情况下的输出delay是(N+1) * R (假设输出速率足够大)
如果把排队放在输入,最坏情况下delay,理想状态下可以降低到2R
但问题是,如果马上要输出的几个包是同一个输出口,一个输出口同时只能输出一个,就和原来的没有区别了
一种解决方法是Virtual Output Queues,在每个输入口都做M个队列,M是输出口的数量,每次输出都从M个不同的输出口取
举了一个有趣的例子是红绿灯车道,车在进入路口前(“输入口”)分好队列,同时输出的就可以增加,排队时间就可以减少,但要求的道路宽度变多
FIFO的问题和其他选项
经典排队问题,如果有一个大包堵着,所有包的排队时间都会增加
strict priority
很符合直觉,同一个输入口再来几个队列,并分好优先级,同一个端口接受多个队列输入的时候总是优先取高优先级的包
也是经典问题:饿死,但问题不像sys那样严重,饿死就饿死了(),广泛应用
或者weight priority,解决饿死问题
取每个队列的概率 p = wi / sum(wi) (用Round方法)
考虑到每个包的大小不一样,理论上概率应该应用到字节上而不是包上,但又不能拆开包发送,怎么做呢?
WFQ, 利用我们在知道包大小这个事实
在每个队列计数(virtual_time),权重为w的队列在发送一个size大小的包后,self.vt += size / w, 之后把self.vt push进最小堆
调度下一个包时从最小堆里pop()就行
rate guarantees (Guaranteed Delay)
RSVP RFC2205
给出连接总耗时10ms上界, 传输时间 路程/c = 5ms, 打包时间=每个包的字节数/数据线最大速度 = 0.48ms
剩下最多4.52ms的排队延迟, 分在3个router, 要保证不丢包, 就需要至少 15 Mb * 4.52 / 3ms = 24000bit = 3KB的 buffer
buffer的设计在 “给定一定最大延迟的情况下,不允许丢包”
Unit 4: Congestion Control
堵塞控制
tcp堵塞控制
堵塞是不可避免的
堵塞带来丢包,由于重传等,会带来更多的数据流量,进一步加深堵塞
max-min fair: 不能通过减少另一个流的速率的方法增加其中一个流的速率时
先前的想法
- 简单的在各个可能的router上平均分配流量 问题在于没有任何反馈机制
- network-based 显式反馈ECN,当router堵塞发生的时候,给数据源发送包指示拥堵
- end-host-based 通过在发送端观察网络的行为(发送的请求是否超时?是否丢包?)调整发送速度
实际的TCP采用end-host based
TCP varies the number of oudstanding packets in the network bt varying window size:
Window size = min{Advertised Window(given by receiver), Congestion Window}
AIMD(Additive Increase Multiplicative Decrease)
If a packet receive OK : W <- W + 1/W
If a packet is dropped(at Window size W): W <- W/2
称为tcp锯齿或者AIMD锯齿
需要注意的是,实际上在加的中间过程中,网络速率就已经拉满了,后面再加实际上是在嗅探router的缓冲区大小
single flow AIMD
前期 router buffer空
RTT = constant
吞吐量T=W=dN/dt
dW/dN = 1/W , dW/dt = T/W = const
W,T 正比于t
当W大于输出速率R(常数), router buffer开始积累
dW / dt = R/W ⇒ W = sqrt (Rt+C)
此时吞吐量为R,RTT=k* buffersize+D
趋势上和W相同(但不完全一样,因为T输入=R+buffersize变化率=W/RTT),可以得到buffer size 随时间变化
multi flows AIMD
在多道数据流的情况下,之前的AIMD不是很适用
问题在于减少的时机不是所有流同步的,部分流减少后buffer又不满,其他流就不减少,总体发生丢包的时间就很分散随机,导致宏观上router的buffer一直趋于满的
多流时 吞吐量T也不再等于(忽略常数)窗口大小W(多个流的平均)
(buffer始终趋于满的)
总吞吐量近似恒等于R
RTT也近似为常数
那么对于每一个流,都是W/2 → W → W/2的流程
又由于buffer是满的,RTT恒定,dW/dt = T/W = 1/ RTT = const ,直线
平均吞吐 T‘ = 3/4 Wmax/RTT
一次丢包前发送的包数A = 3/8 Wmax^2 ,则丢包率p=1/A
则T’ = sqrt(3/2) * 1/RTT*sqrt(p)
考虑到每个包字节数不同,再乘一个平均字节数MSS
T’ = sqrt(3/2) * MSS/RTT*sqrt(p)
也就是说,AIMD对两个量敏感: RTT(服务器有多远,router 排队时间多大)和 丢包率p
TCP堵塞控制
早期TCP(没有堵塞控制)的问题在于,端的速率超过router的速率太多,receiver返回的windows size实际上远远超过了网络实际能容纳的大小,不断丢包重传
TCP Tahoe: 加上congestion window, timeout estimation和self-clocking
congestion window中的二阶段congestion control
- Slow start:在连接建立或者发生丢包时,AIMD前面线性增加疑似有点太慢了,加速一下
- Congestion avoidance: AIMD
Slow Start
Window starts at Maximum Segment Size MSS
Increase window by MSS for each ACK packet
dW/dt = dN/dt = W ⇒ W = exp(t)
指数增加试探吞吐上限
Congestion avoidance
- Increase by MSS^2/congestion window for each ACK
- Behavior: increase by MSS each RTT
- Linear increase
在两种状态间切换使用3个信号作为信息:
- Inc ACK: 传输良好
- Dup ACK: 发生丢包/延迟
- Timeout: 网络崩溃
Timeout Estimation
太小,会频繁超时,回到slow start,影响速度
太长,会频繁重发,影响速度
挑战在于RTT是高度动态且负载相关的
早期的算法
问题在于假设了RTT的方差不是很大,但这是不对的
大方差下的RTT会频繁带来过小或者过大的问题
将方差纳入考虑,实验效果好
self-clocking
应该叫一个原则/实现方式,体现为TCP的堵塞窗口不由外部时钟决定,而是根据收到的ACK频率自动改变发送速率(即前面的slow start和AIMD)
TCP Reno
- 3-Dup ACK不再返回到1,返回1/2 * Wmax (fast recovery)