docker發現端口是tcp6的 致使沒法訪問前端

原文: docker發現端口是tcp6的 致使沒法訪問前端

最近偶爾發現一個比較奇怪的現象,netstat 查看監聽的服務端口時,卻只顯示了 tcp6 的監控, 可是服務明明是能夠經過 tcp4 的 ipv4 地址訪問的,那爲何沒有顯示 tcp4 的監聽呢?php

以 sshd 監聽的 22 端口爲例:html

# netstat -tlnp | grep :22
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      1444/sshd
tcp6       0      0 :::22                   :::*                    LISTEN      1444/sshd

能夠看到,netstat 顯示錶示 sshd 既監聽在 ipv4 的地址,又監聽在 ipv6 的地址。前端

而再看看 httpd 進程:linux

# netstat -tlnp | grep :80
tcp6       0      0 :::80                   :::*                    LISTEN      19837/httpd

卻發現只顯示了監聽在 ipv6 的地址上 ,可是,經過 ipv4 的地址明明是能夠訪問訪問的。docker

下面來看下怎樣解釋這個現象。app

首先,關閉 ipv6 而且重啓 httpd:ssh

# sysctl net.ipv6.conf.all.disable_ipv6=1
# systemctl restart httpd

如今,看下 httpd 監聽的地址:socket

# netstat -tlnp | grep :80
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      33697/httpd

能夠看到,已經只監聽到 ipv4 地址了。tcp

那爲何在 ipv6 開啓的時候,netstat 只顯示了 tcp6 的監聽而非像 sshd 那樣既顯示 tcp 又顯示 tcp6 的監聽呢?函數

咱們下載 httpd 的源碼看一看,在代碼 server/listen.c 的 open_listeners() 函數中, 有相關注釋:

/* If we have the unspecified IPv4 address (0.0.0.0) and
 * the unspecified IPv6 address (::) is next, we need to
 * swap the order of these in the list. We always try to
 * bind to IPv6 first, then IPv4, since an IPv6 socket
 * might be able to receive IPv4 packets if V6ONLY is not
 * enabled, but never the other way around.
 * ... 省略 ...
 */

上面提到,ipv6 其實是能夠處理 ipv4 的請求的當 V6ONLY 沒有開啓的時候,反之否則; 那麼 V6ONLY 是在何時開啓呢?

繼續 follow 代碼到 make_sock() 函數,能夠發現以下代碼:

#if APR_HAVE_IPV6
#ifdef AP_ENABLE_V4_MAPPED
    int v6only_setting = 0;
#else
    int v6only_setting = 1;
#endif
#endif

在這個函數中,能夠看到若是監聽的地址是 ipv6,那麼會去設置 IPV6_V6ONLY 這個 socket 選項, 如今,關鍵是看 AP_ENABLE_V4_MAPPED 是怎麼定義的。

在 configure(注意,若是是直接經過代碼數獲取的,可能沒有這個文件,而只有 configure.ac/in 文件)文件中, 能夠找到:

# Check whether --enable-v4-mapped was given.
if test "${enable_v4_mapped+set}" = set; then :
  enableval=$enable_v4_mapped;
  v4mapped=$enableval

else

    case $host in
    *freebsd5*|*netbsd*|*openbsd*)
        v4mapped=no
        ;;
    *)
        v4mapped=yes
        ;;
    esac
    if ap_mpm_is_enabled winnt; then
                v4mapped=no
    fi

fi


if test $v4mapped = "yes" -a $ac_cv_define_APR_HAVE_IPV6 = "yes"; then

$as_echo "#define AP_ENABLE_V4_MAPPED 1" >>confdefs.h

因此,在 Linux 中,默認狀況下,AP_ENABLE_V4_MAPPED 是 1,那麼 httpd 就會直接監聽 ipv6, 由於此時 ipv6 的 socket 可以處理 ipv4 的請求;另外,bind() 系統調用會對用戶空間的進程透明處理 ipv6 沒有開啓的狀況,此時會監聽到 ipv4。

而若是咱們在編譯 httpd 的時候使用 --disable-v4-mapped 參數禁止 ipv4 mapped,那麼默認狀況下, httpd 會分別監聽在 ipv4 和 ipv6,而非只監聽 ipv6,以下所示:

# netstat -tlnp | grep :80
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      40576/httpd
tcp6       0      0 :::80                   :::*                    LISTEN      40576/httpd

而,若是在 /etc/httpd/conf/httpd.conf 中將 Listen 設置爲只監聽 ipv6 地址,以下:

Listen :::80

那麼,將能夠看到 netstat 只顯示 tcp6 的監聽:

# systemctl restart httpd
# netstat -tlnp | grep :80
tcp6       0      0 :::80                   :::*                    LISTEN      40980/httpd

而且,你會發現如今不能經過 ipv4 地址訪問 httpd 了。

# telnet 192.168.1.100 80
Trying 192.168.1.100...
telnet: Unable to connect to remote host: Connection refused

因此,netstat 只是很真實的顯示監聽的端口而已,可是須要注意 ipv6 實際上在 Linux 上也支持 ipv4。

相關文章
相關標籤/搜索