ARP 协议笔记

ARP 协议是什么

在具体学习 ARP(Address Resolution Protocol) 协议之前,我们应该先了解 ARP 协议的使用场景。大多数人对 ARP 协议可能和我一样,都有一个大概的印象。比如它是在已知 IP 地址的情况下,用来查找 MAC 地址的协议。这里也试着将 wikipedia 上的定义翻译过来,给出一个较为全面准确的定义:

ARP 协议是一种通信协议,用来发现网络层地址(比如 IPv4地址)关联的链路层地址(通常是 MAC 地址)。

以下的内容都来自于 wikipedia: https://en.wikipedia.org/wiki/Address_Resolution_Protocol

ARP 报文

ARP 协议使用一种格式来表示地址解析的请求或响应。ARP 消息的大小取决于链路层或者网络层地址的大小。报文头指明了每一层使用的网络类型以及地址的大小。报文头以 operation code(op) 结束,code 为 1 时表示请求,为 2 时表示响应。报文内容部分由四个地址组成,分别为发送者的硬件地址(Sender hardware address,简称 SHA)、发送者的网络层地址(Sender protocol address,简称 SPA)、目标的硬件地址(Target hardware address,简称 THA)、目标的网络层地址(Target protocol address,简称 TPA)。

arp package

上图是以 IPv4 为例。这种情况下,SHA 和 THA 的大小为 48bit,SPA 和 TPA 的大小的 32bit。报文头的大小固定是 8 个字节。在 IPv4 的情况下就是总共有 28 个字节。下面,也以 IPv4 为例分别对 ARP 报文的每个字段进行解释。

1~2,Hardware type(HTYPE): 指明链路层协议类型,以太网是1。

3~4,Protocol type (PTYPE):指明网络层协议类型。对于 IPv4 来说,值是 0x0800。

5,Hardware address length(HLEN):硬件地址的长度。以太网地址的长度是6。

6,Protocol length(PLEN):网络层地址的长度。IPv4 地址长度是 4。

7~8,Operation(OP):指明发送方执行的操作,1是请求,2是响应。

到此,ARP 的报文头结束。

9~14,Sender hardware address(SHA):发送方的 MAC 地址。在 ARP 请求中,它代表的是发送请求方的地址。在 ARP 响应中,它代表的是这次 ARP 请求查找的主机地址。

15~18,Sender protocol address(SPA):发送方的网络层地址。

19~24,Target hardware address(THA):接收方的 MAC 地址。在 ARP 请求中,这个字段是被忽略的。在 ARP 响应中,这个字段用来表示 ARP 请求源主机的地址。

25~28,Target protocol address(TPA):接收方的网络层地址。

ARP 的以太网帧类型是 0x0806。

例子

在一个办公室中的两台电脑 c1(192.168.1.100) 和 c2(192.168.1.101) ,在局域网内通过以太网接口和交换机连接,中间没有网关和路由器。

下面我通过 linux 的网桥和 network namespace 来模拟这一场景:

# 准备交换机
ip link add name switch type bridge
# 准备一根网线,一头连接电脑c1,一头连接交换机
ip link add name veth_c10 type veth peer name veth_c11
# 准备一根网线,一头连接电脑c1,一头连接交换机
ip link add name veth_c20 type veth peer name veth_c21
# 准备电脑c1
ip netns add c1
# 准备电脑c2
ip netns add c2
# 将网线插入 c1
ip link set veth_c11 netns c1
# 将网线插入 c2
ip link set veth_c21 netns c2
# 将两根网线都插到交换机
ip link set veth_c10 master switch
ip link set veth_c20 master switch
# 启动交换机
ip link set switch up
# 启动c1
ip link set veth_c10 up
# 启动c2
ip link set veth_c20 up
# 为c1和c2分配ip
ip netns exec c1 ip addr add 192.168.1.100/24 dev veth_c11
ip netns exec c2 ip addr add 192.168.1.101/24 dev veth_c21
ip netns exec c1 ip link set veth_c11 up
ip netns exec c2 ip link set veth_c21 up

环境准备好了之后,c1 想要跟 c2 通信,此时 c1 需要知道 c2 的 MAC 地址。首先它会查找本地是否有缓存的 ARP 表。因为我们的环境刚刚创建好,所以肯定是没有缓存的,那么这个时候,c1 就会发送 ARP 请求,来查找 c2 的 MAC 地址。为了看到 c1 和 c2 之间的所有通信,我们可以用 tcpdump 或 wireshark 来抓交换机上的包,我这里为了展示的更清晰,采用 wireshark 来抓包。

从 c1 向 c2 发送一次 ping。

ip netns exec c1 ping -c 1 192.168.1.101

wireshark 抓包截图如下:

wireshark

第一条是 ARP 请求。它是封装在以太网帧中的。

arp request

以太网帧的广播地址是 ff:ff:ff:ff:ff:ff,源地址是 2e:ee:58:76:59:fc。类型是 ARP。ARP 请求中因为不知道目标的 MAC 地址,所以是 00:00:00:00:00:00。十六进制表示如下:

arp request hex

ARP 响应报文如下:

arp reply。通过这个响应我们也能知道 c1 的 MAC 地址是 2e:ee:58:76:59:fc,c2 的 MAC 地址是 76:cb:15:06:92:87。这个时候,我们也可以看一下 arp 表的情况。

ip netns exec c1 arp -a
? (192.168.1.101) at 76:cb:15:06:92:87 [ether] on veth_c11

ARP 探针(ARP probe)

ARP 探针是一种 SPA 全为 0 的请求。在使用一个 IPv4 地址之前,实现了这个规范的主机必须检查这个地址是否已经在使用了。就是通过这样一个请求来检查的。

为什么要 SPA 全为 0 呢?这是为了防止如果存在冲突,这个请求可能会污染其他主机的 arp 表。

ARP 通告(ARP announcements)

ARP 可以用来作为一种简单的通告协议。当发送方的 IP 地址或者 MAC 地址发生改变后,用来更新其他主机的 MAC 表映射。ARP 通告请求在 target 字段上包含了 SPA 的值(TPA=SPA),THA 为 0,然后广播出去。因为 TPA 为自己的网络层地址,所以不会有其他主机的 ARP 响应。但是其他主机都会收到发送方的 MAC 地址和 IP 地址,那么就可以更新自己的缓存。

ARP 欺骗(ARP spoofing)和 代理 ARP(proxy ARP)

ARP 欺骗很好理解,就是让 ARP 请求的发送方收到错误的 ARP 响应。比如现在我们有三台电脑:

  • c1: 192.168.1.100(2e:ee:58:76:59:fc)
  • c2: 192.168.1.101(76:cb:15:06:92:87)
  • c3: 192.168.1.102(12:07:6b:be:20:d2)

c1 想给 c2 发送数据,在 c1 发 ARP 请求的时候,我们将 ARP 响应中 c2 的 MAC 地址改为 c3 的 MAC地址。然后 c1 的数据就都会发给 c3 了,但是 c1 仍然认为自己在和 c2 通信,这就是 ARP 欺骗了。

代理 ARP 和 ARP 欺骗很像,只是目的不太一样。代理 ARP 的使用场景一般是两台主机不在同一个二层网内,这样通过代理 ARP 的方式来做流量转发。

TLS1.2 RFC5426中一些术语解释

最近想为我的cats服务器加上https的支持。因为最近有一点点的忙,这个项目已经很久没有提交新的代码了。之所以没有用一些开源的库去做,因为这个项目的目的就是锻炼我的代码能力,以及英文RFC的阅读能力。但是在看RFC5426时遇到了一些挫折,里面有大量的密码学上的专业词汇。因此买了一本《图解密码技术》,这里将RFC5426中的专业词汇和概念单独拿出来做一次笔记。

PRF( pseudorandom function ) algorithm: 伪随机函数算法。随机数有三类性质:1.随机性。2.不可预测性。3.不可重现性。这三个性质要求越来越严格。满足1,称为弱伪随机数,满足1、2,称为强伪随机数,满足1、2、3,称为真伪随机数。在密码学的体系中,要求至少达到强伪随机数才能保证安全。

public key encryption:公开密钥加密(英语:Public-key cryptography),也称为非对称加密(英语:asymmetric cryptography),是密码学的一种算法,它需要两个密钥,一个是公开密钥,另一个是私有密钥;一个用作加密的时候,另一个则用作解密。使用其中一个密钥把明文加密后所得的密文,只能用相对应的另一个密钥才能解密得到原本的明文;甚至连最初用来加密的密钥也不能用作解密。由于加密和解密需要两个不同的密钥,故被称为非对称加密;不同于加密和解密都使用同一个密钥的对称加密。虽然两个密钥在数学上相关,但如果知道了其中一个,并不能凭此计算出另外一个;因此其中一个可以公开,称为公钥,任意向外发布;不公开的密钥为私钥,必须由用户自行严格秘密保管,绝不透过任何途径向任何人提供,也不会透露给要通信的另一方,即使他被信任。参考链接: 公开密钥加密

RSA: 名称不是什么缩写,而是发明人首字母的结合。是一种非对称加密的方法。它的速度比起DES等对称加密算法要慢的多。因为非对称加密中,有一个公钥和一个私钥,那么如果分配公钥则是一个问题。如果通过网络传输公钥,则可能被中间人替换掉公钥进行攻击.因此一般的做法是用可靠的第三方机构签发证书来防止这样的攻击。参考链接:RSA加密演算法

MAC (Message Authentication Code) algorithm: 消息验证码的算法.是经过特定算法后产生的一小段信息,检查某段消息的完整性,以及作身份验证 。它可以用来检查在消息传递过程中,其内容是否被更改过,不管更改的原因是来自意外或是蓄意攻击。同时可以作为消息来源的身份验证,确认消息的来源。在我的理解中,MAC是一种与密钥相关联的单向散列函数。参考链接:消息认证码

HMAC (Keyed-Hashing for Message Authentication): 它通过一个标准算法,在计算哈希的过程中,把key混入计算过程中。其实就是常用的加salt的方式,使得相同的原值生成不同的哈希值。HMAC是用来生成MAC值的。参考链接:密钥散列消息认证码

DSA (digital signing algorithm):DSA是一种更高级的验证方式。一般用于数字签名和认证。DSA 不单单只有公钥、私钥,还有数字签名。私钥加密生成数字签名,公钥验证数据及签名。在DSA数字签名和认证中,发送者使用自己的私钥对文件或消息进行签名,接受者收到消息后使用发送者的公钥来验证签名的真实性。如果数据和签名不匹配则认为验证失败!数字签名,不仅能验证数据的完整性,真实性,还能“对第三方证明”和“防止否认”。参考链接:常见的加密算法之DSA 算法

CBC (Cipher Block Chaining):分组密码的一种工作模式,允许使用同一个分组密码密钥对多于一块的数据进行加密,并保证其安全性。其他的工作模式还有:ECB,PCBC,CFB,OFB,CTR等。参考链接:分组密码工作模式

SHA256, SHA1, MD5: 常见的几种信息摘要算法(有时候也称为哈希算法,单向散列函数等)。

SSL (Secure Socket Layer):安全套接字层

TLS (Transport Layer Security Protocol):安全传输层协议,TLS的后续工作是在SSL的基础上进行的。

stream cipher encryption:在密码学中,流密码(英语:Stream cipher),又译为流加密、数据流加密,是一种对称加密算法,加密和解密双方使用相同伪随机加密数据流(pseudo-random stream)作为密钥,明文数据每次与密钥数据流顺次对应加密,得到密文数据流。实践中数据通常是一个位(bit)并用异或(xor)操作加密。参考链接: 流密码

block cipher encryption:在密码学中,分组加密(英语:Block cipher),又称分块加密或块密码,是一种对称密钥算法。它将明文分成多个等长的模块(block),使用确定的算法和对称密钥对每组分别加密解密。分组加密是极其重要的加密协议组成,其中典型的如DES和AES作为美国政府核定的标准加密算法,应用领域从电子邮件加密到银行交易转帐,非常广泛。参考链接: 分组加密

authenticated encryption with additional data (AEAD) encryption:认证加密(英语:Authenticated encryption,AE)和用于关联数据的认证加密(authenticated encryption with associated data,AEAD,AE的变种)是一种能够同时保证数据的保密性、 完整性和真实性的一种加密模式。参考链接: 用于关联数据的认证加密

compression algorithm:数据压缩算法

master secret:主密码用来生成对称密码的秘钥,消息认证码的秘钥以及对称密码CBC模式所使用的初始化向量(IV)

HTTP协议中的缓存控制

一、总览

HTTP协议中有以下的头部字段和缓存相关(很多内容都是复制的MDN的文档)

字段名 请求头包含 响应头包含 含义 出现的协议版本
Cache-Control 是否缓存、缓存时间、缓存验证等 HTTP/1.1
Pragma 只有一种用法:Pragma: no-cache。在响应头中没有规定 HTTP/1.0
Vary 它决定了对于未来的一个请求头,应该用一个缓存的回复(response)还是向源服务器请求一个新的回复 HTTP/1.1
If-Match 在请求方法为 GET 和 HEAD 的情况下,服务器仅在请求的资源满足此首部列出的 ETag 之一时才会返回资源。而对于 PUT 或其他非安全方法来说,只有在满足条件的情况下才可以将资源上传 HTTP/1.1
If-None-Match 对于 GET 和 HEAD 请求方法来说,当且仅当服务器上没有任何资源的 ETag 属性值与这个首部中列出的相匹配的时候,服务器端会才返回所请求的资源,响应码为 200 。对于其他方法来说,当且仅当最终确认没有已存在的资源的 ETag 属性值与这个首部中所列出的相匹配的时候,才会对请求进行相应的处理。 HTTP/1.1
If-Modified-Since 服务器只在所请求的资源在给定的日期时间之后对内容进行过修改的情况下才会将资源返回,状态码为200。如果请求的资源从那时起未经修改,那么返回一个不带有消息主体的304响应,而在 Last-Modified 首部中会带有上次修改时间。不同于If-Unmodified-Since, If-Modified-Since 只可以用在 GET 或 HEAD 请求中。 HTTP/1.1
If-Unmodified-Since 只有当资源在指定的时间之后没有进行过修改的情况下,服务器才会返回请求的资源,或是接受 POST 或其他 non-safe 方法的请求。如果所请求的资源在指定的时间之后发生了修改,那么会返回 412 (Precondition Failed) 错误。 HTTP/1.1
ETag TagHTTP响应头是资源的特定版本的标识符。这可以让缓存更高效,并节省带宽,因为如果内容没有改变,Web服务器不需要发送完整的响应。而如果内容发生了变化,使用ETag有助于防止资源的同时更新相互覆盖(“空中碰撞”) HTTP/1.1
Expires Expires 响应头包含日期/时间, 即在此时候之后,响应过期 HTTP/1.1
Last-Modified 包含源头服务器认定的资源做出修改的日期及时间。 它通常被用作一个验证器来判断接收到的或者存储的资源是否彼此一致。由于精确度比 ETag 要低,所以这是一个备用机制。包含有 If-Modified-Since 或 If-Unmodified-Since 首部的条件请求会使用这个字段。 HTTP/1.1
Date 消息生成的时间 HTTP/1.1
If-Range If-Range HTTP 请求头字段用来使得 Range 头字段在一定条件下起作用:当字段值中的条件得到满足时,Range 头字段才会起作用,同时服务器回复206 部分内容状态码,以及Range 头字段请求的相应部分;如果字段值中的条件没有得到满足,服务器将会返回 200 OK 状态码,并返回完整的请求资源。 HTTP/1.1

二、详细说明

一眼看上去,缓存相关的字段确实有很多。但是实际上,稍微理一理思路即可。

上面所有的字段都在围绕着3个点:

  • 1.是否要缓存
  • 2.缓存多久
  • 3.缓存是否有效

2.1 是否要缓存

一个HTTP的客户端(包括浏览器,以及CDN等缓存代理)如何知道当前的请求是否要缓存呢?

Cache-Control中,有下列取值来决定是否缓存:

public:表明响应可以被任何对象(包括:发送请求的客户端,代理服务器,等等)缓存。
private:表明响应只能被单个用户缓存,不能作为共享缓存(即代理服务器不能缓存它),可以缓存响应内容。
no-store:缓存不应存储有关客户端请求或服务器响应的任何内容。

2.2 缓存多久

源服务器上的内容可能随时发生变化,那么如何知道什么时候去检查缓存是否更新了呢?HTTP协议中有以下字段规定了一个缓存的有效期。

Cache-Control

max-age={seconds}:设置缓存存储的最大周期,超过这个时间缓存被认为过期(单位秒)。与Expires相反,时间是相对于请求的时间。
s-maxage={seconds}:覆盖max-age 或者 Expires 头,但是仅适用于共享缓存(比如各个代理),并且私有缓存中它被忽略。
max-stale[={seconds}]:表明客户端愿意接收一个已经过期的资源。 可选的设置一个时间(单位秒),表示响应不能超过的过时时间。
min-fresh={seconds}:表示客户端希望在指定的时间内获取最新的响应。

Expire

Expires: {http-date}: 表示在http-date之后,这个缓存就过期了。如果http-date是一个无效的时间值,则代表已过期。如果在Cache-Control响应头设置了 "max-age" 或者 "s-max-age" 指令,那么 Expires 头会被忽略。

Date和Last-Modified

如果在`Cache-Control`和`Expire`都没有返回的情况下,也可以通过`Date`头和`Last-Modified`头去计算缓存的有效期。缓存的寿命就等于头里面Date的值减去Last-Modified的值除以10。

2.3 缓存是否有效

源服务器上的内容可能随时发生变化, 那么HTTP客户端如果知道自己缓存的内容是否有效呢?这里要分几种情况:

  • 服务器的响应中有Cache-Control:must-revalidate头:当前缓存在有效时间内,此时缓存默认就是有效的。当前缓存过了有效时间,则会向服务器验证缓存是否过期。如果服务返回304,则代表缓存没有过期。
  • 服务器的响应中有Cache-Control: no-cachePragma:no-cache头:则表示每一次都要从服务器验证缓存是否过期。no-cache的优先级是要大于Pragma的

注:no-cache和must-revalidate的区别

在RFC7234中说到:

“must-revalidate” 响应头指令表示一旦该响应过期,这个缓存在向源服务器成功验证之前禁止使用。在任何情况下,一个缓存都必须遵循”must-revalidate”指令;特殊情况下,如果源服务器无法连接,必须生成504(Getway Timeout)响应。

“no-cache”响应头指令表示缓存在向源服务器成功验证之前禁止使用(注:不论缓存是否过期)。如果”no-cache”指令指明了一或多个字段,缓存可以被用来响应之后的请求。但是,在没有和源服务器进行验证的情况下,任何”no-cache”中列出的字段都禁止在之后的响应中被发送。这使得源服务器可以阻止某些字段被重复使用,但是仍然可以缓存响应的其他部分。

“no-cache”中的字段不仅仅限于http1.1协议中列举出来的字段。字段名是大小写不敏感的。使用双引号包围。

因此个人认为,在某些时候max-age=0;must-revalidate 可以等同于 no-cache。

那么这个验证机制是什么样的?也分几种情况

  • 根据文件指纹ETag:在服务器返回了一个文件的ETag的情况下,HTTP客户端可以根据If-None-MatchIf-Match来向服务器验证当前缓存是否过期。
  • 根据文件修改时间:在服务器返回了Last-Modified的情况下,HTTP客户端可以根据If-Unmodified-SinceIf-Modified-Since来向服务器验证当前缓存是否过期。
  • 一个比较特殊的If-RangeIf-Range通常出现在分段请求当中,用来分段请求的资源主体是否发生了变化。它的值既可以是etag,也可以是GMT时间戳。

注:因为Last-Modified精确到秒,在精确度上比ETag低,所以应该以ETag为主。

2.4 关于vary字段

上面说了三个点,但是没有涉及到vary字段。vary和缓存并不是直接相关的。取一段MDN的说明:

Vary 是一个HTTP响应头部信息,它决定了对于未来的一个请求头,应该用一个缓存的回复(response)还是向源服务器请求一个新的回复。它被服务器用来表明在 content negotiation algorithm(内容协商算法)中选择一个资源代表的时候应该使用哪些头部信息(headers).

在响应状态码为 304 Not Modified 的响应中,也要设置 Vary 首部,而且要与相应的 200 OK 响应设置得一模一样。

举个例子,如果服务器返回的网页是分手机版和电脑版的,一般我们会根据user-agent来判断浏览器是手机浏览器还是电脑上的浏览器。假设有一个中间代理的请求:

手机用户1请求index.html---------->中间代理------------>源服务器
电脑用户1请求index.html---------->中间代理------------>源服务器

在电脑用户1请求index.html时,中间代理会向原服务器请求还是直接使用本地缓存的副本呢?

如果原服务器在第一次请求时响应头中有vary:user-agent则会重新请求。因为两次请求的user-agent是不同的,因此缓存不能被重复使用。但是如果没有指定则使用本地缓存作为响应。


参考文档

HTTP缓存控制小结
HTTP 协议中 Vary 的一些研究
http://www.cnblogs.com/chyingp/p/no-cache-vs-must-revalidate.html
HTTP缓存

HTTP Caching | MDN

Cache-Control | MDN
Pragma | MDN
Vary | MDN
If-Match | MDN
If-None-Match | MDN
If-Modified-Since | MDN
If-Unmodified-Since | MDN
ETag | MDN
Expires | MDN
Last-Modified | MDN
If-Range | MDN