Как «ответить» на пакет UDP с помощью IPv6

Я привык писать код с использованием IPv4, где вы можете использовать recvfrom и sendto для ответа отправителю, но с IPv6 все кажется немного сложнее. Хотя я сейчас работаю в Linux, этот вопрос больше касается IPv6 и локальных адресов в целом.

Компьютер A: fe80::eabf:bbff:cae0:8d3e Компьютер Б: fe80::da3a:ddff:fee3:e257

Чтобы пропинговать компьютер A с компьютера B, я могу:

ping fe80::eabf:bbff:cae0:8d3e%eth0

или отправлять пакеты на компьютер А, я могу:

echo "xyz" | nc fe80::eabf:bbff:cae0:8d3e%eth0 5353 -u -w 1

На компьютере А я могу выполнить:

    struct sockaddr_in6 sin6 = {
        .sin6_family = AF_INET6,
        .sin6_addr = IN6ADDR_ANY_INIT,
        .sin6_port = htons( MDNS_PORT )
    };

    sd6 = socket( AF_INET6, SOCK_DGRAM, 0 );

    if ( bind( sd6, (struct sockaddr *)&sin6, sizeof(sin6) ) == -1 ); // Checks (didn't fail)

...

    struct sockaddr sender = { 0 };
    socklen_t sl = sizeof( sender );
    int r = recvfrom( sock, buffer, sizeof(buffer), 0, (struct sockaddr*) &sender, &sl );
    printf( "%d / %d\n", r, sender.sin6_family );
    for( i = 0; i < sl; i++ )
        printf( "%02x ", ((uint8_t*)&sender)[i] );
    printf( "\n" );
    sendto( sock, "hello", 5, 0, &sender, sl );

И при отправке строки nc сверху я получаю следующее:

4 / 10
0a 00 a9 89 00 00 00 00 fe 80 00 00 00 00 00 00 da 3a dd ff fe e3 e2 57 02 00 00 00 

В этом есть смысл: это адрес компьютера B. Но во многих случаях пакет так и не передается. Я думаю, это потому, что иногда, если я делаю холодный пинг компьютера B с компьютера A, пинги тоже никогда не проходят.

Без сопоставления, определяющего интерфейс, кажется, что IPv6 не всегда может быть разрешен.

Как мне сделать так, чтобы мои ответы на пакеты IPv6 возвращались через интерфейс, откуда они пришли?

Прежде чем зайти слишком далеко по пути recvmsg, похоже, это не работает для IPv6 так же, как для IPv4, что касается IPv6, при использовании recvmsg нет управляющей полезной нагрузки и, следовательно, нет возможности по-настоящему ответить sendto -- РЕДАКТИРОВАТЬ : Есть способ - использовать setsockopt с IPPROTO_IPV6, IPV6_RECVPKTINFO

Есть идеи? Предложения?

Как ты привязал розетку?

user207421 03.06.2024 05:45

Я только что отредактировал вопрос, чтобы добавить эту информацию, @user207421.

Charles Lohr 03.06.2024 05:48

Кажется, это работает гораздо надежнее, если я использую sockaddr_in6 для Recvfrom вместо sockaddr. Я сообщу, если это не удастся вовремя. Если это кажется надежным, я думаю, это и есть ответ на вопрос. Похоже, sin6_scope_id имеет значение! Я заметил, что оно правильно заполняется, когда приходит сообщение с удаленного адреса!

Charles Lohr 03.06.2024 06:09

Ну, вам обязательно стоит использовать sockaddr_in6. Вы не можете получить литр из пинты горшка.

user207421 03.06.2024 08:47

Не используйте адресацию Link-Local, которая относится к одной и той же сети для каждого интерфейса. Используйте что-то вроде ULA или глобальной адресации, которая будет однозначно идентифицировать источник. «Адреса Link-Local предназначены для использования для адресации по одному каналу в таких целях, как автоматическая настройка адреса, обнаружение соседей или когда маршрутизаторы отсутствуют». Если вы хотите использовать обычную сеть, используйте что-то отличное от локальной адресации.

Ron Maupin 03.06.2024 15:52

@RonMaupin: Судя по использованию MDNS_PORT в сообщении ОП, они на самом деле пишут код «для таких целей, как автоматическая настройка адреса или обнаружение соседей» - под что подпадает mDNS со стандартным использованием локального IPv6.

grawity_u1686 04.06.2024 08:09

mDNS должен использовать многоадресную рассылку, отсюда и буква «m» в mDNS.

Ron Maupin 04.06.2024 13:08

Да, но если установлен флаг UNICAST-RESPONSE, вы должны возвращать ответы непосредственно отправителю.

Charles Lohr 04.06.2024 19:48
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
8
58
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Если вы объявите переменную как struct sockaddr, то она будет меньше хранящейся в ней sockaddr_in6 — в моей системе она на целых 12 байт меньше — что приводит к переполнению буфера каждый раз, когда recvfrom() пытается поместить туда адрес inet6. Либо recvfrom() перезаписывает другую переменную за пределами структуры (возможно, он помещает мусор в переменную 'sl' при сохранении адреса), либо наоборот (если recvfrom() обновляет &sl новым размером, он может перезаписать часть адреса) .

Общий тип, подходящий всем, который вам нужен, будет struct sockaddr_storage.

struct sockaddr мал, потому что он предшествует IPv6, а увеличение размера общедоступного типа в Linux не работает, поэтому он полезен только при использовании для приведения указателей. (Кстати, многие проекты в конечном итоге изобретают union sockaddr_any или что-то подобное, как более удобный способ избежать приведения типов.)

(Да, sin6_scope_id имеет значение, но recvfrom() всегда заполняет его — в любом случае он думает, что заполняет sockaddr_in6.)

Я почти уверен, что это правильно. Если у меня больше не возникнет проблем, я скоро отмечу этот ответ как правильный.

Charles Lohr 03.06.2024 07:58

Да, я могу подтвердить, что именно это и происходило. Спасибо за пояснение!

Charles Lohr 03.06.2024 20:17

Другие вопросы по теме