기기의 IP 주소를 가져옵니다. 그러나 Linux 머신 의 IP 주소를

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

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

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


좀 더 명확하게하려면 :

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

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



답변

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


답변

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

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


답변

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


답변

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

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

답변

이것은 유닉스의 많은 풍미에서 작업 할 수 있다는 장점이 있습니다. 그리고 모든 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;
}


답변

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

시스템에 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 포함) 등과 같이 종종 도움이되는 모든 네트워크 인프라 장치에 연결할 수 있습니다.

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

‘그런가 말했다. 밖.


답변

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

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