2013-05-10 34 views
6

Eksperymentuję z gniazdami IPv6, w szczególności z funkcją "podwójnego stosu" oferowaną w systemie Windows Vista i nowszych wersjach, a najwyraźniej w systemie Unix domyślnie. Zauważyłem, że kiedy wiążę mój serwer z określonym adresem IP lub z rozpoznawaniem nazwy hosta mojego komputera lokalnego, nie mogę zaakceptować połączenia z klienta IPv4. Kiedy jednak połączę się z INADDR_ANY, mogę.Podłączanie klienta IPv4 do serwera IPv6: odmówiono połączenia

Proszę uwzględnić poniższy kod dla mojego serwera. Widać, że śledzę informacje Microsoftu tworzenia gniazdo IPv6, a następnie ustawienie flagi IPV6_V6ONLY do zera:

addrinfo* result, *pCurrent, hints; 

memset(&hints, 0, sizeof hints); // Must do this! 
hints.ai_family = AF_INET6; 
hints.ai_socktype = SOCK_STREAM; 
hints.ai_flags = AI_PASSIVE;  // We intend to use the addrinfo in a call to connect(). (I know it is ignored if we specify a server to connect to...) 

int nRet = getaddrinfo("powerhouse", "82", &hints, &result); 

SOCKET sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); 

int no = 0; 
if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&no, sizeof(no)) != 0) 
    return -1; 

if (bind(sock, result->ai_addr, result->ai_addrlen) == SOCKET_ERROR) 
    return -1; 

if (listen(sock, SOMAXCONN) == SOCKET_ERROR) 
    return -1; 

SOCKET sockClient = accept(sock, NULL, NULL); 

Oto kod dla mojego klienta. Można zobaczyć utworzyć gniazdo IPv4 i próbować połączyć się z serwerem:

addrinfo* result, *pCurrent, hints; 

memset(&hints, 0, sizeof hints); // Must do this! 
hints.ai_family = AF_INET; 
hints.ai_socktype = SOCK_STREAM; 

if (getaddrinfo("powerhouse", "82", &hints, &result) != 0) 
    return -1; 

SOCKET sock = socket(result->ai_family, result->ai_socktype, result->ai_protocol); 
int nRet = connect(sock, result->ai_addr, result->ai_addrlen); 

wynik z mojego connect rozmowy jest zawsze 10061: odmowa połączenia.

Jeśli zmienię mój kod serwera, aby powiązać się z :: (lub przekazać hosta NULL do getaddrinfo() (to samo)) i zmienić kod klienta, aby określić hosta NULL w wywołaniu getaddrinfo(), a następnie Klient V4 może połączyć się dobrze.

Czy ktoś może wyjaśnić, dlaczego? Nie czytałem niczego, co musimy określić hosta NULL (stąd używać INADDR_ANY), jeśli chcemy zachowanie dual-socket. Nie może to być wymagane, ponieważ mam hosta wieloadresowego i chcę akceptować IPv4 tylko na niektórych dostępnych adresach IP?

EDIT 15/05/2013:

To jest odpowiednia dokumentacja, która ma zdobyć mnie mylić, dlaczego mój kod nie powiedzie się:

Od Dual-Stack Sockets for IPv6 Winsock Applications

„Windows Vista i później oferuje możliwość utworzenia pojedynczego gniazda IPv6 , które może obsłużyć zarówno ruch IPv6, jak i IPv4. Na przykład, tworzone jest gniazdo nasłuchiwania TCP IP2 dla TCP , umieszczane w podwójnym stacku Tryb k, i zobowiązany do portu 5001. To gniazdo dual-stack może akceptować połączenia z klientów IPv6 TCP podłączeniu do portu 5001 i od klientów IPv4 TCP łączących do portu 5001. "

" Domyślnie gniazda IPv6 utworzone w systemie Windows Vista i późniejszym tylko działa przez protokół IPv6. Aby gniazdo IPv6 na było wyposażone w gniazdo z dwoma stosami, należy ustawić funkcję setsockopt za pomocą opcji gniazda IPV6_V6ONLY, aby ustawić tę wartość na zero, zanim gniazdo zostanie przypisane do adresu IP. Po ustawieniu opcji gniazda IPV6_V6ONLY na na zero, gniazdo utworzone dla rodziny adresów AF_INET6 może być używane do wysyłania i odbierania pakietów do iz adresu IPv6 lub adresu odwzorowanego IPv4 . (Kopalnia nacisk)”

+0

Potrzebujesz punktu końcowego IPv4 dla połączeń IPv4, "bind" jest jawnie punktem końcowym IPv6, a nie symbolem wieloznacznym. –

+0

@ Steve-o Nie rozumiem, co masz na myśli, przepraszam. Możesz wyjaśnić? – Wad

+0

Bindowanie implementuje filtr, aby akceptować tylko dane o adresie docelowym pasującym do powiązanego adresu. Pakiety IPv4 będą miały docelowy adres IPv4, dlatego zostaną odrzucone. –

Odpowiedz

7

IPv4 i IPv6 są dwa oddzielne protokoły Pakiety jednego protokołu nie mogą być obsługiwane za pomocą innego protokołu Dlatego koncepcja podwójnego stosu istnieje:.. System działa zarówno IPv4 jak i IPv6 stosy protokołów, ma zarówno adresy IPv4 i IPv6, itp.

Systemy operacyjne mają podstęp, w którym można mieć gniazdo IPv6, które nasłuchuje na wszystkich adresach IPv4 i IPv6.Nadal musisz mieć obie rodziny adresów na hoście i działa to tylko po powiązaniu z adresem wieloznacznym. Po związaniu tego gniazda ze stałym adresem, który już nie działa i działa tylko dla adresu, do którego się zobowiązałeś.

Jeśli chcesz słuchać na wszystkich dostępnych adresach, ustaw IPV6_V6ONLY na 0 i słuchaj na adresie wieloznacznym. Klienci IPv4 zostaną pokazani jako używający adresów IPv6 rozpoczynających się od ::ffff: z ostatnimi 32 bitami zawierającymi adres IPv4.

Aby połączyć się z określonymi adresami, potrzebne będą gniazda przypisane do każdego adresu, którego chcesz słuchać. Następnie należy użyć np. select(...), aby monitorować te gniazda i reagować na te, które stają się aktywne, ponieważ ktoś się z nimi łączy.

+0

Dziękuję za komentarz Sandera, twój komentarz ma sens. Czy możesz podać link do miejsca, w którym jest to udokumentowane - nie mogłem go znaleźć. Na przykład http://msdn.microsoft.com/en-us/library/windows/desktop/bb513665%28v=vs.85%29.aspx wydaje się, że jedyną rzeczą, którą musimy zrobić, to ustawić IPV6_V6ONLY na zero ... – Wad

+0

Oficjalna dokumentacja to RFC 3493: http://tools.ietf.org/html/rfc3493.html. Ale scenariusz ten nie jest tam opisany, ponieważ jest technicznie niemożliwy. Jeśli wiążesz serwer z adresem IPv6, klient może się połączyć tylko wtedy, gdy używa dokładnego adresu jako adresu docelowego. Klient tylko IPv4 nigdy nie może połączyć się z adresem IPv6, tak jak klient tylko IPv6 nie może połączyć się z adresem IPv4. Źródło i miejsce docelowe muszą zawsze mieć ten sam protokół. IPv6 nie jest wstecznie zgodny z IPv4 na drucie ... –

+0

Dzięki Sander. Wiążę się z adresami IPv6, tak, ale jak wspomniano powyżej do Steve-o poprzez ustawienie IPPROTO_IPV6, dokumentacja mówi, że moje gniazdo IPv6 może również akceptować klienta IPv4: adres IPv4 zostanie zamapowany na adres IPv6.Rzeczywiście, kiedy uruchamiam mój serwer i patrzę na TCPView, po wywołaniu setsockopt() widzę * dwa * powiązane gniazda na powerhouse, jeden z każdego protokołu IPv4 i IPv6. Dlatego nie mogę zrozumieć, dlaczego to się nie udaje ... – Wad

2

Ten link http://www.masterraghu.com/subjects/np/introduction/unix_network_programming_v1.3/ch12lev1sec2.html daje więcej informacji na temat połączenia IPv4 i IPv6,

większości wypadków dual-stack należy użyć następujących zasad w kontaktach z słuchania gniazd:

  • nasłuchiwania IPv4 gniazdo może akceptować połączenia przychodzące tylko od klientów IPv4.
  • Jeśli serwer posiada gniazdo IPv6 odsłuchu, który związany adres wieloznaczny i opcji gniazda IPV6_V6ONLY (rozdział 7.8) nie jest ustawiona, że gniazdo może przyjmować połączenia przychodzące zarówno z klientami IPv4 lub klientów IPv6. W przypadku połączenia z klienta IPv4, lokalny adres serwera dla tego połączenia będzie odpowiadał adresowi IPv6 przypisanemu protokołowi IPv4: .
  • Jeśli serwer posiada gniazdo słuchania IPv6, który związany adres IPv6 inny niż IPv4 odwzorowany adres IPv6, lub związał się wieloznaczny adres ale został ustawiony gniazda opcję IPv6_V6ONLY (rozdział 7,8), że gniazdo może przyjąć połączenia przychodzące tylko od klientów IPv6.