How to capture ping's return value in C++
Does anyone know, how to capture ping’s return value in c++? According to this link: ping should return 0 on success, 1 on failure such as unknown host, illegal packet size, etc. and 2 On a unreachable host or network.
In C++ I called ping with the system () , e.g. int ret = system(«ping 192.168.1.5»); .
My problem is, that ret ‘s value is 0 or 1 . It will never 2 ! If I think correctly, this is because, this return value I get, is the system functions return value, not ping’s. So how could i get ping’s return vlaue?
Thanks in advance!
Edit: Right now i use this system(«ping 192.169.1.5 > ping_res.txt»); but i don’t want to work with files (open, and read them), that’s why i want to capture, th return value , if possible 🙂
4 Answers 4
If you are on Windows, it might be better to use IcmpSendEcho2 directly to implement the ping functionality in your application.
A simple solution would be pipe the output of ping to to a file and read it.
system(«ping 192.169.1.5 > ping_res.txt»);
And read ping_res.txt to get the info you need.
From man 3 system on Linux:
RETURN VALUE
The value returned is -1 on error (e.g. fork(2) failed), and the return status of the command otherwise. This latter return status is in the format specified in wait(2).
Then from man 2 wait on Linux:
If status is not NULL, wait() and waitpid() store status information in the int to which it points. This integer can be inspected with the following macros (which take the integer itself as an argument, not a pointer to it, as is done in wait() and wait- pid()!):
WIFEXITED(status)
returns true if the child terminated normally, that is, by calling exit(3) or _exit(2), or by returning from main().WEXITSTATUS(status)
returns the exit status of the child. This consists of the least significant 8 bits of the status argument that the child specified in a call to exit(3) or _exit(2) or as the argument for a return statement in main(). This macro should only be employed if WIFEXITED returned true.
From sys/wait.h on Linux:
From bits/waitstatus.h on Linux:
In other words, you will wan to use these macros if you are using Linux. Are you using HP-UX? I notice you link to information for HP-UX. If so, what does your man 3 system page say?
Also, keep in mind that system invokes «sh -c command» and you will receive the return value of sh:
EXIT STATUS
The following exit values shall be returned:
0 The script to be executed consisted solely of zero or more blank lines or comments, or both.
1-125 A non-interactive shell detected a syntax, redirection, or variable assignment error.
127 A specified command_file could not be found by a non-interactive shell.
Otherwise, the shell shall return the exit status of the last command it invoked or attempted to invoke (see also the exit utility in Special Built-In Utilities).
C как получить результаты ping
Prerequisites: ICMP | Raw Socket | Internet Checksum | DNS Ping is a necessity for debugging the Internet. Ping is a basic Internet tool that allows a user to verify that a particular IP address exists and can accept requests., with other facilities.
Ping sends out ICMP packets by opening a RAW socket, which is separate from TCP and UDP. Since IP does not have any inbuilt mechanism for sending error and control messages. It depends on Internet Control Message Protocol (ICMP) to provide error control. It is used for reporting errors and management queries.
Example of Ubuntu Ping
Working Mechanism The Internet Ping program works much like a sonar echo-location, sending a small packet of information containing an ICMP ECHO_REQUEST to a specified computer, which then sends an ECHO_REPLY packet in return. The packet has a TTL (time-to-live) value determining max number of router hops. If the packet does not reach, then the sender is noted back with the error. Errors are of following types:
- TTL Expired in Transit
- Destination Host Unreachable
- Request Timed Out i.e. no reply
- Unknown Host
Implementation The steps followed by a simple ping program are:
- Take a hostname as input
- Do a DNS lookup
DNS lookup can be done using gethostbyname(). The gethostbyname() function converts a normal human-readable website and returns a structure of type hostent which contains IP address in the form of binary dot notation and also address type.
Пишем ping на Си
Видите это довольное лицо? Это Майк Муусс — автор, наверное самой часто используемой утилиты ping (недаром он на фотке так радуется).
Некоторое время назад мне самому довелось написать ping, но в виде отдельной функции.
И на мой взгляд, для си-программистов, которые только начинают работать с сетью, это очень полезная утилита для самостоятельной разработки. Почему? Потому, что для разработки этой утилиты нужно научится делать, казалось бы, самые примитивные действия — отправлять и принимать пакет.
Давайте сначала вкратце определим требования конкретно для нашей функции ping. Функция нужна нам для проверки целостности и качества соединения между хостами. И для этого нам достаточно отправить ICMP-эхопакет с запросом целевому хосту и получить от нее ответ. Для оценки качества соединения будем использовать время между запросом и получением ответа.
Системные вызовы
Операционная система (в нашем случае Linux) позволяет получать доступ к сетевым устройствам посредством системных вызовов. Для ping мне потребовались следующие вызовы:
- socket — используется для создания сокета
- select — в нашем случае используется для проверки состояния сокета
- sendto — для отправки данных
- recvfrom — для получения данных
- inet_aton — для преобразования ip-aдреса в строковом виде в структуру sockaddr_in
- и другие
Реализация
Функция для получения текущего времени
Для начала напишем вспомогательную функцию для получения текущего времени в миллисекундах.
Данные
Далее нужно определится с тем, какие данные будем отправлять. Объявим структуру, который будет иметь icmp заголовок и поле данных. Эта структура будет служить нашим пакетом.
Напишем функцию для заполнения нашей структуры.
Функция ping
Сигнатура нашей основной функции будет следующим.
На входе мы получаем ip-адрес пингуемого хоста и таймаут ожидания. На выходе результат выполнения и третий аргумент time куда запишем время пинга хоста.
Тут же, с помощью inet_aton , преобразуем ip в строковом виде в структуру sockaddr_in .
Сокет
Далее нам нужно создать сокет путем вызова соответствующей функции.
Директива SOCK_RAW говорит о том, что мы отправляем “сырой” пакет без использования транспортного уровня (UDP/TCP). С помощью директивы IPPROTO_ICMP говорим, что мы будем использовать протокол ICMP. Функция socket() возвращает нам файловый дескриптор, который будем использовать в последующем.
Отправляем пакет
Перед тем как отправить пакет фиксируем время.
И отправляем пакет.
Получаем ответ
Для получения ответа нужно считать принятые данные из сокета. Данные будут записаны в структуру ip_pkt .
Таймауты
Далее на мой взгляд идет самая сложная часть. Делать вызов recvfrom , то есть принимать данные из сети, нам нужно только при наличии данных. Для этого нам поможет системный вызов select , который используется для отслеживания состояния сокета. А именно:
- Я взвожу select на требуемый таймаут, после чего происходит блокировка на этой функции до момента пока не появятся данные или не истечет время
- Если select разблокировался по таймауту, значит время истекло и мы выходим из цикла
- Если select разблокировался из-за появления данных, то вызываем recvfrom и записываем данные в структуру ip_pkt
- Если данные адресованы не нам, взводим select заново, ведь у нас еще осталось время
Запуск
Компилируем, запускаем ping и видим, что все работает.
Стоп! Но почему для запуска требуется sudo? А потому, что мы используем сырые пакеты. Помните директиву SOCK_RAW ?
Тогда почему стандартная утилита ping не требует sudo? Ничего страшного. Пара нехитрых действий и наш ping тоже запускается без sudo.
Если есть вопросы или замечания — пишите! Я обязательно постараюсь ответить. Спасибо!
C++, ping и traceroute
Изучая сетевое программирование и имея в портфолио несколько проектиков на C++, связанных с сетевым программированием, я решил написать что-нубудь, что будет иметь реальное практическое применение.
Первое, что мне пришло в голову — утилита ping.
Ping — утилита для проверки целостности и качества соединений в сетях на основе TCP/IP, а также обиходное наименование самого запроса
Я подумал, что почитав доки: https://www.rfc-editor.org/rfc/rfc792, смогу написать собственную имплементацию.
В принципе, алгоритм прост и понятен: отправляешь пакет и засекаешь время до ответа.
Спустя несколько дней был готов приемлемый вариант ping-а, который есть на Github.
Для понимания работы traceroute необходимо иметь представление о работе ping-а, так что разбор некоторых строк кода не повредит.
В этой строчке мы получаем идентификатор потока нашего ping-a, которому выделено поле в протоколе ICMP.
Далее создается структура ICMP заголовка:
Поля type, code, checksum — обязательные. В пинге мы использовали только echo, структура из 7 — 11 строк, но другие структуры — осколки имплементации ICMP, которые в принципе можно было бы убрать. В дальнейшем те же структуры будут использоваться и в traceroute.
Далее идет функция генерации интернет-чексуммы:
После отправки пакета, нам нужно засечь время, которое пакет шел от нас к цели и обратно:
Строки создания пакета и присваивания его полям значений опущены, так как не содержат критически важной информации. Стоит отметить только, что ICMP выделяет поле для бинарных данных, которые в коде заполнены набором единиц и нулей.
Traceroute
Traceroute — это служебная компьютерная программа, предназначенная для определения маршрутов следования данных в сетях TCP/IP.
Для начала, я проанализировал пакеты оригинального линуксового traceroute Wireshark-ом.
Поскольку отправка «сырых» пакетов требует рут-привилений, traceroute использует UDP, отправляя пакеты с увеличивающися TTL на рандомный порт цели и ждет получения ответа о закрытости порта.
Все же я подумал, что писать на ICMP будет проще, хоть и для запуска нужен будет рут.
Traceroute отправляет эхо-пакеты с увеличивающимся TTL. TTL — Time To Live, время жизни пакета и по дефолту он равен 30. Время жизни пакета уменьшается после прохождения им каждого узла в сети. Допустим, мы хотим найти маршрут до 1.1.1.1:
Если отправить пакет с TTL 1, то он упрется в первый узел (обычно в ваш роутер 192.168.0.1 или в подсеть) и тот вернет ответ: TTL exceeded, что значит «время жизни истекло». Из его ответа можно вытащить ip_src и таким образом узнать IP-адрес первого узла. Взяв TTL 2, можно узнать IP-адрес второго узла и т.д
Меняется тип пакета (8) и каждую итерацию цикла увеличивается TTL.
В следующем коде мы проверяем, от кого пришел ответ — от цели или нет. Если от цели, то прерываем цикл и показываем результаты.