Makinenin IP adresini alın


96

Bu Soru, daha önce sorulan Yerel bilgisayarın IP Adresini Al -Soru ile hemen hemen aynıdır . Ancak, bir Linux Makinesinin IP adreslerini bulmam gerekiyor .

Yani: C ++ 'da programlı olarak uygulamamın üzerinde çalıştığı linux sunucusunun IP adreslerini nasıl tespit ederim. Sunucular en az iki IP adresine sahip olacak ve belirli bir IP adresine ihtiyacım var (belirli bir ağdaki (genel olan)).

Eminim bunu yapmanın basit bir işlevi vardır - ama nerede?


İşleri biraz daha netleştirmek için:

  • Sunucu açıkça "localhost" a sahip olacaktır: 127.0.0.1
  • Sunucunun dahili (yönetim) bir IP adresi olacaktır: 172.16.xx
  • Sunucunun harici (genel) bir IP adresi olacaktır: 80.190.xx

Uygulamamı ona bağlamak için harici IP adresini bulmam gerekiyor. Açıkçası INADDR_ANY'ye de bağlanabilirim (ve aslında şu anda yaptığım şey bu). Yine de genel adresi tespit etmeyi tercih ederim.


3
Neden ifconfig'in popenini işaretleyelim? Gerçekten yapılacak doğru şey bu. Kitaplıklar değişir, ancak ifconfig ve popen her zaman orada olacaktır.
Erik Aronesty

3
Hayır, ifconfig, routevb kaldırılıyor ipkomutu. Şimdi bunun yerine onu kullanmalısın.
Anders

ifconfig bugün hala diğer yaklaşımlardan daha fazla mimari üzerinde çalışmaktadır. Bu yüzden ip komutuna değiştirin. Ne zaman / uygunsa. popen hala daha iyi bir çözüm.
Erik Aronesty

Yanıtlar:


107

İoctl çözümünü os x'te sorunlu buldum (POSIX uyumlu, bu yüzden linux'a benzer olmalı). Ancak getifaddress () aynı şeyi kolayca yapmanıza izin verir, os x 10.5'te benim için iyi çalışıyor ve aşağıda aynı olması gerekir.

Aşağıda, makinenin tüm IPv4 adresini yazdıracak hızlı bir örnek yaptım (ayrıca getifaddrs'ın başarılı olup olmadığını kontrol etmelisiniz, yani 0 döndürür).

Güncelledim, IPv6 adreslerini de göster.

#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;
}

7
+1 ve bu sıkıcı ioctl'ler yerine bu güzel yaklaşımı getirdiğin için teşekkürler! Bununla birlikte, IP6'yı ele aldığınız hala yanlış - sockaddr_in6'ya çevirmelisiniz, örneğintmpAddrPtr=&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
Andrey

2
@Andrey, teşekkürler. Cevabımı güncelledim (yine de test etme şansım
olmadı

2
benim durumumda verir love wlan0arayüzler. İhtiyacım olan şey wlan0ya eth0da internet bağlantısı olan, yani kullanmalı mıyım strcmpyoksa daha iyi bir yol mu?
Sudip Bhattarai

28
  1. Bir soket oluşturun.
  2. Performans ioctl(<socketfd>, SIOCGIFCONF, (struct ifconf)&buffer);

Ve yapıları hakkında /usr/include/linux/if.hbilgi için okuyun . Bu size sistemdeki her arayüzün IP adresini vermelidir. Ayrıca ek ioctl'leri de okuyun .ifconfifreq/usr/include/linux/sockios.h


Ben sadece bağlı olduğu arayüzün adresini vereceği izlenimine kapılmıştım.
Matt Joiner

@MattJoiner Hayır, man sayfasına bir bakın, SIOCGIFCONF tüm arayüzler hakkında bilgi verir. Burada alıntı yapılan kılavuz sayfasının bir kısmı: "Arayüz (taşıma katmanı) adreslerinin bir listesini döndür. Bu, şu anda uyumluluk için yalnızca AF_INET (IPv4) ailesinin adresleri anlamına gelir. Kullanıcı, ioctl'ye argüman olarak bir ifconf yapısını iletir. ifc_req'teki bir ifreq yapıları dizisine işaretçi ve ifc_len'deki bayt cinsinden uzunluğu. Çekirdek, ifreq'leri çalıştıran tüm mevcut L3 arayüz adresleriyle doldurur "
Stéphane

26

Jjvainio'nun cevabını beğendim . Zan Lnyx'in dediği gibi, belirli bir harici ana bilgisayara bağlantı için kullanılacak ethernet arayüzünün IP adresini bulmak için yerel yönlendirme tablosunu kullanır. Bağlı bir UDP soketi kullanarak, gerçekte herhangi bir paket göndermeden bilgileri alabilirsiniz. Yaklaşım, belirli bir harici ana bilgisayar seçmenizi gerektirir. Çoğu zaman, iyi bilinen herhangi bir genel IP hile yapmalıdır. Bu amaçla Google'ın genel DNS sunucusu adresi 8.8.8.8'i beğeniyorum, ancak farklı bir harici ana bilgisayar IP'si seçmek isteyebileceğiniz zamanlar olabilir. İşte tam yaklaşımı gösteren bazı kodlar.

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);
}

2
bu bana özel ip 127. *. *. * verir. belki NAT'ın arkasında olduğum için?
eugene

14

Sizin de fark ettiğiniz gibi, tek bir "yerel IP adresi" diye bir şey yoktur. Belirli bir ana bilgisayara gönderilebilecek yerel adresi nasıl bulacağınız aşağıda açıklanmıştır.

  1. Bir UDP soketi oluşturun
  2. Soketi bir dış adrese bağlayın (sonunda yerel adresi alacak olan ana bilgisayar)
  3. Yerel adresi almak için getsockname kullanın

Bunu hiç böyle yapmadım ama hoşuma gitti. İşletim sistemi yönlendirme tablosunu otomatik olarak kullanır ve arayüz listesini taramaktan çok daha kolaydır.
Zan Lynx

5
Bu, dış adresin ne olacağını bildiğinizi varsayar. Veri merkezlerinde genellikle durum böyle değildir.
Christopher

Her zaman farklı kıtalara atanmış olan üç kişiyi deneyebilir (IANA bunu kolaylaştırır) ve üçte ikisini en iyi şekilde kabul edebilirsiniz.
Joshua

9

Bunun birçok unix çeşidi üzerinde çalışma avantajı vardır ... ve herhangi bir işletim sistemi üzerinde çalışmak için onu önemsiz bir şekilde değiştirebilirsiniz. Yukarıdaki tüm çözümler bana ayın evresine bağlı olarak derleyici hataları veriyor. Bunu yapmanın iyi bir POSIX yolu olduğu an ... bunu kullanmayın (bu yazıldığı sırada durum böyle değildi).

// 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;
}

bu çözüm için teşekkür ederim, benim için harika çalışıyor ve sadece cl programlarından biraz veri almak istediğinizde birçok durumda çok kullanışlı oluyor
zitroneneis

İfconfig gerekli verileri çıktılarsa da, herhangi bir programı bu şekilde kullanmayı kesinlikle önermiyorum. İfconfig'in kaynağına gerçekten girmek ve işlevselliği elde etmek, çalıştırıp çıktısını ayrıştırmaktan çok daha iyidir.
Zaur Nasibov

1
@MartinMeeser: "inet" ve ardından ":" ayrı ayrı arayarak dili nötr hale getirdi.
Erik Aronesty

1
O zaman Latin temelli karakter seti bile olmayan başka dilleriniz var. Çözüm, kullanımdan kaldırılan komut araçlarını kullanmamaktır ifconfig. ipŞimdi kullanmalısın . Uygun dil nötr çıktı oluşturmak yolu seti etmektir LANGiçin ortam değişkeni Cgibi:LANG=C ls -l
Anders

1
Teşekkürler. Kolay ve harika bir çözüm!
Ahmad Afkandeh

5

Sorunuzun kesin bir doğru cevabı olduğunu sanmıyorum. Bunun yerine, dilediğiniz şeye nasıl yaklaşacağınıza dair büyük bir yol var. Bu nedenle, nasıl yapılacağına dair bazı ipuçları vereceğim.

Bir makinenin 2'den fazla arabirimi varsa (bir olarak losayılır), doğru arabirimi kolayca otomatik olarak algılamakta sorun yaşarsınız. İşte nasıl yapılacağına dair bazı tarifler.

Örneğin sorun, ana bilgisayarların genel IP'yi bazı özel IP'ye değiştiren ve istekleri ileten bir NAT güvenlik duvarının arkasındaki bir DMZ'de olmalarıdır. Makinenizde 10 arabirim olabilir, ancak yalnızca biri genel olana karşılık gelir.

Hatta otomatik algılama bile çalışmaz, eğer güvenlik duvarınız kaynak IP'yi tamamen farklı bir şeye çevirir. Bu nedenle, varsayılan yolun ortak bir arayüzle arayüzünüze gittiğinden bile emin olamazsınız.

Varsayılan rota üzerinden tespit et

Bu, şeyleri otomatik olarak algılamak için önerdiğim yol

Bunun gibi bir şey ip r get 1.1.1.1genellikle size varsayılan rotaya sahip arayüzü söyler.

Bunu en sevdiğiniz komut dosyası / programlama dilinde yeniden oluşturmak istiyorsanız strace ip r get 1.1.1.1, sarı tuğlalı yolu kullanın ve izleyin.

İle ayarlayın /etc/hosts

Kontrolü elinde tutmak istiyorsan bu benim tavsiyem

Sen bir girişi oluşturabilir /etc/hostsgibi

80.190.1.3 publicinterfaceip

Ardından publicinterfaceip, genel arayüzünüze başvurmak için bu takma adı kullanabilirsiniz .

Ne yazık ki haproxybu hileyi IPv6 ile karıştırmıyor

Çevreyi kullanın

Olmamanız /etc/hostsdurumunda bu iyi bir çözümdür.root

Aynı /etc/hosts. ama bunun için ortamı kullanın. Deneyebilirsin /etc/profileya da bunun ~/.profileiçin.

Dolayısıyla, programınızın bir değişkene ihtiyacı MYPUBLICIPvarsa, aşağıdaki gibi bir kod ekleyebilirsiniz (bu C, ondan C ++ oluşturmaktan çekinmeyin):

#define MYPUBLICIPENVVAR "MYPUBLICIP"

const char *mypublicip = getenv(MYPUBLICIPENVVAR);

if (!mypublicip) { fprintf(stderr, "please set environment variable %s\n", MYPUBLICIPENVVAR); exit(3); }

Eğer komut / programı çağırabilir Yani /path/to/your/scriptböyle

MYPUBLICIP=80.190.1.3 /path/to/your/script

bu bile işe yarar crontab.

Tüm arayüzleri numaralandırın ve istemediklerinizi ortadan kaldırın

Çaresiz yol kullanamazsan ip

Neyi istemediğinizi biliyorsanız, tüm arayüzleri numaralandırabilir ve tüm yanlış olanları göz ardı edebilirsiniz.

Burada zaten bu yaklaşım için bir cevap https://stackoverflow.com/a/265978/490291 gibi görünüyor .

DLNA gibi yap

Kendini alkole boğmaya çalışan sarhoş adamın yolu

Ağınızdaki tüm UPnP ağ geçitlerini numaralandırmayı deneyebilir ve bu şekilde bazı "harici" şeyler için uygun bir yol bulabilirsiniz. Bu, varsayılan rotanızın göstermediği bir rota üzerinde bile olabilir.

Bu konuda daha fazla bilgi için belki https://en.wikipedia.org/wiki/Internet_Gateway_Device_Protocol adresine bakın.

Bu, varsayılan rotanız başka bir yeri gösteriyor olsa bile, gerçek genel arayüzünüzün hangisi olduğu size iyi bir izlenim verir.

Daha da fazlası var

Dağın peygamberle buluştuğu yer

IPv6 yönlendiricileri size doğru IPv6 önekini vermek için kendilerini tanıtırlar. Öneke bakmak size bir dahili IP'si mi yoksa global bir IP'si mi olduğuna dair bir ipucu verir.

Uygun bir ağ geçidi bulmak için IGMP veya IBGP çerçevelerini dinleyebilirsiniz.

2 ^ 32'den az IP adresi vardır. Bu nedenle, bir LAN üzerinde hepsine ping atmak uzun sürmez. Bu size, sizin bakış açınızdan İnternet'in çoğunun nerede bulunduğu konusunda istatistiksel bir ipucu verir. Ancak ünlü https://de.wikipedia.org/wiki/SQL_Slammer'dan biraz daha duyarlı olmalısınız

ICMP ve hatta ARP, ağ yan bandı bilgileri için iyi kaynaklardır. Size de yardımcı olabilir.

DHCP (hatta DHCPv6) gibi çoğu zaman yardımcı olacak tüm ağ altyapısı cihazlarınızla iletişim kurmak için Ethernet Yayın adresini kullanabilirsiniz.

Bu ek liste muhtemelen sonsuzdur ve her zaman eksiktir, çünkü her ağ aygıtı üreticisi, kendi aygıtlarını otomatik olarak nasıl algılayacakları konusunda yeni güvenlik açıkları icat etmektedir. Bu, olmaması gereken bazı ortak arayüzlerin nasıl tespit edileceği konusunda genellikle çok yardımcı olur.

Nuff dedi. Dışarı.


4

Steve Baker'ın söylediklerine ek olarak, netdevice (7) kılavuz sayfasında SIOCGIFCONF ioctl'nin açıklamasını bulabilirsiniz .

Ana bilgisayardaki tüm IP adreslerinin listesine sahip olduğunuzda, istemediğiniz adresleri filtrelemek için uygulamaya özel mantık kullanmanız gerekir ve bir IP adresinizin kaldığını umarsınız.


4

Sert kodlamayın: Bu, değişebilecek türden bir şeydir. Birçok program bir yapılandırma dosyasında okuyarak ve ne söylüyorsa onu yaparak neye bağlanacağını anlar. Bu şekilde, gelecekte programınızın genel IP olmayan bir şeye bağlanması gerekirse, bunu yapabilir.


2

Soru Linux'u belirttiği için, bir makinenin IP adreslerini keşfetmek için en sevdiğim teknik netlink kullanmaktır. NETLINK_ROUTE protokolünün bir netlink soketini oluşturarak ve bir RTM_GETADDR göndererek, uygulamanız mevcut tüm IP adreslerini içeren bir mesaj (lar) alacaktır. Burada bir örnek verilmiştir .

Mesaj işlemenin basit bir kısmı için libmnl uygundur. NETLINK_ROUTE'un farklı seçenekleri (ve bunların nasıl ayrıştırıldıkları) hakkında daha fazla bilgi edinmeye meraklıysanız, en iyi kaynak iproute2'nin kaynak kodu (özellikle izleme uygulaması) ve çekirdekteki alma işlevleridir. Rtnetlink'in man sayfası da yararlı bilgiler içerir.


1

Curl ile biraz entegrasyon yapabilirsiniz, bu kadar kolay bir şey: curl www.whatismyip.orgkabuktan size genel ip'inizi verecektir. Bir tür harici sunucuya güveniyorsunuz, ancak bir NAT'ın arkasında iseniz her zaman olacaksınız.


Neden daha fazla insanın bu siteyi kullanmadığını asla anlamıyorum. Hatta makine tarafından okunabilen bir sayfaları vardır, böylece bilgileri gözden geçirmek zorunda kalmazsınız.
deft_code

1
Proxy içinde çalışıyorsanız doğru IP'yi alamayabilirsiniz.
Jack

1
icanhazip.com IP'yi döndürür ve başka bir şey döndürmez. Bir bash betiğine gömmek için mükemmel!
lukecyca

ipinfo.io/ip başka bir alternatiftir. İpinfo.io'yu curl ederseniz , ana bilgisayar adını, coğrafi konum bilgilerini ve daha fazlasını JSON biçiminde döndürür.
Ben Dowling


-10

// 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();

}


10
Bu, web sitesi içeriğinin hassas bir şekilde ayrıştırılması yoluyla yerel kontrol dışındaki hizmetlere dayanan ağır, yalnızca Windows (linux hakkında soru), kötü biçimlendirilmiş bir çözümdür. Bu "çözümün" olumlu bir yanı bulamıyorum.
viraptor

1
Hah! HTML ayrıştırması, tam olarak ne yapıldığına dair bir uyarı olan bir yorumu bile arar.
notlesh
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.