文章

Linux - ip绑定

部署一个springboot服务,绑定8122端口。通过netstat查看,发现只有ipv6地址被绑定了:

1
2
3
4
⚡ netstat -anp | grep :8122
(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
tcp6       0      0 :::8122                 :::*                    LISTEN      137833/java

但实际上通过ipv4也能访问8122端口。

这是为什么?

  • 猜想一:不同ip相同端口的请求可以互通
  • 猜想二:还是说ipv4的请求被转发给了ipv6?
  1. prerequisite - NIC
  2. prerequisite - ip的一些写法
    1. 未定义地址
    2. loop地址
  3. ip和port的关系
    1. 访问同一机器的不同ip是否是等效的
    2. 不同ip可以绑定同一个端口吗
    3. 所有ip均绑定同一端口
    4. 只绑定ipv6,通过ipv4访问
  4. ip映射

prerequisite - NIC

Network Interface Card

没联网的时候,每个网卡只有一个mac地址。比如下面的有线网卡enp2s0和无线网卡wlp3s0:

1
2
3
4
2: enp2s0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast state DOWN group default qlen 1000
    link/ether f4:8e:38:f2:06:4c brd ff:ff:ff:ff:ff:ff
3: wlp3s0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default qlen 1000
    link/ether 5a:b6:fa:69:d0:4c brd ff:ff:ff:ff:ff:ff permaddr 3c:f8:62:da:51:15

无线网卡联网之后,有一个ipv4地址,有一个ipv6地址(和ipv4一样,一般分到的ipv6地址是fc/fd/fe开头的,也是私有地址):

1
2
3
4
5
6
7
8
2: enp2s0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast state DOWN group default qlen 1000
    link/ether f4:8e:38:f2:06:4c brd ff:ff:ff:ff:ff:ff
3: wlp3s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 3c:f8:62:da:51:15 brd ff:ff:ff:ff:ff:ff
    inet 10.236.112.97/19 brd 10.236.127.255 scope global dynamic noprefixroute wlp3s0
       valid_lft 431994sec preferred_lft 431994sec
    inet6 fe80::4d1d:d8d9:e463:27cc/64 scope link noprefixroute 
       valid_lft forever preferred_lft forever
  • https://cloud.tencent.com/developer/article/1468099

网卡有单栈(ipv4-only,ipv6-only)和双栈(nic dual stack)

双栈网卡就是两种协议都能支持的网卡。

  • https://www.juniper.net/documentation/us/en/software/junos/is-is/topics/concept/ipv6-dual-stack-understanding.html

双栈网卡如果两种协议都能支持,用哪种?有以下决策方式:

  • The dual-stacked device can interoperate equally with IPv4 devices, IPv6 devices, and other dual-stacked devices. When both devices are dual stacked, the two devices agree on which IP version to use;
  • The transition is driven by DNS. If a dual-stacked device queries the name of a destination and DNS gives it an IPv4 address (a DNS A Record), it sends IPv4 packets. If DNS responds with an IPv6 address (a DNS AAAA Record), it sends IPv6 packets;

prerequisite - ip的一些写法

未定义地址

代表任何地址。

  • ipv6:0:0:0:0:0:0:0:0。由于ipv6任意连续两个0都可以简写为::,所以全零可以缩写为::
  • ipv4:0.0.0.0,ipv4没有简写。

某个ipv6的写法:::80其实可以分成两部分:

  1. ::指的是全0的ipv6;
  2. :80指端口为80。

二者加起来,指的是任意ipv6地址的80端口。

loop地址

  • ipv6:0:0:0:0:0:0:0:1,根据缩写规则,前7个零可以简写,最终简写为::1
  • ipv4:127.0.0.1。同样没有简写。

查看WSL里的hosts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
% cat /etc/hosts
# This file was automatically generated by WSL. To stop automatic generation of this file, add the following entry to /etc/wsl.conf:
# [network]
# generateHosts = false
127.0.0.1       localhost
127.0.1.1       win10Home.localdomain   win10Home

192.168.1.107   host.docker.internal
192.168.1.107   gateway.docker.internal
127.0.0.1       kubernetes.docker.internal

# The following lines are desirable for IPv6 capable hosts
::1     ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

可以看到ipv4的loop域名是localhost,ipv6的loop域名是ip6-localhost

ip和port的关系

先来验证一下猜想一:不同ip相同端口的请求究竟能不能互通

测试机器有两个ip:

  • localhost loop地址:ipv4为127.0.0.1/8,对应ipv6为::1/128
  • 内网ip:10.108.160.77/24,对应ipv6为fe80::6e92:bfff:fe6a:d3a/64

访问同一机器的不同ip是否是等效的

使用netcat监听localhost:3456(其实就是监听127.0.0.1:3456):

1
2
3
% nc -v -l localhost 3456
Ncat: Version 7.50 ( https://nmap.org/ncat )
Ncat: Listening on 127.0.0.1:3456

绑定显示127.0.0.1:3456

1
2
3
4
> netstat -anp | grep :3456
(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
tcp        0      0 127.0.0.1:3456          0.0.0.0:*               LISTEN      9672/nc

尝试使用内网ip连接3456端口,失败:

1
2
3
% nc -v -z 10.108.160.77 3456
Ncat: Version 7.50 ( https://nmap.org/ncat )
Ncat: Connection refused.

说明不同ip实际上就是对应不同的网卡,访问一个ip不等于访问另一个ip。监听localhost的3456的服务并不能通过10.108.160.77收到请求。

此时只能通过127.0.0.1:3456连接到这个nc server process。

不同ip可以绑定同一个端口吗

再起一个netcat服务,监听10.108.160.77:3456:

1
2
3
% nc -v -l 10.108.160.77 3456
Ncat: Version 7.50 ( https://nmap.org/ncat )
Ncat: Listening on 10.108.160.77:3456

成功。

查看端口绑定:

1
2
3
4
5
> netstat -anp | grep :3456
(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
tcp        0      0 10.108.160.77:3456      0.0.0.0:*               LISTEN      10808/nc
tcp        0      0 127.0.0.1:3456          0.0.0.0:*               LISTEN      9672/nc

此时有两个netcat进程分别监听不同ip的3456端口。

说明不同IP可以绑定同一个端口。ip+port二者共同组成一个socket,确定一个服务进程的位置。

  • https://segmentfault.com/q/1010000016970247/a-1020000016970972

所有ip均绑定同一端口

关闭上面两个进程,不指定某一ip,使用nc监听所有ip的3456端口:

1
2
3
4
% nc -v -l 3456
Ncat: Version 7.50 ( https://nmap.org/ncat )
Ncat: Listening on :::3456
Ncat: Listening on 0.0.0.0:3456

绑定显示tcp4和tcp6的所有ip都绑定了3456端口:

1
2
3
4
5
% netstat -anp | grep :3456
(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
tcp        0      0 0.0.0.0:3456            0.0.0.0:*               LISTEN      82931/nc
tcp6       0      0 :::3456                 :::*                    LISTEN      82931/nc

此时可以通过任意ip的3456端口连接到这个nc server process。

只绑定ipv6,通过ipv4访问

需要使用bsd版本的netcat:

1
2
▶ nc -6 -l -v 3456    
Listening on :: 3456

查看绑定情况,只有tpc6:

1
2
3
4
$ netstat -anp | grep :3456
(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
tcp6       0      0 :::3456                 :::*                    LISTEN      7655/nc

通过网卡的ipv6访问netcat server:

1
2
$ nc -6 -v -z fe80::6e92:bfff:fe6a:d3a%wlp3s0 3456
Connection to fe80::6e92:bfff:fe6a:d3a%wlp3s0 3456 port [tcp/*] succeeded!

符合预期。

使用ipv6的时候还需要指定网卡名称:https://unix.stackexchange.com/a/136473/283488

还能通过ipv6 loop ip访问:

1
2
$ nc -6 -v -z ::1 3456   
Connection to ::1 3456 port [tcp/*] succeeded!

但是竟然也能通过ipv4地址访问:

1
2
nc -4 -v -z 127.0.0.1 3456
Connection to 127.0.0.1 3456 port [tcp/*] succeeded!

这是为什么?springboot也是只显示绑定了ipv6,却能通过ipv4访问,看来道理是一样的。

ip映射

  • https://serverfault.com/a/755784/801099
  • http://httpd.apache.org/docs/2.0/bind.html#ipv6

通过上述文档可以看到,确实是只绑定了ipv6,但是linux默认会做ipv4映射,所有监听ipv6某端口的程序,也会收到ipv4该端口的连接请求。

所以,linux上有的显示只绑定了ipv6,有的显示ipv6和ipv4都绑定了,就不足为奇了:

  • https://www.cnblogs.com/wlzjdm/p/8684202.html

但有的显示绑定ipv6,实际也只能通过ipv6访问,是因为关了ipv4映射。

究竟有没有关ipv4映射,这一点通过netstat并不能看出来,netstat只是忠实地显示出了程序绑定了ip而已

本文由作者按照 CC BY 4.0 进行授权