Как увидеть заголовки TCP, IP в моем HTTP-прокси?

У меня есть разветвленный HTTP-прокси, реализованный на моем Ubuntu 14.04 x86_64 со следующей схемой (я сообщаю основной код и псевдокод только для того, чтобы показать концепцию):

  1. socketClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  2. bind(socketClient,(struct sockaddr*)&addr, sizeof(addr));
  3. listen(socketClient, 50);
  4. newSocket = accept(socketClient, (struct sockaddr*)&cliAddr, sizeof(cliAddr));
  5. получить запрос от клиента, проанализировать его, чтобы разрешить запрошенное имя хоста в IP-адресе;
  6. fork(), открыть соединение с удаленным сервером и обработать запрос;
  7. дочерний процесс: если это запрос GET, отправьте исходный запрос на сервер, а пока сервер отправляет данные, отправьте данные с сервера клиенту;
  8. дочерний процесс: иначе, если это запрос CONNECT, отправить строку 200 ok клиенту и опросить как дескриптор сокета клиента, так и дескриптор сокета сервера с помощью select(); если я читаю данные из серверного сокета, отправляю эти данные клиенту; иначе, если я прочитаю данные из клиентского сокета, отправлю эти данные на сервер.

Хорошо, что этот прокси работает, плохо, что теперь я должен собирать статистику; это плохо, потому что я работаю на уровне, на котором я не могу получить интересующие меня данные. Меня не волнует полезная нагрузка, мне просто нужно проверить в заголовках IP и TCP нужные мне флаги.

Например, меня интересует:

  • отслеживание соединения;
  • количество отправленных и полученных пакетов.

Что касается первого, я бы проверил в заголовке TCP флаг SYN, SYN/ACK и затем последний ACK; что касается второго, я бы просто добавлял +1 к моему счетчику каждый раз, когда char buffer[1500] заполняется данными, когда я send() или recv() полный пакет.

Я понял, что это неправильно: SOCK_STREAM не имеет понятия пакета, это просто непрерывный поток байтов! char buffer[1500], который я использую в пунктах 7 и 8, имеет полезную статистику, я могу установить его емкость на 4096 байт, но я не могу отслеживать отправленные или полученные TCP-пакеты, потому что TCP имеет сегменты , а не пакеты.

Я также не смог разобрать char buffer[] в поисках флага SYN в заголовке TCP, потому что заголовки IP и TCP удалены из заголовка (из-за уровня, над которым я работаю, указанного с флагом IPPROTO_TCP) и, если я правильно понял, char buffer[] содержит только полезную нагрузку, бесполезную для меня.

Так что, если я работаю на слишком высоком уровне, я должен пойти ниже: однажды я видел простой анализатор сокетов raw, в котором unsigned char buffer[65535] преобразовывался в struct ethhdr, iphdt, tcphdr, и он мог видеть все флаги все заголовки, вся статистика, которая меня интересует!

После радости разочарование: поскольку raw сокеты работают на низком уровне, в них нет некоторых важных для моего прокси понятий; raw сокеты не могут bind, listen и accept; мой прокси прослушивает фиксированный порт, но raw сокеты не знают, что такое порт, он принадлежит уровню TCP, и они bind к указанному интерфейсу с setsockopt.

Итак, если бы я socket(PF_INET, SOCK_RAW, ntohs(ETH_P_ALL)), я мог бы разобрать буфер, где я recv() и send() в 0,7 и 0,8, но я должен использовать recvfrom() и sendto()... но все это звучит довольно грязно, и это включает в себя хороший рефакторинг моего кода.

Как я могу сохранить неповрежденной структуру моего прокси (bind, listen, accept на фиксированный порт и интерфейс) и расширить свое поле зрения для заголовков IP и TCP?


person elmazzun    schedule 29.08.2016    source источник
comment
Я отредактирую вопрос для более подробной информации.   -  person elmazzun    schedule 29.08.2016
comment
Очень маловероятно, что вы вообще получите какой-либо UDP с HTTP-прокси.   -  person Steffen Ullrich    schedule 29.08.2016
comment
Почему вы не хотите использовать специальный сниффер, такой как wireshark? Вы можете прослушивать весь трафик на определенном интерфейсе, а затем фильтровать его по протоколу и номерам портов. Вы также можете написать свой собственный сниффер, используя метод необработанных сокетов: binarytides.com/ код-анализатора пакетов-c-linux   -  person kfx    schedule 29.08.2016
comment
Вы создали требования, которые, кажется, сильно расходятся с дизайном вашего проекта. Вы уверены, что вам нужно знать о конкретном пакетном трафике? Как вы думаете, зачем вам это нужно? Если ваше приложение является единственным трафиком, привязанным к этому интерфейсу, вы можете сопоставить его с локальным интерфейсом и запросить у него эту статистику.   -  person Brian Cain    schedule 29.08.2016
comment
В качестве альтернативы вы можете взломать код ядра, чтобы напечатать или экспортировать все, что вы хотите, при условии, что у вас есть разрешения на запуск пользовательского ядра с вашим приложением (также может быть достаточно загружаемого модуля, зависит от того, что именно вы хотите сделать).   -  person kfx    schedule 29.08.2016
comment
Этот код является частью моей диссертации, я могу использовать некоторые внешние библиотеки, но не полную программу, как Wireshark. Да, @SteffenUllrich, вы правы, UDP не требуется, я думаю, что слишком много включил в проверку заголовка XD   -  person elmazzun    schedule 29.08.2016


Ответы (1)


Я предлагаю открыть сырой сокет, например, в другом потоке вашего приложения. Проанализируйте весь трафик и отфильтруйте соответствующие пакеты по адресам и номерам портов. В основном вы хотите реализовать свой собственный анализатор пакетов:

int sniff()
{
    int sockfd;
    int len;
    int saddr_size;
    struct sockaddr saddr;
    unsigned char buffer[65536];

    sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);
    if (sockfd < 0) {
        perror("socket");
        return -1;
    }
    while (1) {
        saddr_size = sizeof(saddr);
        len = recvfrom(sockfd, buffer, sizeof(buffer), 0, &saddr, &saddr_size);
        if (len < 0) {
            perror("recvfrom");
            close(sockfd);
            return -1;
        }

        // ... do the things you want to do with the packet received here ...
    }
    close(sockfd);
    return 0;
}

Вы также можете привязать этот необработанный сокет к определенному интерфейсу, если знаете, какой интерфейс будет использоваться для трафика прокси. Например, для привязки к «eth0»:

setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, "eth0", 4);

Используйте вызовы функций getpeername() и getsockname(), чтобы найти локальные и удаленные адреса и номера портов ваших TCP-соединений. Вы захотите отфильтровать пакеты по ним.

person kfx    schedule 29.08.2016