Получить IP-адрес машины
Этот вопрос почти такой же, как ранее запрошенный Получить IP-адрес локального компьютера -Question. Однако мне нужно найти IP-адрес Linux Machine.
Итак: как я - программно в С++ - обнаруживает IP-адреса сервера linux, на котором работает мое приложение. У серверов будет как минимум два IP-адреса, и мне нужен конкретный (тот, который указан в данной сети (общедоступный)).
Я уверен, что для этого есть простая функция, но где?
Чтобы сделать вещи более ясными:
- На сервере, очевидно, будет "localhost": 127.0.0.1
- Сервер будет иметь внутренний (управляющий) IP-адрес: 172.16.x.x
- Сервер будет иметь внешний (общедоступный) IP-адрес: 80.190.x.x
Мне нужно найти внешний IP-адрес для привязки моего приложения к нему. Очевидно, я могу также привязываться к INADDR_ANY (и фактически тому, что я делаю в данный момент). Я бы предпочел обнаружить публичный адрес.
Ответы
Ответ 1
Я обнаружил, что решение ioctl проблематично для os x (что соответствует 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;
}
Ответ 2
- Создайте сокет.
- Выполнить
ioctl(<socketfd>, SIOCGIFCONF, (struct ifconf)&buffer);
Прочтите /usr/include/linux/if.h
для информации о структурах ifconf
и ifreq
. Это должно дать вам IP-адрес каждого интерфейса в системе. Также читайте /usr/include/linux/sockios.h
для дополнительных ioctls.
Ответ 3
Мне нравится jjvainio answer. Как говорит Zan Lnyx, он использует локальную таблицу маршрутизации, чтобы найти IP-адрес интерфейса ethernet, который будет использоваться для подключения к определенному внешнему хосту. Используя подключенный UDP-сокет, вы можете получить информацию без фактической отправки каких-либо пакетов. Подход требует, чтобы вы выбрали конкретный внешний хост. В большинстве случаев любой известный публичный IP-адрес должен делать трюк. Для этой цели мне нравится адрес 8.8.8.8 общего DNS-сервера Google, но иногда вы можете выбрать другой внешний 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);
}
Ответ 4
Как вы выяснили, нет такой вещи, как один "локальный IP-адрес". Здесь, как узнать локальный адрес, который может быть отправлен на конкретный хост.
- Создание сокета UDP
- Подключите сокет к внешнему адресу (хост, который в конечном итоге получит локальный адрес)
- Используйте getockname для получения локального адреса
Ответ 5
У этого есть преимущество в работе над многими вариантами unix... и вы можете модифицировать его тривиально, чтобы работать с любыми 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;
}
Ответ 6
В дополнение к тому, что сказал Стив Бейкер, вы можете найти описание SIOCGIFCONF ioctl на странице netdevice (7).
После того, как у вас будет список всех IP-адресов на хосте, вам придется использовать конкретную логику приложения для фильтрации адресов, которые вы не хотите, и надеемся, что у вас останется один IP-адрес.
Ответ 7
Не печатайте его: это то, что может измениться. Многие программы выясняют, к чему привязываться, читая в файле конфигурации и делая все, что говорит. Таким образом, если ваша программа когда-нибудь в будущем должна связываться с чем-то, что не является общедоступным IP, он может это сделать.
Ответ 8
Я не думаю, что есть окончательный правильный ответ на ваш вопрос. Вместо этого есть большой набор способов, как приблизиться к тому, что вы пожелаете. Поэтому я предоставлю некоторые подсказки, как это сделать.
Если машина имеет более 2 интерфейсов (lo
считается как одна), у вас возникнут проблемы с автоматическим определением правильного интерфейса. Вот несколько рецептов о том, как это сделать.
Проблема, например, в том, что хосты находятся в DMZ за брандмауэром NAT, который изменяет публичный IP-адрес на некоторый частный IP-адрес и пересылает запросы. Ваш компьютер может иметь 10 интерфейсов, но только один соответствует общедоступному.
Даже автоопределение не работает, если вы находитесь на двойном NAT, где ваш брандмауэр даже переводит source-IP во что-то совершенно другое. Поэтому вы даже не можете быть уверены, что маршрут по умолчанию ведет к вашему интерфейсу с открытым интерфейсом.
Обнаружьте его по маршруту по умолчанию
Это мой рекомендуемый способ автоматического определения вещей
Что-то вроде 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
для ссылки на ваш общедоступный интерфейс.
К сожалению, haproxy
не проверяет этот трюк с IPv6
Использовать среду
Это хороший обход для /etc/hosts
, если вы не root
То же, что и /etc/hosts
. но для этого используйте среду. Вы можете попробовать /etc/profile
или ~/.profile
для этого.
Следовательно, если вашей программе нужна переменная MYPUBLICIP
, тогда вы можете включить такой код (это C, не стесняйтесь создавать С++ из него):
#define MYPUBLICIPENVVAR "MYPUBLICIP"
const char *mypublicip = getenv(MYPUBLICIPENVVAR);
if (!mypublicip) { fprintf(stderr, "please set environment variable %s\n", MYPUBLICIPENVVAR); exit(3); }
Итак, вы можете позвонить в script/program /path/to/your/script
следующим образом
MYPUBLICIP=80.190.1.3 /path/to/your/script
это даже работает в crontab
.
Перечислить все интерфейсы и устранить те, которые вы не хотите
Отчаянный способ, если вы не можете использовать ip
Если вы знаете, чего не хотите, вы можете перечислить все интерфейсы и игнорировать все ложные.
Здесь уже представляется ответ fooobar.com/questions/68605/... для этого подхода.
Сделайте это как DLNA
Путь пьяного человека, который пытается утопить себя в алкоголе
Вы можете попытаться перечислить все шлюзы UPnP в своей сети и таким образом найти правильный маршрут для какой-либо "внешней" вещи. Это даже может быть на маршруте, на котором не указан маршрут по умолчанию.
Подробнее об этом, возможно, см. https://en.wikipedia.org/wiki/Internet_Gateway_Device_Protocol
Это дает вам хорошее представление о том, какой из них является вашим реальным общедоступным интерфейсом, даже если ваш маршрут по умолчанию находится в другом месте.
Есть еще больше
Где гора встречает пророка
Маршрутизаторы IPv6 рекламируют себя, чтобы предоставить вам правильный префикс IPv6. Глядя на префикс, вы получаете подсказку о том, есть ли у него внутренний IP-адрес или глобальный.
Вы можете прослушивать кадры IGMP или IBGP, чтобы найти подходящий шлюз.
Существует менее 2 ^ 32 IP-адресов. Следовательно, LAN не требует много времени, чтобы просто пинговать их все. Это дает вам статистический намек на то, где большая часть Интернета находится с вашей точки зрения. Однако вы должны быть немного более разумными, чем знаменитый https://de.wikipedia.org/wiki/SQL_Slammer
ICMP и даже ARP являются хорошими источниками для информации о боковой стороне сети. Это также поможет вам.
Вы можете использовать Ethernet Broadcast address для связи со всеми сетевыми инфраструктурными устройствами, которые часто помогают, например, DHCP (даже DHCPv6) и т.д.
Этот дополнительный список, вероятно, бесконечен и всегда неполный, потому что каждый производитель сетевых устройств активно изобретает новые дыры в безопасности о том, как автоматически определять свои собственные устройства. Это часто помогает в определении того, как обнаружить какой-то публичный интерфейс, где бы не было.
'Нафф сказал. Out.
Ответ 9
Вы можете сделать некоторую интеграцию с curl как-то так же просто, как: curl www.whatismyip.org
из оболочки вы получите свой глобальный ip. Вы зависите от какого-то внешнего сервера, но всегда будете, если находитесь за NAT.
Ответ 10
Поскольку вопрос задает Linux, мой любимый способ обнаружения IP-адресов машины - использовать netlink. Создав сокет netlink протокола NETLINK_ROUTE и отправив RTM_GETADDR, ваше приложение получит сообщение (ы), содержащее все доступные IP-адреса. Приведен пример здесь.
Для простоты обработки сообщений libmnl удобен. Если вы интересуетесь различными опциями NETLINK_ROUTE (и как они разбираются), лучшим источником является исходный код iproute2 (особенно приложение монитора), а также функции приема в ядре. Страница руководства rtnetlink также содержит полезную информацию.
Ответ 11
Элегантное решение для Linux для Linux можно найти по адресу:
http://www.damnsmalllinux.org/f/topic-3-23-17031-0.html
Ответ 12
// Использовать HTTP-запрос к хорошо известному серверу, который отсылает общедоступный IP-адрес
void GetPublicIP (CString и csIP)
{ // Инициализировать COM bool bInit = false; if (SUCCEEDED (CoInitialize (NULL))) { //COM был инициализирован bInit = true;
//Создание объекта запроса HTTP MSXML2:: IXMLHTTPRequestPtr HTTPRequest; HRESULT hr = HTTPRequest.CreateInstance( "MSXML2.XMLHTTP" ); if (SUCCEEDED (hr)) { // Создаем запрос на веб-сайт, который возвращает общедоступный IP-адрес VARIANT Async; Async.vt = VT_BOOL; Async.boolVal = VARIANT_FALSE; CComBSTR ccbRequest = L "http://whatismyipaddress.com/";
// Открыть запрос if (SUCCEEDED (HTTPRequest- > raw_open (L "GET", ccbRequest, Async))) { // Отправить запрос if (SUCCEEDED (HTTPRequest- > raw_send())) { // Получить ответ CString csRequest = HTTPRequest- > GetresponseText();
// Разбираем IP-адрес CString csMarker = "<! - свяжитесь с нами перед использованием script, чтобы получить ваш IP-адрес - >"; int iPos = csRequest.Find(csMarker); if (iPos == -1) вернуть; iPos + = csMarker.GetLength(); int iPos2 = csRequest.Find(csMarker, iPos); if (iPos2 == -1) вернуть;
// Создаем IP-адрес int nCount = iPos2 - iPos; csIP = csRequest.Mid(iPos, nCount); } } }
}
//Unitialize COM
if (bInit) CoUninitialize();
Код>
}