В настоящее время я пытаюсь отправлять пакеты через необработанный сокет в Rust. В настоящее время я использую libc для управления сокетом.
Мне удалось отправить пакет, но отправленный пакет никогда не совпадает с исходным. Вот что происходит, пакет, который я отправляю, такой:
const EXAMPLE: [u8; 44] = [
0x00, 0x04, 0x00, 0x01, 0x00, 0x06, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,/* Pay attention here*/ 0x08, 0x06,
0x00, 0x01, 0x08, 0x00, 0x06, 0x04, 0x00, 0x01, 0x00, 0xd8, 0x61, 0x57, 0x60, 0x36, 0xc0, 0xa8,
0x02, 0x08, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0xa8, 0x02, 0x01,
];
По сути это arp-запрос по адресу 192.168.2.1.
Отправляется такой пакет (согласно Wireshark)
00 04 00 01 00 06 ff ff ff ff ff ff 00 00 11 00 // Notice the extra 0x0011 here?
08 06 00 01 08 00 06 04 00 01 00 d8 61 57 60 36
c0 a8 02 08 ff ff ff ff ff ff c0 a8 02 01
Я знаю, что такое добавление дополнительных байтов. Вот что я делаю:
let sock = unsafe { socket(AF_PACKET, SOCK_RAW, 0x3) }; // Allow all protocols
let addr = sockaddr_ll {
sll_family: 0x3,
sll_protocol: 0x11,
sll_ifindex: 2,
sll_addr: [0; 8],
sll_halen: 0,
sll_hatype: 0,
sll_pkttype: 0,
};
let ret = unsafe { sendto(
sock.as_raw_fd(),
EXAMPLE.as_ptr().cast(),
EXAMPLE.len(),
0,
&addr as *const _ as *const sockaddr,
mem::size_of::<sockaddr_ll>() as u32,
) };
if ret < 0 {
unreachable!() // No an error
}
После некоторого расследования я пришел к выводу, что лишние байты принадлежат sll_protocol.
Я не уверен на 100%, но я заметил, что длина sockaddr_ll составляет 20 байт, а длина sockaddr — 16. Это оставляет
4 байта. Это как раз размер дополнительных байтов. Причина, по которой появляются дополнительные байты, заключается в том, что я использую
тип указателя от sockaddr_ll до sockaddr в результате по какой-то причине вставляются 4 дополнительных байта
пакет. Если это так, то с какой целью mem::size_of::<sockaddr_ll>() это должно было быть обработано правильно?
так как я указываю размер адреса. Кроме того, если мое предположение верно, как мне исправить ошибку?
Весь код вы можете найти здесь: https://github.com/SakPetios/ExtraBytes/tree/main
Я ожидаю увидеть ответ ARP при отправке пакета.
Заранее благодарим вас за любую помощь в этом вопросе.

Ваш код в основном работает для меня. Похоже, вы поменяли местами MAC источника и назначения в своем кадре, поэтому ваш кадр не был широковещательным и, вероятно, был отфильтрован сетевой картой получателя.
MAC-адрес назначения идет первым, поскольку он позволяет коммутатору быстро принять решение о пересылке до того, как будет получен весь кадр (сквозное переключение).
Я изменил полезную нагрузку на более простой шаблон:
const EXAMPLE: [u8; 44] = [
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0xAA, 0xBB, 0x01, 0x02,
0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12,
0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E,
];
Я тоже изменила sll_protocol: 0xCCDD, чтобы оно выпрыгивало, но я никогда не видела этого значения в кадре.
Обнюхиваю другой компьютер, получающий кадр:
# tcpdump -i eth1 -n not ip
08:38:53.644143 01:02:03:04:05:06 > ff:ff:ff:ff:ff:ff, ethertype Unknown (0xaabb), length 60:
0x0000: 0102 0304 0506 0708 090a 0b0c 0d0e 0f10 ................
0x0010: 1112 1314 1516 1718 191a 1b1c 1d1e 0000 ................
0x0020: 0000 0000 0000 0000 0000 0000 0000 ..............
Ethertype (два байта после исходного MAC) соответствует данным в буфере. Мы также видим, что заполнение было добавлено для обеспечения минимального размера кадра Ethernet.
Итак... либо драйвер вашего устройства делает что-то странное с sll_protocol, цитируя man-страницу package(7):
Пакеты SOCK_RAW передаются драйверу устройства и обратно без каких-либо изменений в данных пакета. [...] Этот пакет затем ставится в очередь в неизмененном виде к сетевому драйверу интерфейса, определенного адресом назначения. Некоторые драйверы устройств всегда добавляют другие заголовки.
Или, что более вероятно, вы прослушивали отправителя с помощью Wireshark, и кадр, который вы просматривали, не является кадром Ethernet. Ядро Linux на самом деле не получает обратно свой собственный кадр при отправке, поэтому то, что вы видите, скорее всего, основано на структуре, которая была передана драйверу для отправки.
Аппаратное обеспечение и драйвер Ethernet могут выполнять большую дополнительную работу по изменению кадра, например разгрузку контрольной суммы TCP или, возможно, добавление тега VLAN, то есть вещи, которые ядро не может «прочитать» для анализа.
Другие вещи, которые я заметил в вашем коде: (похоже, это не влияет на результат)
socket(AF_PACKET, SOCK_RAW, 0x3)
Я не уверен, откуда вы взяли значение 0x3, я думаю, оно должно быть равно нулю.
И sll_family: 3, выглядит неправильно. В справочной странице пакета (7) написано:
struct sockaddr_ll {
unsigned short sll_family; /* Always AF_PACKET */
И мои заголовки говорят:
#define AF_AX25 3 /* Amateur Radio AX.25 */
#define AF_PACKET 17 /* Packet family */
Думаю, это проигнорируют, но всё же.
И сделайте себе одолжение и никогда не пропускайте проверку ошибок при работе с C API. Вы не всегда будете получать разумные последующие ошибки.
let sock = unsafe { socket(AF_PACKET, SOCK_RAW, 0) };
if sock < 0 {
eprintln!("socket(): {}", Error::last_os_error());
return;
}
Пакет ARP для Ethernet имеет длину 28 октетов. Кадр Ethernet будет иметь 14 октетов для заголовка Ethernet, 28 октетов для полезной нагрузки (пакет ARP) и четыре октета для FCS. Это составит 46 октетов для полного кадра Ethernet.