LINUX.ORG.RU

Отправка днных с указанного сетевого интерфейса

 


0

2

Пишу простой сетевой клиент для отправки tcp по книге Стивенса. И у меня возник вопрос. Как указать сетевой интерфейс, через который должна происходить отправка данных? (у меня в системе несколько сетевых карт eth0 eth1 eth2 eth3)

Ответ на: комментарий от slovazap

bind не поможет, пока можно сделать так:

route add 192.168.142.32 dev eth42

На уровне ip даже смысла в словах «привязать к интерфейсу» нет. В случае raw сокетов конечно же можно указать ethernet или другой поддерживаемый физический адрес но только при отправке конкретного пакета, и маршрутизация одна фигня будет иметь место быть, только в другой таблице.

pon4ik ★★★★★ ()

Я тебе открою ещё одну тайну - у каждого интерфейса может быть более одного IP адреса (равно как и ноль) и они даже не обязаны быть в одной подсети.

ip addr add 192.168.1.1 lo
pon4ik ★★★★★ ()
Ответ на: комментарий от gib85159

Надеюсь осталось понятно, что в контексте твоей текущей задачи это не нужно и решается добавлением маршрутов(что конечно тоже можно сделать из кода, но непонятно - зачем). Если нет сначала подтяни своё понимание на тему уровней протоколов пока не станет очевидным странность изначального вопроса.

pon4ik ★★★★★ ()

Я бы сказал что вариантов несколько.

0 Общие моменты, что-то типа предупреждения. Мы понимаем, что работаем от root. Мы понимаем что в данном случае скорее всего пакеты RAW (сырые, самодельные), что так же тербует прав root. В случае raw-пакетов мы помним что практически вся инфраструктура ядра Linux для TCP/IP идёт лесом. Т.е. файерволл здесь работать не будет, т.к. raw означает для системы что пакет полностью обрабатывается приложением самостоятельно.

Вариант 1. Если нам просто надо отправить самосторятельно скрафченный пакет, то я бы предложил использовать libnet. Библиотека в принципе старенькая, но с другой стороны в ethernet тоже ничего новенького.

Как-то вот так:

libnet_t *l; /* Контекст либы. */

/* device это как раз char *device, т.е., имя интерфейса. 
 * Так же здесь я использовал LIBNET_LINK_ADV, что означает 
 * перевод интерфейса в adv. mode. 
 * Возможно что лучше будет использовать либо LIBNET_RAW4 или
 * LIBNET_RAW6 для отдельных случаев. */
l = libnet_init(LIBNET_LINK_ADV, device, error_information); 

Неплохая стартовая точка для разборок с данной либой. Пакеты там при её помощи крафтить тоже можно. В общем, рекомендую. И вот тоже неплохая дока по теме.

Вариант 2. Если у нас есть уже перехваченное TCP соединение, то скорее всего бы использовали libpcap. В этом случае как-то так:

int rc = pcap_findalldevs(&alldevs,errbuf);
if(-1 == rc)
{
    fprintf(stderr,"Error in pcap_findalldevs(): %s\n", errbuf);
    exit(1);
}
char *device = NULL;
device = alldevs->name; /* Здесь я беру имя первой карты в системе, но смысл, IMHO, понятен. */

Т.е., тоже в принципе можно, но случай это явно отдельный, т.к. это перехват линка и только потом инжектинг в поток своих пакетов.

Вариант 3. Как-то вот так примерно (проверку возвращаемого значения верните в код – setsockopt() не должен быть < 0). Не всегда срабатывает, если не сработает, то см. вариант 4:

char* device = "eth2"; /* Так, кажется у Вас? */
setsockopt(sfd, SOL_SOCKET, SO_BINDTODEVICE, device, strlen(device));

Вариант 4. Как-то так (почти тоже что и вриант 3, но «страшнее») И здесь проверки возвращаемых значений верните в код (setsockopt() не должен быть < 0):

struct ifreq ifr;
memset(&ifr, 0, sizeof(struct ifreq));
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "eth2");
ioctl(fd, SIOCGIFINDEX, &ifr);

printf("[[%d]]\n", ifr.ifr_ifindex );
setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE,  (void*)&ifr, sizeof(struct ifreq));

Вариантов на выбор несколько. Зависит от того, что Вы собираетесь делать дальше. Если будет дополнительная информация и если будет нужна дополнительная помощь, то возможно я и смогу помочь.

Moisha_Liberman ()
Последнее исправление: Moisha_Liberman (всего исправлений: 2)