2014-12-11 52 views
0

我正在嘗試編寫一個將任意UDP數據包轉換爲自定義協議並返回的透明代理。我試圖使用透明代理來讀取需要轉換的傳入UDP數據包,並寫入剛剛被反向轉換的傳出UDP數據包。MSG_PROXY無法爲透明代理提供/指定備用地址

我對我使用UDP套接字的兩種口味的插座設置如下:

static int 
setup_clear_sock(uint16_t proxy_port) 
{ 
    struct sockaddr_in saddr; 
    int sock; 
    int val = 1; 
    socklen_t ttllen = sizeof(std_ttl); 

    sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); 
    if (sock < 0) 
    { 
     perror("Failed to create clear proxy socket"); 
     return -1; 
    } 
    if (getsockopt(sock, IPPROTO_IP, IP_TTL, &std_ttl, &ttllen) < 0) 
    { 
     perror("Failed to read IP TTL option on clear proxy socket"); 
     return -1; 
    } 
    if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < 0) 
    { 
     perror("Failed to set reuse address option on clear socket"); 
     return -1; 
    } 
    if (setsockopt(sock, IPPROTO_IP, IP_TRANSPARENT, &val, sizeof(val)) < 0) 
    { 
     perror("Failed to set transparent proxy option on clear socket"); 
     return -1; 
    } 
    saddr.sin_family = AF_INET; 
    saddr.sin_port = htons(proxy_port); 
    saddr.sin_addr.s_addr = INADDR_ANY; 
    if (bind(sock, (struct sockaddr *) &saddr, sizeof(saddr)) < 0) 
    { 
     perror("Failed to bind local address to clear proxy socket"); 
     return -1; 
    } 

    return sock; 
} 

我有兩個不同的,但可能相關的問題。首先,當我從這個套接字讀取傳入的UDP數據包,使用此代碼:

struct sock_double_addr_in 
{ 
    __SOCKADDR_COMMON (sin_); 
    in_port_t sin_port_a; 
    struct in_addr sin_addr_a; 
    sa_family_t sin_family_b; 
    in_port_t sin_port_b; 
    struct in_addr sin_addr_b; 
    unsigned char sin_zero[sizeof(struct sockaddr) - __SOCKADDR_COMMON_SIZE - 8 
          - sizeof(struct in_addr) - sizeof(in_port_t)]; 
}; 


void 
handle_clear_sock(void) 
{ 
    ssize_t rcvlen; 
    uint16_t nbo_udp_len, coded_len; 
    struct sockaddr_in saddr; 
    struct sock_double_addr_in sdaddr; 
    bch_coding_context_t ctx; 
    socklen_t addrlen = sizeof(sdaddr); 

    rcvlen = recvfrom(sock_clear, &clear_buf, sizeof(clear_buf), 
         MSG_DONTWAIT | MSG_PROXY, 
         (struct sockaddr *) &sdaddr, &addrlen); 
    if (rcvlen < 0) 
    { 
     perror("Failed to receive a packet from clear socket"); 
     return; 
    } 

    .... 

我沒有看到一個目的地址回來sdaddr。 sin_family_b,sin_addr_b和sin_port_b字段全部爲零。我在gdb中完成了塊結構的內存轉儲,實際上字節從內核回到零(這在我的結構定義中並不是一個錯誤的位置)。

爲了測試的目的,通過硬編碼一個固定的IP地址和端口來臨時解決這個問題,我可以調試我的代理應用程序的其餘部分,直到我發送一個剛剛被反向轉換的傳出UDP數據包。使用此代碼會發生這種情況:

.... 

    udp_len = ntohs(clear_buf.u16[2]); 
    if (udp_len + 6 > decoded_len) 
     fprintf(stderr, "Decoded fewer bytes (%u) than outputting in clear " 
         "(6 + %u)!\n", decoded_len, udp_len); 

    sdaddr.sin_family = AF_INET; 
    sdaddr.sin_port_a = clear_buf.u16[0]; 
    sdaddr.sin_addr_a.s_addr = coded_buf.u32[4]; 
    sdaddr.sin_family_b = AF_INET; 
    sdaddr.sin_port_b = clear_buf.u16[1]; 
    sdaddr.sin_addr_b.s_addr = coded_buf.u32[3]; 
    if (sendto(sock_clear, &(clear_buf.u16[3]), udp_len, MSG_PROXY, 
       (struct sockaddr *) &sdaddr, sizeof(sdaddr)) < 0) 
     perror("Failed to send a packet on clear socket"); 
} 

並且數據包從不出現。我檢查了我構建的sdaddr結構的全部內容,並且所有字段都很好看。 UDP有效負載數據看起來不錯。從sendto()系統調用返回沒有錯誤 - 實際上,它返回零。數據包從來沒有出現在wireshark中。

那麼我的透明代理髮生了什麼?我如何得到這個工作? (FWIW:開發主機是一個通用的x86_64 ubuntu 14.04 LTS盒。)謝謝!

回答

0

好的,所以我有一半的答案。

事實證明,如果我只是使用RAW IP套接字,打開IP_HDRINCL選項,並在完整IP標頭的用戶空間中構建傳出UDP數據包,則內核將遵守非本地源地址併發送這樣的數據包。

爲了達到這個目的,我現在使用了第三個套接字sock_output,並且解碼的UDP數據包正確地出來了。 (有趣的一面是:UDP校驗和字段必須爲零或正確的校驗和值,否則會導致內核無聲地丟棄數據包,並且永遠不會看到它發生。內核不會填充正確的校驗和如果你把它清零,但是如果你指定它,它將會驗證它是正確的。不用發送有故意校驗和的UDP這種方式。)

所以問題的前半部分仍然是:當我讀到數據包從sock_clear與MSG_PROXY標誌recvfrom(),爲什麼我沒有得到實際的目標地址在sdaddr的後半部分?