ios 客戶端IPV6網路超時等相關問題

PeachRain發表於2019-03-12

1、前序

首先,眾所周知的原因,蘋果從很早開始都要求開發者必須支援IPV6,剛開始由於更重原因,改造還是很慢,無論是作為開發者的我們還是運營商,但是隨著時間的推進,企業和行動通訊供應商在逐步部署IPv6 DNS64/NAT64網路。IPv6DNS64/NAT64是一個僅有IPv6的網路,且能通過轉換繼續支援IPv4。同時4G的普及,由於IPv4地址的限制,為了保證4G開發的擴充套件性,需要IPv6的支援。當然目前國內的三大運營商,他們的IPV6的發展規模現在各不相同。

通過這裡就瞭解到了一些前提。這邊再說下DNS64/NAT64轉換流程

DNS64/NAT64轉換流程

參考Apple官方文件 Supporting IPv6 DNS64/NAT64 Networks

ios 客戶端IPV6網路超時等相關問題

ios 客戶端IPV6網路超時等相關問題
運營商為了相容問題,大多數的網路供應商實現了一個叫DNS64/NAT64的轉換流程。這是個純IPv6網路,並通過轉換也可繼續訪問IPv4的內容。這樣能保證所有的網路都能訪問。上圖: 就是目前運營商的一個標準解析流程

如果客戶端向DNS64伺服器發起一個DNS查詢,當DNS找到一個基於IPv6的地址後,立刻返回客戶端。如果無法找到對應的IPv6地址,DNS64伺服器將請求IPv4地址,然後DNS64伺服器將IPv4作為字首合成一個IPv6地址,並且將其返回給客戶端。這樣,客戶端將總是獲得一個IPv6目標地址.詳細流程見下圖

ios 客戶端IPV6網路超時等相關問題

而對於客戶端如果本地網路同時提供IPv4和IPv6的環境時(稱為雙棧),訪問IPv4伺服器就直接建立IPv4 socket進行連結。

如果本地網路只提供了IPv6環境(稱為IPv6-Only),訪問IPv4伺服器時就先轉成IPv6地址,閘道器收到後再由NAT64服務解析成IPv4,走後面的IPv4網路。

2.由bug產生的思考?

bug:有客戶反應,在4G狀態下,上傳圖片會超時失敗,但是在WiFi下沒有這個問題。

非常奇怪的bug,因為經過我不停的復現就是復現不了,但是客戶的客戶端確實必現的,在嘗試努力了幾次後,險些放棄查詢原因,認為是正常的網路不穩定的錯誤,但是在後期,陸續又有客戶反饋有類似問題,而且都是在4G的網路下。通過本地日誌檢視,除錯,大致猜測問題可能和IPV6網路有關。

3.突破口

真的是運氣,在某個陰鬱天氣的日子,我又通過自己的手機嘗試了下問題,問題復現,而且是必現,公司其他同事的手機都沒問題,只有我的手機問題復現,哈哈哈哈,立刻開啟電腦,開啟xcode進行除錯操作,發現問題所在是在底層的socket連線庫的問題。靈機一動,把sim卡放入其他人的手機嘗試,問題復現。。。看來問題和運營商也有關係。

問題定位到socket IPV6連線error錯誤。嘗試驗證自己的想法。 驗證手機的網路地址

手機訪問檢測網站

問題復現的sim卡:

ios 客戶端IPV6網路超時等相關問題

正常沒有該問題的sim卡:

ios 客戶端IPV6網路超時等相關問題

確實問題和IPV6網路有關。證實。

4.bug排故

通過除錯發現,在判斷網路時,該sim卡網路被判斷為IPV6-only,但是實際上是IPV^/IPV4雙棧的網路。發現原來是判斷網路的方法實現有問題

int32 getdefaultgateway(in_addr* addr) 判斷是否支援ipv4
複製程式碼

百度或者谷歌能搜到很多相關實現 例如:

int getdefaultgateway(in_addr_t * addr)
{
    int mib[] = {CTL_NET, PF_ROUTE, 0, AF_INET,
        NET_RT_FLAGS, RTF_GATEWAY};
    size_t l;
    char * buf, * p;
    struct rt_msghdr * rt;
    struct sockaddr * sa;
    struct sockaddr * sa_tab[RTAX_MAX];
    int i;
    int r = -1;
    if(sysctl(mib, sizeof(mib)/sizeof(int), 0, &l, 0, 0) < 0) {
        return -1;
    }
    if(l>0) {
        buf = malloc(l);
        if(sysctl(mib, sizeof(mib)/sizeof(int), buf, &l, 0, 0) < 0) {
            return -1;
        }
        for(p=buf; p<buf+l; p+=rt->rtm_msglen) {
            rt = (struct rt_msghdr *)p;
            sa = (struct sockaddr *)(rt + 1);
            for(i=0; i<RTAX_MAX; i++) {
                if(rt->rtm_addrs & (1 << i)) {
                    sa_tab[i] = sa;
                    sa = (struct sockaddr *)((char *)sa + ROUNDUP(sa->sa_len));
                } else {
                    sa_tab[i] = NULL;
                }
            }

            if( ((rt->rtm_addrs & (RTA_DST|RTA_GATEWAY)) == (RTA_DST|RTA_GATEWAY))
               && sa_tab[RTAX_DST]->sa_family == AF_INET
               && sa_tab[RTAX_GATEWAY]->sa_family == AF_INET) {


                if(((struct sockaddr_in *)sa_tab[RTAX_DST])->sin_addr.s_addr == 0) {
                    char ifName[128];
                    if_indextoname(rt->rtm_index,ifName);

                    if(strcmp("en0",ifName)==0){

                        *addr = ((struct sockaddr_in *)(sa_tab[RTAX_GATEWAY]))->sin_addr.s_addr;
                        r = 0;
                    }
                }
            }
        }
        free(buf);
    }
    return r;
}
複製程式碼

為什麼明明支援IPV6但是判斷為IPV4呢。深入方法除錯檢視

ios 客戶端IPV6網路超時等相關問題

關鍵就是en0的判斷,en0表示的是wifi,也就是說只有在wifi狀態下才能進入if邏輯中,4G狀態下是進入不了判斷語句的。

嘗試把此判斷去除,問題解決。。。 問題排除

此處可以進行兜底邏輯,如果是IPV^-only的地址,進行是否可以合成IPV6的判斷。如果運營商支援IPV6-only就一定有IPV4合成IPV6的能力,用於支援IPV4。如果是雙棧就不用去合成IPV6地址,可以直接走IPV4地址。

相關文章