To pytanie jest prawie takie samo, jak wcześniej zadane Uzyskaj adres IP komputera lokalnego - Pytanie. Jednak muszę znaleźć adres (y) IP komputera z systemem Linux .
A więc: Jak - programowo w C ++ - wykryć adresy IP serwera linux, na którym działa moja aplikacja. Serwery będą miały co najmniej dwa adresy IP i potrzebuję konkretnego (tego w danej sieci (publicznej)).
Jestem pewien, że jest do tego prosta funkcja - ale gdzie?
Aby było trochę jaśniej:
- Serwer będzie miał oczywiście adres „localhost”: 127.0.0.1
- Serwer będzie miał wewnętrzny (zarządzający) adres IP: 172.16.xx
- Serwer będzie miał zewnętrzny (publiczny) adres IP: 80.190.xx
Muszę znaleźć zewnętrzny adres IP, aby powiązać z nim moją aplikację. Oczywiście mogę też powiązać się z INADDR_ANY (a właściwie to właśnie robię w tej chwili). Wolałbym jednak wykryć adres publiczny.
c++
linux
networking
ip-address
BlaM
źródło
źródło
ifconfig
,route
etc są przestarzałe dlaip
komendy. Teraz powinieneś tego użyć.Odpowiedzi:
Znalazłem problematyczne rozwiązanie ioctl na OS X (które jest zgodne z POSIX, więc powinno być podobne do Linuksa). Jednak getifaddress () pozwoli ci zrobić to samo z łatwością, działa dobrze dla mnie na os x 10.5 i powinno być takie samo poniżej.
Zrobiłem poniżej szybki przykład, który wyświetli cały adres IPv4 maszyny (powinieneś również sprawdzić, czy getifaddrs się powiodło, tj. Zwraca 0).
Zaktualizowałem, że pokazuje również adresy IPv6.
#include <stdio.h> #include <sys/types.h> #include <ifaddrs.h> #include <netinet/in.h> #include <string.h> #include <arpa/inet.h> int main (int argc, const char * argv[]) { struct ifaddrs * ifAddrStruct=NULL; struct ifaddrs * ifa=NULL; void * tmpAddrPtr=NULL; getifaddrs(&ifAddrStruct); for (ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next) { if (!ifa->ifa_addr) { continue; } if (ifa->ifa_addr->sa_family == AF_INET) { // check it is IP4 // is a valid IP4 Address tmpAddrPtr=&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr; char addressBuffer[INET_ADDRSTRLEN]; inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN); printf("%s IP Address %s\n", ifa->ifa_name, addressBuffer); } else if (ifa->ifa_addr->sa_family == AF_INET6) { // check it is IP6 // is a valid IP6 Address tmpAddrPtr=&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr; char addressBuffer[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN); printf("%s IP Address %s\n", ifa->ifa_name, addressBuffer); } } if (ifAddrStruct!=NULL) freeifaddrs(ifAddrStruct); return 0; }
źródło
tmpAddrPtr=&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
lo
iwlan0
interfejsy w moim przypadku. To, czego potrzebuję, towlan0
lub to,eth0
co ma połączenie z Internetem, więcstrcmp
czy powinienem użyć, czy jest lepszy sposób?ioctl(<socketfd>, SIOCGIFCONF, (struct ifconf)&buffer);
Przeczytaj
/usr/include/linux/if.h
informacje o strukturachifconf
iifreq
. Powinno to dać adres IP każdego interfejsu w systemie. Przeczytaj także/usr/include/linux/sockios.h
o dodatkowych ioctls.źródło
Podoba mi się odpowiedź jjvainio . Jak mówi Zan Lnyx, używa lokalnej tablicy routingu do znalezienia adresu IP interfejsu Ethernet, który byłby używany do połączenia z określonym hostem zewnętrznym. Korzystając z podłączonego gniazda UDP, można uzyskać informacje bez wysyłania żadnych pakietów. Takie podejście wymaga wybrania określonego hosta zewnętrznego. W większości przypadków każdy dobrze znany publiczny adres IP powinien załatwić sprawę. W tym celu podoba mi się adres publicznego serwera DNS Google 8.8.8.8, ale może się zdarzyć, że będziesz chciał wybrać inny zewnętrzny adres IP hosta. Oto kod, który ilustruje pełne podejście.
void GetPrimaryIp(char* buffer, size_t buflen) { assert(buflen >= 16); int sock = socket(AF_INET, SOCK_DGRAM, 0); assert(sock != -1); const char* kGoogleDnsIp = "8.8.8.8"; uint16_t kDnsPort = 53; struct sockaddr_in serv; memset(&serv, 0, sizeof(serv)); serv.sin_family = AF_INET; serv.sin_addr.s_addr = inet_addr(kGoogleDnsIp); serv.sin_port = htons(kDnsPort); int err = connect(sock, (const sockaddr*) &serv, sizeof(serv)); assert(err != -1); sockaddr_in name; socklen_t namelen = sizeof(name); err = getsockname(sock, (sockaddr*) &name, &namelen); assert(err != -1); const char* p = inet_ntop(AF_INET, &name.sin_addr, buffer, buflen); assert(p); close(sock); }
źródło
Jak już wiesz, nie ma czegoś takiego jak pojedynczy „lokalny adres IP”. Oto jak znaleźć lokalny adres, który można wysłać do określonego hosta.
źródło
Ma to tę zaletę, że działa na wielu odmianach unixa ... i można go zmodyfikować w trywialny sposób, aby działał na dowolnym systemie operacyjnym. Wszystkie powyższe rozwiązania dają mi błędy kompilatora w zależności od fazy księżyca. W chwili, gdy istnieje dobry sposób POSIX, aby to zrobić ... nie używaj tego (w czasie, gdy to było pisane, tak nie było).
// ifconfig | perl -ne 'print "$1\n" if /inet addr:([\d.]+)/' #include <stdlib.h> int main() { setenv("LANG","C",1); FILE * fp = popen("ifconfig", "r"); if (fp) { char *p=NULL, *e; size_t n; while ((getline(&p, &n, fp) > 0) && p) { if (p = strstr(p, "inet ")) { p+=5; if (p = strchr(p, ':')) { ++p; if (e = strchr(p, ' ')) { *e='\0'; printf("%s\n", p); } } } } } pclose(fp); return 0; }
źródło
ifconfig
. Powinieneś użyćip
teraz. Właściwy sposób generować moc języka neutralnego jest ustawienieLANG
zmiennej środowiskowej doC
, jak:LANG=C ls -l
Nie sądzę, aby istniała ostateczna prawidłowa odpowiedź na twoje pytanie. Zamiast tego istnieje wiele sposobów, jak zbliżyć się do tego, co chcesz. Dlatego podam kilka wskazówek, jak to zrobić.
Jeśli maszyna ma więcej niż 2 interfejsy (
lo
liczy się jako jeden), będziesz mieć problemy z łatwym automatycznym wykrywaniem właściwego interfejsu. Oto kilka przepisów, jak to zrobić.Na przykład problem polega na tym, że hosty znajdują się w strefie DMZ za firewallem NAT, który zmienia publiczny adres IP na prywatny i przekazuje żądania. Twój komputer może mieć 10 interfejsów, ale tylko jeden odpowiada publicznemu.
Nawet autodetekcja nie działa w przypadku podwójnego NAT, w którym zapora sieciowa tłumaczy nawet źródłowy adres IP na coś zupełnie innego. Nie możesz więc być nawet pewien, że trasa domyślna prowadzi do twojego interfejsu z interfejsem publicznym.
Wykryj go domyślną trasą
Coś jak
ip r get 1.1.1.1
zwykle mówi ci interfejs, który ma domyślną trasę.Jeśli chcesz odtworzyć to w swoim ulubionym języku skryptów / programowania, użyj
strace ip r get 1.1.1.1
żółtej cegły i postępuj zgodnie z nią.Ustaw to za pomocą
/etc/hosts
Możesz utworzyć wpis w
/etc/hosts
polubieniu80.190.1.3 publicinterfaceip
Następnie możesz użyć tego aliasu,
publicinterfaceip
aby odnieść się do swojego interfejsu publicznego.Korzystaj ze środowiska
Tak samo jak
/etc/hosts
. ale użyj do tego środowiska. Możesz spróbować/etc/profile
lub~/.profile
do tego.Dlatego jeśli twój program potrzebuje zmiennej
MYPUBLICIP
, możesz dołączyć kod taki jak (to jest C, możesz stworzyć z niego C ++):#define MYPUBLICIPENVVAR "MYPUBLICIP" const char *mypublicip = getenv(MYPUBLICIPENVVAR); if (!mypublicip) { fprintf(stderr, "please set environment variable %s\n", MYPUBLICIPENVVAR); exit(3); }
Możesz więc wywołać swój skrypt / program w
/path/to/your/script
ten sposóbMYPUBLICIP=80.190.1.3 /path/to/your/script
to działa nawet w
crontab
.Wylicz wszystkie interfejsy i wyeliminuj te, których nie chcesz
Jeśli wiesz, czego nie chcesz, możesz wyliczyć wszystkie interfejsy i zignorować wszystkie fałszywe.
Tutaj wydaje się już odpowiedź https://stackoverflow.com/a/265978/490291 dla tego podejścia.
Zrób to jak DLNA
Możesz spróbować wyliczyć wszystkie bramy UPnP w Twojej sieci iw ten sposób znaleźć właściwą trasę dla jakiejś „zewnętrznej” rzeczy. Może to być nawet na trasie, na którą nie wskazuje Twoja domyślna trasa.
Więcej na ten temat można znaleźć na stronie https://en.wikipedia.org/wiki/Internet_Gateway_Device_Protocol
Daje to dobre wrażenie, który z nich jest Twoim prawdziwym publicznym interfejsem, nawet jeśli domyślna trasa prowadzi gdzie indziej.
Jest ich jeszcze więcej
Routery IPv6 ogłaszają się, podając właściwy prefiks IPv6. Spojrzenie na prefiks daje wskazówkę, czy ma jakiś wewnętrzny adres IP, czy globalny.
Możesz nasłuchiwać ramek IGMP lub IBGP, aby znaleźć odpowiednią bramę.
Istnieje mniej niż 2 ^ 32 adresów IP. Dlatego w sieci LAN po prostu pingowanie ich wszystkich nie zajmuje dużo czasu. To daje Ci statystyczną wskazówkę, gdzie z Twojego punktu widzenia znajduje się większość Internetu. Jednak powinieneś być nieco bardziej rozsądny niż słynny https://de.wikipedia.org/wiki/SQL_Slammer
ICMP, a nawet ARP są dobrymi źródłami informacji o paśmie bocznym sieci. To też może ci pomóc.
Możesz użyć adresu rozgłoszeniowego Ethernet, aby skontaktować się ze wszystkimi urządzeniami infrastruktury sieciowej, które często pomagają, jak DHCP (nawet DHCPv6) i tak dalej.
Ta dodatkowa lista jest prawdopodobnie nieskończona i zawsze niekompletna, ponieważ każdy producent urządzeń sieciowych zajmuje się wymyślaniem nowych luk w zabezpieczeniach dotyczących automatycznego wykrywania własnych urządzeń. Co często bardzo pomaga w wykrywaniu publicznego interfejsu, którego nie powinno być.
- powiedział Nuff. Na zewnątrz.
źródło
W nawiązaniu do tego, co powiedział Steve Baker, opis SIOCGIFCONF ioctl można znaleźć na stronie podręcznika netdevice (7) .
Gdy masz już listę wszystkich adresów IP na hoście, będziesz musiał użyć logiki specyficznej dla aplikacji, aby odfiltrować adresy, których nie chcesz i mieć nadzieję, że został Ci jeden adres IP.
źródło
Nie koduj tego na sztywno: to może się zmienić. Wiele programów wymyśla, z czym się wiązać, czytając plik konfiguracyjny i robiąc wszystko, co mówi. W ten sposób, jeśli Twój program w przyszłości będzie musiał powiązać się z czymś, co nie jest publicznym adresem IP, może to zrobić.
źródło
Ponieważ pytanie określa Linuksa, moją ulubioną techniką odkrywania adresów IP maszyny jest użycie netlink. Tworząc gniazdo netlink protokołu NETLINK_ROUTE i wysyłając RTM_GETADDR, Twoja aplikacja otrzyma wiadomość (y) zawierające wszystkie dostępne adresy IP. Przykładem warunkiem jest tutaj .
Aby uprościć część obsługi wiadomości, wygodna jest biblioteka libmnl. Jeśli chcesz dowiedzieć się więcej o różnych opcjach NETLINK_ROUTE (i o tym, jak są one analizowane), najlepszym źródłem jest kod źródłowy iproute2 (zwłaszcza aplikacja monitorująca), a także funkcje odbierające w jądrze. Strona podręcznika rtnetlink również zawiera przydatne informacje.
źródło
Integrację z curl można przeprowadzić w tak prosty sposób, jak:
curl www.whatismyip.org
z powłoki dostaniesz globalne IP. Jesteś w pewnym sensie zależny od zewnętrznego serwera, ale zawsze będziesz za NATem.źródło
Eleganckie rozwiązanie oparte na skryptach dla systemu Linux można znaleźć pod adresem : http://www.damnsmalllinux.org/f/topic-3-23-17031-0.html
źródło
// Use a HTTP request to a well known server that echo's back the public IP address void GetPublicIP(CString & csIP) { // Initialize COM bool bInit = false; if (SUCCEEDED(CoInitialize(NULL))) { // COM was initialized bInit = true;
// Create a HTTP request object MSXML2::IXMLHTTPRequestPtr HTTPRequest; HRESULT hr = HTTPRequest.CreateInstance("MSXML2.XMLHTTP"); if (SUCCEEDED(hr)) { // Build a request to a web site that returns the public IP address VARIANT Async; Async.vt = VT_BOOL; Async.boolVal = VARIANT_FALSE; CComBSTR ccbRequest = L"http://whatismyipaddress.com/"; // Open the request if (SUCCEEDED(HTTPRequest->raw_open(L"GET",ccbRequest,Async))) { // Send the request if (SUCCEEDED(HTTPRequest->raw_send())) { // Get the response CString csRequest = HTTPRequest->GetresponseText(); // Parse the IP address CString csMarker = "<!-- contact us before using a script to get your IP address -->"; int iPos = csRequest.Find(csMarker); if (iPos == -1) return; iPos += csMarker.GetLength(); int iPos2 = csRequest.Find(csMarker,iPos); if (iPos2 == -1) return; // Build the IP address int nCount = iPos2 - iPos; csIP = csRequest.Mid(iPos,nCount); } } } } // Unitialize COM if (bInit) CoUninitialize();
}
źródło