note/network

网络常识

对于网络我其实是一知半解,这篇文章基本上是我知道什么就写些什么,因此十分零碎。

协议

网络协议有好几层,这些层级环环相扣。在 OSI 概念模型中,网络协议有七层;而在事实标准 TCP/IP 协议栈中,舍弃了其中的 L5 和 L6,并套用了 OSI 的 L1 物理层,共有四层。

  • L2: 数据链路层,以太网、Wi-Fi、PPPoE 等
  • L3: 网络层,IP
  • L4: 传输层,TCP、UDP
  • 可选的传输加密层,TLS/SSL
  • L7: 应用层,HTTP、FTP、SSH、IMAP 等

数据链路层由网卡实现,用于连接计算机,构成网络。以太网用于构成有线局域网,Wi-Fi 用于构成无线局域网,PPP 和 PPPoE 用于连接用户和 ISP。

网络层由操作系统内核实现,用于将网络相连,构成互联网。

传输层由程序(调用 libc)实现,一般来说有 TCP 和 UDP 两种选择。TCP 会建立连接,并且提供不错的校验纠错功能,而代价就是减小了数据位占比。而 UDP 则是一种无连接的传输层协议。现在还有一些新兴的类传输层协议,如 KCP,他们一般运行在 UDP 之上,提供比 UDP 好的数据校验,比 TCP 高的传输效率。

IP

IP 地址指示一台计算机在网络上位置,数据包通过 IP 地址在路由器间寻路。流行的 IP 协议有 IPv4 和 IPv6 两个版本。

IPv4 地址为一串 32 位的数字,标准的表示方法为每 8 位用一个 0~255 的十进制数表示,中间用 . 分开,如 123.123.123.123。只有 32 位的 IPv4 可以分配的地址十分有限。

于是人们又提出了 IPv6。IPv6 地址为一串 128 位的数字,标准的表示方法是每 16 位用一个 0x0000~0xffff 的十六进制数表示,中间用 : 分开,如 1234:5678:9abc:def0:0000:0000:0000:0001。并且为了简便,有两条简写的规则:

  • 每段开头的零可以省略。所以上面的地址可简写为 1234:5678:9abc:def0:0:0:0:1
  • 连续的零段可以省略为 ::,一个地址中只能这样省略一次。上面的地址可以进一步简写为 1234:5678:9abc:def0::1

因特网上的 IP 地址块由 IANA 和 RIR 负责分配。RIR 是各个地区的网络资源分配组织,如亚太地区的 RIR 为 APNIC,北美地区为 RIPE。IANA 首先将地址块分配给 RIR,RIR 之下还有 LIR。个人或组织向所属的 RIR 定期缴纳会费即可成为 LIR,LIR 可以申请或替他人或其他组织代为申请 IP 地址块。

以下是 IP 协议中特殊的地址,使用它们不需要向 RIR 申请。

环回地址

环回 IP 用于代指本机,一台机器上的程序可以通过环回 IP 互相通信。

IPv4 的环回 IP 段为 127.0.0.1/8。可见在 IPv4 中 1/256 的地址都分配给了环回地址,挺浪费的。

IPv6 中只分配了一个环回 IP,::1/128

私有地址

私有 IP 仅在一个局域网内使用,使用私有 IP 不需要向 RIR 申请,因为它不会传播到其他网络。

IPv4 的私有 IP 如下(RFC1918):

  • 10.0.0.0/8
  • 192.168.0.0/16
  • 172.16.0.0/12

IPv4 的私有 IP 有挺多问题,比如对于 NAT 套 NAT 的情况和两个局域网合并的情况,都十分容易造成 IP 重叠导致需要大量的重新配置。因此 IPv6 中使用一种叫做 ULA 的私有地址(RFC4193)。

  • 一个局域网应该在 fd00::/8 下分配一个 /48 使用。
  • fd 后面的 40 位应当随机生成。如 fdc8:541e:9e06::/48

链路本地地址

链路本地地址是不能被路由的地址。IPv4 的 link-local 地址段为 169.254.0.0/16,是 system wise 的。IPv6 的 link-local 地址段为 fe80::/10,为 link wise 的,使用时需指定网卡。

DNS

IP 地址可以帮助数据包找到目的主机,但却不具有很高的可读性。而域名正是为了解决这一点。通过 DNS,人们只需要输入域名,计算机就可以找到域名所对应的 IP 地址,从而找到对应的主机。

域名的最终管理组织为 ICANN,负责分配顶级域名(TLD)给不同组织。人们熟知的顶级域名如 com, org, net, cn 等。ICANN 把这些顶级域名交给不同的组织管理。这些组织把顶级域名的子域名 —— 二级域名开放给他人注册。如 land 为 Donuts Inc. 所有,我要注册 melty.land 最终就是向这个组织提的申请。一般来说人们注册的域名都是二级域名,有了二级域名你就可以在你的二级域名上添加子域名,具体方法是设置 nameserver。

注意,没有一级域名这个概念。

DNS 系统中有四种 nameserver,分别是 root server、TLD server、authoritative server 和 recursive server。

Root nameserver 有 13 台,由 ICANN 管理,分布在全球各地。储存了各顶级域名对应的 TLD nameserver。

TLD nameserver 由 ICANN 授权的组织管理,储存了二级域名的 authoritative nameserver record。

Authoritative nameserver 由注册二级域名的个人或组织指定,可以是 registrar 提供,也可以是自建,储存了二级域名下的 AAAAAMXNSTXT 等最终的 record。

Recursive nameserver 用于加速 DNS 解析,它会缓存用户向 root server, TLD server 和 authoritative server 发送 DNS 请求。通常由 ISP 提供并通过 DHCP 层层传递到用户的路由器,也有第三方的服务,如 Google 的 dns.google (8.8.8.8)、OpenDNS(208.67.222.222, 208.67.220.220)、CloudFlare 的 1.1.1.1。在 Linux 上用户也可以通过 dnsmasq 或 systemd-resolved 自行搭建本地的 recursive nameserver(也叫 resolver)。

DNS 记录(record)是一个域名和一项结果的对应关系,有很多种类型。

  • A: IPv4 记录。
  • AAAA: IPv6 记录。
  • CNAME: 别名记录,指向另一个域名,从而使对该域名的解析返回指向域名的结果。
  • TXT: 文本记录,可以放一些辅助信息。如 SPF 中用于验证邮件的发送者、TLS DV 提供商用于验证域名所有权等。
  • MX: 邮件交换记录,指向另一个域名。告诉邮件的发送方邮件该域名的邮件服务不在该域名指向的 IP 下,而应当发送至哪个域名。
  • NS: nameserver 记录,指向另一个 DNS 服务器的域名。告诉请求方这个域名的信息我这里没有,你应该到这个 DNS 服务器查询。上面说的 root server 指向 TLD server,TLD server 指向 authoritative nameserver 就是通过这个记录实现的。
  • PTR: IP 指向域名的记录,见反向 DNS

解析过程

下面简述一下访问 melty.land 时所经历的 DNS 解析过程。为了方便描述我将用 Linux 下的 dig 工具来查询 DNS,这是使用方法:

dig [<type>] <domain> [@<nameserver>] [+short]
  1. 程序调用 libc,向 recursive nameserver 发出请求

    dig A melty.land @192.168.1.1
  2. Recursive nameserver 发现自己的缓存里没有该项,于是向一台 root nameserver 发出请求

    dig A melty.land @a.root-servers.net
  3. Root nameserver 告诉 recursive nameserver 自己也没有,但找到了 melty.land 的所在的顶级域名的 TLD nameserver,于是不返回 ANSWER SECTION,而是在 AUTHORITY SECTION 中返回了 NS 记录

    ;; AUTHORITY SECTION:
    land.			172800	IN	NS	v2n1.nic.land.
    land.			172800	IN	NS	v0n2.nic.land.

    并且由于这些 nameserver 的域名 nic.land 本身也在 land 顶级域名下,所以在 ADDITIONAL SECTION 中附上了这些域名的 AAAAA 记录(这些记录被称为“胶水记录”,是 TLD 管理组织预先在 ICANN 填写的)

    ;; ADDITIONAL SECTION:
    v2n1.nic.land.		172800	IN	A	161.232.11.11
    v2n1.nic.land.		172800	IN	AAAA	2a01:8840:f5::11
    v0n2.nic.land.		172800	IN	A	65.22.22.11
    v0n2.nic.land.		172800	IN	AAAA	2a01:8840:18::11
  4. Recursive nameserver 收到了 root server 的回答,缓存 land 顶级域名的 NS 结果,并向其中一台 TLD nameserver 发送请求

    dig A melty.land @v2n1.nic.land
  5. TLD nameserver 在数据库中没找到 melty.landA 记录,但找到了 melty.land 使用的 authoritative nameserver,于是在 AUTHORITY SECTION 中返回

    ;; AUTHORITY SECTION:
    melty.land.		3600	IN	NS	etta.ns.cloudflare.com.
    melty.land.		3600	IN	NS	fred.ns.cloudflare.com.
  6. Recursive nameserver 收到了 TLD nameserver 的回答,缓存 melty.landNS 记录结果,然后向其中一台 authoritative nameserver 发送请求

    dig A melty.land @etta.ns.cloudflare.com
  7. Authoritative nameserver 找到了 @.melty.landA 记录,发回给 recursive nameserver。

    ;; ANSWER SECTION:
    melty.land.		300	IN	A	233.233.233.233
  8. Recursive nameserver 缓存该 A 记录,返回给请求的程序

    ;; ANSWER SECTION:
    melty.land.		300	IN	A	233.233.233.233

反向 DNS

反向 DNS 为 IP 指向域名的 DNS,使用 PTR 记录表示。常用于邮件发送验证等。它看起来是这样的:

  • IPv4: 将 IP 按照点分十进制,每八位用一个 0~255 的十进制数表示,然后反过来,接上 .in-addr.arpa 查询。如 123.456.789.12 则应当查询 12.789.456.123.in-addr.arpa
  • IPv6: 将 IP 以十六进制数表示,然后按十六进制位倒过来以 . 连接,接上 ip6.arpa。如 1234:5678:9abc:def0::1 则应当查询 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.f.e.d.c.b.a.9.8.7.6.5.4.3.2.1.ip6.arpa

ICANN 将 arpa. 顶级域名分配给了 IANA。在 RIR IRR 数据库的 domain 对象中,可以使用 nserver 指定你的 IP 块的反向 DNS 服务器。查询此 IP 块的 IP 时 RIR 的服务器就会返回该 IP 块指向你服务器的 NS 记录。

另外 IPv4 的反向 DNS 中会出现分配的 IP 块前缀不是 8 的倍数的情况,如查询 123.456.789.128/25 下的 123.456.789.129。这种情况无法直接指向下一级,而是会以 CNAME 记录指向 129.128/25.789.456.123.in-addr.arpa.,然后附上 128/25.789.456.123.in-addr.arpa NS 记录。IPv6 没有这个问题。

传输层

传输层的主要作用是复用 IP,将计算机之间的通信转化为进程之间的通信。对于 TCP,还有重传功能以确保通信可靠。

TCP 和 UDP 的头内有 16 位用于表示端口,因此各有 65536 个端口。将 IP 和端口写在一起,称为一个套接字。IPv4 中套接字标准的表示方法为 IP:port,IPv6 中为 [IP]:port

0~1023 为一些标准端口,它们常常因为历史原因有一些约定俗成的用法,由 IANA 负责标准化。因此虽然可以,但不建议将它们用于别的用途(Linux 下这些端口只有具有 CAP_NET_ADMIN 的进程可以绑定)。

  • tcp/80: HTTP
  • tcp/443: HTTPS
  • tcp/22: SSH
  • tcp/179: BGP
  • udp/53: DNS
  • udp/123: NTP

TLS/SSL

TLS 和 SSL 是一个东西的两个名字。该层不在 OSI 概念模型中,硬要归类的话应该属于传输层。用于给传输层的数据加密。

SSL 需要找第三方签发证书,这是因为非对称加密本身的交换公钥过程无法保证安全性。操作系统和浏览器中会预先保存许多可信的 Certificate Authorities 的根证书。被签发方首先生成一对钥匙,将公钥发给 CA。CA 使用私钥,在验证被签发方具有对域名的所有权后给公钥签名,称作签发证书。这样通过这些 CA 签名的公钥也会被信任,可以用于加密该域名的流量。

证书根据验证所有权的方式不同,分为 DV、OV、EV。DV 只需要对域名的所有权进行验证,如 DNS 记录、网站验证等。OV 和 EV 则需要验证组织或企业的所有权。有免费的 DV 服务,如 Let's Encrypt,可以通过 ACME 接口自动续签。

HTTP

HTTP/HTTPS 是使用得最广泛的两种网络传输协议。网页即是通过 HTTP 传输。HTTP 是一种基于请求和回应的协议:客户端向服务器发出请求,服务器将内容回应给客户端。

请求方式

  • GET: 让服务器将一个 URI 对应的内容发给我。网页就是这么加载的。

  • POST: 我先给服务器发送一点信息,服务器将我发送信息后处理的结果发给我。比如一个网络知识竞赛,我把每题的答案和我的身份信息发送给服务器,然后服务器返回给我我当前的排名之类的。

    对于键值对数据,POST 的主体有三种,他们的 MIME type 分别是 multipart/form-dataapplication/x-www-form-url-encodedapplication/jsonapplication/x-www-form-url-encoded 是将键值对数据按照 query string 的方式编码,很多时候比 multipart/form-data 更紧凑些。而 application/json 就是 JSON 字符串了。

    • curl 的 -F key=value 发送的是 multipart/form-data-d key=value 发送的是 application/x-www-form-url-encoded
    • 浏览器的 fetch API 当 body 是 FormData 时发送 multipart/form-data,当 body 是 URLSearchParams 时发送 application/x-www-form-url-encoded
    • Python requests 中,request.post() 通过可选参数 data= 发送的是 application/x-www-form-url-encoded,通过 json= 发送的是 application/json

Headers

List of HTTP header fileds - Wikipedia

出现在请求和回应中,描述传输的文件类型和安全选项等。下面列出了一些通用的 headers 名,除了通用的 headers 之外也可以自行指定,比如 AWS 就在 Application Load Balancer 中使用了 X-Amzn-Trace-Id header。

Content-Type 指定文件的类型,是 HTML(text/html)、JSON(application/json)、还是纯文本(text/plain),这决定了返回的内容应该被浏览器渲染出来、以纯文本显示还是作为下载。所有 MIME 类型列表

Access-Control-Allow-Origin 回应中出现,是否允许别的站点在浏览器上运行时引用我的内容,设为 * 即为允许。

Accept-Language 请求中出现,告诉服务器你的语言。

Referer 请求中出现,告诉服务器你从哪个链接过来的。比如在手机浏览器上从其他搜索引擎跳转到百度知道时会出现一个“返回百度”的按钮,就是通过这个实现的。

CookiesSet-Cookie 见下。

Cookies

Cookies 是一列储存在浏览器中的短文本列表,可以用来储存身份信息、登录状态等。在发送请求时,浏览器会将储存的该网站的 cookies 用 Cookies header 发出;收到服务器的回应时,浏览器会根据 Set-Cookie header 中的子段更改储存的 cookies。

Cookies 本质上只是一对被浏览器特殊照顾的 HTTP headers 而已,除浏览器外其他环境中要指定请求中发送的 cookies 完全是可以的。比如 cURL 可以用 --cookie 'key=value' 来发送 cookie,用 --cookie-jar filename.cookies 指定接收到的 Set-Cookie 储存在那个文件中;Python 的 requests 库可以使用 requests.get('some-url.com', cookies={'key': 'value'}) 来发送 cookies,还可以在一个 requests.Session 中创建请求来保存从回应中获得的 cookies。

代理

客户端的请求打包、发往代理服务器,代理服务器将请求数据解包、发往目标服务器;目标服务器收到代理服务器的请求后、将回应的数据发往代理服务器,最后代理服务器将回应的数据打包、发到客户端。

代理的用途很广,中间人攻击、抓包进行网络数据分析都是通过代理来完成的。此外,代理还可以用于绕过防火墙的过滤。

代理运行在应用层,提供应用层或传输层接口。

反向代理

反向代理和前向代理是一样的原理,区别只在于哪部分对外面的互联网是可见的。前向代理对服务器隐藏了客户端的 IP,反向代理对客户端隐藏了服务器的原始地址。

VPN

VPN 用于在公网上建立一个虚拟的内网,同时提供加密。VPN 运行于应用层,提供链路层(L2VPN)或网络层(L3VPN)的接口。

About Me