LINUX.ORG.RU

Обнаружить DHCP

 ,


0

0

Доброго времени суток! Озадачили меня тут: надо проверить из программы (C++) работает ли dhcp или сеть сконфигурирована с постоянным адресом. Гуглеж дал лишь советы проверять leases в /var/lib/dhcpd/dhcpd.leases. Ubuntu 12.04,

eth1      Link encap:Ethernet  HWaddr 00:25:22:89:80:e4  
          inet addr:192.168.0.72  Bcast:192.168.0.255  Mask:255.255.255.0
          inet6 addr: fe80::225:22ff:fe89:80e4/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:4636044 errors:0 dropped:0 overruns:0 frame:0
          TX packets:1305290 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:6073885000 (6.0 GB)  TX bytes:150568682 (150.5 MB)
          Interrupt:45 Base address:0xc000 

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:16436  Metric:1
          RX packets:23315 errors:0 dropped:0 overruns:0 frame:0
          TX packets:23315 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:3925129 (3.9 MB)  TX bytes:3925129 (3.9 MB)
Что посоветуете, люди?

Можно послать DHCPDISCOVER и найти таким образом DHCP-серверы.

Ещё можно узнать адрес сервера у NetworkManager, распарсив вывод команды:

LANG=C nmcli -t -f IP con status uuid $UUID

($UUID — это UUID соединения).

Вместо запуска команды лучше получить нужную информацию напрямую через D-Bus.

gentoo_root ★★★★★ ()
Последнее исправление: gentoo_root (всего исправлений: 1)

В общем случае это невозможно, так как настройки, полученные от DHCP, принципиально ничем не отличаются от настроек, заданных «руками».

Deleted ()

А ещё наличие/отсутствие DHCP никак не коррелирует с «постоянством» IP-адреса.

Deleted ()
Ответ на: комментарий от braboar

А мне не надо настройки получать, нужно лишь определить наличие DHCP сервера.

Варианты:

  • На клиентской машине: определить, получены ли настройки по DHCP, или заданы локально.
  • На клиентской машине: определить, работает ли в локальной сети DHCP-сервер.
  • На сервере: определить, работает ли DHCP-сервер на этой же системе.

Что именно из этого тебе нужно?

Deleted ()
Ответ на: комментарий от Deleted

На клиентской машине: определить, работает ли в локальной сети DHCP-сервер

Именно это. Пардон муа, что не оговорил сразу.

braboar ()
Ответ на: комментарий от braboar

Почитай описание работы DHCP, посмотри снифером какие пакеты шлют dhcp-клиенты при поиске серверов и посмотри исходники dhcp-клиентов.

Тебе нужно отправить в сеть DHCPDISCOVER (насколько я помню) и подождать ответа от сервера (или нескольких серверов). Если ответ придёт - DHCP-сервер работает.

Deleted ()
Ответ на: комментарий от braboar

а вот с этого места поподробнее, пожалуйста.

NetworkManager виден как org.freedesktop.NetworkManager. Сначала в /org/freedesktop/NetworkManager вызываем org.freedesktop.NetworkManager.GetDevices, получаем там список путей, соответствующих устройствам. Для каждого из этих путей вызываем org.freedesktop.NetworkManager.Device.Interface, находим, какому устройству соответствует нужный интерфейс. На нужном устройстве вызываем org.freedesktop.NetworkManager.Device.Dhcp4Config, получаем путь. Если это /, тогда DHCP не использовался, иначе на этом пути можно вызвать org.freedesktop.NetworkManager.DHCP4Config.Options и получить словарь с конфигурацией DHCP.

Это был вариант, если нужно проверить, использовался ли DHCP для конкретного интерфейса. Также можно раскрутить это через org.freedesktop.NetworkManager.ActiveConnections. Полное описание API здесь: http://projects.gnome.org/NetworkManager/developers/api/09/spec.html

Но если таки нужно определить наличие DHCP-сервера, а не факт его использования клиентом, но нужно посылать DHCPDISCOVER.

gentoo_root ★★★★★ ()
Ответ на: комментарий от braboar

Кажется, это то, что мне надо.

А вот я не уверен. Если таки нужно это:

На клиентской машине: определить, работает ли в локальной сети DHCP-сервер

то нужно посылать DHCPDISCOVER и ждать ответа.

Если же сгодится и это:

На клиентской машине: определить, получены ли настройки по DHCP, или заданы локально.

тогда действительно можно опрашивать NetworkManager.

gentoo_root ★★★★★ ()
Ответ на: комментарий от gentoo_root

Я не говорил, что уверен. Там ведь «кажется», не?

А за помощь - спасибо. Я думаю, что смогу обойтись вариантом опроса NetworkManager.

braboar ()
Ответ на: комментарий от braboar

думаю, что смогу обойтись вариантом опроса NetworkManager.

Не рекомендовал бы зацикливаться на NetworkManager'е, так как:
во-первых, существуют и другие менеджеры сети (например, wvdial),
во-вторых, сеть может работать и без менеджеров (в дебиане, например, может задаваться через /etc/network/interfaces).

Ну и в-третьих, даже если на этой машине стоит NetworkManager и настройки заданы вручную, в сети могут быть работоспособные dhcp-сервера.

Novator ★★★★ ()
Ответ на: комментарий от Novator

Согласен

Ибо уже по граблям прошел. NetworkManager работает, а файла сервиса нет. Придется искать другие пути. Кстати, там где надо определяться, в /etc/network/interfaces практически пусто. С чем еще посоветуете повозиться?

braboar ()
Ответ на: Согласен от braboar

Почтай для начала:
http://ru.wikipedia.org/wiki/DHCP#.D0.9E.D0.B1.D0.BD.D0.B0.D1.80.D1.83.D0.B6....

Погуглируй по слову DHCPDISCOVER как и советовали.

Т.е. тебе нужно посылать в сеть по протоколу UDP специально сформированный широковещательный запрос. И слушать ответы на 67/68 портах UDP.

Возможно, тебе придется изучить работу с udp-пакетами, а также формат запросов. А может быть тебе повезет, и найдешь готовую C++ библиотеку для работы с DHCP. Ищи ;-)

Novator ★★★★ ()
Ответ на: комментарий от Novator

Спасибо.

Читал, читаю и буду читать. Только давайте посмотрим на задачу с другой стороны. В /etc/network/interfaces нет адреса, заданного явно. NeworkManager работает, в сети есть dhcp-сервер. Можно ли узнать (и как), какой ip у машины? Пропала связь, через некоторое время восстановилась, какой адрес получен?

braboar ()
Ответ на: Спасибо. от braboar

В /etc/network/interfaces нет адреса, заданного явно

Эммм... Почему-то ты всё время говоришь о файлах и о службах.
Мне кажется тебе нужно делать упор на системные функции из системных библиотек.

Я не писал на C++, но уверен, что из «приплюснутых ссей» есть вызов сетевых системных функций, в том числе и определение поднятых на данный момент интерфейсов и ip-адресов.

Работай через системные функции.

p.s. Сам пишу на Ruby, тут всё проще :)

Novator ★★★★ ()
Ответ на: комментарий от Deleted

А ещё наличие/отсутствие DHCP никак не коррелирует с «постоянством» IP-адреса.

вообще-то коррелирует - если DHCP есть, то клиент от него получает IP, а если его нет, то использует свой IP из своих настроек. Конечно IRL возможны варианты.

drBatty ★★ ()
Ответ на: комментарий от annulen

Которого вполне может не быть

Вот в этом и есть одна из проблем линукса — существует куча велосипедов, делающих одно и то же и конфликтующих друг с другом, и на компьютере пользователя запущен абсолютно произвольный набор программ, одинаковое будет только ядро. Как новой программе взаимодействовать с этим всем зоопарком, непонятно, потому что недостаточно стандартизированы уровни абстракции. Было бы гораздо лучше, если бы такие уровни абстракции существовали. Например, где-то был бы описан стандарт, каким должен быть API, через который настраивается сеть. Тогда программы, которым необходимо получить или изменить какие-либо настройки, связанные с сетью, использовали бы только этот API, а программа, делающая настройки сети, вызывая методы ядра, предоставляла бы этот API, и было бы гораздо проще разрабатывать программы. Но сейчас таких стандартов нет, есть только попытки создания подобных API (одна их этих попыток — NetworkManager, пример конкурирующих разных API для одной цели — logind и ConsoleKit). Конечно, есть небольшое число стандартизированных вещей (например, стандарты xdg и freedesktop), но они необязательны и их недостаточно. И одна из причин этого — свободное ПО, для которого очень трудно сделать такие стандарты, потому что тот, кто не захочет им подчиняться, может форкнуть проект и забить на эти стандарты (пример этому — два недавно появившихся форка udev), и это очень сильно тормозит развитие линукса.

gentoo_root ★★★★★ ()
Ответ на: комментарий от drBatty

если DHCP есть, то клиент от него получает IP

Клиент не обязан получать адрес от DHCP, даже если в сети работает DHCP-сервер.

а если его нет, то использует свой IP из своих настроек

Динамический IPv4-адрес можно получить и без DHCP, например link local 169.254.0.0/16.

И, к тому же, адреса, выдаваемые по DHCP, не обязаны быть динамическими, они могут быть и жёстко завязаны на конкретных клиентов.

Deleted ()
Ответ на: комментарий от Novator

зачем формировать DHCP запрос самостоятельно, если можно вызвать уже имеющуюся в системе программу-DHCP-клиент?

Harald ★★★★★ ()
Ответ на: Доктор, скажите мне... от braboar

где этот самый DHCP хранит полученный от сервера ip?

зачем его где-то хранить? Он устанавливается при настройке интерфейса, у меня это делает /sbin/dhcpcd, ну а посмотреть его можно ip a или ifconfig.

drBatty ★★ ()
Ответ на: комментарий от Deleted

Клиент не обязан получать адрес от DHCP, даже если в сети работает DHCP-сервер.

не обязан. А кто говорит, что обязан? Но на кой хрен ставить в сети DHCP сервер(ы), а потом статически прописывать IP?

Динамический IPv4-адрес можно получить и без DHCP, например link local 169.254.0.0/16.

и что?

И, к тому же, адреса, выдаваемые по DHCP, не обязаны быть динамическими, они могут быть и жёстко завязаны на конкретных клиентов.

я в курсе. Могут и не жёстко. dhcpd можно настроить так, что-бы он выдавал клиенту последний его IP, если это возможно. На практике это приводит к тому, что IP очень редко меняется, и почти постоянный (постоянный, если клиентов не очень много, и IP адресов всегда и на всех хватает. Если клиентов больше 64K, а пул в 64К, то очевидно, возникнет момент, когда 65536у клиенту придётся выдать чужой IP из пула.) К сожалению у нас ISP с эти не заморачиваются.

drBatty ★★ ()

Озадачили меня тут: надо проверить из программы (C++) работает ли dhcp или сеть сконфигурирована с постоянным адресом.

кстати, а как же /var/log/message?

Nov 23 21:43:03 ksu dhcpcd[1507]: eth0: rebinding lease of 192.168.1.5
Nov 23 21:43:03 ksu dhcpcd[1507]: eth0: acknowledged 192.168.1.5 from 192.168.1.1
Nov 23 21:43:03 ksu dhcpcd[1507]: eth0: checking for 192.168.1.5
Nov 23 21:43:08 ksu dhcpcd[1507]: eth0: leased 192.168.1.5 for 604800 seconds
Nov 23 21:43:08 ksu dhcpcd[1507]: forked to background, child pid 1547
очевидно, у меня dhcp есть. и оно работает (адрес кстати жёстко привязан к MAC, и никогда не меняется)

drBatty ★★ ()
Ответ на: комментарий от drBatty

Но на кой хрен ставить в сети DHCP сервер(ы), а потом статически прописывать IP?

Совершенно типичная ситуация: DHCP только для клиентских машин.

Deleted ()
Ответ на: комментарий от drBatty

кстати, а как же /var/log/message?

Ага, тут ещё один вариант забыли предложить: вывести окошко «У вас сеть настроена по DHCP?» с кнопками Да/Нет 8).

А вообще, автор судя по всему сам не знает что он хочет и зачем ему это надо. То ему надо определить, что адрес получен по DHCP, то ему надо определить, что в сети работает DHCP-сервер...

Deleted ()
Ответ на: комментарий от Deleted

Совершенно типичная ситуация: DHCP только для клиентских машин.

в смысле сервера имеют статический IP? А зачем им определять, какой у них IP? Модно подумать, они этого не знают... Если не знают, пусть смотрят в своих /etc/network/interfaces (для деба). или в /etc/rc.d/rc.inet1.conf для слаки, а для RHEL я не помню.

drBatty ★★ ()
Ответ на: комментарий от Deleted

А вообще, автор судя по всему сам не знает что он хочет и зачем ему это надо.

+1

drBatty ★★ ()
Ответ на: комментарий от Deleted

Ты точно разделом не ошибся?

откуда я знаю, может автор пишет свой NetworkManager, а может утилиту для сервера? ИМХО серверная утилита должна игнорить DHCP сервера, зачем они ей?

drBatty ★★ ()
Ответ на: комментарий от braboar

Работай через системные функции.

Какие?

А поискать по словам «linux net system api» слабо?
Вот, например:
http://www.linuxhowtos.org/C_C /socket.htm
http://www.yolinux.com/TUTORIALS/Sockets.html
http://www.kernel.org/doc/man-pages/online/pages/man7/socket.7.html
http://www.tenouk.com/cnlinuxsockettutorials.html
http://www.tenouk.com/Module39b.html

есть вызов сетевых системных функций

Разумеется, есть.

После того как ознакомишься с системными функциями, кумекаешь дальше.
Для начала неплохо бы определить, какие вообще интерфейсы и айпишники подняты на этом компе. Тут есть несколько способов:
http://stackoverflow.com/questions/2021549/how-do-i-output-my-hosts-ip-addres...
http://www.binarytides.com/get-local-ip-in-c-on-linux/
http://stackoverflow.com/questions/579783/how-to-detect-ip-address-change-pro...
http://www.techpulp.com/blog/2008/07/find-local-ip-address-and-local-port-num...

Например код из первой ссылки:

#include <stdio.h>
#include <stropts.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <linux/netdevice.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>

int print_addresses(const int domain)
{
  int s;
  struct ifconf ifconf;
  struct ifreq ifr[50];
  int ifs;
  int i;

  s = socket(domain, SOCK_STREAM, 0);
  if (s < 0) {
    perror("socket");
    return 0;
  }

  ifconf.ifc_buf = (char *) ifr;
  ifconf.ifc_len = sizeof ifr;

  if (ioctl(s, SIOCGIFCONF, &ifconf) == -1) {
    perror("ioctl");
    return 0;
  }

  ifs = ifconf.ifc_len / sizeof(ifr[0]);
  printf("interfaces = %d:\n", ifs);
  for (i = 0; i < ifs; i++) {
    char ip[INET_ADDRSTRLEN];
    struct sockaddr_in *s_in = (struct sockaddr_in *) &ifr[i].ifr_addr;

    if (!inet_ntop(domain, &s_in->sin_addr, ip, sizeof(ip))) {
      perror("inet_ntop");
      return 0;
    }

    printf("%s - %s\n", ifr[i].ifr_name, ip);
  }

  close(s);

  return 1;
}

int main(int argc, char *argv[])
{
  int domains[] = { AF_INET, AF_INET6 };
  int i;

  for (i = 0; i < sizeof(domains) / sizeof(domains[0]); i++)
    if (!print_addresses(domains[i]))
      return 1;

  return 0;
}

Т.е. как минимум три системных способа:
1) через библиотеку sys/socket.h
2) через вызов /sbin/ifconfig
3) через просмотр /proc/net/route

После того как определил интерфейсы, определяешь локальные по началу «192.168.x.x» или «10.x.x.x».
В эти подсети шлёш широковещательные udp-запросы.
(Почитай теорию о широковещательных запросах здесь:
http://www.tenouk.com/Module41c.html)

Но запросы не простые, а DHCPDISCOVER:
http://social.msdn.microsoft.com/Forums/en-SG/wsk/thread/7220b8fb-f9cf-4840-b...

// create the dhcp socket
struct sockaddr_in myname;
struct ifreq interface;
int sock;
int flag = 1;
/* Set up the address we're going to bind to. */
bzero(&myname,sizeof(myname));
myname.sin_family = AF_INET;
myname.sin_port = htons(DHCP_CLIENT_PORT);
myname.sin_addr.s_addr = INADDR_ANY; /* listen on any address */
bzero(&myname.sin_zero,sizeof(myname.sin_zero));
/* create a socket for DHCP communications */
sock=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sock < 0) {
printf("\e[31mClient: Could not create socket!\n\e[0m");
return;
}
struct ifreq ifr;
unsigned char client_hardware_address[MAX_DHCP_CHADDR_LENGTH]="";
strncpy((char *)&ifr.ifr_name,m_devName.toAscii().data(),sizeof(ifr.ifr_name));
/* try and grab hardware address of requested interface */
if(ioctl(sock,SIOCGIFHWADDR,&ifr)<0) {
printf("\e[31mClient: Could not get hardware address of interface '%s'\n
e[0m",m_devName.toAscii().data());
return;
}
memcpy(&client_hardware_address[0],&ifr.ifr_hwaddr.sa_data,6);
/* set the reuse address flag so we don't get errors when restarting */
flag = 1;
if(setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(char *)&flag,sizeof(flag))<0){
printf("\e[31mClient: Could not set reuse address option on DHCP socket!\n\e[0m");
return;
}
/* set the broadcast option - we need this to listen to DHCP broadcast messages */
if(setsockopt(sock,SOL_SOCKET,SO_BROADCAST,(char *)&flag,sizeof flag)<0){
printf("\e[31mClient: Could not set broadcast option on DHCP socket!\n\e[0m");
return;
}
/* bind socket to interface */
strncpy(interface.ifr_ifrn.ifrn_name,m_devName.toAscii().data(),IFNAMSIZ);
if(setsockopt(sock,SOL_SOCKET,SO_BINDTODEVICE,(char *)&interface,sizeof(interface))<0){
printf("\e[31mClient: Could not bind socket to interface %s. Run as root...\n\e[0m", m_devName.toAscii().data());
return;
}
/* bind the socket */
if(bind(sock,(struct sockaddr *)&myname,sizeof(myname))<0){
printf("\e[31mClient: Could not bind to DHCP socket (port %d)! Run as root...\n\e[0m",DHCP_CLIENT_PORT);
return;
}

// ---
// sends a DHCPDISCOVER broadcast message in an attempt to find DHCP servers
dhcp_packet discover_packet;
struct sockaddr_in sockaddr_broadcast;
/* clear the packet data structure */
bzero(&discover_packet,sizeof(discover_packet));
/* boot request flag (backward compatible with BOOTP servers) */
discover_packet.op = BOOTREQUEST;
/* hardware address type */
discover_packet.htype = ETHERNET_HARDWARE_ADDRESS;
/* length of our hardware address */
discover_packet.hlen = ETHERNET_HARDWARE_ADDRESS_LENGTH;
discover_packet.hops = 0;
/* transaction id is supposed to be random */
srand(time(NULL));
u_int32_t packet_xid = random();
discover_packet.xid = htonl(packet_xid);
/*discover_packet.secs=htons(65535);*/
discover_packet.secs = 0xFF;
/* tell server it should broadcast its response */
discover_packet.flags = htons(DHCP_BROADCAST_FLAG);
/* our hardware address */
memcpy(discover_packet.chaddr,client_hardware_address,ETHERNET_HARDWARE_ADDRESS_LENGTH);
/* first four bytes of options field is magic cookie (as per RFC 2132) */
discover_packet.options[0]='\x63';
discover_packet.options[1]='\x82';
discover_packet.options[2]='\x53';
discover_packet.options[3]='\x63';
/* DHCP message type is embedded in options field */
discover_packet.options[4] = DHCP_OPTION_MESSAGE_TYPE; /* DHCP message type option identifier */
discover_packet.options[5] = '\x01'; /* DHCP message option length in bytes */
discover_packet.options[6] = DHCPDISCOVER;
/* send the DHCPDISCOVER packet to broadcast address */
sockaddr_broadcast.sin_family = AF_INET;
sockaddr_broadcast.sin_port = htons(DHCP_SERVER_PORT);
sockaddr_broadcast.sin_addr.s_addr = INADDR_BROADCAST;
/* send the DHCPDISCOVER packet out */
send_dhcp_packet(&discover_packet,sizeof(discover_packet),sock,&sockaddr_broadcast);

Потом ловишь ответы на твою широковещалку от dhcp-серверов (если они есть в сетях) и составляешь список dhcp-серверов:
http://cboard.cprogramming.com/c-programming/124445-dhcp-discover.html
http://stackoverflow.com/questions/603577/how-to-tell-which-interface-the-soc...

После чего понимаешь, есть dhcp-сервера в сети или нет.

Novator ★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.