Почему TCP-соединения IPv4 отображаются как tcp6?

Вот вывод netsat -tupn на моем сервере Debian Jessie:

 Active Internet connections (w/o servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 10.0.0.12:445 10.0.0.20:49729 ESTABLISHED 26277/smbd tcp 0 0 10.0.0.12:443 10.0.0.21:44162 ESTABLISHED 1400/nginx: worker tcp 0 0 10.0.0.12:445 10.0.0.21:46650 ESTABLISHED 23039/smbd tcp 0 0 10.0.0.12:443 10.0.0.20:54584 ESTABLISHED 1400/nginx: worker tcp 0 0 10.0.0.12:139 10.0.0.225:10425 ESTABLISHED 23701/smbd tcp 0 0 10.0.0.12:445 10.0.0.217:49179 ESTABLISHED 21535/smbd tcp 0 0 10.0.0.12:445 10.0.0.217:49178 ESTABLISHED 21534/smbd tcp 0 0 10.0.0.12:445 10.0.0.20:64636 ESTABLISHED 21470/smbd tcp 0 0 10.0.0.12:443 10.0.0.21:44198 ESTABLISHED 1400/nginx: worker tcp 0 0 10.0.0.12:2049 10.0.0.16:752 ESTABLISHED - tcp 0 0 10.0.0.12:222 10.0.0.21:55514 ESTABLISHED 23111/sshd: redacted tcp6 0 0 10.0.0.12:4243 10.0.0.20:64702 ESTABLISHED 31307/java tcp6 0 0 10.0.0.12:48932 162.222.40.93:443 ESTABLISHED 31307/java tcp6 0 0 10.0.0.12:49093 216.17.8.47:443 ESTABLISHED 31307/java 

PID 31307 – это механизм резервного копирования CrashPlan , версия Java 1.7.0_45 . Двумя адресами IPv4, не относящимися к RFC1918, являются серверы CrashPlan, а 10.0.0.20:64702 – это мой компьютер, на котором запущен клиент.

Почему последние три соединения отображаются как tcp6, даже если они являются адресами IPv4?

Это происходит потому, что по умолчанию сокеты AF_INET6 будут работать как для IPv4, так и для IPv6. См. Раздел 3.7 – Совместимость с узлами IPv4 RFC 3493 – Расширения базового разъема для IPv6

Вот краткий пример кода, который может создать такую ​​ситуацию:

 #include <stdio.h> #include <sys/socket.h> #include <netinet/in.h> #define TEST_PORT 5555 #define xstr(s) str(s) #define str(x) #x int main (int argc, char **argv) { int v6server; int v4client; int rc; struct sockaddr_in6 s6addr = { .sin6_family = AF_INET6, .sin6_flowinfo = 0, .sin6_port = htons(TEST_PORT), .sin6_addr = in6addr_any }; struct sockaddr_in c4addr = { .sin_family = AF_INET, .sin_port = htons(TEST_PORT), .sin_addr = inet_addr("127.0.0.1") }; // Open an IPv6 listener v6server = socket(AF_INET6, SOCK_STREAM, 0); if (v6server < 0) perror("socket()"); rc = bind(v6server, (struct sockaddr *)&s6addr, sizeof(s6addr)); if (rc != 0) perror("bind()"); rc = listen(v6server, 0); if (rc != 0) perror("listen()"); // Connect to the listener with an IPv4 socket v4client = socket(AF_INET, SOCK_STREAM, 0); if (v4client < 0) perror("socket()"); rc = connect(v4client, (struct sockaddr *)&c4addr, sizeof(c4addr)); if (rc != 0) perror("connect()"); // inspect open sockets system("netstat -tan | grep " xstr(TEST_PORT)); close(v4client); close(v6server); } 

Выходной сигнал на моей машине Ubuntu:

 $ make v4v6 cc v4v6.c -o v4v6 $ ./v4v6 tcp 0 0 127.0.0.1:46518 127.0.0.1:5555 ESTABLISHED tcp6 0 0 :::5555 :::* LISTEN tcp6 0 0 127.0.0.1:5555 127.0.0.1:46518 ESTABLISHED $ 
  • Запись tcp6 LISTEN предназначена для прослушивания сокета на порту 5555. Обратите внимание, что это сокет AF_INET6, поэтому он будет принимать входящие соединения IPv4 и IPv6.
  • Запись tcp ESTABLISHED является результатом подключения гнезда AF_INET4 к слушателю (активное соединение).
  • Запись tcp6 ESTABLISHED предназначена для пассивного соединения, порожденного из гнезда прослушивателя. Он отображается как tcp6 , так как он tcp6 прослушивателя tcp6 ; однако он представляет собой соединение с IPv4.

Стоит отметить следующее:

  • Такое поведение является особенным для сокетов AF_INET6. Сокеты AF_INET (IPv4) просто не могут и не будут обрабатывать ничего IPv6.
  • Это поведение может быть переопределено опцией сокета IPV6_V6ONLY . Установка этого параметра приведет к тому, что сокет будет обрабатывать только IPv6 и не допускать ничего IPv4.