Как я могу получить имя/индекс интерфейса, связанный с сокетом TCP?
Автор вопроса: LeeorОтветов: 9
Я пишу TCP-сервер, которому нужно знать, с какого интерфейса приходит каждое соединение. Я не могу использовать адрес/подсеть, чтобы определить, какой интерфейс использовался, поскольку могут существовать интерфейсы с одинаковыми значениями адреса/подсети. Он основан на Linux, и нет необходимости в переносимости кода.
Все, что я смог найти, это функции для получения всех интерфейсов или одного интерфейса по индексу. Я не смог найти способ получить интерфейс, связанный с принятым сокетом TCP.
Любые идеи? Что-то я пропустил?
РЕДАКТИРОВАТЬ: Повторюсь, IP-адреса в моем случае не уникальны. Ни адреса назначения (сам сервер), ни адреса источника (клиенты). Да, это очень экстремальная схема IP.
Единственный правильный ответ здесь, учитывая, что вы ищете код для добавления на свой сервер, - это ответ от User1. Я проверил это, и это работает - вы должны принять это. См. также stackoverflow.com/questions/43659634/ EML 28.04.2017
Используйте getsockname(), чтобы получить IP локального конца соединения TCP. Затем используйте getifaddrs(), чтобы найти соответствующий интерфейс:
struct sockaddr_in addr;
struct ifaddrs* ifaddr;
struct ifaddrs* ifa;
socklen_t addr_len;
addr_len = sizeof (addr);
getsockname(sock_fd, (struct sockaddr*)&addr, &addr_len);
getifaddrs(&ifaddr);
// look which interface contains the wanted IP.
// When found, ifa->ifa_name contains the name of the interface (eth0, eth1, ppp0...)
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)
{
if (ifa->ifa_addr)
{
if (AF_INET == ifa->ifa_addr->sa_family)
{
struct sockaddr_in* inaddr = (struct sockaddr_in*)ifa->ifa_addr;
if (inaddr->sin_addr.s_addr == addr.sin_addr.s_addr)
{
if (ifa->ifa_name)
{
// Found it
}
}
}
}
}
freeifaddrs(ifaddr);
Выше приведен просто грязный пример, необходимы некоторые модификации:
Добавить отсутствующие проверки ошибок
поддержка IPv6
Ответ 2 · рейтинг 4 · Kieron
В общем, вам не нужно знать, через какой интерфейс будут отправляться/получаться пакеты; это работа таблицы маршрутизации ядра. Трудно найти интерфейс для сокета, потому что на самом деле нет прямой связи. Маршрутизация пакетов может меняться в течение времени существования сокета в зависимости от информации о маршрутизации.
Для сокетов дейтаграмм (UDP) вы можете использовать getsockopt(s, IPPROTO_IP, IP_PKTINFO, ...); см. getsockopt(2) и ip(7).
Для потоковых (TCP) сокетов одним из вариантов может быть открытие нескольких прослушивающих сокетов, по одному для каждого интерфейса в системе, и использование setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, ...) для привязки каждого к одному интерфейсу; см. setsockopt(2) и socket(7).
Таблица маршрутизации ядра решает, на какой интерфейс отправить пакет, отсюда и возможность связывания устройств. Беглый просмотр "Linux Socket Programming, Warren W. Gay" говорит о том, что указание интерфейса - это плохо, и что из-за динамики ядра (брандмауэр, переадресация) он более сложен.
Я бы предложил изменить вашу IP-схему таким образом, чтобы IP-информация говорила вам о вашем интерфейсе (ах), просматривая его так же, как это делает ifconfig, в противном случае вы стреляете себе в ногу с точки зрения дизайна.
1) Получите информацию об IP-адресе из сеанса TCP. 2) Найдите, для какого интерфейса (ов) это может быть допустимо.
Однако я буду продолжать искать API ядра. Вам не нужно знать это, абстракция существует по множеству веских причин.
Дополнительная мысль Если подумать над этим, кажется, что если оба интерфейса используют один и тот же IP-адрес, то должна быть разница в маршрутизации диапазонов клиентских адресов (в противном случае использовались бы оба интерфейса). Ваш сервер может проверить таблицу маршрутизации на основе IP-адреса клиента.
Ответ 5 · рейтинг 1 · Wuggy
Я думаю, что использование getsockname() после accept() входящего соединения может быть тем, что вам нужно. Две функции getsockname() и getpeername() получают соответственно локальный и удаленный адреса, к которым привязан сокет. Оба должны быть действительны для полностью подключенного сокета TCP.
Редактировать: хотя это кажется верным для OpenBSD в соответствии с справочной страницей, справочная страница Linux значительно отличается, и поэтому getockname() после accept() в Linux почти наверняка бесполезен. Учит меня пользоваться памятью вместо того, чтобы все проверять. вздыхает
Ответ 6 · JimB
Посмотрите на адрес назначения.
Каждый интерфейс обычно привязан к уникальному адресу. Если несколько интерфейсов связаны друг с другом, вероятно, не имеет значения, какой из них используется.
Единственным исключением из этого является использование ipv6 anycast, но даже в этом случае у вас обычно не будет нескольких интерфейсов на одном хосте с одним и тем же IP-адресом.
Ответ 7 · Kieron
Очевидно, это не то, что я очень глубоко изучил, не говоря уже о том, чтобы попробовать, это может быть что-то для корзины "настолько безумно, что это может сработать" ...
Если это действительно когда-либо будет только для Linux, вы можете написать собственный модуль netfilter, который отслеживает входящие подключения. и отмечает, через какой интерфейс они приходят, и записывает эту информацию куда-нибудь для чтения вашим серверным приложением.
Ответ 8 · Leeor
Предложение Kieron написать модуль netfilter, вероятно, является одним из способов попробовать, но я хотел бы воздержаться от написания моего самого первого модуля ядра для этого решения.
Я придумал еще один вариант использования исходного NAT и перевода исходного порта соединения, чтобы он соответствовал источнику соединения. Я могу назначить диапазоны портов для каждой сети и проверить их на сервере. Единственная проблема заключается в том, что исходный NAT в iptables выполняется в цепочке POSTROUTING, и я не уверен, что он используется для соединений, принимаемых этим хостом, поэтому мне может понадобиться использовать другой сервер.
Здесь нет простых решений, очень жаль, что я не могу получить имя/индекс интерфейса из сокета...
Ответ 9 · Aiden Bell
Я добавляю еще один ответ и потенциальное решение после просмотра источника Wireshark и iftop, которые, похоже, имеют косвенно аналогичную функциональность.
Мне кажется, что вы можете использовать libpcap для обнюхивания интерфейсов. Предполагая, что вы можете идентифицировать какую-то уникальную часть сеанса TCP/IP, вы можете довольно просто отследить его до интерфейса, используя фильтры и отслеживание сеанса.
Никаких модулей ядра (и это хорошо работает с потоками)
http://www.ex-parrot.com/pdw/iftop/ Некоторые простой источник, чтобы заглянуть на www.tcpdump.org/ для libpcap
Я думаю, вы также сможете сопоставлять VLAN, используя его.
Также wireshark может быть полезен для отладки. Надеюсь это поможет! С тех пор это было у меня в голове.