2009-05-11 18 views
6

Piszę serwer TCP, który musi wiedzieć, z którego interfejsu pochodzi każde połączenie. Nie mogę użyć adresu/podsieci do określenia, który interfejs był używany, ponieważ mogą istnieć interfejsy o tych samych wartościach adres/podsieć. Jest oparty na systemie Linux i nie ma potrzeby, aby kod był przenośny.Jak mogę uzyskać nazwę interfejsu/indeks skojarzony z gniazdem TCP?

Wszystko, co mogłem znaleźć, to funkcje umożliwiające uzyskanie wszystkich interfejsów lub pojedynczy interfejs według indeksu. Nie mogłem znaleźć sposobu na połączenie interfejsu z akceptowanym gniazdem TCP.

Wszelkie pomysły? Coś, co przeoczyłem?

EDYCJA: Aby powtórzyć, adresy IP nie są unikalne w moim przypadku. Ani adresy docelowe (sam serwer), ani adresy źródłowe (klienci). Tak, jest to bardzo ekstremalny schemat IP.

+0

tylko poprawna odpowiedź tutaj, biorąc pod uwagę, że szukasz kodu do dodania do twojego serwera, jest tą z User1. Przetestowałem to i działa - powinieneś to zaakceptować. Zobacz także http: // stackoverflow.com/questions/43659634/find-the-interface-used-by-a-connected-socket/43663713 # 43663713 – EML

Odpowiedz

0

Sprawdź adres docelowy.

Każdy interfejs jest zwykle związany z unikalnym adresem. Jeśli wiele interfejsów jest połączonych ze sobą, prawdopodobnie nie ma znaczenia, którego użył.

Jedynym wyjątkiem od tej reguły jest użycie aniwastu ipv6, ale nawet wtedy normalnie nie ma wielu interfejsów na tym samym hoście z tym samym adresem IP.

+0

W tym przypadku interfejsy nie są powiązane z unikatowymi adresami. Nie są też związane. Są one podłączone do różnych sieci, chociaż mają nakładające się/identyczne adresy IP. –

+0

Jeśli masz połączenie TCP, musisz mieć adres IP. Nie musisz wiązać gniazda do konkretnego adresu, ale twój interfejs potrzebuje jednego. – JimB

+0

Tak, połączenie ma adres IP, ale nie jest unikatowe. Nie jest unikalny dla tego interfejsu i nie jest unikalny dla tej sieci. Naprawdę potrzebuję sposobu na uzyskanie interfejsu z samego gniazda. –

1

Myślę, że używając getsockname() po zaakceptowaniu() połączenie przychodzące może być tym, na co masz ochotę. Dwie funkcje getsockname() i getpeername() pobierają odpowiednio adres lokalny i zdalny, do którego przyłączone jest gniazdo. Oba powinny być ważne dla w pełni połączonego gniazda TCP.

Edycja: Choć wydaje się, że jest to prawdą dla OpenBSD, zgodnie ze stroną man, strona podręcznika linuksowego różni się znacznie i dlatego getsockname() po accept() w systemie Linux prawie na pewno nie jest użyteczne. Uczy mnie używać mojej pamięci zamiast sprawdzać wszystko. westchnienie

+0

faktycznie, próbowałem go i to rozwiązanie działa w systemie Linux i Windows – thang

+0

Złe "nazwa" - to zwraca adres * *. OP chce nazwy interfejsu, tj. eth0/etc. Dokumenty glibc mówią: "funkcje i symbole do obsługi adresów gniazd zostały nazwane niekonsekwentnie, czasami używając terminu" nazwa ", a czasami używając" adresu ". – EML

2

Tabela routingu jądra decyduje, który interfejs wysłać pakiet, stąd możliwość wiązania urządzeń. Rzut oka na "Linux Socket Programming, Warren W. Gay" sugeruje, że określenie interfejsu jest złe, a ze względu na dynamikę jądra (firewall, forwarding) jest bardziej skomplikowane.

Sugerowałbym zmianę twojego schematu IP tak, aby informacje IP wskazywały twój interfejs (y) poprzez wyszukiwanie w ten sam sposób, co ifconfig, w przeciwnym razie strzelasz sobie w mądry projekt stopy.

1) Uzyskać informacje IP z sesji TCP 2) Lookup który interfejs (y) może to być ważne dla

będę patrząc w API jądra choć. Nie powinieneś tego wiedzieć, abstrakcja istnieje z wielu dobrych powodów.

Extra Myśl Zastanawiając się nad tym, wydaje się, że jeśli oba interfejsy korzystać z tego samego adresu IP to musi istnieć różnica zakres routingu adres klienta (w przeciwnym razie byłyby stosowane zarówno interfejsy). Twój serwer może sprawdzić tabelę routingu na podstawie klienta IP

+0

Adresy IP klienta również mogą się nakładać. Różnica, która umożliwia nakładanie się tych adresów IP, jest identyfikatorem sieci VLAN. O ile wiem, jest to również niedostępne. –

+0

być może znalazłeś jedyną sytuację, w której nie możesz użyć dowodów poszlakowych do określenia interfejsu. Nie sądzę, że będzie jakiś sposób, ponieważ interfejs jest trochę abstrakcyjny z aplikacji wyższych niż jądro. Mógłbyś zmodyfikować swoje jądro, aby dostarczyć jakieś dane poprzez/proc/tcp_sessions_to_devs lub coś innego, ale to jest BARDZO - erm - zdesperowane? –

+0

Kolejna myśl. Próbuję zajrzeć do interfejsu wykrywającego biblioteki, których używa Wireshark (libpcap) i sprawdzić, w jaki sposób źródło iftop wylicza sesje na urządzeniu. Może być interesujące. –

4

Ogólnie, nie powinieneś wiedzieć, w jakim interfejsie będą wysyłane/odbierane pakiety; to jest zadanie tabeli routingu jądra. Trudno jest znaleźć interfejs dla gniazda, ponieważ tak naprawdę nie ma bezpośredniego powiązania. Routing pakietów może się zmienić w czasie życia gniazda na podstawie informacji o routingu.

Dla gniazd datagramowych (UDP) możesz używać getsockopt(s, IPPROTO_IP, IP_PKTINFO, ...); patrz getsockopt(2) i ip(7).

Dla gniazd strumieniowych (TCP) jedną opcją może być otwieranie wielu gniazd nasłuchujących, po jednym dla każdego interfejsu w systemie i używanie setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, ...) do wiązania każdego z jednym interfejsem; patrz setsockopt(2) i socket(7).

+0

Tak, ustawienie oddzielnego gniazda dla każdego interfejsu będzie działało, ale widząc jako dziesiątki interfejsów i są one rozwijane i usuwane dynamicznie, jest to coś, czego chcę uniknąć. –

0

Oczywiście nie coś co wyglądało na bardzo głęboko, nie mówiąc już próbował, to może być jeden na „tak szalony, że może działać tylko” kosz ...

Jeśli to będzie tylko dla naprawdę nigdy Linux, możesz napisać niestandardowy moduł netfilter, który śledzi przychodzące połączenia i notatki, do których interfejsu wchodzą, i zapisuje te informacje gdzieś, aby aplikacja serwera mogła je odczytać.

0

Propozycja Kierona na napisanie modułu netfiltra jest prawdopodobnie jednym ze sposobów wypróbowania, ale chciałbym powstrzymać się od napisania mojego pierwszego modułu jądra dla tego rozwiązania.

Wymyśliłem inną opcję użycia źródłowego NAT i tłumaczenia portu źródłowego połączenia w celu skorelowania ze źródłem połączenia. Mogę przypisać zakresy portów dla każdej sieci i sprawdzić to na serwerze. Jedyny problem polega na tym, że źródłowy NAT w iptables jest wykonywany w łańcuchu POSTROUTING i nie jestem pewien, czy jest używany dla połączeń akceptowanych przez ten host, więc może potrzebuję użyć innego serwera.

ma łatwych rozwiązań tutaj, szkoda, że ​​nie można uzyskać nazwę/indeks interfejsu z gniazdka ...

+0

Inną opcją netfiltra jest przekazywanie ruchu do dodatkowych lokalnych interfejsów, które zostały skonfigurowane z różnymi adresami IP. – Bell

0

dodaję kolejną odpowiedź i potencjalne rozwiązanie po przeglądając źródła Wireshark i iftop który wydają się mieć pośrednio podobną funkcjonalność.

Wygląda na to, że możesz użyć libpcap do sniffowania interfejsów. Zakładając, że możesz zidentyfikować unikalną część sesji TCP/IP, możesz śledzić ją do interfejsu, po prostu używając filtrów i śledzenia sesji.

Brak modułów jądra (i odgrywa ładne z wątków)

http://www.ex-parrot.com/pdw/iftop/ kilka prostych źródło mieć okiem na www.tcpdump.org/ dla libpcap

myślę, że będzie w stanie dopasować VLAN używając go również.

Również wireshark może być przydatny do debugowania. Mam nadzieję że to pomoże! Od tego czasu jest w moim mózgu.

+0

Tak, to brzmi jak inna opcja. Dzięki za wysiłek! –

2

Użyj adresu getsockname(), aby uzyskać adres IP lokalnego końca połączenia TCP. Następnie użyj getifaddrs() aby znaleźć odpowiedni interfejs:

struct sockaddr_in addr; 
struct ifaddrs* ifaddr; 
struct ifaddrs* ifa; 
socklen_t addr_len; 

addr_len = sizeof (addr); 
getsockname(sock_fd, (struct sockaddr*)&addr, &addr_len); 
getifaddrs(&ifaddr); 

// look which interface contains the wanted IP. 
// When found, ifa->ifa_name contains the name of the interface (eth0, eth1, ppp0...) 
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) 
{ 
    if (ifa->ifa_addr) 
    { 
     if (AF_INET == ifa->ifa_addr->sa_family) 
     { 
      struct sockaddr_in* inaddr = (struct sockaddr_in*)ifa->ifa_addr; 

      if (inaddr->sin_addr.s_addr == addr.sin_addr.s_addr) 
      { 
       if (ifa->ifa_name) 
       { 
        // Found it 
       } 
      } 
     } 
    } 
} 
freeifaddrs(ifaddr); 

powyżej jest po prostu brudny przykład, potrzebne są pewne modyfikacje:

  1. Dodaj brakuje weryfikacje Błąd wsparcia
  2. IPv6
+0

Testowałem to i to działa - dziękuję – EML

Powiązane problemy