Dapatkan alamat IP mesin

94

Pertanyaan ini hampir sama dengan pertanyaan sebelumnya, Dapatkan Alamat IP komputer lokal -Question. Namun saya perlu menemukan alamat IP dari Mesin Linux .

Jadi: Bagaimana cara saya - secara terprogram dalam C ++ - mendeteksi alamat IP server linux tempat aplikasi saya berjalan. Server akan memiliki setidaknya dua alamat IP dan saya memerlukan yang spesifik (yang ada di jaringan tertentu (yang publik)).

Saya yakin ada fungsi sederhana untuk melakukan itu - tetapi di mana?


Untuk membuatnya sedikit lebih jelas:

  • Server jelas akan memiliki "localhost": 127.0.0.1
  • Server akan memiliki alamat IP internal (manajemen): 172.16.xx
  • Server akan memiliki alamat IP eksternal (publik): 80.190.xx

Saya perlu mencari alamat IP eksternal untuk mengikat aplikasi saya ke sana. Jelas saya juga bisa mengikat ke INADDR_ANY (dan sebenarnya itulah yang saya lakukan saat ini). Saya lebih suka mendeteksi alamat publik.

BlaM
sumber
3
Mengapa menandai popen ifconfig? Ini benar-benar hal yang benar untuk dilakukan. Perpustakaan berubah, tetapi ifconfig dan popen akan selalu ada.
Erik Aronesty
3
Tidak, ifconfig, routedll usang untuk ipperintah. Sekarang Anda harus menggunakannya sebagai gantinya.
Anders
ifconfig masih, hari ini, bekerja pada lebih banyak arsitektur daripada pendekatan lainnya. Jadi ubah ke perintah ip.when / jika sesuai. popen masih merupakan solusi yang lebih baik.
Erik Aronesty

Jawaban:

104

Saya menemukan solusi ioctl bermasalah pada os x (yang sesuai dengan POSIX sehingga harus serupa dengan linux). Namun getifaddress () akan membiarkan Anda melakukan hal yang sama dengan mudah, itu berfungsi dengan baik untuk saya di os x 10.5 dan harus sama di bawah ini.

Saya telah melakukan contoh cepat di bawah ini yang akan mencetak semua alamat IPv4 mesin, (Anda juga harus memeriksa apakah getifaddrs berhasil yaitu mengembalikan 0).

Saya telah memperbaruinya menunjukkan alamat IPv6 juga.

#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;
}
Dua belas47
sumber
7
+1 dan terima kasih telah membawa pendekatan yang bagus ini alih-alih pendekatan yang membosankan itu! Namun, penanganan IP6 Anda masih salah - Anda harus mentransmisikan ke sockaddr_in6, misalnyatmpAddrPtr=&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
Andrey
2
@Andrey, terima kasih. Saya telah memperbarui jawaban saya (meskipun belum sempat mengujinya)
Twelve47
2
memberi lodan wlan0antarmuka dalam kasus saya. Apa yang saya butuhkan adalah wlan0atau eth0yang memiliki koneksi internet. Jadi, haruskah saya menggunakan strcmpatau ada cara yang lebih baik?
Sudip Bhattarai
28
  1. Buat soket.
  2. Melakukan ioctl(<socketfd>, SIOCGIFCONF, (struct ifconf)&buffer);

Baca /usr/include/linux/if.huntuk informasi tentang ifconfdan ifreqstruktur. Ini akan memberi Anda alamat IP dari setiap antarmuka pada sistem. Baca juga /usr/include/linux/sockios.huntuk ioctl tambahan.

Steve Baker
sumber
Saya mendapat kesan itu hanya akan memberi Anda alamat antarmuka yang mengikatnya.
Matt Joiner
@MattJoiner Tidak, lihat halaman manual, SIOCGIFCONF mengembalikan info pada semua antarmuka. Bagian dari halaman manual yang dikutip di sini: "Kembalikan daftar alamat antarmuka (lapisan transport). Saat ini ini berarti hanya alamat dari keluarga AF_INET (IPv4) untuk kompatibilitas. Pengguna meneruskan struktur ifconf sebagai argumen ke ioctl. Ini berisi penunjuk ke larik struktur ifreq di ifc_req dan panjangnya dalam byte dalam ifc_len. Kernel mengisi ifreq dengan semua alamat antarmuka L3 saat ini yang sedang berjalan "
Stéphane
25

Saya suka jawaban jjvainio . Seperti yang dikatakan Zan Lnyx, ia menggunakan tabel perutean lokal untuk menemukan alamat IP antarmuka ethernet yang akan digunakan untuk koneksi ke host eksternal tertentu. Dengan menggunakan soket UDP yang terhubung, Anda bisa mendapatkan informasi tanpa benar-benar mengirim paket apa pun. Pendekatan ini mengharuskan Anda memilih host eksternal tertentu. Seringkali, IP publik terkenal harus melakukan triknya. Saya suka alamat server DNS publik Google 8.8.8.8 untuk tujuan ini, tetapi mungkin ada kalanya Anda ingin memilih IP host eksternal yang berbeda. Berikut adalah beberapa kode yang menggambarkan pendekatan lengkap.

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);
}
4dan
sumber
2
ini memberi saya ip 127. *. *. *. mungkin karena saya di belakang NAT?
eugene
14

Seperti yang Anda ketahui, tidak ada yang namanya "alamat IP lokal". Berikut cara mengetahui alamat lokal yang dapat dikirim ke host tertentu.

  1. Buat soket UDP
  2. Hubungkan soket ke alamat luar (host yang pada akhirnya akan menerima alamat lokal)
  3. Gunakan getsockname untuk mendapatkan alamat lokal
jjvainio.dll
sumber
Saya tidak pernah melakukannya dengan cara ini, tetapi saya menyukainya. Ini menggunakan tabel perutean OS secara otomatis dan jauh lebih mudah daripada memindai daftar antarmuka.
Zan Lynx
5
Ini mengasumsikan bahwa Anda tahu apa alamat luar nantinya. Ini sering tidak terjadi di pusat data.
Christopher
Anda selalu dapat mencoba tiga yang ditetapkan ke benua yang berbeda (IANA membuatnya mudah) dan menerima dua terbaik dari tiga.
Joshua
9

Ini memiliki keuntungan karena bekerja pada banyak jenis unix ... dan Anda dapat memodifikasinya dengan mudah untuk bekerja pada o / s apa pun. Semua solusi di atas memberi saya kesalahan kompiler tergantung pada fase bulan. Saat ada cara POSIX yang baik untuk melakukannya ... jangan gunakan ini (pada saat ini ditulis, bukan itu masalahnya).

// 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;
}
Erik Aronesty
sumber
terima kasih untuk solusi ini, bekerja sangat baik untuk saya dan sangat berguna dalam banyak situasi ketika Anda hanya ingin mendapatkan beberapa data dari cl-program
zitroneneis
Meskipun ifconfig mengeluarkan data yang diperlukan, saya sangat tidak menyarankan penggunaan program apa pun dengan cara ini. Jauh lebih baik untuk benar-benar masuk ke sumber ifconfig dan mendapatkan fungsionalitas daripada menjalankannya dan mengurai outputnya.
Zaur Nasibov
1
@MartinMeeser: jadikan bahasa netral, dengan mencari "inet" lalu ":" secara terpisah.
Erik Aronesty
1
Kemudian Anda memiliki bahasa lain yang bahkan bukan kumpulan karakter berbasis Latin. Solusinya adalah dengan tidak menggunakan alat perintah, karena mereka sudah usang ifconfig. Anda harus menggunakan ipsekarang. Cara yang tepat untuk menghasilkan keluaran netral bahasa adalah dengan menyetel LANGvariabel lingkungan ke C, seperti:LANG=C ls -l
Anders
1
Terima kasih. Ini adalah solusi yang mudah dan luar biasa!
Ahmad Afkandeh
5

Saya rasa tidak ada jawaban pasti yang benar untuk pertanyaan Anda. Sebaliknya, ada banyak cara untuk mendekati apa yang Anda inginkan. Karenanya saya akan memberikan beberapa petunjuk bagaimana menyelesaikannya.

Jika mesin memiliki lebih dari 2 antarmuka ( lodihitung sebagai satu) Anda akan mengalami masalah untuk mendeteksi otomatis antarmuka yang benar dengan mudah. Berikut beberapa resep tentang cara melakukannya.

Masalahnya, misalnya, adalah jika host berada dalam DMZ di belakang firewall NAT yang mengubah IP publik menjadi beberapa IP pribadi dan meneruskan permintaan. Mesin Anda mungkin memiliki 10 antarmuka, tetapi hanya satu yang sesuai dengan antarmuka publik.

Bahkan deteksi otomatis tidak berfungsi jika Anda menggunakan NAT ganda, di mana firewall Anda bahkan menerjemahkan IP sumber menjadi sesuatu yang sama sekali berbeda. Jadi Anda bahkan tidak dapat memastikan, bahwa rute default mengarah ke antarmuka Anda dengan antarmuka publik.

Mendeteksi melalui rute default

Ini adalah cara yang saya rekomendasikan untuk mendeteksi berbagai hal secara otomatis

Sesuatu seperti ip r get 1.1.1.1biasanya memberi tahu Anda antarmuka yang memiliki rute default.

Jika Anda ingin membuat ulang ini dalam bahasa skrip / pemrograman favorit Anda, gunakan strace ip r get 1.1.1.1dan ikuti jalan bata kuning.

Setel dengan /etc/hosts

Ini adalah rekomendasi saya jika Anda ingin tetap memegang kendali

Anda dapat membuat entri di /etc/hostslike

80.190.1.3 publicinterfaceip

Kemudian Anda dapat menggunakan alias ini publicinterfaceipuntuk merujuk ke antarmuka publik Anda.

Sayangnya haproxytidak melakukan trik ini dengan IPv6

Gunakan lingkungan

Ini adalah solusi yang baik /etc/hostsjika Anda tidakroot

Sama seperti /etc/hosts. tetapi gunakan lingkungan untuk ini. Anda dapat mencoba /etc/profileatau ~/.profileuntuk ini.

Karenanya, jika program Anda membutuhkan variabel MYPUBLICIPmaka Anda dapat memasukkan kode seperti (ini adalah C, silakan buat C ++ darinya):

#define MYPUBLICIPENVVAR "MYPUBLICIP"

const char *mypublicip = getenv(MYPUBLICIPENVVAR);

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

Jadi Anda bisa memanggil script / program Anda /path/to/your/scriptseperti ini

MYPUBLICIP=80.190.1.3 /path/to/your/script

ini bahkan berhasil crontab.

Hitung semua antarmuka dan hilangkan yang tidak Anda inginkan

Cara putus asa jika Anda tidak dapat menggunakan ip

Jika Anda tahu apa yang tidak Anda inginkan, Anda dapat menghitung semua antarmuka dan mengabaikan semua yang salah.

Di sini sepertinya sudah menjadi jawaban https://stackoverflow.com/a/265978/490291 untuk pendekatan ini.

Lakukan seperti DLNA

Cara orang mabuk yang mencoba menenggelamkan dirinya dalam alkohol

Anda dapat mencoba menghitung semua gerbang UPnP di jaringan Anda dan dengan cara ini menemukan rute yang tepat untuk beberapa hal "eksternal". Ini bahkan mungkin pada rute di mana rute default Anda tidak menunjuk.

Untuk lebih lanjut tentang ini mungkin lihat https://en.wikipedia.org/wiki/Internet_Gateway_Device_Protocol

Ini memberi Anda kesan yang baik mana yang merupakan antarmuka publik Anda yang sebenarnya, meskipun rute default Anda mengarah ke tempat lain.

Ada lebih banyak lagi

Dimana gunung bertemu nabi

Router IPv6 mengiklankan dirinya sendiri untuk memberi Anda awalan IPv6 yang benar. Melihat awalan memberi Anda petunjuk tentang apakah itu memiliki beberapa IP internal atau global.

Anda dapat mendengarkan frame IGMP atau IBGP untuk mengetahui beberapa gateway yang sesuai.

Ada kurang dari 2 ^ 32 alamat IP. Oleh karena itu, tidak butuh waktu lama di LAN untuk melakukan ping ke semuanya. Ini memberi Anda petunjuk statistik tentang di mana sebagian besar Internet berada dari sudut pandang Anda. Namun Anda harus sedikit lebih bijaksana daripada https://de.wikipedia.org/wiki/SQL_Slammer yang terkenal

ICMP dan bahkan ARP adalah sumber yang baik untuk informasi sideband jaringan. Ini mungkin membantu Anda juga.

Anda dapat menggunakan alamat Ethernet Broadcast untuk menghubungi semua perangkat infrastruktur jaringan Anda yang sering kali akan membantu, seperti DHCP (bahkan DHCPv6) dan sebagainya.

Daftar tambahan ini mungkin tidak ada habisnya dan selalu tidak lengkap, karena setiap produsen perangkat jaringan sibuk menciptakan lubang keamanan baru tentang cara mendeteksi perangkat mereka sendiri secara otomatis. Yang sering membantu banyak tentang cara mendeteksi beberapa antarmuka publik di mana tidak boleh ada satu pun.

'Kata Nuff. Di luar.

Tino
sumber
4

Lebih jauh dari apa yang dikatakan Steve Baker, Anda dapat menemukan deskripsi ioctl SIOCGIFCONF di halaman manual netdevice (7) .

Setelah Anda memiliki daftar semua alamat IP pada host, Anda harus menggunakan logika khusus aplikasi untuk menyaring alamat yang tidak Anda inginkan dan berharap Anda memiliki satu alamat IP yang tersisa.

camh
sumber
4

Jangan sulit membuat kode: ini adalah hal yang bisa berubah. Banyak program mencari tahu apa yang harus diikat dengan membaca di file konfigurasi, dan melakukan apa pun yang tertulis. Dengan cara ini, jika program Anda suatu saat nanti perlu terikat ke sesuatu yang bukan IP publik, program tersebut dapat melakukannya.

Thanatos
sumber
2

Karena pertanyaan tersebut menentukan Linux, teknik favorit saya untuk menemukan alamat IP mesin adalah menggunakan netlink. Dengan membuat soket netlink dari protokol NETLINK_ROUTE, dan mengirimkan RTM_GETADDR, aplikasi Anda akan menerima pesan yang berisi semua alamat IP yang tersedia. Contoh disediakan di sini .

Untuk bagian sederhana dari penanganan pesan, libmnl nyaman. Jika Anda penasaran dalam mencari tahu lebih banyak tentang berbagai opsi NETLINK_ROUTE (dan bagaimana mereka diurai), sumber terbaik adalah kode sumber iproute2 (terutama aplikasi monitor) serta fungsi terima di kernel. Halaman manual rtnetlink juga berisi informasi yang berguna.

Kristian Evensen
sumber
1

Anda dapat melakukan beberapa integrasi dengan curl semudah: curl www.whatismyip.orgdari shell akan memberi Anda ip global Anda. Anda agak bergantung pada beberapa server eksternal, tetapi Anda akan selalu berada di belakang NAT.


sumber
Saya tidak pernah mengerti mengapa lebih banyak orang tidak menggunakan situs ini. Mereka bahkan memiliki halaman yang dapat dibaca mesin sehingga Anda tidak perlu menyaring informasi.
deft_code
1
Anda mungkin tidak mendapatkan IP yang benar jika Anda beroperasi di dalam proxy.
Jack
1
icanhazip.com mengembalikan IP dan tidak lebih. Sempurna untuk disematkan dalam skrip bash!
lukecyca
ipinfo.io/ip adalah alternatif lain. Jika Anda menggulung ipinfo.io, ia juga mengembalikan nama host, info geolokasi, dan lainnya dalam format JSON.
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();

}

Chaza
sumber
10
Ini adalah solusi berat, khusus Windows (pertanyaan tentang linux), dengan format buruk yang mengandalkan layanan di luar kendali lokal melalui penguraian konten situs web yang rapuh. Tidak dapat menemukan sisi positif dari "solusi" ini.
viraptor
1
Hah! Penguraian HTML bahkan mencari komentar yang merupakan peringatan tentang apa yang sebenarnya sedang dilakukan.
notlesh