기기의 IP 주소를 가져옵니다.


94

이 질문은 이전에 요청한 로컬 컴퓨터의 IP 주소 얻기- 질문과 거의 동일 합니다 . 그러나 Linux 머신 의 IP 주소를 찾아야합니다 .

그래서 : C ++ 에서 프로그래밍 방식으로 애플리케이션이 실행중인 Linux 서버의 IP 주소를 어떻게 감지 합니까? 서버에는 최소한 두 개의 IP 주소가 있으며 특정 주소가 필요합니다 (주어진 네트워크에있는 주소 (공용 주소)).

이를 수행하는 간단한 기능이 있다고 확신합니다. 그러나 어디에서?


좀 더 명확하게하려면 :

  • 서버는 분명히 "localhost": 127.0.0.1을 가질 것입니다.
  • 서버에는 내부 (관리) IP 주소가 있습니다. 172.16.xx
  • 서버에는 외부 (공용) IP 주소가 있습니다 : 80.190.xx

내 애플리케이션을 바인딩 할 외부 IP 주소를 찾아야합니다. 분명히 INADDR_ANY에 바인딩 할 수도 있습니다 (실제로 내가하는 일입니다). 그래도 공개 주소를 감지하고 싶습니다.


3
ifconfig의 팝업을 표시하는 이유는 무엇입니까? 정말 옳은 일입니다. 라이브러리는 변경되지만 ifconfig 및 popen은 항상 거기에 있습니다.
Erik Aronesty

3
아니, ifconfig, route등은 위해 사용되지 않습니다 ip명령. 이제 대신 사용해야합니다.
Anders

ifconfig는 오늘날에도 다른 어떤 접근 방식보다 더 많은 아키텍처에서 작동합니다. 따라서 적절한 경우 ip 명령으로 변경하십시오. popen은 여전히 ​​더 나은 솔루션입니다.
Erik Aronesty 2011 년

답변:


104

OS x에서 ioctl 솔루션이 문제가 있음을 발견했습니다 (POSIX 호환이므로 Linux와 유사해야 함). 그러나 getifaddress ()를 사용하면 똑같은 일을 쉽게 할 수 있습니다. os x 10.5에서 잘 작동하며 아래에서도 동일해야합니다.

아래에서 시스템의 모든 IPv4 주소를 인쇄하는 간단한 예제를 수행했습니다 (getifaddrs가 성공했는지 확인해야합니다. 즉, 0을 반환합니다).

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

7
지루한 ioctl 대신이 멋진 접근 방식을 가져와 주셔서 감사합니다! 그러나 IP6 처리는 여전히 올바르지 않습니다. sockaddr_in6로 캐스트해야합니다. 예를 들면 다음과 같습니다tmpAddrPtr=&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
Andrey

2
@Andrey, 감사합니다. 내 대답을 업데이 트했습니다 (그것은 비록 테스트 할 기회가 없었어요)
Twelve47

2
제 경우에는 제공 lo하고 wlan0인터페이스합니다. 내가 요구하는 것은 wlan0eth0있는 인터넷 connection.So,해야 내가 사용을가 strcmp하거나 더 좋은 방법이 THRE입니까?
Sudip Bhattarai

28
  1. 소켓을 만듭니다.
  2. 행하다 ioctl(<socketfd>, SIOCGIFCONF, (struct ifconf)&buffer);

및 구조 /usr/include/linux/if.h에 대한 정보를 읽으십시오 . 시스템에있는 각 인터페이스의 IP 주소를 제공해야합니다. 추가 ioctl에 대해서도 읽어보십시오 .ifconfifreq/usr/include/linux/sockios.h


나는 그것이 바인딩하는 인터페이스의 주소만을 줄 것이라는 인상을 받았습니다.
Matt Joiner 2011 년

@MattJoiner 아니요, 매뉴얼 페이지를 살펴보십시오. SIOCGIFCONF는 모든 인터페이스에 대한 정보를 반환합니다. 여기에 인용 된 매뉴얼 페이지의 일부 : "인터페이스 (전송 계층) 주소 목록을 반환합니다. 이것은 현재 호환성을 위해 AF_INET (IPv4) 계열의 주소 만 의미합니다. 사용자는 ioctl에 인수로 ifconf 구조를 전달합니다. 여기에는 ifc_req의 ifreq 구조 배열에 대한 포인터 및 ifc_len의 바이트 길이입니다. 커널은 실행중인 모든 현재 L3 인터페이스 주소로 ifreq를 채 웁니다. "
Stéphane

25

나는 jjvainio의 대답을 좋아 합니다. Zan Lnyx가 말했듯이, 로컬 라우팅 테이블을 사용하여 특정 외부 호스트에 연결하는 데 사용될 이더넷 인터페이스의 IP 주소를 찾습니다. 연결된 UDP 소켓을 사용하면 실제로 패킷을 보내지 않고도 정보를 얻을 수 있습니다. 접근 방식을 사용하려면 특정 외부 호스트를 선택해야합니다. 대부분의 경우 잘 알려진 공용 IP가 트릭을 수행해야합니다. 이 목적을 위해 Google의 공용 DNS 서버 주소 8.8.8.8을 좋아하지만 다른 외부 호스트 IP를 선택하고 싶을 때가 있습니다. 다음은 전체 접근 방식을 보여주는 코드입니다.

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
이것은 나에게 priviate IP 127. *. *. * 제공합니다. 내가 NAT 뒤에 있기 때문일까요?
유진 2011-04-07

14

알다시피 단일 "로컬 IP 주소"와 같은 것은 없습니다. 특정 호스트에 보낼 수있는 로컬 주소를 찾는 방법은 다음과 같습니다.

  1. UDP 소켓 만들기
  2. 소켓을 외부 주소 (결국 로컬 주소를받을 호스트)에 연결합니다.
  3. getsockname을 사용하여 로컬 주소 가져 오기

이런 식으로 한 적이 없지만 좋아합니다. OS 라우팅 테이블을 자동으로 사용하며 인터페이스 목록을 스캔하는 것보다 훨씬 쉽습니다.
Zan Lynx

5
이것은 외부 주소가 무엇인지 알고 있다고 가정합니다. 데이터 센터에서는 그렇지 않은 경우가 많습니다.
크리스토퍼

당신은 항상 다른 대륙에 할당 된 세 가지를 시도하고 (IANA는 이것을 쉽게 할 수 있습니다) 세 가지 중 가장 좋은 두 가지를 받아 들일 수 있습니다.
Joshua

9

이것은 유닉스의 많은 풍미에서 작업 할 수 있다는 장점이 있습니다. 그리고 모든 O / S에서 작업하도록 사소하게 수정할 수 있습니다. 위의 모든 솔루션은 달의 위상에 따라 컴파일러 오류를 제공합니다. 그것을하기위한 좋은 POSIX 방법이있는 순간 ... 이것을 사용하지 마십시오 (이 글이 쓰여진 당시에는 그렇지 않았습니다).

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

이 솔루션에 감사드립니다. 저에게 잘 작동하며 cl 프로그램에서 데이터를 얻고 싶을 때 많은 상황에서 매우 편리합니다
zitroneneis

ifconfig가 필요한 데이터를 출력하지만 이러한 방식으로 프로그램을 사용하지 않는 것이 좋습니다. 실제로 ifconfig의 소스에 들어가서 실행하고 출력을 파싱하는 것보다 기능을 얻는 것이 훨씬 낫습니다.
Zaur Nasibov 2012-06-12

1
@MartinMeeser : "inet"다음 ":"을 따로 찾아서 언어 중립적으로 만들었습니다.
Erik Aronesty 2014 년

1
그렇다면 라틴어 기반 문자 집합이 아닌 다른 언어가 있습니다. 해결책은 명령 도구를 사용하지 않는 것 ifconfig입니다. 지금 사용해야합니다 ip. 언어 중립적 출력을 생성 하는 적절한 방법은 다음 과 같이 LANG환경 변수를 설정하는 C것입니다.LANG=C ls -l
Anders

1
감사. 쉽고 멋진 솔루션입니다!
Ahmad Afkandeh

5

귀하의 질문에 대한 확실한 정답이 없다고 생각합니다. 대신 원하는 것에 가까워지는 방법이 많이 있습니다. 따라서이를 수행하는 방법에 대한 몇 가지 힌트를 제공 할 것입니다.

시스템에 2 개 이상의 인터페이스 ( lo하나로 계산)가있는 경우 올바른 인터페이스를 쉽게 자동 감지하는 데 문제가 있습니다. 이를 수행하는 방법에 대한 몇 가지 요리법이 있습니다.

예를 들어 문제는 호스트가 NAT 방화벽 뒤의 DMZ에있어 공용 IP를 일부 개인 IP로 변경하고 요청을 전달하는 경우입니다. 컴퓨터에는 10 개의 인터페이스가있을 수 있지만 공용 인터페이스에는 하나만 해당됩니다.

방화벽이 소스 IP를 완전히 다른 것으로 변환하는 double-NAT에있는 경우 자동 감지조차 작동하지 않습니다. 따라서 기본 경로가 공용 인터페이스가있는 인터페이스로 연결되는지 확신 할 수 없습니다.

기본 경로를 통해 감지

이것이 자동 감지에 권장되는 방법입니다.

같은 뭔가가 ip r get 1.1.1.1일반적으로 당신에게 기본 경로를 가진 인터페이스를 말한다.

좋아하는 스크립팅 / 프로그래밍 언어로 이것을 재현 strace ip r get 1.1.1.1하려면 노란 벽돌 길을 사용 하고 따르십시오.

설정 /etc/hosts

당신이 통제력을 유지하고 싶다면 이것은 나의 추천입니다

/etc/hosts같은 항목을 만들 수 있습니다.

80.190.1.3 publicinterfaceip

그런 다음이 별칭 publicinterfaceip을 사용 하여 공용 인터페이스를 참조 할 수 있습니다.

슬프게도 haproxyIPv6로이 트릭을 익히지 않습니다.

환경 사용

이것은 /etc/hosts당신이 그렇지 않은 경우에 대한 좋은 해결 방법입니다 .root

과 동일 /etc/hosts. 그러나 이것을 위해 환경을 사용하십시오. /etc/profile또는 ~/.profile이것을 시도 할 수 있습니다 .

따라서 프로그램에 변수가 필요한 경우 MYPUBLICIP다음과 같은 코드를 포함 할 수 있습니다 (이것은 C입니다. 여기에서 C ++를 자유롭게 만들 수 있습니다).

#define MYPUBLICIPENVVAR "MYPUBLICIP"

const char *mypublicip = getenv(MYPUBLICIPENVVAR);

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

따라서 다음 /path/to/your/script과 같이 스크립트 / 프로그램을 호출 할 수 있습니다.

MYPUBLICIP=80.190.1.3 /path/to/your/script

이것은 crontab.

모든 인터페이스를 열거하고 원하지 않는 인터페이스 제거

사용할 수없는 필사적 인 방법 ip

원하지 않는 것을 알고있는 경우 모든 인터페이스를 열거하고 모든 잘못된 인터페이스를 무시할 수 있습니다.

이 접근 방식에 대한 답변은 이미 https://stackoverflow.com/a/265978/490291 인 것 같습니다 .

DLNA처럼

술에 빠져 죽으려는 술취한 남자의 길

네트워크의 모든 UPnP 게이트웨이를 열거 할 수 있으며이 방법으로 "외부"항목에 대한 적절한 경로를 찾을 수 있습니다. 기본 경로가 가리 키지 않는 경로에있을 수도 있습니다.

이에 대한 자세한 내용은 https://en.wikipedia.org/wiki/Internet_Gateway_Device_Protocol을 참조 하십시오.

이렇게하면 기본 경로가 다른 곳을 가리키는 경우에도 실제 공용 인터페이스가 어떤 것인지 좋은 인상을 받게됩니다.

더 있습니다

산이 선지자를 만나는 곳

IPv6 라우터는 올바른 IPv6 접두사를 제공하기 위해 스스로를 광고합니다. 접두사를 보면 내부 IP가 있는지 글로벌 IP가 있는지에 대한 힌트를 얻을 수 있습니다.

IGMP 또는 IBGP 프레임을 수신하여 적합한 게이트웨이를 찾을 수 있습니다.

2 ^ 32 개 미만의 IP 주소가 있습니다. 따라서 LAN에서 모두 ping하는 데 오래 걸리지 않습니다. 이것은 귀하의 관점에서 인터넷의 대부분이 어디에 위치하는지에 대한 통계적 힌트를 제공합니다. 그러나 당신은 유명한 https://de.wikipedia.org/wiki/SQL_Slammer 보다 조금 더 현명해야합니다.

ICMP 및 ARP도 네트워크 측 파대 정보에 대한 좋은 소스입니다. 그것은 당신에게도 도움이 될 것입니다.

이더넷 브로드 캐스트 주소를 사용하여 DHCP (DHCPv6 포함) 등과 같이 종종 도움이되는 모든 네트워크 인프라 장치에 연결할 수 있습니다.

이 추가 목록은 아마도 끝이없고 항상 불완전 할 것입니다. 모든 네트워크 장치 제조업체가 자신의 장치를 자동으로 감지하는 방법에 대해 새로운 보안 허점을 발명하고 있기 때문입니다. 그것은 종종 존재하지 않는 일부 공용 인터페이스를 감지하는 방법에 많은 도움이됩니다.

'그런가 말했다. 밖.


4

Steve Baker가 말한 것 외에도 netdevice (7) 매뉴얼 페이지 에서 SIOCGIFCONF ioctl에 대한 설명을 찾을 수 있습니다 .

호스트의 모든 IP 주소 목록이 있으면 애플리케이션 별 논리를 사용하여 원하지 않는 주소를 필터링하고 하나의 IP 주소가 남아 있기를 바랍니다.


4

하드 코딩하지 마십시오. 이것은 변경 될 수있는 일종의 것입니다. 많은 프로그램이 구성 파일을 읽고 그 내용을 수행하여 바인딩 할 대상을 파악합니다. 이렇게하면 나중에 프로그램이 공개 IP가 아닌 것에 바인딩해야하는 경우 그렇게 할 수 있습니다.


2

질문에서 Linux를 지정했듯이 컴퓨터의 IP 주소를 찾는 데 가장 좋아하는 기술은 netlink를 사용하는 것입니다. NETLINK_ROUTE 프로토콜의 넷 링크 소켓을 만들고 RTM_GETADDR을 보내면 사용 가능한 모든 IP 주소가 포함 된 메시지가 애플리케이션에 수신됩니다. 여기에 예가 제공 됩니다 .

메시지 처리의 일부를 간단히하기 위해 libmnl이 편리합니다. NETLINK_ROUTE의 다양한 옵션 (및 구문 분석 방법)에 대해 자세히 알고 싶은 경우 가장 좋은 소스는 iproute2 (특히 모니터 응용 프로그램) 의 소스 코드 와 커널의 수신 기능입니다. rtnetlink 의 man 페이지 에도 유용한 정보가 있습니다.


1

다음과 같이 쉽게 curl과 통합 할 수 있습니다. curl www.whatismyip.org 할 수 있습니다. 쉘에서 글로벌 IP를 얻을 수 있습니다. 일부 외부 서버에 의존하고 있지만 NAT 뒤에있는 경우 항상 그럴 것입니다.


더 많은 사람들이이 사이트를 사용하지 않는 이유를 이해하지 못합니다. 그들은 심지어 기계로 읽을 수있는 페이지를 가지고 있기 때문에 정보를 스크랩 할 필요가 없습니다.
deft_code

1
프록시 내부에서 작업하는 경우 올바른 IP를 얻지 못할 수 있습니다.
Jack

1
icanhazip.com 은 IP 반환합니다. bash 스크립트에 삽입하기에 완벽합니다!
lukecyca 2011 년

ipinfo.io/ip 는 또 다른 대안입니다. ipinfo.io 를 컬 하면 호스트 이름, 위치 정보 등을 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();

}


10
이것은 무겁고 Windows 전용 (리눅스에 대한 질문)이며 웹 사이트 콘텐츠의 취약한 파싱을 통해 로컬 제어를 벗어난 서비스에 의존하는 형식이 잘못된 솔루션입니다. 이 "솔루션"의 긍정적 인 측면을 찾을 수 없습니다.
viraptor

1
하! HTML 구문 분석은 수행중인 작업에 대한 경고 인 주석도 찾습니다.
notlesh
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.