Всем привет, мы снова встретились, я ваш друг Цюаньчжаньцзюнь.
Эта статья разделена на 3 части
1: Формат сообщения
2: Причины
Глава 3. Как работать со стеком протоколов Linux
4: Как получить прикладной уровень
1:
Сообщение выглядит следующим образом: 10.30.13.1 отправил UDP-сообщение на порт 80 с адреса 10.30.16.10. Порт 80 фактически слушает TCP.
Сервер ответил сообщением о том, что порт ICMP недоступен. Часть данных ICMP представляет собой запрос данных на уровне IP UDP и выше.
2: Причина
Первая причина заключается в том, что на порту, соответствующем серверу, получающему сообщение UDP, не включен UDP-сервер. Обратите внимание, что приведенное здесь описание не означает, что порт не открыл службу, а означает, что служба UDP не включена. Если служба TCP включена, порт недоступен, все равно будет возвращен.
3: Обработка UDP ядром Linux:
(1):Когда сервер получает UDP-запрос:
Прежде всего, как сервер, когда пакет маршрутизируется и IP-адрес назначения отправляется на локальный компьютер, после решения сетевого фильтра:
Вызовите ip_local_deliver_finish, который вызывает различные функции интерфейса уровня 4 для обработки в соответствии с типом протокола в заголовке IP (TCP/UDP/ICMP/...). Итак, как упоминалось ранее, даже если служба TCP включена, если хэш сокета, созданного сервером, не соответствует хешу сокета, найденного UDP, порт будет недоступен.
Для udp обработчиком является udp_rcv, который напрямую вызывает __udp4_lib_rcv для поиска соответствующего sock.
Если sk не существует, если (sk != NULL), ответьте: пункт назначения icmp недоступен (это процесс, когда у сервера нет соответствующего порта для приема UDP). Функция очень проста.
Поэтому, будучи сервером, если он получает сообщение, порт назначения которого не отслеживается, он напрямую ответит, что порт недоступен.
Итак, как клиенту справиться с сообщением «порт недоступен», на которое отвечает сервер?
Исходная идея была очень простой, я думал, что разные протоколы не будут мешать друг другу, то есть TCP и UDP не будут мешать напрямую.
Не говоря уже об этом невзрачном icmp? Тогда я подумал неправильно.
(2)Получение ответа ICMP о недостижимости порта в качестве клиента:
В качестве клиента сообщение о недостижимости порта поступает в ip_local_deliver_finish, который вызывает для обработки функцию icmp_rcv. (На самом деле, это также причина, по которой я думал, что клиентский udp не будет реагировать на данные о недостижимости порта, потому что процесс обработки udp — udp_rcv).
На самом деле, самое важное в функции icmp_rcv — это этоназывается:icmp_pointers[icmph->type].handler(skb);
handler = icmp_unreach
Последний шаг функции icmp_unreach — это ее последний шаг:
Это очень похоже на ip_local_deliver_finish?
Это очень похоже,Просто ip_local_deliver_finish,называетсяipprot->handler,И здесьназываетсяipprot->err_handler
Для udp err_handler = udp_err = __udp4_lib_err
В этой функции приложение будет отвечать только в том случае, если оно введет следующий процесс:
__udp4_lib_errсначала на основеskb->dataсерединаdipиsip,Найти розетку,skb->dataдаicmpнагрузка
Поэтому сначала вызовите __udp4_lib_lookup, чтобы найти сокет. При передаче параметров необходимо поменять местами sip и dig.
__udp4_lib_err:
Предварительные условиядаinet->recverrнеправильный0,илиinet->recverrдля0нодаudpвTCP_ESTABLISHEDсостояние。
В противном случае приложение никогда не получит данных о том, что порт недоступен, и будет просто ждать истечения времени ожидания чтения. Итак, чтобы получить ситуацию, когда порт udp недоступен
Есть 2 метода:
(1):
int val = 1;
setsockopt(fd, IPPROTO_IP, IP_RECVERR , &val,sizeof(int));
(2):
Выполните операцию подключения к udp и измените sendto на отправку.
4:
Исходная программа для udp, чтобы узнать, что порт недоступен (метод 1: установить параметры сокета; метод 2: подключить UDP)
Обратите внимание, что в случае блокировки Recvfrom заблокируется, даже если получено сообщение о недостижимости порта, он заблокируется. Но после метода 1 и метода 2 Recvfrom вернет значение, возвращаемое значение -1, а затем определит, является ли errno ECONNREFUSED, чтобы определить, получено ли сообщение о недостижимости порта.
#include <stdio.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <string.h>
#include <errno.h>
unsigned char revc_buf[1024];
int main()
{
int fd,ret,recv_len,size=1024;
struct sockaddr_in server_addr,addr;
int val = 1;
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr("192.168.2.254");
server_addr.sin_port = htons(77);
fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(fd < 0)
{
perror("socket fail ");
return -1;
}
printf("socket sucess\n");
//Способ 1
#if 1
setsockopt(fd, IPPROTO_IP, IP_RECVERR , &val,sizeof(int));
if(sendto(fd, "nihao", strlen("nihao"), 0, (const struct sockaddr *)&(server_addr), sizeof(struct sockaddr_in))<0)
{
perror("sendto fail ");
return -1;
}
printf("sendto sucess\n");
recv_len = recvfrom(fd, revc_buf, sizeof(revc_buf), 0, (struct sockaddr *)&addr, (int *)&size);
if (recv_len == -1)
{
if (errno == ECONNREFUSED)
{
printf("Recv port unreachable\n");
}
}
//Метод 2
#elif 0
ret = connect(fd, (const struct sockaddr *) &(server_addr), sizeof (struct sockaddr_in));
if(ret < 0)
{
printf("connect fail\n");
return -1;
}
ret = send(fd, "ni hao", strlen("nihao"),0);
if(ret < 0)
{
printf("write fail\n");
return -1;
}
ret = recvfrom(fd, revc_buf, sizeof(revc_buf), 0, (struct sockaddr *)&addr, (int *)&size);
if (ret == -1) {
if (errno == ECONNREFUSED)
{
printf("Recv port unreachable\n");
}
}
#endif
close(fd);
return 0;
}
если вам полезно,Пожалуйста, дайте мне один юань:http://www.mrpre.com/
Издатель: Лидер стека программистов полного стека, укажите источник для перепечатки: https://javaforall.cn/157911.html Исходная ссылка: https://javaforall.cn